SQL入門(4): 嵌入式SQL語言

本節講述內容: 程序員

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); }
相關文章
相關標籤/搜索