前幾天一個朋友要我寫點關(guān)于數(shù)據(jù)庫編程方面的東西,可一直由于工作比較忙,到現(xiàn)在已經(jīng)一個多星期了,正好煙草的項目由于最終方案的原因而停止了,新的ATM的P端的程序昨天基本已經(jīng)順利調(diào)整完了。相信今天上午是個清閑的上午,就寫點關(guān)于動態(tài)SQL方面的東西吧。
嵌入SQL語言都是靜態(tài)SQL語言,即在編譯時已經(jīng)確定了引用的表和列。主變量不改變表和列信息。我們使用主變量改變查詢參數(shù),但是不能用主變量代替表名或列名。否則,系統(tǒng)報錯。動態(tài)SQL語句就是來解決這個問題。
動態(tài)SQL語句的目的是,不是在編譯時確定SQL的表和列,而是讓程序在運行時提供,并將SQL語句文本傳給DBMS執(zhí)行。靜態(tài)SQL語句在編譯時已經(jīng)生成執(zhí)行計劃。而動態(tài)SQL語句,只有在執(zhí)行時才產(chǎn)生執(zhí)行計劃。動態(tài)SQL語句首先執(zhí)行PREPARE語句要求DBMS分析、確認(rèn)和優(yōu)化語句,并為其生成執(zhí)行計劃。DBMS還設(shè)置SQLCODE以表明語句中發(fā)現(xiàn)的錯誤。當(dāng)程序執(zhí)行完“PREPARE”語句后,就可以用EXECUTE語句執(zhí)行執(zhí)行計劃,并設(shè)置SQLCODE,以表明完成狀態(tài)。
使用動態(tài)SQL,共分成四種方法:
方法 支持的SQL語句 實現(xiàn)方法
1 該語句內(nèi)不包含宿主變量,該語句不是查詢語句 execute immediate
2 該語句內(nèi)包含輸入宿主變量 ,該語句不是查詢語句 prepare和execute
3 包含已知數(shù)目的輸入宿主變量或列的查詢 prepare和fetch
4 包含未知數(shù)目的輸入宿主變量或列的查詢 prepare和fetch,用描述符
按照功能和處理上的劃分,動態(tài)SQL應(yīng)該分成兩類來解釋:動態(tài)修改和動態(tài)查詢。方法1和方法2完成動態(tài)修改。方法3和方法4完成了動態(tài)查詢。
一、動態(tài)修改
方法1和方法2完成動態(tài)修改。對于方法1,表示要執(zhí)行一個完整的T-SQL語句,該語句沒有宿主變量,不是一個查詢語句。因為沒有宿主變量來帶入不同的參數(shù),所以不能通過方法1來重復(fù)執(zhí)行修改語句。具體語法為:
exec sql [at connection_name] execute immediate
{: host_variable | string};
其中,host_variable和string是存放完整T-SQL語句。
例:提示用戶輸入被更新書的條件,然后組合成為一個完整的SQL語句,并執(zhí)行更新。
exec sql begin declare section;
CS_CHAR sqlstring[200];
exec sql end declare section;
char cond[150];
exec sql whenever sqlerror call err_p();
exec sql whenever sqlwarning call warn_p();
strcpy(sqlstring,
"update titles set price=price*1.10 where ");
printf("Enter search condition:");
scanf("%s", cond);
strcat(sqlstring, cond);
exec sql execute immediate :sqlstring;
exec sql commit work;
對于方法2,可以執(zhí)行一個包含輸入宿主變量的動態(tài)修改語句。該方法要使用PREPARE語句和EXECUTE語句。PREPARE語句是動態(tài)SQL語句獨有的語句。其語法為:
PREPARE 語句名 FROM 宿主變量|字符串
該語句接收含有SQL語句串的宿主變量,并把該語句送到DBMS。DBMS編譯語句并生成執(zhí)行計劃。在語句串中包含一個“?”表明參數(shù),當(dāng)執(zhí)行語句時,DBMS需要參數(shù)來替代這些“?”。PREPRARE執(zhí)行的結(jié)果是,DBMS用語句名標(biāo)志準(zhǔn)備后的語句。SQL SERVER編譯后的語句以臨時存儲過程的形式存放在緩沖區(qū)中。語句名類似于游標(biāo)名,是一個SQL標(biāo)識符。在執(zhí)行SQL語句時,EXECUTE語句后面是這個語句名。請看下面這個例子:
EXEC SQL BEGIN DECLARE SECTION;
char prep[] = "INSERT INTO mf_table VALUES(?,?,?)";
char name[30];
char car[30];
double num;
EXEC SQL END DECLARE SECTION;
EXEC SQL PREPARE prep_stat FROM :prep;
while (SQLCODE == 0)
{
strcpy(name, "Elaine");
strcpy(car, "Lamborghini");
num = 4.9;
EXEC SQL EXECUTE prep_stat USING :name, :car, :num;
}
在這個例子中,prep_stat是語句名,prep宿主變量的值是一個INSERT語句,包含了三個參數(shù)(3個“?”)。PREPARE的作用是,DBMS編譯這個語句并生成執(zhí)行計劃,并把語句名標(biāo)志這個準(zhǔn)備后的語句。值得注意的是,PREPARE中的語句名的作用范圍為整個程序,所以不允許在同一個程序中使用相同的語句名在多個PREPARE語句中。
EXECUTE語句是動態(tài)SQL獨有的語句。它的語法如下:
EXECUTE 語句名 USING 宿主變量 | DESCRIPTOR 描述符名
請看上面這個例子中的“EXEC SQL EXECUTE prep_stat USING :name, :car, :num;”語句,它的作用是,請求DBMS執(zhí)行PREPARE語句準(zhǔn)備好的語句。當(dāng)要執(zhí)行的動態(tài)語句中包含一個或多個參數(shù)標(biāo)志時,在EXECUTE語句必須為每一個參數(shù)提供值,如::name、:car和:num。這樣的話,EXECUTE語句用宿主變量值逐一代替準(zhǔn)備語句中的參數(shù)標(biāo)志(“?”),從而,為動態(tài)執(zhí)行語句提供了輸入值。
使用主變量提供值,USING子句中的主變量數(shù)必須同動態(tài)語句中的參數(shù)標(biāo)志數(shù)一致,而且每一個主變量的數(shù)據(jù)類型必須同相應(yīng)參數(shù)所需的數(shù)據(jù)類型相一致。各主變量也可以有一個伴隨主變量的指示符變量。當(dāng)處理EXECUTE語句時,如果指示符變量包含一個負(fù)值,就把NULL值賦予相應(yīng)的參數(shù)標(biāo)志。除了使用主變量為參數(shù)提供值,也可以通過SQLDA提供值(關(guān)于這個,后邊會講到)。
二、動態(tài)游標(biāo)
使用動態(tài)游標(biāo)可以完成方法3。
游標(biāo)分為靜態(tài)游標(biāo)和動態(tài)游標(biāo)兩類。對于靜態(tài)游標(biāo),在定義游標(biāo)時就已經(jīng)確定了完整的SELECT語句。在SELECT語句中可以包含主變量來接收輸入值。當(dāng)執(zhí)行游標(biāo)的OPEN語句時,主變量的值被放入SELECT語句。在OPEN語句中,不用指定主變量,因為在DECLARE CURSOR語句中已經(jīng)放置了主變量。請看下面靜態(tài)游標(biāo)的例子:
EXEC SQL BEGIN DECLARE SECTION;
char szLastName[] = "White";
char szFirstName[30];
EXEC SQL END DECLARE SECTION;
EXEC SQL
DECLARE author_cursor CURSOR FOR
SELECT au_fname FROM authors WHERE au_lname = :szLastName;
EXEC SQL OPEN author_cursor;
EXEC SQL FETCH author_cursor INTO :szFirstName;
動態(tài)游標(biāo)和靜態(tài)游標(biāo)不同。以下是動態(tài)游標(biāo)使用的句法:
1)、聲明游標(biāo):
對于動態(tài)游標(biāo),在DECLARE CURSOR語句中不包含SELECT語句。而是,定義了在PREPARE中的語句名,PREPARE語句規(guī)定與查詢相關(guān)的語句名稱。具體語法為:
exec sql [at connection_name] declare cursor_name
cursor for statement_name;
如:EXEC SQL DECLARE author_cursor CURSOR FOR select_statement;
值得注意的是,聲明動態(tài)游標(biāo)是一個可執(zhí)行語句,應(yīng)該在PREPARE語句后執(zhí)行。
2)、打開游標(biāo)
完整語法為:OPEN 游標(biāo)名 [USING 主變量名 | DESCRIPTOR 描述名]
在動態(tài)游標(biāo)中,OPEN語句的作用是使DBMS定位相關(guān)的游標(biāo)在第一行查詢結(jié)果前。當(dāng)OPEN語句成功執(zhí)行完畢后,游標(biāo)處于打開狀態(tài),并為FETCH語句做準(zhǔn)備。OPEN語句執(zhí)行一條由PREPARE語句預(yù)編譯的語句。如果動態(tài)查詢正文中包含有一個或多個參數(shù)標(biāo)志時,OPEN語句必須為這些參數(shù)提供參數(shù)值。USING子句的作用就是規(guī)定參數(shù)值。可以使用主變量提供參數(shù)值,也可以通過描述名(即SQLDA)提供參數(shù)值。如:EXEC SQL OPEN author_cursor USING :szLastName;。
3)、取一行值
FETCH語法為:FETCH 游標(biāo)名 INTO USING DESCRIPTOR 描述符名。
動態(tài)FETCH語句的作用是,把游標(biāo)移到下一行,并把這一行的各列值送到SQLDA中。注意的是,靜態(tài)FETCH語句的作用是用主變量表接收查詢到的列值。在方法3中,使用的是靜態(tài)FETCH語句獲得值。動態(tài)FETCH語句只在方法4中使用。
4)、關(guān)閉游標(biāo)
如:EXEC SQL CLOSE c1;
關(guān)閉游標(biāo)的同時,會釋放由游標(biāo)添加的鎖和放棄未處理的數(shù)據(jù)。在關(guān)閉游標(biāo)前,該游標(biāo)必須已經(jīng)聲明和打開。另外,程序終止時,系統(tǒng)會自動關(guān)閉所有打開的游標(biāo)。
總之,在動態(tài)游標(biāo)的DECLARE CURSOR語句中不包含SELECT語句。而是,定義了在PREPARE中的語句名,用PREPARE語句規(guī)定與查詢相關(guān)的語句名稱。當(dāng)PREPARE語句中的語句包含了參數(shù),那么在OPEN語句中必須指定提供參數(shù)值的主變量或SQLDA。動態(tài)DECLARE CURSOR語句是一個可執(zhí)行語句。該子句必須在OPEN、FETCH、CLOSE語句之前使用。請看下面這個例子,描述了完成方法3的五個步驟:PREPARE、DECLARE、OPEN、FETCH和CLOSE。
……
EXEC SQL BEGIN DECLARE SECTION;
char szCommand[] = "SELECT au_fname FROM authors WHERE au_lname = ?";
char szLastName[] = "White";
char szFirstName[30];
EXEC SQL END DECLARE SECTION;
EXEC SQL PREPARE select_statement FROM :szCommand;
EXEC SQL DECLARE author_cursor CURSOR FOR select_statement;
EXEC SQL OPEN author_cursor USING :szLastName;
EXEC SQL FETCH author_cursor INTO :szFirstName;
EXEC SQL CLOSE author_cursor;
………
下面是一個實現(xiàn)方法3的實際例子。提示用戶輸入排序的條件,并把符合條件的書信息顯示出來。
……
exec sql begin declare section;
CS_CHAR sqlstring[200];
CS_FLOAT bookprice,condprice;
CS_CHAR booktitle[200];
exec sql end declare section;
char orderby[150];
exec sql whenever sqlerror call err_p();
exec sql whenever sqlwarning call warn_p();
strcpy(sqlstring,
"select title,price from titles\
where price>? order by ");
printf("Enter the order by clause:");
scanf("%s", orderby);
strcat(sqlstring, orderby);
exec sql prepare select_state from :sqlstring;
exec sql declare select_cur cursor for
select_state;
condprice = 10; /* 可以提示用戶輸入這個值*/
exec sql open select_cur using :condprice;
exec sql whenever not found goto end;
for (;;)
{
exec sql fetch select_cur
into :booktitle,:bookprice;
printf("%20s %bookprice=%6.2f\n",
booktitle, bookprice);
}
end:
exec sql close select_cur;
exec sql commit work;
………
三、SQLDA
要實現(xiàn)方法4,則需要使用SQLDA(也可以使用SQL Descriptors,請讀者參閱幫助信息)。可以通過SQLDA為嵌入SQL語句提供不確定的輸入數(shù)據(jù)和從嵌入SQ語句中輸出不確定數(shù)據(jù)。理解SQLDA的結(jié)構(gòu)是理解動態(tài)SQL的關(guān)鍵。
我們知道,動態(tài)SQL語句在編譯時可能不知道有多少列信息。在嵌入SQL語句中,這些不確定的數(shù)據(jù)是通過SQLDA完成的。SQLDA的結(jié)構(gòu)非常靈活,在該結(jié)構(gòu)的固定部分,指明了多少列等信息(如下圖中的sqld=2,表示為兩列信息),在該結(jié)構(gòu)的后面,有一個可變長的結(jié)構(gòu)(sd_column結(jié)構(gòu)),說明每列的信息。
SQLDA結(jié)構(gòu)
Sd_Sqld=2
Sd_column
……
Sd_datafmt
Sd_Sqllen
Sd_sqldata
…..
Sd_datafmt
Sd_Sqllen
Sd_Sqldata
…..
具體SQLDA的結(jié)構(gòu)在sqlda.h中定義,是:
typedef struct _sqlda
{
CS_SMALLINT sd_sqln;
CS_SMALLINT sd_sqld;
struct _sd_column
{
CS_DATAFMT sd_datafmt;
CS_VOID *sd_sqldata;
CS_SMALLINT sd_sqlind;
CS_INT sd_sqllen;
CS_VOID*sd_sqlmore;
} sd_column[1];
} syb_sqlda;
typedef syb_sqlda SQLDA;
從上面這個定義看出,SQLDA是一種由兩個不同部分組成的可變長數(shù)據(jù)結(jié)構(gòu)。從位于SQLDA開端的sd_sqln到sd_sqld為固定部分,用于標(biāo)志該SQLDA,并規(guī)定這一特定的SQLDA的長度。而后是一個或多個sd_column結(jié)構(gòu) ,用于標(biāo)志列數(shù)據(jù)或參數(shù)。當(dāng)用SQLDA把參數(shù)送到執(zhí)行語句時,每一個參數(shù)都是一個sd_column結(jié)構(gòu);當(dāng)用SQLDA返回輸出列信息時,每一列都是一個sd_column 結(jié)構(gòu)。具體每個元素的含義為:
lSd_Sqln。分配的sd_column結(jié)構(gòu)的個數(shù)。等價于可以允許的最大輸入?yún)?shù)的個數(shù)或輸出列的個數(shù)。
lSd_Sqld。目前使用的sd_column結(jié)構(gòu)的個數(shù)。
lSd_column[].sd_datafmt。標(biāo)志同列相關(guān)的CS_DATAFMT結(jié)構(gòu)。
lSd_column[].sd_Sqldata。指向數(shù)據(jù)的地址。注意,僅僅是一個地址。
lSd_column[].sd_sqllen。sd_sqldata指向的數(shù)據(jù)的長度。
lSd_column[].sd_Sqlind。代表是否為NULL。如果該列不允許為NULL,則該字段不賦值;如果該列允許為NULL,則:該字段若為0,表示數(shù)據(jù)值不為NULL,若為-1,表示數(shù)據(jù)值為NULL。
lSd_column[].sd_sqlmore。保留為將來使用。
下面我們來看一個具體的例子。這個例子是通過output_descriptor查詢數(shù)據(jù)庫中的數(shù)據(jù),是通過input_descriptor傳遞參數(shù)。這個例子的作用是,模擬一個動態(tài)查詢,并顯示查詢結(jié)果。動態(tài)查詢的執(zhí)行過程如下:
1)、如同構(gòu)造動態(tài)UPDATE語句或DELETE語句的方法一樣,程序在緩沖器中構(gòu)造一個有效的SELECT語句。
2)、程序用PREPARE語句把動態(tài)查詢語句送到DBMS,DBMS準(zhǔn)備、確認(rèn)和優(yōu)化語句,并生成一個應(yīng)用計劃。
3)、動態(tài)DECLARE CURSOR語句說明查詢游標(biāo),動態(tài)DECLARE CURSOR語句規(guī)定與動態(tài)SELECT語句有關(guān)的語句名稱。如:例子中的statement。
4)、程序用DESCRIBE語句請求DBMS提供SQLDA中描述信息,即告訴程序有多少列查詢結(jié)果、各列名稱、數(shù)據(jù)類型和長度。DESCRIBE語句只用于動態(tài)查詢。具體見下一節(jié)。
5)、為SQLDA申請存放一列查詢結(jié)果的存儲塊(即:sqldata指向的數(shù)據(jù)區(qū)),也為SQLDA的列的指示符變量申請空間。程序把數(shù)據(jù)區(qū)地址和指示符變量地址送入SQLDA,以告訴DBMS向何處回送查詢結(jié)果。
6)、動態(tài)格式的OPEN語句。即打開存放查詢到的數(shù)據(jù)集(動態(tài)SELECT語句產(chǎn)生的數(shù)據(jù))的第一行。
7)、動態(tài)格式的FETCH語句把游標(biāo)當(dāng)前行的結(jié)果送到SQLDA。(動態(tài)FETCH語句和靜態(tài)FETCH語句的不同是:靜態(tài)FETCH語句規(guī)定了用主變量接收數(shù)據(jù);而動態(tài)FETCH語句是用SQLDA接收數(shù)據(jù)。)并把游標(biāo)指向下一行結(jié)果集。
8)、CLOSE語句關(guān)閉游標(biāo)。
具體程序如下:
exec sql include sqlca;
exec sql include sqlda;
...
/*input_ descriptor是通過SQLDA傳遞參數(shù),output_descriptor是通過SQLDA返回列數(shù)據(jù)*/
SQLDA *input_descriptor, *output_descriptor;
CS_SMALLINT small;
CS_CHAR character[20];
/*申請空間*/
input_descriptor = (SQLDA *)malloc(SYB_SQLDA_SIZE(3));
/*設(shè)置參數(shù)的最大個數(shù)*/
input_descriptor->sqlda_sqln = 3;
/*申請空間*/
output_descriptor = (SQLDA *)malloc(SYB_SQLDA_SIZE(3));
/*設(shè)置列數(shù)的最大值*/
output_descriptor->sqlda_sqln = 3;
*p_retcode = CS_SUCCEED;
/*連接數(shù)據(jù)庫服務(wù)器*/
exec sql connect "sa" identified by password;
/* 創(chuàng)建一張example表,并插入一些例子數(shù)據(jù),用于演示SQLDA的使用*/
exec sql drop table example;
exec sql create table example (fruit char(30), number int);
exec sql insert example values ('tangerine', 1);
exec sql insert example values ('pomegranate', 2);
exec sql insert example values ('banana', 3);
/* 準(zhǔn)備和描述查詢語句*/
exec sql prepare statement from
"select fruit from example where number = ?";
/*describe語句的作用是,將查詢所需要的參數(shù)信息存放在input_descriptor中*/
exec sql describe input statement using descriptor input_descriptor;
/*設(shè)置SQLDA中指向參數(shù)數(shù)據(jù)的地址信息(sqldata)和數(shù)據(jù)長度(sqlda_sqllen)*/
input_descriptor->sqlda_column[0].sqlda_datafmt.datatype =CS_SMALLINT_TYPE;
input_descriptor->sqlda_column[0].sqlda_sqldata = &small;
input_descriptor->sqlda_column[0].sqlda_sqllen = sizeof(small);
small = 2;
/*將查詢語句的列信息存放在output_descriptor中*/
exec sql describe output statement using descriptor output_descriptor;
if (output_descriptor->sqlda_sqld != 1 ||
output_descriptor->sqlda_column[0].sqlda_datafmt.datatype !=
CS_CHAR_TYPE)
FAIL;
else
printf("first describe output \n");
/*設(shè)置存放列數(shù)據(jù)的地址信息*/
output_descriptor->sqlda_column[0].sqlda_sqldata = character;
output_descriptor->sqlda_column[0].sqlda_datafmt.maxlength = 20;
/*通過input_descriptor將輸入?yún)?shù)帶入查詢語句,并將結(jié)果通過output_descriptor帶出*/
exec sql execute statement into descriptor output_descriptor \
using descriptor input_descriptor;
/*打印結(jié)果---單行結(jié)果*/
printf("expected pomegranate, got %s\n",character);
/*釋放申請的內(nèi)存空間*/
exec sql deallocate prepare statement;
/* 多行結(jié)果示例。對多行查詢語句做準(zhǔn)備和描述操作*/
exec sql prepare statement from \
"select number from example where fruit = ?";
/*為多行結(jié)果聲明游標(biāo)*/
exec sql declare c cursor for statement;
exec sql describe input statement using descriptor input_descriptor;
/*設(shè)置查詢的參數(shù)地址信息*/
input_descriptor->sqlda_column->sqlda_sqldata = character;
input_descriptor->sqlda_column->sqlda_datafmt.maxlength =CS_NULLTERM;
/*設(shè)置參數(shù)值為banana,也可以提示用戶輸入這些信息*/
strcpy(character, "banana");
input_descriptor->sqlda_column->sqlda_sqllen = CS_NULLTERM;
/*打開游標(biāo)*/
exec sql open c using descriptor input_descriptor;
/*設(shè)置輸出列的信息*/
exec sql describe output statement using descriptor output_descriptor;
/*設(shè)置存放數(shù)據(jù)的地址信息*/
output_descriptor->sqlda_column->sqlda_sqldata = character;
output_descriptor->sqlda_column->sqlda_datafmt.datatype =CS_CHAR_TYPE;
output_descriptor->sqlda_column->sqlda_datafmt.maxlength = 20;
output_descriptor->sqlda_column->sqlda_sqllen = 20;
output_descriptor->sqlda_column->sqlda_datafmt.format =
(CS_FMT_NULLTERM | CS_FMT_PADBLANK);
exec sql fetch c into descriptor output_descriptor;
/*打印列的數(shù)據(jù)*/
printf("expected pomegranate, got %s\n", character);
exec sql commit work;
……….
上面這個例子是典型的動態(tài)查詢程序。該程序中演示了PREPARE語句和DESCRIBE語句的處理方式,以及為程序中檢索到的數(shù)據(jù)分配空間。要注意程序中如何設(shè)置sqlda_column結(jié)構(gòu)中的的各個變量。這個程序也演示了OPEN、FETCH和CLOSE語句在動態(tài)查詢中的應(yīng)用。值得注意的是,F(xiàn)ETCH語句只使用了SQLDA,不使用主變量。由于程序中預(yù)先申請了sqlda_column結(jié)構(gòu)中的SQLDATA空間,所以DBMS知道將查詢到的數(shù)據(jù)保存在何處。該程序還考慮了查詢數(shù)據(jù)為NULL的處理。
值得注意的是,SQDA結(jié)構(gòu)不是SQL標(biāo)準(zhǔn)。每個數(shù)據(jù)庫廠商的實現(xiàn)方式有可能不同。
四、DESCRIBE語句
該語句只有動態(tài)SQL才有。該語句是在PREPARE語句之后,在OPEN語句之前使用。該語句的作用是,設(shè)置SQLDA中的描述信息,如:列名、數(shù)據(jù)類型和長度等。DESCRIBE語句的語法為:
DESCRIBE 語句名 INTO 描述符名
如:exec sql describe output statement using descriptor output_descriptor;。
在執(zhí)行DESCRIBE前,用戶必須給出SQLDA中的SQLN的值(表示最多有多少列),該值也說明了SQLDA中最多有多少個sqlda_column結(jié)構(gòu)。然后,執(zhí)行DESCRIBE語句,該語句填充每一個sqlda_column結(jié)構(gòu)。每個sqlda_column結(jié)構(gòu)中的相應(yīng)列為:
lSd_datafmt結(jié)構(gòu):列名等信息。
lSd_sqllen列:給出列的長度。
注意,sd_sqldata列不填充。由程序在FETCH語句之前,給出數(shù)據(jù)緩沖器地址和指示符地址。
<!--[if !supportLineBreakNewLine]-->
下邊的話,給出兩個例子吧,方便對上邊講到的東西做個對比:
1)、TELECOM程序
該程序是模擬電信費用查詢。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#if defined ( DB2 )
#define SQLNOTFOUND 100
#include <sql.h>
#elif defined ( ORA7 )
#define SQLNOTFOUND 1403
#endif
#if defined (SYBASE)
#define SQLNOTFOUND 100
#endif
EXEC SQL INCLUDE sqlca;
EXEC SQL BEGIN DECLARE SECTION;
char user[30];
char passwd[30];
char Usr_name[61];
char Dev_no[9];
long Call_flg;
char Called_arno[11];
char Called_no[15];
char Call_dat[21];
double Call_dur;
double Call_rate;
double Call_fee;
double Add_fee;
char as_dev_no[9];
EXEC SQL END DECLARE SECTION;
void main(){
char statusbuf[1024], s[30];
/*連接到SQL SERVER服務(wù)器*/
printf("\nplease enter your userid ");
gets(user);
printf("\npassword ");
gets(passwd);
exec sql connect :user identified by :passwd;
exec sql use pubs2;
/*輸入想要查詢的電話號碼*/
printf("\nPlease enter the telephone number:");
gets(as_dev_no );
/*聲明游標(biāo)*/
EXEC SQL DECLARE c1 CURSOR FOR
SELECT bas_infot.Usr_name, auto10a_list.Dev_no, auto10a_list.Call_flg, auto10a_list.Called_arno, auto10a_list.Called_no
, auto10a_list.Call_dat, auto10a_list.Call_dur, auto10a_list.Call_rate, auto10a_list.Call_fee,
FROM auto10a_list, bas_infot
WHERE ( auto10a_list.Dev_no = bas_infot.Dev_no ) AND auto10a_list.Dev_no = :as_dev_no;
/*打開游標(biāo),指向查詢相關(guān)電話信息的結(jié)果集*/
EXEC SQL OPEN c1; /* :rk.2:erk. */
do{
/*取出一行數(shù)據(jù)到各個變量*/
EXEC SQL FETCH c1 INTO
:Usr_name, :Dev_no, :Call_flg, :Called_arno, :Called_no, :Call_dat, :Call_dur, :Call_rate, :Call_fee, :Add_fee;
if( (sqlca.sqlcode == SQLNOTFOUND) || (sqlca.sqlcode <0) )
break;
/*顯示數(shù)據(jù)*/
printf("%s,%s,%d,%s,%s,%s,%7.0f,%8.3f,%7.2f,%6.2f\n"
, Usr_name, Dev_no, Call_flg, Called_arno, Called_no, Call_dat, Call_dur, Call_rate, Call_fee, Add_fee );
}while(1);
EXEC SQL CLOSE c1;
EXEC SQL DEALLOCATE CURSOR c1;
Exec sql disconnect all;
return (0);
}
2)、 ADHOC程序
該程序的功能是:用戶輸入任意SQL語句,并執(zhí)行和打印結(jié)果。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/* Defines for BINDING */
/*初試化SQLDA*/
int init_da (SQLDA **DAPointer, int DAsqln);
/*為存放列數(shù)據(jù)的sd_column結(jié)構(gòu)申請空間*/
int alloc_host_vars (SQLDA *sqldaPointer);
/*釋放SQLDA所申請的空間*/
void free_da (SQLDA *sqldaPointer);
/*獲取列名信息*/
char * readColName (SQLDA *sqldaPointer, short sd_columnIndex, char * buffer);
/*獲取列數(shù)據(jù)*/
char * readCol (SQLDA *sqldaPointer, short sd_columnIndex, char * buffer);
#ifdef __cplusplus
}
#endif
/*定義最大列數(shù)*/
#define MAX_COLUMNS255
#define MAX_CHAR_FOR_DOUBLE20
#define MAX_CHAR_FOR_LONG15
#define MAX_CHAR_FOR_DATETIME30
#define MAX_CHAR_FOR_DEFAULT100
EXEC SQL INCLUDE SQLCA ;
EXEC SQL INCLUDE SQLDA ;
#define SQLSTATE sqlca.sqlstate
#define SQLCODE sqlca.sqlcode
/*處理SQL語句*/
int process_statement( char * ) ;
int main() {
int rc ;
char st[1024];
char tmpstr[1024];
/*獲得SQL語句*/
printf("Please enter the any sql statement:");
gets( st);
/* 處理該語句 */
rc = process_statement( st ) ;
/*打印處理結(jié)果*/
printf( "%d", rc);
printf("the sqlcode is %d",SQLCODE);
}
/******************************************************************************
* FUNCTION : process_statement
* 處理sql 語句
*****************************************************************************/
int process_statement ( char * sqlInput ) {
int counter = 0 ;
SQLDA * sqldaPointer ;
short sqlda_d ; /* Total columns */
short idx;
char buffer[4096];
char varname[1024];
char colnamelist[4096];
EXEC SQL BEGIN DECLARE SECTION ;
char st[1024] ;
EXEC SQL END DECLARE SECTION ;
strcpy( st, sqlInput ) ;
/* 為SQLDA結(jié)構(gòu)申請空間" */
if (init_da( &sqldaPointer, MAX_COLUMNS ) == -1)
{
return -1;
}
/*準(zhǔn)備SQL語句*/
EXEC SQL PREPARE statement1 from :st ;
if (SQLCODE < 0)
{
free_da(sqldaPointer);
return SQLCODE;
}
/*獲取查詢列的信息到SQLDA結(jié)構(gòu)*/
EXEC SQL DESCRIBE statement1 USING DESCRIPTOR sqldaPointer ;
/* 如果SQLCODE為0,則表示為SELECT語句 */
if ( SQLCODE != 0 ) {
free_da(sqldaPointer);
return SQLCODE;
} /* end if */
sqlda_d = sqldaPointer->sd_sqld ;
if ( sqlda_d > 0 ) {
/* 為存放列數(shù)據(jù)的sd_column結(jié)構(gòu)申請空間 */
if (alloc_host_vars( sqldaPointer ) == -1)
{free_da(sqldaPointer);
return -1;
}
/*聲明游標(biāo)*/
EXEC SQL DECLARE pcurs CURSOR FOR statement1 ;
/打開游標(biāo)*/
EXEC SQL OPEN pcurs ;
if (SQLCODE < 0)
return SQLCODE;
/*取一行數(shù)據(jù)到SQLDA結(jié)構(gòu)*/
EXEC SQL FETCH pcurs INTO DESCRIPTOR sqldaPointer;
if (SQLCODE < 0)
{
EXEC SQL CLOSE pcurs ;
return SQLCODE;
}
/*顯示列標(biāo)題 */
colnamelist[0] = 0;
for ( idx=0; idx< sqlda_d; idx++)
{ strcat(colnamelist, readColName(sqldaPointer, idx, buffer));
if (idx < sqlda_d -1)
strcat(colnamelist, ",");
}
/* 顯示行數(shù)據(jù)*/
while ( SQLCODE == 0 ) {
counter++ ;
for ( idx=0; idx< sqlda_d; idx++)
printf("%s",readCol(sqldaPointer, idx, buffer));
EXEC SQL FETCH pcurs INTO DESCRIPTOR sqldaPointer ;
} /* endwhile */
/*關(guān)閉游標(biāo)*/
EXEC SQL CLOSE pcurs ;
EXEC SQL DEALLOCATE CURSOR pcurs;
/* 釋放為SQLDA申請的空間 */
free_da( sqldaPointer ) ;
} else { /* 不是SELECT語句*/
EXEC SQL EXECUTE statement1 ;
free_da( sqldaPointer ) ;
if (SQLCODE < 0)
return SQLCODE;
} /* end if */
return( 0 ) ;
} /* end of program : ADHOC.CP */
/******************************************************************************* PROCEDURE : init_da
*為SQLDA分配空間。使用SQLDASIZE 獲得SQLDA的大小。如果返回-1,則表示分配
*空間不成功。
******************************************************************************/
int init_da (SQLDA **DAPointer, int DAsqln) {
int idx;
*DAPointer = (SQLDA *)malloc(SYB_SQLDA_SIZE(DAsqln));
if (*DAPointer == NULL)
return (-1);
memset (*DAPointer, '\0', SYB_SQLDA_SIZE(DAsqln));
(*DAPointer)->sd_sqln = DAsqln;
(*DAPointer)->sd_sqld = 0;
return 0;
}
/******************************************************************************* FUNCTION : alloc_host_vars
*為存放列數(shù)據(jù)的sd_column結(jié)構(gòu)申請空間。如果返回-1,則表示不能獲得足夠內(nèi)存。
******************************************************************************/
int alloc_host_vars (SQLDA *sqldaPointer) {
short idx;
for (idx = 0; idx < sqldaPointer->sd_sqld; idx++) {
switch (sqldaPointer->sd_column[idx].sd_datafmt.datatype ) {
case CS_CHAR_TYPE:
case CS_VARCHAR_TYPE:
sqldaPointer->sd_column[idx].sd_datafmt.datatype = CS_CHAR_TYPE;
sqldaPointer->sd_column[idx].sd_sqldata = (char *) malloc (sqldaPointer->sd_column[idx].sd_sqllen + 1 );
sqldaPointer->sd_column[idx].sd_sqllen ++;
sqldaPointer->sd_column[idx].sd_datafmt.format = CS_FMT_NULLTERM;
break;
case CS_TINYINT_TYPE:
case CS_SMALLINT_TYPE:
case CS_INT_TYPE:
case CS_VOID_TYPE:
case CS_USHORT_TYPE:
sqldaPointer->sd_column[idx].sd_datafmt.datatype = CS_CHAR_TYPE;
sqldaPointer->sd_column[idx].sd_sqldata = (char *) malloc (MAX_CHAR_FOR_LONG);
sqldaPointer->sd_column[idx].sd_sqllen = MAX_CHAR_FOR_LONG;
sqldaPointer->sd_column[idx].sd_datafmt.format = CS_FMT_NULLTERM;
break;
case CS_REAL_TYPE:
case CS_FLOAT_TYPE:
case CS_BIT_TYPE:
case CS_MONEY_TYPE:
case CS_MONEY4_TYPE:
sqldaPointer->sd_column[idx].sd_datafmt.datatype = CS_CHAR_TYPE;
sqldaPointer->sd_column[idx].sd_sqldata = (char *) malloc (MAX_CHAR_FOR_DOUBLE);
sqldaPointer->sd_column[idx].sd_sqllen = MAX_CHAR_FOR_DOUBLE;
sqldaPointer->sd_column[idx].sd_datafmt.format = CS_FMT_NULLTERM;
break;
case CS_DATETIME_TYPE:
case CS_DATETIME4_TYPE:
sqldaPointer->sd_column[idx].sd_datafmt.datatype = CS_CHAR_TYPE;
sqldaPointer->sd_column[idx].sd_sqldata = (char *) malloc (MAX_CHAR_FOR_DATETIME);
sqldaPointer->sd_column[idx].sd_sqllen = MAX_CHAR_FOR_DATETIME;
sqldaPointer->sd_column[idx].sd_datafmt.format = CS_FMT_NULLTERM;
break;
case CS_NUMERIC_TYPE:
case CS_DECIMAL_TYPE:
sqldaPointer->sd_column[idx].sd_datafmt.datatype = CS_CHAR_TYPE;
sqldaPointer->sd_column[idx].sd_sqldata = (char *) malloc (sqldaPointer->sd_column[idx].sd_datafmt.precision + 3 );
sqldaPointer->sd_column[idx].sd_sqllen = sqldaPointer->sd_column[idx].sd_datafmt.precision + 3;
sqldaPointer->sd_column[idx].sd_datafmt.format = CS_FMT_NULLTERM;
break;
default:
sqldaPointer->sd_column[idx].sd_datafmt.datatype = CS_CHAR_TYPE;
sqldaPointer->sd_column[idx].sd_sqldata = (char *) malloc (MAX_CHAR_FOR_DEFAULT);
sqldaPointer->sd_column[idx].sd_sqllen = MAX_CHAR_FOR_DEFAULT;
sqldaPointer->sd_column[idx].sd_datafmt.format = CS_FMT_NULLTERM;
break;
} /* endswitch */
if (sqldaPointer->sd_column[idx].sd_sqldata == NULL) {
return (-1);
}
} /* endfor */
return 0;
}
/******************************************************************************* FUNCTION : free_da
* 釋放SQLDA申請的空間。
******************************************************************************/
void free_da (SQLDA *sqldaPointer) {
short idx;
for (idx = 0; idx < sqldaPointer->sd_sqld; idx++) {
free (sqldaPointer->sd_column[idx].sd_sqldata);
} /* endfor */
free (sqldaPointer);
}
/******************************************************************************* PROCEDURE : readColName
* 返回列名
******************************************************************************/
char * readColName (SQLDA *sqldaPointer, short sd_columnIndex, char * buffer) {
strcpy(buffer, sqldaPointer->sd_column[sd_columnIndex].sd_datafmt.name);
return buffer;
}
/******************************************************************************* PROCEDURE : readCol
* 返回列數(shù)據(jù)。
******************************************************************************/
char * readCol (SQLDA *sqldaPointer, short sd_columnIndex, char * buffer){
short numBytes;
short idx, ind ; /* Array idx variables */
/* Variables for decoding packed decimal data */
char tmpstr[1024];
short collen;
char *dataptr;
/* 檢查是否為NULL */
if ( sqldaPointer->sd_column[sd_columnIndex].sd_sqlind )
{ buffer[0] = 0;
return buffer;
}
/*返回列數(shù)據(jù)到buffer變量*/
strcpy( buffer, (char *) sqldaPointer->sd_column[ sd_columnIndex ].sd_sqldata);
return buffer;
}
/* COMMENT OUT OFF */
<!--[endif]-->
<!--[if !supportLineBreakNewLine]--><!--[endif]-->
http://rickya.bokee.com/viewdiary.14009093.html