本節講述內容: 程序員
1.嵌入式SQL 語言概述sql
2.變量聲明與數據庫鏈接數據庫
3.數據集與遊標ide
4.可滾動遊標與數據庫的增刪改fetch
5.狀態捕捉以及錯誤處理機制ui
(一)嵌入式SQL語言atom
以前咱們所學的都是交互式SQL 語言: select .. from .. where..spa
嵌入式SQL語言 表示 將SQL語言嵌入到 某一種高級語言中使用, 好比C++ ,Java, powerbuilder等設計
它們也稱爲宿主語言(host language).3d
複雜的檢索不能用一條SQL語句完成, 須要結合高級語言中的順序\分支\循環結構幫助處理.
if [conditon] then SQL_query else SQL_query end if
do while [condition] SQL_query end do
還有在SQL語句的檢索結果基礎上,再進行處理的
SQL_query1 for ... do process the record next SQL_query 2 if .. then else end if
交互式SQL: select sname, sage from student where sname='xy';
嵌入式SQL: 以宿主語言C語言爲例,
exec sql select sname, sage into :vsname, :vsage from student where sname='xy';
主要區別:
(1) exex sql 是一個引導詞, 它引導sql 語句, 將SQL語句預編譯成C編譯器可識別的語句.
(2) 增長 into 子句, 用於把SQL 語句的檢索結果賦給高級語言的程序變量
(3) 用冒號開頭 表示高級語言的程序變量 :vsname , :vsage
冒號很重要, 用於區分是程序變量 仍是 表的字段!! .... 還有不少特色以後在詳細介紹
爲啥要學嵌入式SQL , 用來解決啥問題?
下面逐個解決上述8個問題
(一) 數據庫的鏈接(問題1)
在嵌入式SQL 操做以前, 首先須要與數據庫進行鏈接
不一樣的DMBS 的語句是有差異的,
在嵌入式SQL程序執行以後, 須要斷開數據庫
SQL 執行的提交與撤銷
SQL語句在執行過程當中, 必需要有提交, 撤銷語句
提交: exec sql commit work;
撤銷: exec sql rollback work;
不少DBMS 都設計了捆綁 提交\撤銷 與斷開鏈接在一塊兒的語句, 以保證在斷開鏈接以前
使用戶確認提交或 撤銷先前的工做, Oracle 中就是這樣:
exec sql commit release;
exec sql rollback release;
爲何須要提交和撤銷呢? 這個設計到數據庫中的'' 事務 ''處理
什麼是事務? 從應用程序員角度來看, 事物是一個存取或者改變數據庫內容的程序的一次執行,
或者說是一條或者多條SQL 語句的一次執行被看作是一個事務
事務 通常由應用程序員提出, 所以有開始和結束, 結束前須要提交或者撤銷
begin transaction exec sql... exec sql... exec sql commit work | exec sql rollback work --提交或者撤銷 end transaction
注意: 提交表示這一系列操做對數據庫的更新是有效的, 撤銷則表示無效
其實從 任何一個SQL語句執行 就表示了一個事務的開始, 到了 commit 或 rollback 則結束一個事務,
所以上述的 begin end 能夠省略.
事務的ACID 特性
A : atomicity 原子性, DBMS保證表示事務中的一組操做是不可分的,要麼全作,要麼一條也不作
C : consistency 一致性,例如兩我的同時在買車票,會不會買到同一張車票
I: isolation 隔離性 兩個事務操做互不干擾
D: durability 已提交事務的影響是持久的, 被撤銷的事務影響能夠恢復
事務處理技術是DBMS的核心處理技術!!
(二) 變量聲明(問題2)
exec sql select sname, sage into :vsname, :vsage from student where sname=:specname;
加了冒號表示高級語言的程序變量, 這些變量須要聲明
exec sql begin declare section; --開始聲明 char vsname[10], specname[10] ='xy' ; int vsage; exec sql end declare section; -- 結束聲明
注: 宿主程序的字符串變量長度要比字符型字段多1, 由於宿主程序的字符串尾部多一個終止符'\0' .
-- 變量的聲明與使用
exec sql begin declare section; char vsname[10], specname[10] ='xy' ; int vsage; exec sql end declare section; -- 用戶在此處 能夠基於鍵盤輸入給specname 賦值 exec sql select sname, sage into :vsname, :vsage from student where sname=:specname;
實例: 數據庫鏈接+變量定義
#include<stdio.h> #include"prompt.h" exec sql include sqlca; --sqlca 表示SQL的通訊區, communication area char cid_prompt[]="please enter customer id:"; int main() { exec sql begin declare section; --下面聲明變量 char cust_id[5], cust_name[14]; float cust_discnt; exec sql end declare section; exec sql whenever sqlerror goto report_error;-- 錯誤捕獲 exec sql whenever not found goto notfound; -- 記錄沒有找到 strcpy(user_name,"poneilsql");-- 字符串賦值 strcpy(user_pwd,"123456"); exec sql connect :user_name identified by :user_pwd; -- 鏈接數據庫 while((prompt(cid_prompt,1,cust_id,4))>=0){ exec sql select cname,discnt into :cust_name,:cust_discnt from customers where cid=:cust_id; -- 根據輸入的客戶id 找到名字和折扣 exec sql commit work;-- 提交 printf("customer's name is %s and discount is %.1f\n",cust_name, cust_discnt); continue; -- 接着循環,再輸入客戶id notfound:printf("can't find customer %s, continuing\n", cust_id);} exec sql commit release; -- 斷開數據庫的鏈接 return 0; report_error: -- 前面報錯的執行 print_dberror(); exec sql rollback release; -- 斷開鏈接 return 1; }
(三) 數據集與遊標(問題3 4 5)
問題3: SQL 語句如何執行?
問題4: 如何將SQL 檢索到的結果傳遞迴宿主程序進行處理?
問題5: 如何將靜態SQL , SQL語句中的常量更換爲變量?
如何讀取單行數據和多行數據, 單行結果處理與多行結果處理的差別: into 子句 和 遊標 cursor
1. 檢索單行結果, 能夠將結果直接傳送到宿主主程序的變量中, select ... into ...
exec sql select sname, sage into :vsname, :vsage from student where sname=:specname;
2. 若是是多行結果, 則須要使用遊標cursor
遊標是指向某個檢索記錄的指針, 經過這個指針, 每次讀一行, 處理一行,
接着再讀一行...,直到所有處理完畢 fetch..into... (一次一行)
須要先定義一個cursor-->再打開-->接着一條一條處理-->最後關閉
exec sql delcare cur_student cursor for --遊標名 select sno, sname, sclass from student where sclass='0315'; -- 定義遊標 exec sql open cur_student; --打開遊標 exec sql fetch cur_student into :vsno, :vsname, :vsclass; --取數據
... exec sql close cur_student; --關閉遊標
具體實例:
已知表orders(cid, aid, product, dollars) 客戶id, 代理人id, 產品, 金額
遊標: 給定一個客戶id, 選出該客戶下的全部代理商 和 金額(多行數據)
#define True 1 #include<stdio.h> #include"prompt.h" exec sql include sqlca; --sqlca 表示SQL的通訊區, communication area exec sql begin declare section; --聲明變量 char cust_id[5], agent_id[14]; double dollar_sum; exec sql end declare section; int main() { char cid_prompt[]="please enter customer id:"; -- 定義提示字符串 exec sql declare agent_dollars cursor for -- 定義遊標 select aid,sum(dollars) from orders where cid=:cust_id group by aid; exec sql whenever sqlerror goto report_error;-- 錯誤捕獲 exec sql connect to testdbl; --鏈接數據庫 exec sql whenever not found goto finish; -- 記錄沒有找到 while((prompt(cid_prompt,1,cust_id,4))>=0){ exec sql open agent_dollars; -- 打開遊標 while(True){ -- 打印每一條記錄 exec sql fetch agent_dollars into :agent_id,:dollar_sum; printf("%s %11.2f\n",agent_id, dollar_sum) }; finish: exec sql close agent_dollars; -- 關閉遊標 exec sql commit work; -- 提交 exec sql disconnect current;--斷開鏈接 return 0; report_error: -- 前面報錯的執行 print_dberror(); exec sql rollback;-- 撤銷 exec sql disconnect current; --斷開鏈接 return 1; }
總結遊標:
exec sql delcare cur_student cursor for --遊標名 select sno, sname, sclass from student where sclass=:vclass; -- 定義遊標 order by sno for read only; --只讀, 不可更新
cursor 數據讀取 fetch : exec sql fetch cursor_name into host_variable
exec sql delcare cur_student cursor for --遊標名 select sno, sname, sclass from student where sclass=:vclass; -- 定義遊標 order by sno for read only; --只讀, 不可更新 exec sql open cur_student; -- 打開 exec sql fetch cur_student into :vsno, :vsname, :vsage; -- 使用 exec sql close cur_student; -- 關閉
可滾動遊標與數據庫的增刪改
標註的遊標 始終是自開始到結束方向移動的, 每fetch 一次,向結束方向移動一次,
每一條記錄只能被訪問一次, 再次訪問該記錄只能關閉遊標後從新打開
可不能夠實現遊標的向上移動呢? ODBC (open database connectivity) 是一種跨DBMS
的DB 操做平臺, 它在應用程序與實際的DBMS之間提供了一種通用的接口,
不少DBMS不支持可滾動遊標, 可是經過ODBC 可實現該功能
定義中增長了 scroll
使用以下:
可滾動遊標移動時須要判斷 是否到告終束位置, 或者到了起始位置,
EOF表示最後一條記錄的後面位置
BOF表示起始位置的前面
若是不須要區分最上 最下, 則能夠用whenever not found 進行檢測
用遊標進行數據庫的增刪改
1. 查找刪除(與交互式delete 語句相同)
exec sql delete from customers c where c.city='harbin' and not exists (select * from orders o where o.cid=c.cid) -- 刪除 城市是哈爾濱 且在訂單 orders表裏面沒有記錄的.
2. 定位刪除
exec sql declare delcust cursor for select cid from customers c where c.city='harbin' and not exists (select * from orders o where o.cid=c.cid) for update of cid; exec sql open delcust while(True){ exec sql fetch delcust into :cust_id; exec sql delete from customers where current of delcust;}
1. 查找更新
exec sql update student s set scalss='0315' where s.sclass='0314';
2.定位更新
exec sql declare stud cursor for select * from student s where s.sclass='0314' and for update of sclass; exec sql open stud while(True){ exec sql fetch stud into :vsno, :vsname,:vsclass; exec sql update student set sclass='0315' where current of stud;}
插入語句
exec sql insert into student(sno,sname,sclass) values ('031501','xy','0315'); exec sql insert into master_stud(sno,sname,sclass) select sno,sname,sclass from student;
綜合實例: 求數據庫中某一列位於中值的那一行
--已知表 orders(cid,aid,product,dollars) -- 尋找數據庫中某一列位於中值的那一行 #include<stdio.h> #include"prompt.h" exec sql include sqlca; --sqlca 表示SQL的通訊區, communication area char cid_prompt[]="please enter customer id:"; -- 定義提示字符串 int main() { exec sql begin declare section; --聲明變量 char cid[5], user_name[20], user_pwd[10]; double dollars; int ocount; exec sql end declare section; exec sql declare dollars_cursor cursor for -- 定義遊標 select dollars from orders where cid=:cid and dollars is not null order by dollars; exec sql whenever sqlerror goto report_error;-- 錯誤捕獲 strcpy(user_name,"poneilsql");-- 字符串賦值 strcpy(user_pwd,"123456"); exec sql connect :user_name identified by :user_pwd; -- 鏈接數據庫 --exec sql whenever not found goto finish; -- 記錄沒有找到 while((prompt(cid_prompt,1,cust_id,4))>=0){ exec sql select count(dollars) into :ocount from orders where cid=:cid; if(ocount==0) {printf("no record reviewed for cid value %s\n",cid); continue;} exec sql open dollars_cursor; for (i=0;i<(ocount+1)/2;i++) exec sql fetch dollars_cursor into :dollars ; exec sql close dollars_cursor; exec sql commit work; -- 提交 printf("median dollar amount=%f\n",dollars); }