ESQL/C資料(完全版)一

第六章 嵌入式SQL(E-SQL)簡介3
第一節 什麼是嵌入SQL語言?3
1.1 嵌入SQL程序的組成元素3
1.2 什麼是靜態SQL和動態SQL?4
1.3 什麼是SQLCA?4
1.4 什麼是SQLDA?5
第二節 SYBASE SQL SERVER嵌入式SQL語言5
2.1 一個嵌入SQL語言的簡單例子5
2.2 嵌入SQL的處理過程6
2.3 嵌入SQL語句總覽7
2.3.1 宿主變量7
2.3.2 連接數據庫12
2.3.3 數據的查詢和修改13
2.3.4 遊標的使用13
2.3.5 SQLCA15
2.3.6 WHENEVER16
2.3.7 批處理18
2.3.8 事務18
2.4動態SQL語句18
2.4 .1 動態修改19
2.4.2 動態遊標20
2.4.3 SQLDA23
2.4.4 DESCRIBE語句27
2.5 兩個例子程序27
2.5.1 TELECOM程序27
2.5.2 ADHOC程序29
第三節 IBM DB2嵌入SQL語言35
3.1 一個簡單示例35
3.2 嵌入SQL語句37
3.2.1宿主變量37
3.2.2單行查詢39
3.2.3多行查詢39
3.2.4插入、刪除和修改操作40
3.2.5 SQLCA43
3.2.6事務45
3.3 DB2的嵌入SQL程序處理過程46
3.4 DB2的動態SQL嵌入語句53
3.4.1 基本方法53
3.4.2 動態遊標55
3.4.3 SQLDA55
第四節 ORACLE數據庫的嵌入SQL語言66
4.1 基本的SQL語句66
4.1.1宿主變量和指示符66
4.1.2 查詢68
4.1.3 修改數據68
4.1.4 遊標68
4.2 嵌入PL/SQL71
4.3 動態SQL語句72
4.3.1 ORACLE動態SQL語句的一些特點72
4.3.2 使用動態SQL的四種方法72
4.3.3 SQLDA75
第五節INFORMIX的嵌入SQL/C語言89
5.1 一個簡單的入門例子89
5.2 宿主變量91
5.3 嵌入SQL的處理過程96
5.4 動態SQL語言96
5.4.1 SQLDA97
第六節MICROSOFT SQL SERVER7嵌入式SQL語言105
6.1 一個嵌入SQL語言的簡單例子105
6.2 嵌入SQL的處理過程106
6.3 嵌入SQL語句112
6.3.1 聲明嵌入SQL語句中使用的C變量112
6.3.2 連接數據庫115
6.3.3 數據的查詢和修改115
6.3.4 遊標的使用116
6.3.5 SQLCA117
6.3.6 WHENEVER118
6.4動態SQL語句119
6.4 .1 動態修改119
6.4.2 動態遊標120
6.4.3 SQLDA122
6.4.4 DESCRIBE語句130
6.5 API130

 

 

 

 

 

 

 

第六章 嵌入式SQL(E-SQL)簡介
第一節 什麼是嵌入SQL語言?
SQL是一種雙重式語言,它既是一種用於查詢和更新的交互式數據庫語言,又是一種應用程序進行數據庫訪問時所採取的編程式數據庫語言。SQL語言在這兩種方式中的大部分語法是相同的。在編寫訪問數據庫的程序時,必須從普通的編程語言開始(如C語言),再把SQL加入到程序中。所以,嵌入式SQL語言就是將SQL語句直接嵌入到程序的源代碼中,與其他程序設計語言語句混合。專用的SQL預編譯程序將嵌入的SQL語句轉換爲能被程序設計語言(如C語言)的編譯器識別的函數調用。然後,C編譯器編譯源代碼爲可執行程序。
各個數據庫廠商都採用嵌入SQL語言,並且都符合ANSI/ISO的標準。所以,如果採用合適的嵌入SQL語言,那麼可以使得你的程序能夠在各個數據庫平臺上執行(即:源程序不用做修改,只需要用相應數據庫產品的預編譯器編譯即可)。當然,每個數據庫廠商又擴展了ANSI/ISO的標準,提供了一些附加的功能。這樣,也使得每個數據庫產品在嵌入SQL方面有一些區別。本章的目標是,對所有的數據庫產品的嵌入SQL做一個簡單、實用的介紹。
當然,嵌入SQL語句完成的功能也可以通過應用程序接口(API)實現。通過API的調用,可以將SQL語句傳遞到DBMS,並用API調用返回查詢結果。這個方法不需要專用的預編譯程序。
1.1 嵌入SQL程序的組成元素
我們以IBM的DB2嵌入SQL爲例,來看看嵌入SQL語句的組成元素。
例1、連接到SAMPLE數據庫,查詢LASTNAME爲JOHNSON的FIRSTNAME信息。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "util.h"
#include <sqlca.h>
EXEC SQL INCLUDE SQLCA; (1)
main()
{
EXEC SQL BEGIN DECLARE SECTION; (2)
char firstname[13];
char userid[9];
char passwd[19];
EXEC SQL END DECLARE SECTION;
EXEC SQL CONNECT TO sample; (3)
EXEC SQL SELECT FIRSTNME INTO :firstname (4)
FROM employee
WHERE LASTNAME = 'JOHNSON';(4)
printf( "First name = %s/n", firstname );
EXEC SQL CONNECT RESET; (5)
return 0;
}
上面是一個簡單的靜態嵌入SQL語句的應用程序。它包括了嵌入SQL的主要部分:
(1)中的include SQLCA語句定義並描述了SQLCA的結構。SQLCA用於應用程序和數據庫之間的通訊,其中的SQLCODE返回SQL語句執行後的結果狀態。
(2)在BEGIN DECLARE SECTION和END DECLARE SECTION之間定義了宿主變量。宿主變量可被SQL語句引用,也可以被C語言語句引用。它用於將程序中的數據通過SQL語句傳給數據庫管理器,或從數據庫管理器接收查詢的結果。在SQL語句中,主變量前均有“:”標誌以示區別。
(3)在每次訪問數據庫之前必須做CONNECT操作,以連接到某一個數據庫上。這時,應該保證數據庫實例已經啓動。
(4)是一條選擇語句。它將表employee中的LASTNAME爲“JOHNSON”的行數據的FIRSTNAME查出,並將它放在firstname變量中。該語句返回一個結果。可以通過遊標返回多個結果。當然,也可以包含update、insert和delete語句。
(5)最後斷開數據庫的連接。
從上例看出,每條嵌入式SQL語句都用EXEC SQL開始,表明它是一條SQL語句。這也是告訴預編譯器在EXEC SQL和“;”之間是嵌入SQL語句。如果一條嵌入式SQL語句佔用多行,在C程序中可以用續行符“/”。
1.2 什麼是靜態SQL和動態SQL?
嵌入SQL語言,分爲靜態SQL語言和動態語言兩類。靜態SQL語言,就是在編譯時已經確定了引用的表和列。宿主變量不改變表和列信息。可以使用主變量改變查詢參數值,但是不能用主變量代替表名或列名。
動態SQL語言就是:不在編譯時確定SQL的表和列,而是讓程序在運行時提供,並將SQL語句文本傳給DBMS執行。靜態SQL語句在編譯時已經生成執行計劃。而動態SQL語句,只有在執行時才產生執行計劃。動態SQL語句首先執行PREPARE語句要求DBMS分析、確認和優化語句,併爲其生成執行計劃。DBMS還設置SQLCODE以表明語句中發現的錯誤。當程序執行完“PREPARE”語句後,就可以用EXECUTE語句執行執行計劃,並設置SQLCODE,以表明完成狀態。
1.3 什麼是SQLCA?
應用程序執行時,每執行一條SQL語句,就返回一個狀態符和一些附加信息。這些信息反映了SQL語句的執行情況,它有助於用戶分析應用程序的錯誤所在。這些信息都存放在sqlca.h的sqlca結構中。如果一個源文件中包含SQL語句,則必須要在源程序中定義一個SQLCA結構,而且名爲SQLCA。最簡單的定義方法是在源文件中加入一些語句:EXEC SQL INCLUDE sqlca.h。每個數據庫產品都提供了SQLCA結構。

1.4 什麼是SQLDA?
我們知道,動態SQL語句在編譯時可能不知道有多少列信息。在嵌入SQL語句中,這些不確定的數據是通過SQLDA完成的。SQLDA的結構非常靈活,在該結構的固定部分,指明瞭多少列等信息,在該結構的後面有一個可變長的結構,說明每列的信息。在從數據庫獲得數據時,就可以採用SQLDA來獲得每行的數據。各個數據庫產品的SQLDA結構都不完全相同。
第二節 SYBASE SQL Server嵌入式SQL語言
2.1 一個嵌入SQL語言的簡單例子
我們首先來看一個簡單的嵌入式SQL語言的程序(C語言):用sa(口令爲password)連接數據庫服務器,並將所有書的價格增加10%。這個例子程序如下:
例1、
/*建立通訊區域*/
Exec sql include sqlca;
main()
{
/*聲明宿主變量*/
EXEC SQL BEGIN DECLARE SECTION;
char user[30],passwd[30];
EXEC SQL END DECLARE SECTION;
/*錯誤處理*/
EXEC SQL WHENEVER SQLERROR CALL err_p();
/*連接到SQL SERVER服務器*/
printf("/nplease enter your userid ");
gets(user);
printf("/npassword ");
gets(passwd);
exec sql connect :user identified by :passwd;
exec sql use pubs2;
EXEC SQL update titles set price=price*1.10;
EXEC SQL commit work;
/*斷開數據庫服務器的連接*/
Exec sql disconnect all;
return (0);
}
/*錯誤處理程序*/
err_p()
{
printf("/nError occurred: code %d./n%s", /
sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc);
}
從上面這個例子,我們看出嵌入SQL的基本特點是:
1、每條嵌入式SQL語句都用EXEC SQL開始,表明它是一條SQL語句。這也是告訴預編譯器在EXEC SQL和“;”之間是嵌入SQL語句。
2、如果一條嵌入式SQL語句佔用多行,在C程序中可以用續行符“/”,在Fortran中必須有續行符。其他語言也有相應規定。
3、每一條嵌入SQL語句都有結束符號,如:在C中是“;”。
4、嵌入SQL語句的關鍵字不區分大小寫。
5、可以使用“/*….*/”來添加註釋。也可以使用“--”來添加註釋。
2.2 嵌入SQL的處理過程
嵌入SQL的處理過程如下圖所示:

 

 

 

 

 

 

圖6-1 SYBASE SQL SERVER嵌入SQL程序處理過程
嵌入SQL程序的後綴爲.cp。嵌入SQL處理的第一步是預編譯。預編譯器(cpre.exe)處理過程分爲兩個小步:
第一小步:語法分析。檢查嵌入SQL語句的語法正確性。
第二小步:增加一些預編譯器變量,並註釋了所有的嵌入的SQL語句,將嵌入SQL語句轉換爲對client-library中函數的調用(注意:在連接時,編譯後的代碼需要使用client-library中的庫文件)。如果在編譯時,設置一些選項,則生成存儲過程。預編譯後可能產生3個文件:一個C文件(肯定產生),一個列表文件(需要在編譯時設置選項,才能產生)和一個isql腳本文件(需要在編譯時設置選項,才能產生)。列表文件包含了輸入文件的源語句和一些警告信息和錯誤信息。Isql腳本文件包含了預編譯器產生的存儲過程腳本文件。這個存儲過程是用T-SQL寫的。總之,預編譯器的處理方法是,註釋了嵌入的SQL語句,用一些特定的函數代替。
第二步是C源程序的編譯和鏈接。cl是編譯和鏈接命令的集成命令,編譯的結果是產生.obj,在鏈接時,將C的系統庫和SQL Server提供的庫文件同目標文件連接在一起。最後生成.exe。也可以使用SET LIB語句設置庫文件的環境信息。

2.3 嵌入SQL語句總覽
除了print、readtext和writetext外,大多數的Transact-SQL語句都可以在嵌入SQL中使用。嵌入SQL語句的語法爲:“exec sql [at connection_name] sql_statement; ”。那麼,你可以用Transact-SQL語句來替代sql_statement 就可以完成嵌入SQL的編寫。(同T-SQL相比,嵌入SQL提供了:自動數據類型轉換、動態SQL、SQLCA數據結構等功能。)
但是,也有一些嵌入式SQL所特有的語句,有些嵌入式SQL語句的名字同Transact-SQL語句相同,但是語句的語法有所不同。
嵌入SQL語句應該包含五個步驟:
1)、通過SQLCA建立應用程序和SQL SERVER的SQL通信區域。
2)、聲明宿主變量。
3)、連接到SQL SERVER。
4)、通過SQL語句操作數據。
5)、處理錯誤和結果信息。
嵌入式SQL語句分爲靜態SQL語句和動態SQL語句兩類。下面我們按照功能講解這些語句。本節講解靜態SQL語句的作用。動態SQL語句將在下一節講解。同動態SQL相關的一些語句也在下一節中講解。
2.3.1 宿主變量
1)、聲明方法
宿主變量(host variable)就是在嵌入式SQL語句中引用主語言說明的程序變量(如例中的user[31]變量)。如:

EXEC SQL BEGIN DECLARE SECTION;
char user[31],passwd[31];
EXEC SQL END DECLARE SECTION;
…………
exec sql connect :user identified by :passwd;
………….
在嵌入式SQL語句中使用主變量前,必須採用BEGIN DECLARE SECTION 和END DECLARE SECTION之間給主變量說明。這兩條語句不是可執行語句,而是預編譯程序的說明。主變量是標準的C程序變量。嵌入SQL語句使用主變量來輸入數據和輸出數據。C程序和嵌入SQL語句都可以訪問主變量。
另外,在定義宿主變量時也可以使用client-library定義的數據類型,如:CS_CHAR。這些定義存放在cspublic.h文件中。如:
EXEC SQL BEGIN DECLARE SECTION;
CS_CHAR user[30],passwd[30];
EXEC SQL END DECLARE SECTION;
client-library定義的數據類型共有:CS_BINARY、CS_BIT、 CS_BOOL、 CS_CHAR、 CS_DATETIME、CS_DATETIME4、 CS_DECIMAL、 CS_FLOAT、 CS_REAL、CS_IMAGE、 CS_INT、 CS_MONEY、 CS_MONEY4、 CS_NUMERIC、CS_RETCODE、 CS_SMALLINT、 CS_TEXT、 CS_TINYINT、CS_VARBINARY、 CS_VARCHAR、 CS_VOID。
爲了便於識別主變量,當嵌入式SQL語句中出現主變量時,必須在變量名稱前標上冒號(:)。冒號的作用是,告訴預編譯器,這是個主變量而不是表名或列名。不能在聲明時,初始化數組變量。
由上可知,SYBASE SQL SERVER使用宿主變量傳遞數據庫中的數據和狀態信息到應用程序,應用程序也通過宿主變量傳遞數據到SYBASE數據庫。根據上面兩種功能,宿主變量分爲輸出宿主變量和輸入宿主變量。在SELECT INTO和FETCH語句之後的宿主變量稱作“輸出宿主變量”,這是因爲從數據庫傳遞列數據到應用程序。如:

exec sql begin declare section;
CS_CHAR id[5];
exec sql end declare section;
exec sql select title_id into :id from titles
where pub_id = "0736" and type = "business";

除了SELECT INTO和FETCH語句外的其他SQL語句(如:INSERT、UPDATE等語句)中的宿主變量,稱爲“輸入宿主變量”。這是因爲從應用程序向數據庫輸入值。如:
exec sql begin declare section;
CS_CHAR id[7];
CS_CHAR publisher[5];
exec sql end declare section;
...
exec sql delete from titles where title_id = :id;
exec sql update titles set pub_id = :publisher
where title_id = :id;
另外,也可以通過宿主變量獲得存儲過程的執行狀態信息。如:
exec sql begin declare section;
CS_SMALLINT retcode;
exec sql end declare section;
exec sql begin transaction;
exec sql exec :retcode = update_proc;
if (retcode != 0)
{
exec sql rollback transaction;
也可以通過宿主變量獲得存儲過程的返回值。如:
exec sql exec a_proc :par1 out, :par2 out;
2)、主變量的數據類型
SYBASE SQL SERVER支持的數據類型與程序設計語言支持的數據類型之間有很大差別。這些差別對主變量影響很大。一方面,主變量是一個用程序設計語言的數據類型說明並用程序設計語言處理的程序變量;另一方面,在嵌入SQL語句中用主變量保存數據庫數據。所以,在嵌入SQL語句中,必須映射C數據類型爲合適的SQL Server數據類型。必須慎重選擇主變量的數據類型。在SQL SERVER中,預編譯器能夠自動轉換兼容的數據類型。請看下面這個例子:
EXEC SQL BEGIN DECLARE SECTION;
int hostvar1 = 39;
char *hostvar2 = "telescope";
float hostvar3 = 355.95;
EXEC SQL END DECLARE SECTION;

EXEC SQL UPDATE inventory
SET department = :hostvar1
WHERE part_num = "4572-3";

EXEC SQL UPDATE inventory
SET prod_descrip = :hostvar2
WHERE part_num = "4572-3";

EXEC SQL UPDATE inventory
SET price = :hostvar3
WHERE part_num = "4572-3";
在第一個update語句中,department列爲smallint數據類型(integer ),所以應該把hostvar1定義爲int數據類型(integer)。這樣的話,從C到SQL Server的hostvar1可以直接映射。在第二個update語句中,prod_descip列爲varchar數據類型,所以應該把hostvar2定義爲字符數組。這樣的話,從C到SQL Server的hostvar2可以從字符數組映射爲varchar數據類型。在第三個update語句中,price列爲money數據類型。在C語言中,沒有相應的數據類型,所以用戶可以把hostvar3定義爲C的浮點變量或字符數據類型。SQL Server可以自動將浮點變量轉換爲money數據類型(輸入數據),或將money數據類型轉換爲浮點變量(輸出數據)。
注意的是,如果數據類型爲字符數組,那麼SQL Server會在數據後面填充空格,直到填滿該變量的聲明長度(CS_CHAR數據類型除外)。
下表列出了C的數據類型和SQL SERVER數據類型的一些兼容關係:
可兼容的C數據類型分配的SQL Server數據類型SYBASE提供的數據類型描述
shortSmallintCS_SMALLINT2字節整數
IntSmallintCS_SMALLINT2字節整數
LongIntCS_INT4字節整數
FloatRealCS_REAL4字節浮點數
DoubleFloatCS_FLOAT8字節浮點數
CharCarchar[X]VARCHARCS_CHAR字符數據類型
Unsigned charBinaryVarbinaryCS_BINARYBinary數據類型
Unsigned char tinyintCS_TINYINT1字節整數
無DatetimeCS_DATETIME8字節datetime類型
無SmalldatetimeCS_DATETIME44字節datetime類型
無DecimalCS_DECIMALDecimal數據類型
無numericCS_NUMERICNumeric數據類型
無MoneyCS_MONEY8字節money類型
無smallmoneyCS_MONEY44字節money類型
Unsigned char TextCS_TEXT文本數據類型
Unsigned char imageCS_IMAGE圖象數據類型
無booleanCS_BITBit數據類型
因爲C沒有date或time數據類型,所以SQL Server的date或time列將被轉換爲字符。缺省情況下,使用以下轉換格式:mm dd yyyy hh:mm:ss[am | pm]。你也可以使用字符數據格式將C的字符數據存放到SQL Server的date列上。你也可以使用Transact-SQL中的convert語句來轉換數據類型。如:SELECT CONVERT(char, date, 8) FROM sales。
下表是從SQL SERVER數據類型到C的數據類型的轉換關係:
SQL SERVER 數據類型C數據類型
CS_TINYINTCS_SMALLINTCS_INTCS_REALCS_CHARCS_MONEYCS_DATETIME
char可以可以可以可以可以可以可以
varchar可以可以可以可以可以可以可以
bit可以可以可以可以可以可以
binary可以可以可以可以可以可以
tinyint可以可以可以可以可以可以
smallint可以可以可以可以可以可以
int可以可以可以可以可以可以
float可以可以可以可以可以可以
money可以可以可以可以可以可以
datetime可以可以
decimal可以可以可以可以可以可以
numeric可以可以可以可以可以可以

下表是從C的數據類型到SQL SERVER數據類型的轉換關係:
C數據類型SQL SERVER數據類型
tinyintbitsmallintintfloatcharmoneydatetimedecimalnumeric
Unsigned char可以可以可以可以可以需要自己轉換可以可以可以
Short int可以可以可以可以可以需要自己轉換可以可以可以
Long int可以可以可以可以可以需要自己轉換可以可以可以
Double float可以可以可以可以可以需要自己轉換可以可以可以
Char需要自己轉換需要自己轉換需要自己轉換需要自己轉換需要自己轉換可以需要自己轉換可以需要自己轉換需要自己轉換
money可以可以可以可以可以可以可以可以可以
datetime需要自己轉換可以

3)、主變量和NULL
大多數程序設計語言(如C)都不支持NULL。所以對NULL的處理,一定要在SQL中完成。我們可以使用主機指示符變量(host indicator variable)來解決這個問題。在嵌入式SQL語句中,主變量和指示符變量共同規定一個單獨的SQL類型值。如:
EXEC SQL SELECT price INTO :price :price_nullflag FROM titles
WHERE au_id = "mc3026"
其中,price是主變量,price_nullflag是指示符變量。
使用指示符變量的語法爲:: host_variable [[indicator] : indicator_variable]。其中,indicator可以不寫。針對宿主變量是輸出宿主變量,還是輸入宿主變量。指示符變量共分兩種情況。
情況1:同輸出宿主變量一起使用,則indicator_varibale爲:
l-1。表示相應列值爲NULL。表示主變量應該假設爲NULL。(注意:宿主變量的實際值是一個無關值,不予考慮)。
l0。表示非NULL值。該變量存放了非NULL的列值。
l>0。表示宿主變量包含了列值的截斷值。該指示變量存放了該列值的實際長度。

下面是一個同輸出宿主變量一起使用的指示變量的例子:
exec sql begin declare section;
CS_CHAR id[6];
CS_SMALLINT indic;
CS_CHAR pub_name[41];
exec sql end declare section;
exec sql select pub_id into :id indicator :indic
from titles where title
like "%Stress%";
if (indic == -1)
{
printf("/npub_id is null");
}
else
{
exec sql select pub_name into :pub_name
from publishers where pub_id = :id;
printf("/nPublisher: %s", pub_name);
情況2:同輸入宿主變量一起使用,則indicator_varibale爲:
l-1。表示主變量應該假設爲NULL。(注意:宿主變量的實際值是一個無關值,不予考慮)。應該將NULL賦值給相應列。
l0。表示非NULL值。該變量存放了非NULL值。應該將宿主變量的值賦值給相應列。
對於以下語句:
EXEC SQL SELECT price INTO :price :price_nullflag FROM titles
WHERE au_id = "mc3026"
如果不存在mc3026寫的書,那麼price_nullflag爲-1,表示price爲NULL;如果存在,則price爲實際的價格。下面我們再看一個update的例子:
EXEC SQL UPDATE closeoutsale
SET temp_price = :saleprice :saleprice_null, listprice = :oldprice;
如果saleprice_null是-1,則上述語句等價爲:
EXEC SQL UPDATE closeoutsale
SET temp_price = null, listprice = :oldprice;
我們也可以在指示符變量前面加上“INDICATOR”關鍵字,表示後面的變量爲指示符變量。如:
EXEC SQL UPDATE closeoutsale
SET temp_price = :saleprice INDICATOR :saleprice_null;
指示符變量也是宿主變量,定義指示符變量同定義宿主變量一樣。它應該是一個2個字節的整數(short或CS_SMALLINT)。
2.3.2 連接數據庫
在程序中,使用CONNECT語句來連接數據庫。該語句的完整語法爲:
exec sql connect : user [identified by : password]
[at : connection_name] [using : server]
[labelname labelname labelvalue labelvalue...] 其中,
lserver爲服務器名。如省略,則爲本地服務器名。
lconnection_name爲連接名。可省略。如果你僅僅使用一個連接,那麼無需指定連接名。可以使用SET CONNECTION來使用不同的連接。
luser爲登錄名。
lpassword爲密碼。
如:使用my_id用戶和passes密碼連接到SYBASE服務器。
exec sql begin declare section;
CS_CHAR user[16];
CS_CHAR passwd[16];
CS_CHAR server[BUFSIZ];
exec sql end declare section;
strcpy(server,"SYBASE");
strcpy(passwd,"passes");
strcpy(user, "my_id");
exec sql connect :user identified by :passwd using
:server;
請看下面這些例子來理解連接名的使用方法。
...
exec sql begin declare section;
CS_CHAR user[16];
CS_CHAR passwd[16];
CS_CHAR name;
CS_INT value, test;
CS_CHAR server_1[BUFSIZ];
CS_CHAR server_2[BUFSIZ];
exec sql end declare section;
...
strcpy (server_1, "sybase1");
strcpy (server_2, "sybase2");
strcpy(user, "my_id");
strcpy(passwd, "mypass");
exec sql connect :user identified by :passwd
at connection_2 using :server_2;
exec sql connect :user identified by :passwd using
:server_1;
/* 下面這個語句使用了"server_1"的連接*/
exec sql select royalty into :value from authors
where author = :name;
if (value == test)
{
/* 下面這個語句使用了"connection_2"連接 */
exec sql at connection_2 update authors
set column = :value*2
where author = :name;

在嵌入SQL語句中,使用DISCONNECT語句斷開數據庫的連接。其語法爲:
DISCONNECT [connection_name | ALL | CURRENT]
其中,connection_name爲連接名。ALL表示斷開所有的連接。CURRENT表示斷開當前連接。斷開連接會回滾當前事務、刪除臨時表、關閉遊標和釋放鎖等。
2.3.3 數據的查詢和修改
可以使用SELECT INTO語句查詢數據,並將數據存放在主變量中。如:查詢lastname爲stringer的firstname信息。
EXEC SQL SELECT au_fname INTO :first_name
from authors where au_lname = "stringer";
使用DELETE語句刪除數據。其語法類似於Transact-SQL中的DELETE語法。如:
EXEC SQL DELETE FROM authors WHERE au_lname = 'White'
使用UPDATE語句可以更新數據。其語法類似Transact-SQL中的UPDATE語法。如:
` EXEC SQL UPDATE authors SET au_fname = 'Fred' WHERE au_lname = 'White'
使用INSERT語句可以插入新數據。其語法就是Transact-SQL中的INSERT語法。如:
EXEC SQL INSERT INTO homesales (seller_name, sale_price)
real_estate('Jane Doe', 180000.00);
多行數據的查詢和修改請參見下一節——遊標。
2.3.4 遊標的使用
用嵌入式SQL語句查詢數據分成兩類情況。一類是單行結果,一類是多行結果。對於單行結果,可以使用SELECT INTO語句;對於多行結果,你必須使用cursor(遊標)來完成。遊標(Cursor)是一個與SELECT語句相關聯的符號名,它使用戶可逐行訪問由SQL Server返回的結果集。先請看下面這個例子,這個例子的作用是逐行打印staff表的id、name、dept、 job、years、salary和comm的值。
………..
EXEC SQL DECLARE C1 CURSOR FOR
SELECT id, name, dept, job, years, salary, comm FROM staff;
EXEC SQL OPEN c1;
while (SQLCODE == 0)
{
/* SQLCODE will be zero if data is successfully fetched */

EXEC SQL FETCH c1 INTO :id, :name, :dept, :job, :years, :salary, :comm;
if (SQLCODE == 0)
printf("%4d %12s %10d %10s %2d %8d %8d",
id, name, dept, job, years, salary, comm);
}
EXEC SQL CLOSE c1;
………
從上例看出,你首先應該定義遊標結果集,即定義該遊標的SELECT語句返回的行的集合。然後,使用FETCH語句逐行處理。
值得注意的是,嵌入SQL語句中的遊標定義選項同Transact-SQL 中的遊標定義選項有些不同。必須遵循嵌入SQL語句中的遊標定義選項。
1)、聲明遊標:
如:EXEC SQL DECLARE C1 CURSOR FOR
SELECT id, name, dept, job, years, salary, comm FROM staff;
其中,C1是遊標的名稱。
2)、打開遊標
如:EXEC SQL OPEN c1;
完整語法爲:EXEC SQL OPEN 遊標名 [USING 主變量名 | DESCRIPTOR 描述名]。關於動態OPEN遊標的描述見第四節。
3)、取一行值
如:EXEC SQL FETCH c1 INTO :id, :name, :dept, :job, :years, :salary, :comm;
關於動態FETCH語句見第四小節。
4)、關閉遊標
如:EXEC SQL CLOSE c1;
關閉遊標的同時,會釋放由遊標添加的鎖和放棄未處理的數據。在關閉遊標前,該遊標必須已經聲明和打開。另外,程序終止時,系統會自動關閉所有打開的遊標。
也可以使用UPDATE語句和DELETE語句來更新或刪除由遊標選擇的當前行。使用DELETE語句刪除當前遊標所在的行數據的具體語法如下:
DELETE [FROM] {table_name | view_name} WHERE CURRENT OF cursor_name
其中,
ltable_name是表名,該表必須是DECLARE CURSOR中SELECT語句中的表。
lview_name是視圖名,該視圖必須是DECLARE CURSOR中SELECT語句中的視圖。
lcursor_name是遊標名。
請看下面這個例子,逐行顯示firstname和lastname,詢問用戶是否刪除該信息,如果回答“是”,那麼刪除當前行的數據。
EXEC SQL DECLARE c1 CURSOR FOR
SELECT au_fname, au_lname FROM authors ;
EXEC SQL OPEN c1;
while (SQLCODE == 0)
{
EXEC SQL FETCH c1 INTO :fname, :lname;
if (SQLCODE == 0)
{
printf("%12s %12s/n", fname, lname);
printf("Delete? ");
scanf("%c", &reply);
if (reply == 'y')
{
EXEC SQL DELETE FROM authors WHERE CURRENT OF c1;
printf("delete sqlcode= %d/n", SQLCODE(ca));
}
}
}
EXEC SQL CLOSE c1;
2.3.5 SQLCA
DBMS是通過SQLCA(SQL通信區)嚮應用程序報告運行錯誤信息。SQLCA是一個含有錯誤變量和狀態指示符的數據結構。通過檢查SQLCA,應用程序能夠檢查出嵌入式SQL語句是否成功,並根據成功與否決定是否繼續往下執行。預編譯器自動會在嵌入SQL語句中插入SQLCA數據結構。在程序中可以使用EXEC SQL INCLUDE SQLCA,目的是告訴SQL預編譯程序在該程序中包含一個SQL通信區。也可以不寫,系統會自動加上SQLCA結構。
下表是SQLCA結構中的變量和作用:

變量 數據類型 作用

sqlcaid char 包含“sqlca”的字符串
sqlcabc long SQLCA的長度
sqlcode long 包含最近一次語句執行的返回代碼
sqlwarn[0] 到
sqlwarn[7] char 警告標誌。如果是“W”,那麼表示有警報信息。
sqlerrm.sqlerrmc[ ] char 錯誤信息。
sqlerrm.sqlerrml long 錯誤信息的長度。
sqlerrp char 檢測錯誤或警告信息的過程。
sqlerrd[6] long 警告或錯誤的詳細信息。[2]中存放影響行的個數。

下面仔細講解幾個重要的變量。
1)、SQLCODE
SQLCA結構中最重要的部分是SQLCODE變量。在執行每條嵌入式SQL語句時,DBMS在SQLCA中設置變量SQLCODE值,以指明語句的完成狀態:
1、0該語句成功執行,無任何錯誤或報警。
2、<0 出現了嚴重錯誤。
3、>0 出現了報警信息。
4、100沒有數據存在。在FETCH語句中,表示到達結果集的末尾。在UPDATE、
DELETE、INSERT語句中,表示沒有滿足條件的數據。
例:顯示錯誤信息。
printf("/nError occurred: code %d./n%s",
sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc);
在SYBASE SQL SERVER中,也可以單獨定義SQLCODE。如:
long SQLCODE;
exec sql open cursor pub_id;
while (SQLCODE == 0)
{
exec sql fetch pub_id into :pub_name;
…..

2)、SQLSTATE
SQLSTATE變量也是SQLCA結構中的成員。它同SQLCODE一樣,都是返回錯誤信息。SQLSTATE是在SQLCODE之後產生的。這是因爲,在制定SQL2標準之前,各個數據庫廠商都採用SQLCODE變量來報告嵌入式SQL語句中的錯誤狀態。但是,各個廠商沒有采用標準的錯誤描述信息和錯誤值來報告相同的錯誤狀態。所以,標準化組織增加了SQLSTATE變量,規定了通過SQLSTATE變量報告錯誤狀態和各個錯誤代碼。因此,目前使用SQLCODE的程序仍然有效,但也可用標準的SQLSTATE錯誤代碼編寫新程序。值得注意的是,Open client emebeded SQL/C11.1.x並不完全支持SQLSTATE。
SQLSTATE是一個字符串參數。具體含義如下:

值 作用

00XXX 成功
01XXX 警告
02XXX 不存在數據
其他值 錯誤

2.3.6 WHENEVER
在每條嵌入式SQL語句之後立即編寫一條檢查SQLCODE/SQLSTATE值的程序,是一件很繁瑣的事情。爲了簡化錯誤處理,可以使用WHENEVER語句。該語句是SQL預編譯程序的指示語句,而不是可執行語句。它通知預編譯程序在每條可執行嵌入式SQL語句之後自動生成錯誤處理程序,並指定了錯誤處理操作。
用戶可以使用WHENEVER語句通知預編譯程序去如何處理三種異常處理:
lWHENEVER SQLERROR action:表示一旦sql語句執行時遇到錯誤信息,則執行action,action中包含了處理錯誤的代碼(SQLCODE<0)。
lWHENEVER SQLWARNING action:表示一旦sql語句執行時遇到警告信息,則執行aciton,即action中包含了處理警報的代碼(SQLCODE=1)。
lWHENEVER NOT FOUND action:表示一旦sql語句執行時沒有找到相應的元組,則執行action,即action包含了處理沒有查到內容的代碼(SQLCODE=100)。
針對上述三種異常處理,用戶可以指定預編譯程序採取以下三種行爲(action):
lWHENEVER …GOTO:通知預編譯程序產生一條轉移語句。
lWHENEVER…CONTINUE:通知預編譯程序讓程序的控制流轉入到下一個主語言語句。
lWHENEVER…CALL:通知預編譯程序調用函數。
其完整語法如下:
WHENEVER {SQLWARNING | SQLERROR | NOT FOUND} {CONTINUE | GOTO stmt_label | CALL function()}
例:WHENEVER的作用
EXEC SQL WHENEVER sqlerror GOTO errormessage1;
EXEC SQL DELETE FROM homesales
WHERE equity < 10000;
EXEC SQL DELETE FROM customerlist
WHERE salary < 40000;
EXEC SQL WHENEVER sqlerror CONTINUE;
EXEC SQL UPDATE homesales
SET equity = equity - loanvalue;
EXEC SQL WHENEVER sqlerror GOTO errormessage2;
EXEC SQL INSERT INTO homesales (seller_name, sale_price)
real_estate('Jane Doe', 180000.00);
.
.
.
errormessage1:
printf("SQL DELETE error: %ld/n, sqlcode);
exit();

errormessage2:
printf("SQL INSERT error: %ld/n, sqlcode);
exit();
WHENEVER語句是預編譯程序的指示語句。在上面這個例子中,由於第一個WHENEVER語句的作用,前面兩個DELETE語句中任一語句內的一個錯誤會在errormessage1中形成一個轉移指令。由於一個WHENEVER語句替代前面WHENEVER語句,所以,嵌入式UPDATE語句中的一個錯誤會直接轉入下一個程序語句中。嵌入式INSERT語句中的一個錯誤會在errormessage2中產生一條轉移指定。
從上面例子看出,WHENEVER/CONTINUE語句的主要作用是取消先前的WHENEVER語句的作用。WHENEVER語句使得對嵌入式SQL錯誤的處理更加簡便。應該在應用程序中普遍使用,而不是直接檢查SQLCODE的值。
2.3.7 批處理
嵌入SQL也支持批處理。如:
exec sql insert into TABLE1 values (:val1)
insert into TABLE2 values (:val2)
insert into TABLE3 values (:val3);
SYBASE SQL SERVER將在EXEC SQL和“;”之間的所有T-SQL語句作爲一個批來處理。在上例中,會將這3個語句作爲一組來處理。
2.3.8 事務
SYBASE SQL SERVER預編譯器能夠處理兩種事務模式:ANSI/ISO事務模式和T-SQL模式。在T-SQL模式中,除非有begin transaction外,每個語句都會做提交。可以在編譯時設置事務模式。ANSI/ISO模式是系統的缺省模式。嵌入SQL的事務語法和T-SQL的事務語法是相同的。
2.3.8.1 T-SQL事務模式
1)、開始事務
exec sql [at connect_name]
begin transaction [ transaction_name];
2)、保存事務回滾點
exec sql [at connect_name]
save transaction [ savepoint_name];
3)、提交事務
exec sql [at connect_name] commit transaction
[ transaction_name];
4)、回滾事務
exec sql [at connect_name] rollback transaction
[ savepoint_name | transaction_name];
2.3.8.2 ANSI/ISO事務模式
該模式沒有begin transaction和save transaction。在應用程序中,只要遇到以下語句,就表示事務開始:delete、insert、select、update、open和exec。當遇到commit work或rollback work,就表示事務結束。也就是說,commit和rollback表示當前事務結束,下一個事務開始。
2.4動態SQL語句
前一節中講述的嵌入SQL語言都是靜態SQL語言,即在編譯時已經確定了引用的表和列。主變量不改變表和列信息。在上幾節中,我們使用主變量改變查詢參數,但是不能用主變量代替表名或列名。否則,系統報錯。動態SQL語句就是來解決這個問題。
動態SQL語句的目的是,不是在編譯時確定SQL的表和列,而是讓程序在運行時提供,並將SQL語句文本傳給DBMS執行。靜態SQL語句在編譯時已經生成執行計劃。而動態SQL語句,只有在執行時才產生執行計劃。動態SQL語句首先執行PREPARE語句要求DBMS分析、確認和優化語句,併爲其生成執行計劃。DBMS還設置SQLCODE以表明語句中發現的錯誤。當程序執行完“PREPARE”語句後,就可以用EXECUTE語句執行執行計劃,並設置SQLCODE,以表明完成狀態。
使用動態SQL,共分成四種方法:

方法 支持的SQL語句 實現方法

1 該語句內不包含宿主變量,該語句不是查詢語句 execute immediate
2 該語句內包含輸入宿主變量 ,該語句不是查詢語句 prepare和execute
3 包含已知數目的輸入宿主變量或列的查詢 prepare和fetch
4 包含未知數目的輸入宿主變量或列的查詢 prepare和fetch,用描述符

按照功能和處理上的劃分,動態SQL應該分成兩類來解釋:動態修改和動態查詢。方法1和方法2完成動態修改(參見2.4.1)。方法3和方法4完成了動態查詢(參見2.4.2和2.4.3)。
2.4 .1 動態修改
方法1和方法2完成動態修改。對於方法1,表示要執行一個完整的T-SQL語句,該語句沒有宿主變量,不是一個查詢語句。因爲沒有宿主變量來帶入不同的參數,所以不能通過方法1來重複執行修改語句。具體語法爲:
exec sql [at connection_name] execute immediate
{: host_variable | string};
其中,host_variable和string是存放完整T-SQL語句。
例:提示用戶輸入被更新書的條件,然後組合成爲一個完整的SQL語句,並執行更新。
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,可以執行一個包含輸入宿主變量的動態修改語句。該方法要使用PREPARE語句和EXECUTE語句。PREPARE語句是動態SQL語句獨有的語句。其語法爲:
PREPARE 語句名 FROM 宿主變量|字符串
該語句接收含有SQL語句串的宿主變量,並把該語句送到DBMS。DBMS編譯語句並生成執行計劃。在語句串中包含一個“?”表明參數,當執行語句時,DBMS需要參數來替代這些“?”。PREPRARE執行的結果是,DBMS用語句名標誌準備後的語句。SQL SERVER編譯後的語句以臨時存儲過程的形式存放在緩衝區中。語句名類似於遊標名,是一個SQL標識符。在執行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語句,包含了三個參數(3個“?”)。PREPARE的作用是,DBMS編譯這個語句並生成執行計劃,並把語句名標誌這個準備後的語句。值得注意的是,PREPARE中的語句名的作用範圍爲整個程序,所以不允許在同一個程序中使用相同的語句名在多個PREPARE語句中。
EXECUTE語句是動態SQL獨有的語句。它的語法如下:
EXECUTE 語句名 USING 宿主變量 | DESCRIPTOR 描述符名
請看上面這個例子中的“EXEC SQL EXECUTE prep_stat USING :name, :car, :num;”語句,它的作用是,請求DBMS執行PREPARE語句準備好的語句。當要執行的動態語句中包含一個或多個參數標誌時,在EXECUTE語句必須爲每一個參數提供值,如::name、:car和:num。這樣的話,EXECUTE語句用宿主變量值逐一代替準備語句中的參數標誌(“?”),從而,爲動態執行語句提供了輸入值。
使用主變量提供值,USING子句中的主變量數必須同動態語句中的參數標誌數一致,而且每一個主變量的數據類型必須同相應參數所需的數據類型相一致。各主變量也可以有一個伴隨主變量的指示符變量。當處理EXECUTE語句時,如果指示符變量包含一個負值,就把NULL值賦予相應的參數標誌。除了使用主變量爲參數提供值,也可以通過SQLDA提供值(見節2.4.4)。
2.4.2 動態遊標
使用動態遊標可以完成方法3。
遊標分爲靜態遊標和動態遊標兩類。對於靜態遊標,在定義遊標時就已經確定了完整的SELECT語句。在SELECT語句中可以包含主變量來接收輸入值。當執行遊標的OPEN語句時,主變量的值被放入SELECT語句。在OPEN語句中,不用指定主變量,因爲在DECLARE CURSOR語句中已經放置了主變量。請看下面靜態遊標的例子:
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;
動態遊標和靜態遊標不同。以下是動態遊標使用的句法(請參照本小節後面的例子來理解動態遊標)。
1)、聲明遊標:
對於動態遊標,在DECLARE CURSOR語句中不包含SELECT語句。而是,定義了在PREPARE中的語句名,PREPARE語句規定與查詢相關的語句名稱。具體語法爲:
exec sql [at connection_name] declare cursor_name
cursor for statement_name;
如:EXEC SQL DECLARE author_cursor CURSOR FOR select_statement;
值得注意的是,聲明動態遊標是一個可執行語句,應該在PREPARE語句後執行。
2)、打開遊標
完整語法爲:OPEN 遊標名 [USING 主變量名 | DESCRIPTOR 描述名]
在動態遊標中,OPEN語句的作用是使DBMS定位相關的遊標在第一行查詢結果前。當OPEN語句成功執行完畢後,遊標處於打開狀態,併爲FETCH語句做準備。OPEN語句執行一條由PREPARE語句預編譯的語句。如果動態查詢正文中包含有一個或多個參數標誌時,OPEN語句必須爲這些參數提供參數值。USING子句的作用就是規定參數值。可以使用主變量提供參數值,也可以通過描述名(即SQLDA)提供參數值。如:EXEC SQL OPEN author_cursor USING :szLastName;。
3)、取一行值
FETCH語法爲:FETCH 遊標名 INTO USING DESCRIPTOR 描述符名。
動態FETCH語句的作用是,把遊標移到下一行,並把這一行的各列值送到SQLDA中。注意的是,靜態FETCH語句的作用是用主變量表接收查詢到的列值。在方法3中,使用的是靜態FETCH語句獲得值。動態FETCH語句只在方法4中使用。
4)、關閉遊標
如:EXEC SQL CLOSE c1;
關閉遊標的同時,會釋放由遊標添加的鎖和放棄未處理的數據。在關閉遊標前,該遊標必須已經聲明和打開。另外,程序終止時,系統會自動關閉所有打開的遊標。
總之,在動態遊標的DECLARE CURSOR語句中不包含SELECT語句。而是,定義了在PREPARE中的語句名,用PREPARE語句規定與查詢相關的語句名稱。當PREPARE語句中的語句包含了參數,那麼在OPEN語句中必須指定提供參數值的主變量或SQLDA。動態DECLARE CURSOR語句是一個可執行語句。該子句必須在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;
………
下面是一個實現方法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;
………..
2.4.3 SQLDA
要實現方法4,則需要使用SQLDA(也可以使用SQL Descriptors,請讀者參閱幫助信息)。可以通過SQLDA爲嵌入SQL語句提供不確定的輸入數據和從嵌入SQ語句中輸出不確定數據。理解SQLDA的結構是理解動態SQL的關鍵。
我們知道,動態SQL語句在編譯時可能不知道有多少列信息。在嵌入SQL語句中,這些不確定的數據是通過SQLDA完成的。SQLDA的結構非常靈活,在該結構的固定部分,指明瞭多少列等信息(如下圖中的sqld=2,表示爲兩列信息),在該結構的後面,有一個可變長的結構(sd_column結構),說明每列的信息。


SQLDA結構
Sd_Sqld=2
Sd_column
……



Sd_datafmt
Sd_Sqllen
Sd_sqldata
…..

Sd_datafmt
Sd_Sqllen
Sd_Sqldata
…..

 

圖6-2 SQLDA結構示例
具體SQLDA的結構在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是一種由兩個不同部分組成的可變長數據結構。從位於SQLDA開端的sd_sqln到sd_sqld爲固定部分,用於標誌該SQLDA,並規定這一特定的SQLDA的長度。而後是一個或多個sd_column結構 ,用於標誌列數據或參數。當用SQLDA把參數送到執行語句時,每一個參數都是一個sd_column結構;當用SQLDA返回輸出列信息時,每一列都是一個sd_column 結構。具體每個元素的含義爲:
lSd_Sqln。分配的sd_column結構的個數。等價於可以允許的最大輸入參數的個數或輸出列的個數。
lSd_Sqld。目前使用的sd_column結構的個數。
lSd_column[].sd_datafmt。標誌同列相關的CS_DATAFMT結構。
lSd_column[].sd_Sqldata。指向數據的地址。注意,僅僅是一個地址。
lSd_column[].sd_sqllen。sd_sqldata指向的數據的長度。
lSd_column[].sd_Sqlind。代表是否爲NULL。如果該列不允許爲NULL,則該字段不賦值;如果該列允許爲NULL,則:該字段若爲0,表示數據值不爲NULL,若爲-1,表示數據值爲NULL。
lSd_column[].sd_sqlmore。保留爲將來使用。

下面我們來看一個具體的例子。這個例子是通過output_descriptor查詢數據庫中的數據,是通過input_descriptor傳遞參數。這個例子的作用是,模擬一個動態查詢,並顯示查詢結果。動態查詢的執行過程如下:
1)、如同構造動態UPDATE語句或DELETE語句的方法一樣,程序在緩衝器中構造一個有效的SELECT語句。
2)、程序用PREPARE語句把動態查詢語句送到DBMS,DBMS準備、確認和優化語句,並生成一個應用計劃。
3)、動態DECLARE CURSOR語句說明查詢遊標,動態DECLARE CURSOR語句規定與動態SELECT語句有關的語句名稱。如:例子中的statement。
4)、程序用DESCRIBE語句請求DBMS提供SQLDA中描述信息,即告訴程序有多少列查詢結果、各列名稱、數據類型和長度。DESCRIBE語句只用於動態查詢。具體見下一節。
5)、爲SQLDA申請存放一列查詢結果的存儲塊(即:sqldata指向的數據區),也爲SQLDA的列的指示符變量申請空間。程序把數據區地址和指示符變量地址送入SQLDA,以告訴DBMS向何處回送查詢結果。
6)、動態格式的OPEN語句。即打開存放查詢到的數據集(動態SELECT語句產生的數據)的第一行。
7)、動態格式的FETCH語句把遊標當前行的結果送到SQLDA。(動態FETCH語句和靜態FETCH語句的不同是:靜態FETCH語句規定了用主變量接收數據;而動態FETCH語句是用SQLDA接收數據。)並把遊標指向下一行結果集。
8)、CLOSE語句關閉遊標。
具體程序如下:

exec sql include sqlca;
exec sql include sqlda;
...
/*input_ descriptor是通過SQLDA傳遞參數,output_descriptor是通過SQLDA返回列數據*/
SQLDA *input_descriptor, *output_descriptor;
CS_SMALLINT small;
CS_CHAR character[20];
/*申請空間*/
input_descriptor = (SQLDA *)malloc(SYB_SQLDA_SIZE(3));
/*設置參數的最大個數*/
input_descriptor->sqlda_sqln = 3;
/*申請空間*/
output_descriptor = (SQLDA *)malloc(SYB_SQLDA_SIZE(3));
/*設置列數的最大值*/
output_descriptor->sqlda_sqln = 3;
*p_retcode = CS_SUCCEED;
/*連接數據庫服務器*/
exec sql connect "sa" identified by password;
/* 創建一張example表,並插入一些例子數據,用於演示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);
/* 準備和描述查詢語句*/
exec sql prepare statement from
"select fruit from example where number = ?";
/*describe語句的作用是,將查詢所需要的參數信息存放在input_descriptor中*/
exec sql describe input statement using descriptor input_descriptor;
/*設置SQLDA中指向參數數據的地址信息(sqldata)和數據長度(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");
/*設置存放列數據的地址信息*/
output_descriptor->sqlda_column[0].sqlda_sqldata = character;
output_descriptor->sqlda_column[0].sqlda_datafmt.maxlength = 20;
/*通過input_descriptor將輸入參數帶入查詢語句,並將結果通過output_descriptor帶出*/
exec sql execute statement into descriptor output_descriptor /
using descriptor input_descriptor;
/*打印結果---單行結果*/
printf("expected pomegranate, got %s/n",character);
/*釋放申請的內存空間*/
exec sql deallocate prepare statement;
/* 多行結果示例。對多行查詢語句做準備和描述操作*/
exec sql prepare statement from /
"select number from example where fruit = ?";
/*爲多行結果聲明遊標*/
exec sql declare c cursor for statement;

exec sql describe input statement using descriptor input_descriptor;
/*設置查詢的參數地址信息*/
input_descriptor->sqlda_column->sqlda_sqldata = character;
input_descriptor->sqlda_column->sqlda_datafmt.maxlength =CS_NULLTERM;
/*設置參數值爲banana,也可以提示用戶輸入這些信息*/
strcpy(character, "banana");
input_descriptor->sqlda_column->sqlda_sqllen = CS_NULLTERM;
/*打開遊標*/
exec sql open c using descriptor input_descriptor;
/*設置輸出列的信息*/
exec sql describe output statement using descriptor output_descriptor;
/*設置存放數據的地址信息*/
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;
/*打印列的數據*/
printf("expected pomegranate, got %s/n", character);
exec sql commit work;
……….
上面這個例子是典型的動態查詢程序。該程序中演示了PREPARE語句和DESCRIBE語句的處理方式,以及爲程序中檢索到的數據分配空間。要注意程序中如何設置sqlda_column結構中的的各個變量。這個程序也演示了OPEN、FETCH和CLOSE語句在動態查詢中的應用。值得注意的是,FETCH語句只使用了SQLDA,不使用主變量。由於程序中預先申請了sqlda_column結構中的SQLDATA空間,所以DBMS知道將查詢到的數據保存在何處。該程序還考慮了查詢數據爲NULL的處理。
值得注意的是,SQDA結構不是SQL標準。每個數據庫廠商的實現方式有可能不同。
2.4.4 DESCRIBE語句
該語句只有動態SQL纔有。該語句是在PREPARE語句之後,在OPEN語句之前使用。該語句的作用是,設置SQLDA中的描述信息,如:列名、數據類型和長度等。DESCRIBE語句的語法爲:
DESCRIBE 語句名 INTO 描述符名
如:exec sql describe output statement using descriptor output_descriptor;。
在執行DESCRIBE前,用戶必須給出SQLDA中的SQLN的值(表示最多有多少列),該值也說明了SQLDA中最多有多少個sqlda_column結構。然後,執行DESCRIBE語句,該語句填充每一個sqlda_column結構。每個sqlda_column結構中的相應列爲:
lSd_datafmt結構:列名等信息。
lSd_sqllen列:給出列的長度。
注意,sd_sqldata列不填充。由程序在FETCH語句之前,給出數據緩衝器地址和指示符地址。
2.5 兩個例子程序
2.5.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 SQLNOTFOUND100
#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服務器*/
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 );
/*聲明遊標*/
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;
/*打開遊標,指向查詢相關電話信息的結果集*/
EXEC SQL OPEN c1; /* :rk.2:erk. */
do{
/*取出一行數據到各個變量*/
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;
/*顯示數據*/
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.5.2 ADHOC程序
該程序的功能是:用戶輸入任意SQL語句,並執行和打印結果。


#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/* Defines for BINDING */
/*初試化SQLDA*/
int init_da (SQLDA **DAPointer, int DAsqln);
/*爲存放列數據的sd_column結構申請空間*/
int alloc_host_vars (SQLDA *sqldaPointer);
/*釋放SQLDA所申請的空間*/
void free_da (SQLDA *sqldaPointer);
/*獲取列名信息*/
char * readColName (SQLDA *sqldaPointer, short sd_columnIndex, char * buffer);
/*獲取列數據*/
char * readCol (SQLDA *sqldaPointer, short sd_columnIndex, char * buffer);
#ifdef __cplusplus
}
#endif
/*定義最大列數*/
#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 ) ;
/*打印處理結果*/
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結構申請空間" */
if (init_da( &sqldaPointer, MAX_COLUMNS ) == -1)
{
return -1;
}
/*準備SQL語句*/
EXEC SQL PREPARE statement1 from :st ;
if (SQLCODE < 0)
{
free_da(sqldaPointer);
return SQLCODE;
}
/*獲取查詢列的信息到SQLDA結構*/
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 ) {
/* 爲存放列數據的sd_column結構申請空間 */
if (alloc_host_vars( sqldaPointer ) == -1)
{free_da(sqldaPointer);
return -1;
}
/*聲明遊標*/
EXEC SQL DECLARE pcurs CURSOR FOR statement1 ;
/打開遊標*/
EXEC SQL OPEN pcurs ;
if (SQLCODE < 0)
return SQLCODE;
/*取一行數據到SQLDA結構*/
EXEC SQL FETCH pcurs INTO DESCRIPTOR sqldaPointer;
if (SQLCODE < 0)
{
EXEC SQL CLOSE pcurs ;
return SQLCODE;
}
/*顯示列標題 */
colnamelist[0] = 0;
for ( idx=0; idx< sqlda_d; idx++)
{ strcat(colnamelist, readColName(sqldaPointer, idx, buffer));
if (idx < sqlda_d -1)
strcat(colnamelist, ",");
}
/* 顯示行數據*/
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 */
/*關閉遊標*/
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
*爲存放列數據的sd_column結構申請空間。如果返回-1,則表示不能獲得足夠內存。
******************************************************************************/
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
* 返回列數據。
******************************************************************************/
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;
}
/*返回列數據到buffer變量*/
strcpy( buffer, (char *) sqldaPointer->sd_column[ sd_columnIndex ].sd_sqldata);
return buffer;
}
/* COMMENT OUT OFF */ 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章