日韩黑丝制服一区视频播放|日韩欧美人妻丝袜视频在线观看|九九影院一级蜜桃|亚洲中文在线导航|青草草视频在线观看|婷婷五月色伊人网站|日本一区二区在线|国产AV一二三四区毛片|正在播放久草视频|亚洲色图精品一区

分享

游標(biāo)

 修行的嘟嘟 2011-08-03

還是看下上篇的那段代碼:

create proc pro_abc
as
declare @p1 nvarchar(50),@p2 int;
declare my_cursor cursor scroll dynamic
for SELECT F_CR_JSGM,f_nb_xmbm FROM B_JIHUA_XIANGMU WHERE NOT  F_CR_JSGM IS NULL
open my_cursor
fetch next from my_cursor into @p1,@p2
while(@@fetch_status=0)
begin
update b_jihua_jsgm set f_cr_jsgm2=@p1 where f_nb_xmbm=@p2
fetch next from my_cursor into @p1,@p2
end
close my_cursor
deallocate my_cursor
分解:這段是存儲過程
create proc pro_abc--
as
declare @p1 nvarchar(50),@p2 int;
begin
select 'aaaa'
end

加上游標(biāo)就是這樣了

create proc pro_abc
as
declare @p1 nvarchar(50),@p2 int;--聲明變量和類型
--聲明游標(biāo)的名稱my_cursor;這個游標(biāo)所用到的SQL語句
----scroll表示可隨意移動游標(biāo)指        針(否則只能向前),dynamic表示可以讀寫游標(biāo)(否則游標(biāo)只讀)*/
declare my_cursor cursor scroll dynamic
for SELECT F_CR_JSGM,f_nb_xmbm FROM B_JIHUA_XIANGMU WHERE NOT  F_CR_JSGM IS NULL
--打開游標(biāo)
open my_cursor
--必須用FETCH語句來取得數(shù)據(jù) 可以傳遞參數(shù)
fetch next from my_cursor into @p1,@p2
--fetch_status=0一切正常
while(@@fetch_status=0)
begin
update b_jihua_jsgm set f_cr_jsgm2=@p1 where f_nb_xmbm=@p2
fetch next from my_cursor into @p1,@p2
end
--關(guān)閉游標(biāo)
close my_cursor
--刪除游標(biāo)資源
deallocate my_cursor

XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

聲明游標(biāo) 
象使用其它類型的變量一樣,使用一個游標(biāo)之前,首先應(yīng)當(dāng)聲明它。游標(biāo)的聲明包括兩個部分:游標(biāo)的名稱;這個游標(biāo)所用到的SQL語句

在游標(biāo)的聲明中有一點值得注意的是,如同其它變量的聲明一樣,聲明游標(biāo)的這一段代碼行是不執(zhí)行的,您不能將debug時的斷點設(shè)在這一代碼行上,也不能用IF...END IF語句來聲明兩個同名的游標(biāo),如下列的代碼就是錯誤的。 
IF Is_prov="北京"THEN 
DECLARE CustomerCursor CURSOR FOR 
SELECT acct_no,name,balance 
FROM customer 
WHERE province="北京"; 
ELSE 
DECLARE CustomerCursor CURSOR FOR 
SELECT acct_no,name,balance 
FROM customer 
WHERE province〈〉"北京"; 
END IF 

----------------------------------

由于打開游標(biāo)是對數(shù)據(jù)庫進行一些SQL SELECT的操作,它將耗費一段時間,主要取決于您使用的系統(tǒng)性能和這條語句的復(fù)雜程度。如果執(zhí)行的時間較長,可以考慮將屏幕上顯示的鼠標(biāo)改為hourglass(這句不會)

-------------------------------------

提取數(shù)據(jù) 
當(dāng)用OPEN語句打開了游標(biāo)并在數(shù)據(jù)庫中執(zhí)行了查詢后,您不能立即利用在查詢結(jié)果集中的數(shù)據(jù)。您必須用FETCH語句來取得數(shù)據(jù)。一條FETCH語句一次可以將一條記錄放入程序員指定的變量中。事實上,FETCH語句是游標(biāo)使用的核心。在DataWindow和DataStore中,執(zhí)行了Retrieve()函數(shù)以后,查詢的所有結(jié)果全部可以得到;而使用游標(biāo),我們只能逐條記錄地得到查詢結(jié)果。 
已經(jīng)聲明并打開一個游標(biāo)后,我們就可以將數(shù)據(jù)放入任意的變量中。在FETCH語句中您可以指定游標(biāo)的名稱和目標(biāo)變量的名稱

從語法上講,上面所述的就是一條合法的取數(shù)據(jù)的語句,但是一般我們使用游標(biāo)卻還應(yīng)當(dāng)包括其它的部分。正如我們前面所談到的,游標(biāo)只能一次從后臺數(shù)據(jù)庫中取一條記錄,而在多數(shù)情況下,我們所想要作的是在數(shù)據(jù)庫中從第一條記錄開始提取,一直到結(jié)束。所以我們一般要將游標(biāo)提取數(shù)據(jù)的語句放在一個循環(huán)體內(nèi),直至將結(jié)果集中的全部數(shù)據(jù)提取后,跳出循環(huán)圈。通過檢測SQLCA.SQL-CODE的值,可以得知最后一條FETCH語句是否成功。一般,當(dāng)SQLCODE值為0時表明一切正常,100表示已經(jīng)取到了結(jié)果集的末尾,而其它值均表明操作出了問題,

更多的就是這里了http://www./344071.html

今天就學(xué)到這里 再見
 
 

顯示游標(biāo)

顯示游標(biāo)包括四種屬性:

%ISOPEN :檢測游標(biāo)是否已經(jīng)打開

%FOUND :檢測游標(biāo)結(jié)果集是否存在數(shù)據(jù),存在則返回TRUE

%NOTFOUND :檢測游標(biāo)結(jié)果集是否不存在數(shù)據(jù),不存在則返回TRUE

%ROWCOUNT :返回已提取的實際行數(shù)

使用顯示游標(biāo)

定義游標(biāo):CURSOR cursor_name IS select_statement;

打開游標(biāo):OPEN cursor_name;

提取數(shù)據(jù):FETCH cursor_name INTO variable1[,variable2,…];

FETCH INTO每次只能提取一行數(shù)據(jù),批量數(shù)據(jù)需使用循環(huán)

使用游標(biāo)變量接受數(shù)據(jù):

SQL> declare

2 cursor emp_cursor is

3 select ename,job,sal from emp where deptno=&dno;

4 vname emp.ename%type;

5 vsal emp.sal%type;

6 vjob emp.job%type;

7 begin Oracle 學(xué)習(xí)筆記 第 74 頁 共 90 頁

8 open emp_cursor;

9 loop

10 fetch emp_cursor into vname,vjob,vsal;

11 exit when emp_cursor%notfound;

12 dbms_output.put_line('姓名:'||vname||',崗位:'||vjob||',工資:'||vsal);

13 end loop;

14 close emp_cursor;

15 end;

16 /

輸入 dno 的值: 30

姓名:ALLEN,崗位:SALESMAN,工資:1600

姓名:WARD,崗位:SALESMAN,工資:1250

姓名:JONES,崗位:MANAGER,工資:3272.5

PL/SQL 過程已成功完成。

使用PL/SQL記錄變量接受游標(biāo)數(shù)據(jù):簡化單行數(shù)據(jù)處理

SQL> declare

2 cursor ecur is select ename,sal from emp order by sal desc;

3 erec ecur%rowtype;

4 begin

5 open ecur;

6 loop

7 fetch ecur into erec;

8 exit when ecur%notfound or ecur%rowcount>&n;

9 dbms_output.put_line('姓名:'||erec.ename||',工資:'||erec.sal);

10 end loop;

11 close ecur;

12 end;

13 /

輸入 n 的值: 5

姓名:KING,工資:5000

姓名:FORD,工資:3300

姓名:JONES,工資:3272.5

姓名:BLAKE,工資:2850

姓名:MARY,工資:2000

PL/SQL 過程已成功完成。

使用PL/SQL集合變量接受游標(biāo)數(shù)據(jù):簡化多行多列數(shù)據(jù)處理

SQL> declare

2 cursor ec is select ename,sal from emp where lower(job)=lower('&job');

3 type etype is table of ec%rowtype index by binary_integer;

4 et etype;

5 i int;

6 begin

7 open ec;

8 loop Oracle 學(xué)習(xí)筆記 第 75 頁 共 90 頁

9 i:=ec%rowcount+1;

10 fetch ec into et(i);

11 exit when ec%notfound;

12 dbms_output.put_line('姓名:'||et(i).ename||',工資:'||et(i).sal);

13 end loop;

14 close ec;

15 end;

16 /

輸入 job 的值: manager

姓名:JONES,工資:3272.5

姓名:BLAKE,工資:2850

姓名:CLARK,工資:1500

PL/SQL 過程已成功完成。

游標(biāo)FOR循環(huán)

使用游標(biāo)for循環(huán)時,oracle會隱含的打開游標(biāo),提取數(shù)據(jù)并關(guān)閉游標(biāo)

在游標(biāo)for循環(huán)中引用已定義游標(biāo):

SQL> declare

2 cursor ec is select ename,hiredate from emp order by hiredate desc;

3 begin

4 for erec in ec loop

5 dbms_output.put_line('姓名:'||erec.ename||',工作日期:'||erec.hiredate);

6 exit when ec%rowcount=&n;

7 end loop;

8 end;

9 /

輸入 n 的值: 3

姓名:MARY,工作日期:

姓名:ADAMS,工作日期:23-5月 -87

姓名:SCOTT,工作日期:01-1月 -84

PL/SQL 過程已成功完成。

在游標(biāo)for循環(huán)中直接引用子查詢:

SQL> begin

2 for erec in (select ename,hiredate,rownum from emp order by hiredate) loop

3 dbms_output.put_line('姓名:'||erec.ename||',工作日期:'||erec.hiredate);

4 exit when erec.rownum=&n;end loop;

5 end;

6 /

輸入 n 的值: 2

姓名:ALLEN,工作日期:20-2月 -81

姓名:WARD,工作日期:22-2月 -81

PL/SQL 過程已成功完成。

參數(shù)游標(biāo):參數(shù)只能指定數(shù)據(jù)類型,不能指定長度,而且必須在where子句中引用參數(shù)

SQL> declare Oracle 學(xué)習(xí)筆記 第 76 頁 共 90 頁

2 cursor ec(dno number) is select ename,job from emp where deptno=dno;

3 begin

4 for erec in ec(&dno) loop

5 dbms_output.put_line('姓名:'||erec.ename||',崗位:'||erec.job);

6 end loop;

7 end;

8 /

輸入 dno 的值: 30

姓名:ALLEN,崗位:SALESMAN

姓名:WARD,崗位:SALESMAN

姓名:JONES,崗位:MANAGER

PL/SQL 過程已成功完成。

更新游標(biāo)行

declare

cursor emp_cursor is select ename,sal,deptno from emp for update;

dno int:=&no;

begin

for emp_record in emp_cursor loop

if emp_record.deptno=dno then

dbms_output.put_line(‘姓名:’||emp_record.ename||’,原工資:’||emp_record.sal);

update emp set sal=sal*1.1 where current of emp_cursor;

end if;

end loop;

end;

/

刪除游標(biāo)行

declare

cursor emp_cursor is select ename from emp for update;

name varchar2(10):=lower(‘&name’);

begin

for emp_record in emp_cursor loop

if lower(emp_record.ename)=name then

delete from emp where current of emp_cursor;

else

dbms_output.put_line(‘姓名:’||emp_record.ename);

end if;

end loop;

end;

/

使用for子句在特定表上加共享鎖(涉及多張表時的同步問題)

SQL> declare

2 cursor emp_cursor is Oracle 學(xué)習(xí)筆記第 77 頁 共 90 頁

select a.dname,b.ename from dept a JOIN emp b ON a.deprno=b.deptno;

3 name varchar2(10):=lower('&name');

4 begin

5 for emp_record in emp_cursor loop

6 if lower(emp_record.dname)=name then

7 dbms_output.put_line(‘姓名:’||emp_record.ename);

8 delete from emp where current of emp_cursor;

9 end if;

10 end loop;

11 end;

12 /

輸入 name 的值: sales

PL/SQL過程已完成。

游標(biāo)變量

游標(biāo)變量是基于REF CURSOR類型所定義的變量,它實際上是指向內(nèi)存地址的指針。使用顯式游標(biāo)只能定義靜態(tài)游標(biāo),而通過使用游標(biāo)變量可以在打開游標(biāo)時指定其對應(yīng)的select語句,從而實現(xiàn)動態(tài)游標(biāo)。

使用無返回類型的游標(biāo)變量

SQL> set serveroutput on

SQL> set verify off

SQL> declare

2 type ref_cursor_type is ref cursor;

3 rc ref_cursor_type;

4 v1 number(6);

5 v2 varchar2(10);

6 begin

7 open rc for

8 select &col1 col1,&col2 col2 from &table where &cond;

9 loop

10 fetch rc into v1,v2;

11 exit when rc%notfound;

12 dbms_output.put_line('col1= '||v1||',col2= '||v2);

13 end loop;

14 close rc;

15 end;

16 /

輸入 col1 的值: empno

輸入 col2 的值: ename

輸入 table 的值: emp

輸入 cond 的值: deptno=10

col1= 7782,col2= CLARK

col1= 7839,col2= KING

col1= 7934,col2= MILLER

PL/SQL 過程已成功完成。 Oracle 學(xué)習(xí)筆記 第 78 頁 共 90 頁

使用有返回類型的游標(biāo)變量

SQL> declare

2 type emp_cursor_type is ref cursor return emp%rowtype;

3 ec emp_cursor_type;

4 er emp%rowtype;

5 begin

6 open ec for select * from emp where deptno=&dno;

7 loop

8 fetch ec into er;

9 exit when ec%notfound;

10 dbms_output.put_line('姓名:'||er.ename||',工資:'||er.sal);

11 end loop;

12 close ec;

13 end;

14 /

輸入 dno 的值: 20

姓名:SMITH,工資:800

姓名:JONES,工資:2975

姓名:SCOTT,工資:3000

姓名:ADAMS,工資:1100

姓名:FORD,工資:3000

PL/SQL 過程已成功完成。

使用批量提取

使用fetch…bulk collect提取所有數(shù)據(jù);

SQL> declare

2 cursor ec is

3 select * from emp where lower(job)=lower('&job');

4 type etype is table of emp%rowtype;

5 et etype;

6 begin

7 open ec;

8 fetch ec bulk collect into et;

9 close ec;

10 for i in 1..et.count loop

11 dbms_output.put_line('姓名:'||et(i).ename||',工資:'||et(i).sal);

12 end loop;

13 end;

14 /

輸入 job 的值: clerk

姓名:SMITH,工資:800

姓名:ADAMS,工資:1100

姓名:JAMES,工資:950

姓名:MILLER,工資:1300

PL/SQL 過程已成功完成。 Oracle 學(xué)習(xí)筆記 第 79 頁 共 90 頁

使用LIMIT子句限制提取行數(shù)

SQL> declare

2 cursor ec is select * from emp;

3 type emp_array_type is varray(5) of emp%rowtype;

4 ea emp_array_type;

5 begin

6 open ec;

7 loop

8 fetch ec bulk collect into ea limit &rows;

9 for i in 1..ea.count loop

10 dbms_output.put_line('姓名:'||ea(i).ename||',工資:'||ea(i).sal);

11 end loop;

12 exit when ec%notfound;

13 end loop;

14 close ec;

15 end;

16 /

輸入 rows 的值: 4

姓名:SMITH,工資:800

姓名:ALLEN,工資:1600

姓名:WARD,工資:1250

姓名:JONES,工資:2975

姓名:MARTIN,工資:1250

姓名:BLAKE,工資:2850

姓名:CLARK,工資:2450

姓名:SCOTT,工資:3000

姓名:KING,工資:5000

姓名:TURNER,工資:1500

姓名:ADAMS,工資:1100

姓名:JAMES,工資:950

姓名:FORD,工資:3000

姓名:MILLER,工資:1300

PL/SQL 過程已成功完成。

使用cursor表達式

SQL> declare

2 cursor dept_cursor(no number) is

3 select a.dname,cursor(select * from emp where deptno=a.deptno)

4 from dept a where a.deptno=no;

5 type ref_cursor_type is ref cursor;

6 ec ref_cursor_type;

7 er emp%rowtype;

8 vdname dept.dname%type;

9 begin

10 open dept_cursor(&dno); Oracle 學(xué)習(xí)筆記第 80 頁 共 90 頁

11 loop

12 fetch dept_cursor into vdname,ec;

13 exit when dept_cursor%notfound;

14 dbms_output.put_line('部門名:'||vdname);

15 loop

16 fetch ec into er;

17 exit when ec%notfound;

18 dbms_output.put_line('----雇員名:'||er.ename||',崗位:'||er.job);

19 end loop;

20 end loop;

21 close dept_cursor;

22 end;

23 /

輸入 dno 的值: 10

部門名:ACCOUNTING

----雇員名:CLARK,崗位:MANAGER

----雇員名:KING,崗位:PRESIDENT

----雇員名:MILLER,崗位:CLERK

PL/SQL 過程已成功完成。
 

在存儲過程或觸發(fā)器中使用 Transact-SQL 游標(biāo)的典型過程為:

  1. 聲明 Transact-SQL 變量包含游標(biāo)返回的數(shù)據(jù)。為每個結(jié)果集列聲明一個變量。聲明足夠大的變量來保存列返回的值,并聲明變量的類型為可從列數(shù)據(jù)類型隱式轉(zhuǎn)換得到的數(shù)據(jù)類型。
  2. 使用 DECLARE CURSOR 語句將 Transact-SQL 游標(biāo)與 SELECT 語句相關(guān)聯(lián)。另外,DECLARE CURSOR 語句還定義游標(biāo)的特性,例如游標(biāo)名稱以及游標(biāo)是只讀還是只進。
  3. 使用 OPEN 語句執(zhí)行 SELECT 語句并填充游標(biāo)。
  4. 使用 FETCH INTO 語句提取單個行,并將每列中的數(shù)據(jù)移至指定的變量中。然后,其他 Transact-SQL 語句可以引用那些變量來訪問提取的數(shù)據(jù)值。Transact-SQL 游標(biāo)不支持提取行塊。

使用 CLOSE 語句結(jié)束游標(biāo)的使用。關(guān)閉游標(biāo)可以釋放某些資源,例如游標(biāo)結(jié)果集及其對當(dāng)前行的鎖定,但如果重新發(fā)出一個 OPEN 語句,則該游標(biāo)結(jié)構(gòu)仍可用于處理。由于游標(biāo)仍然存在,此時還不能重新使用該游標(biāo)的名稱。DEALLOCATE 語句則完全釋放分配給游標(biāo)的資源,包括游標(biāo)名稱。釋放游標(biāo)后,必須使用 DECLARE 語句來重新生成游標(biāo)。

-----------------------------------------------------
/*
 Use DECLARE @local_variable, DECLARE CURSOR and SET. */
USE AdventureWorks
GO


DECLARE @MyVariable CURSOR

DECLARE MyCursor CURSOR FOR
SELECT LastName FROM AdventureWorks.Person.Contact

SET @MyVariable =
 MyCursor
GO

/* Use DECLARE @local_variable and SET */
DECLARE @MyVariable CURSOR

SET @MyVariable = CURSOR SCROLL KEYSET FOR
SELECT LastName FROM AdventureWorks.Person.Contact;
DEALLOCATE
 MyCursor;
-----------------------------------------------------

USE AdventureWorks
GO

-- Declare the variables to store the values returned by FETCH.
DECLARE @LastName varchar(50), @FirstName varchar(50)

DECLARE contact_cursor CURSOR FOR

SELECT LastName, FirstName FROM Person.Contact
WHERE LastName LIKE 'B%'

ORDER BY LastName, FirstName

OPEN
 contact_cursor

-- Perform the first fetch and store the values in variables.

--
 Note: The variables are in the same order as the columns
--
 in the SELECT statement. 

FETCH NEXT FROM contact_cursor
INTO @LastName@FirstName


-- Check @@FETCH_STATUS to see if there are any more rows to fetch.
WHILE @@FETCH_STATUS = 0
BEGIN

   
-- Concatenate and display the current values in the variables.
   PRINT 'Contact Name: ' + @FirstName + ' ' +  @LastName

   
-- This is executed as long as the previous fetch succeeds.
   FETCH NEXT FROM contact_cursor
   
INTO @LastName@FirstName

END

CLOSE contact_cursor
DEALLOCATE
 contact_cursor
GO

 

一個游標(biāo)(cursor)可以被看作指向結(jié)果集(a set of rows)中一行的指針(pointer)。游標(biāo)每個時間點只能指向一行,但是可以根據(jù)需要指向結(jié)果集中其他的行。
例如:SELECT * FROM employees WHERE sex='M'會返回所有性別為男的雇員,在初始的時候,游標(biāo)被放置在結(jié)果集中第一行的前面。使游標(biāo)指向第一行,要執(zhí)行FETCH。當(dāng)游標(biāo)指向結(jié)果集中一行的時候,可以對這行數(shù)據(jù)進行加工處理,要想得到下一行數(shù)據(jù),要繼續(xù)執(zhí)行FETCH。FETCH操作可以重復(fù)執(zhí)行,直到完成結(jié)果集中的所有行
 
    在存儲過程中使用游標(biāo),有如下幾個步驟:
      聲明游標(biāo)、打開游標(biāo)、根據(jù)需要一次一行,講游標(biāo)指向的數(shù)據(jù)取到本地變量(local variables)中、結(jié)束時關(guān)閉游標(biāo)
   
   聲明游標(biāo):
    >>-DECLARE--cursor-name--CURSOR----+------------+--------->
                                                          '-WITH HOLD--'
    >-----+--------------------------------+--------------------->
             |                       .-TO CALLER--.  |
              '-WITH RETURN--+------------+--'
                                      '-TO CLIENT--'
    >----FOR--+-select-statement-+----------------------------><
                    '-statement-name---'    
  WITH RETURN子句用于將游標(biāo)所定義的結(jié)果集傳遞給另一個存儲過程或者應(yīng)用(an application)
  如果select語句中包含CURRENT DATE, CURRENT TIME和CURRENT TIMESTAMP,所有的FETCH語句都會返回相同的日期、時間、時間戳值,因為這些特定寄存器是在打開游標(biāo)(OPEN CURSOR)的時候進行檢查的
   
    FETCH語法:
    >>-FETCH--+-------+---cursor-name---------->
                     '-FROM--'
                        .-,----------------.
                        V                        |
    >------INTO-----host-variable---+----------><
    FETCH語句使游標(biāo)指向結(jié)果集中的下一行,并且將游標(biāo)現(xiàn)在的位置賦值給特定的過程變量
例如:一個公司,按照如下規(guī)則計算加薪金額:
 1.公司中除了總裁(president)外,所有人都會至少增加p_min的薪水
 2.任何獎金(bonus)高于$600的員工都會另增加4%
 3.員工的傭金(commission)越高,增加越少。傭金(commission)少于$2000的另增加3%,傭金(commission)在$2000到$3000的增加另2%
 4.傭金(commission)高于$3000的另增加1%
 5.無論每個員工增加多少,增加比例不能高于p_max
CREATE PROCEDURE total_raise ( IN  p_min DEC(4,2)
                                           , IN  p_max DEC(4,2)
                                           , OUT p_total DEC(9,2) )
    LANGUAGE SQL
    SPECIFIC total_raise     
tr: BEGIN
    -- Declare variables
    DECLARE v_salary DEC(9,2);
    DECLARE v_bonus  DEC(9,2);
    DECLARE v_comm   DEC(9,2);
    DECLARE v_raise  DEC(4,2);
    DECLARE v_job    VARCHAR(15) DEFAULT 'PRES';
    -- Declare returncode
    DECLARE SQLSTATE CHAR(5);

    -- Procedure logic
    DECLARE c_emp CURSOR FOR
        SELECT salary, bonus, comm
        FROM   employee
        WHERE  job != v_job;                              -- (1)這里的SELECT定義了結(jié)果集中的行和
                                                                          列
        OPEN c_emp;                                        -- (2)
 
        SET p_total = 0;
        FETCH FROM c_emp INTO v_salary, v_bonus, v_comm;  -- (3)得到一行數(shù)據(jù),并將其
                                                                                           復(fù)制給本地變量
 
        WHILE ( SQLSTATE = '00000' ) DO                   --SQLSTATE 00000: 操作執(zhí)行成功,
                                                                            并且未產(chǎn)生任何類型的警告或異常情
                                                                            況。通過這個可以檢查是否到達最后一行
           SET v_raise = p_min;
           IF ( v_bonus >= 600 ) THEN
               SET v_raise = v_raise + 0.04;
           END IF;

           IF ( v_comm < 2000 ) THEN
               SET v_raise = v_raise + 0.03;
           ELSEIF ( v_comm < 3000 ) THEN
               SET v_raise = v_raise + 0.02;
           ELSE
               SET v_raise = v_raise + 0.01;
           END IF;

           IF ( v_raise > p_max ) THEN
               SET v_raise = p_max;
           END IF;

           SET p_total = p_total + v_salary * v_raise;
           FETCH FROM c_emp INTO v_salary, v_bonus, v_comm;  -- (4)在WHILE邏輯中得到
                                                                                              更多的行數(shù)據(jù)
       END WHILE;

       CLOSE c_emp;                                          -- (5)
   END tr
    如果只是想把結(jié)果集中的第一個值復(fù)制給本地變量,而聲明一個游標(biāo)是不恰當(dāng)?shù)?,因為打開游標(biāo)會耗費很多資源。所以如下這段代碼:
DECLARE c_tmp CURSOR FOR
        SELECT c1
        FROM t1;
OPEN c_emp;
FETCH FROM c_emp INTO v_c1;
CLOSE c_emp;
應(yīng)當(dāng)用有FETCH FIRST 1 ROW ONLY的子句的SQL語句:SELECT c1 INTO v_c1 FROM t1 FETCH FIRST 1 ROW ONLY;
  
  positioned delete:利用游標(biāo)刪除當(dāng)前行 
  一個用于刪除的游標(biāo)(a deletable cursor)應(yīng)該符合以下的要求:
     1.每個outer fullselect中的FROM子句只跟一個表有關(guān)
     2.outer fullselect不包含VALUES, GROUP BY, 或者HAVING子句,并且不包括列函數(shù)
     3.outer fullselect的select列表中不包含DISTINCT
     4.select語句不包含ORDER BY或FOR READ ONLY子句
     5.游標(biāo)是靜態(tài)定義的,或者明確了FOR UPDATE子句
>>-DELETE FROM-|----table-name---------|--------------->
                        +-----view-name---------+
>----WHERE CURRENT OF--cursor-name--------------------><
例如:在emp_act表中,如果記錄的時間比輸入?yún)?shù)p_date早的話,就將該記錄刪除,并返回刪除記錄總數(shù)
CREATE PROCEDURE cleanup_act ( IN  p_date    DATE
                                             , OUT p_deleted INT )
    LANGUAGE SQL
    SPECIFIC cleanup_act                         
ca: BEGIN
    -- Declare variable
    DECLARE v_date DATE;
    -- Declare returncode
    DECLARE SQLSTATE CHAR(5);

    -- Procedure logic

    DECLARE c_emp CURSOR FOR                       -- (1)和上面那種read-only cursor語法
                                                                      類似,只是多了FOR UPDATE
        SELECT emendate
        FROM emp_act
    FOR UPDATE;

    OPEN c_emp;

    FETCH FROM c_emp INTO v_date;                 --注意此處,不要落了
    SET p_deleted = 0;

    WHILE ( SQLSTATE = '00000' ) DO
        IF ( v_date < p_date ) THEN
            DELETE FROM emp_act
            WHERE CURRENT OF c_emp;                -- (2)
            SET p_deleted = p_deleted + 1;
        END IF;
        FETCH FROM c_emp INTO v_date;
    END WHILE;
    CLOSE c_emp;
END ca
直接用DELETE語句刪除而不用游標(biāo)被稱作searched delete。像上例這種情況,采用searched delete會比使用positioned delete效率更高。但用positioned delete可以處理更復(fù)雜的邏輯
 
   Positioned Update
  一個用于更新的游標(biāo)(A cursor is updatable)應(yīng)該The cursor is deletable
>>-UPDATE----+-table-name-------------------+-------------->
                     +-view-name--------------------+
>-----SET--| assignment-clause |--------------------------->
>-----WHERE CURRENT OF--cursor-name-----------------------><
CREATE PROCEDURE upd_raise ( IN p_min DEC(4,2)
                                         , IN p_max DEC(4,2) )
LANGUAGE SQL
    SPECIFIC upd_raise                        
ur: BEGIN
    -- Declare variables
    DECLARE v_salary DEC(9,2);
    DECLARE v_bonus  DEC(9,2);
    DECLARE v_comm   DEC(9,2);
    DECLARE v_raise  DEC(4,2);
    -- Declare returncode
    DECLARE SQLSTATE CHAR(5);
    -- Procedure logic
    DECLARE c_emp CURSOR FOR
        SELECT salary, bonus, comm
        FROM employee
        WHERE job!='PRES'
    FOR UPDATE OF salary;                      -- (1)如果只是更新表中的一部分字段,可以利用
                                                                  FOR UPDATE OF <column list>提高效
                                                           率,讓DB2引擎知道只有這些特定列要UPDATE
    OPEN c_emp;
    FETCH FROM c_emp INTO v_salary, v_bonus, v_comm;
    WHILE ( SQLSTATE = '00000' ) DO
        SET v_raise = p_min;
        IF ( v_bonus >= 600 ) THEN
            SET v_raise = v_raise + 0.04;
        END IF;
        IF ( v_comm < 2000 ) THEN
            SET v_raise = v_raise + 0.03;
        ELSEIF ( v_comm < 3000 ) THEN
            SET v_raise = v_raise + 0.02;
        ELSE
            SET v_raise = v_raise + 0.01;
        END IF;
        IF ( v_raise > p_max ) THEN
            SET v_raise = p_max;
        END IF;
        UPDATE employee                                  
           SET salary = v_salary * (1 + v_raise)
         WHERE CURRENT OF c_emp;
        FETCH FROM c_emp INTO v_salary, v_bonus, v_comm;
    END WHILE;
    CLOSE c_emp;
END ur
 
    使用游標(biāo)時候的COMMIT和ROLLBACK:
      數(shù)據(jù)庫程序中很重要的一點就是事務(wù)處理(transaction或者the unit of work(UOW))。事務(wù)當(dāng)中的任何一部分失敗,整個事物就會失敗。利用COMMIT和ROLLBACK進行適當(dāng)?shù)氖聞?wù)控制對于保證數(shù)據(jù)完整性來說是至關(guān)重要的。
      當(dāng)在使用游標(biāo)的時候使用COMMIT或者ROLLBACK語句時,游標(biāo)的行動取決于是否在生命的時候加了WITH HOLD子句。如果一個游標(biāo)在聲明的時候沒有指定WITH HOLD,那么它的所有資源(游標(biāo),鎖,大對象數(shù)據(jù)類型或者LOB locators)都將在COMMIT或者ROLLBACK之后被釋放。因此,如果需要在完成一個事務(wù)之后使用游標(biāo),就必須重新打開游標(biāo),并從第一行開始執(zhí)行。如果定義了一個游標(biāo)WITH HOLD
,游標(biāo)就會在事務(wù)之間保存它的位置和鎖(lock)。需要明白的是,只有保證游標(biāo)位置的鎖被held了。
  鎖(lock)是個數(shù)據(jù)庫對象(a database object),我們用它來控制多個應(yīng)用訪問同一個資源的方式。而一個LOB locator使存儲在本地變量中的4字節(jié)的值,程序可以用它來查到數(shù)據(jù)庫系統(tǒng)中的LOB對象的值
      定義了WITH HOLD的游標(biāo)在COMMIT之后
       1.仍然保證是打開(open)的
       2.游標(biāo)指向下一個滿足條件的行之前
       3.在COMMIT語句之后只允許FETCH和CLOSE
       4.Positioned delete和positioned update只在同一事務(wù)中fetch的行上可用
       5.所有的LOB locators會被釋放
       6.除了保存聲明為WITH HOLD的游標(biāo)位置的鎖,其他鎖都會釋放
       7.當(dāng)執(zhí)行了數(shù)據(jù)修改語句或者含有WITH HOLD游標(biāo)的修改語句被commit的時候
      所有定義為WITH HOLD的游標(biāo)在ROLLBACK之后:
       1.所有游標(biāo)會被關(guān)閉
       2.所有在該事務(wù)中的鎖會被釋放
       3.所有的LOB locators會被freed
例如:
CREATE PROCEDURE update_department ( )
    LANGUAGE SQL
    SPECIFIC upd_dept                            
ud: BEGIN
    -- Declare variable
    DECLARE v_deptno CHAR(3);
    -- Declare returncode
    DECLARE SQLSTATE CHAR(5);
    DECLARE c_dept CURSOR WITH HOLD FOR
        SELECT deptno
        FROM department
    FOR UPDATE OF location;
    -- Declare condition handler
    DECLARE CONTINUE HANDLER FOR SQLSTATE '24504', SQLSTATE '24501'
      L1: LOOP                                                          -- (1)
         LEAVE L1;
      END LOOP;
    -- Procedure logic
    OPEN c_dept;                                                       --打開游標(biāo),指向第一行前面的位置
    FETCH FROM c_dept INTO v_deptno;                                    -- (2)
    UPDATE department SET location='FLOOR1' WHERE CURRENT OF c_dept;    -- (3)
    COMMIT;                                                             -- (4)因為該游標(biāo)聲明為WITH
                                                                            HOLD,此時游標(biāo)依舊打開,并且
                                                                            指向第二行前面的位置。此時表
                                                                            中第一行的鎖釋放了,且第一行
                                                                            的值更新為FLOOR1
    FETCH FROM c_dept INTO v_deptno;                                    -- (5)得到第二行數(shù)據(jù),執(zhí)行成功
    COMMIT;                                                             -- (6)COMMIT后游標(biāo)指向第三行之
                                                                                前的位置,此時并沒有被fetched
    UPDATE department SET location='FLOOR2' WHERE CURRENT OF c_dept;    -- (7)這行
                                                                         命令執(zhí)行失敗,因為此時游標(biāo)沒有指向
                                                                      任何行,此時游標(biāo)在第二行和第三行之間
    FETCH FROM c_dept INTO v_deptno;                                    -- (8)成功
    UPDATE department SET location='FLOOR3' WHERE CURRENT OF c_dept;    -- (9)成功
    COMMIT;                                                             -- (10)
    FETCH FROM c_dept INTO v_deptno;                                    -- (11)成功
    UPDATE department SET location='FLOOR4' WHERE CURRENT OF c_dept;    -- (12)成
                                                                            功,此時第三行和第四行的值都變了
    ROLLBACK;                                                           -- (13)第四行的值還原。
                                                                          ROLLBACK之后游標(biāo)關(guān)閉了,
                                                                          所有的鎖也都釋放了
    FETCH FROM c_dept INTO v_deptno;                                    -- (14)錯誤
    UPDATE department SET location='FLOOR5' WHERE CURRENT OF c_dept;    -- (15)
    CLOSE c_dept;
    RETURN 0;
END ud
上述存儲過程執(zhí)行前:
DEPTNO LOCATION
------ --------
A00    -
B01    -
C01    -
D01    -
D11    -
上述存儲過程執(zhí)行后:
DEPTNO LOCATION
------ --------
A00    FLOOR1
B01    -
C01    FLOOR3
D01    -
D11    -
如果上例中的游標(biāo)沒有聲明為WITH HOLD,從(5)到(15)的執(zhí)行都會失敗。因為游標(biāo)會在COMMIT或ROLLBACK之后隱性關(guān)閉
 
 
   存儲過程中的Save Points可用于保存事務(wù)回滾的間斷點
>>- SAVEPOINT--savepoint-name----+--------+--------------------->
                                                    '-UNIQUE-'
>--ON ROLLBACK RETAIN CURSORS--+--------------------------+----><
                                                  '-ON ROLLBACK RETAIN LOCKS-'
 
savepoint-name不能以'SYS'開頭,否則會報SQLSTATE 42939的錯誤。UNIQUE選項表示這個save point name不會在Save Point活動期中被reused。ON ROLLBACK RETAIN CURSORS使游標(biāo)在rollback發(fā)生之后還被保留。附加的ON ROLLBACK RETAIN LOCKS防止在ROLLBACK之后鎖丟失
  在一個事務(wù)中,可以定義多個save points
  使用save points的偽代碼:
savepoint A;
Do program logic;
savepoint B;
Do more program logic;
savepoint C;
Do even more program logic;
之后就可以用含有SAVE POINT的ROLLBACK:ROLLBACK TO SAVEPOINT savepoint-name
如果ROLLBACK到了最后一個save point,那么這個save point之前的save point都還是活動的(active),你依舊可以ROLL BACK到更早的save point
例如:
savepoint a;
Do program logic;
savepoint b;
Do more program logic;
savepoint c;
Do even more program logic;
ROLLBACK TO SAVEPOINT c;           (1)將事務(wù)數(shù)據(jù)返回到save point c
Do some new logic;
ROLLBACK TO SAVEPOINT a;           (2)將事務(wù)數(shù)據(jù)返回到save point a
Do some more logic;
ROLLBACK TO SAVEPOINT b;           (3)錯誤,因為此時save point b已經(jīng)不存在了
 
游標(biāo)除了可以在存儲過程中處理數(shù)據(jù)外,還可以用于返回結(jié)果集
比如:
CREATE PROCEDURE read_emp ( )
    LANGUAGE SQL
    SPECIFIC read_emp                           
DYNAMIC RESULT SETS 1                            --(1)如果想用游標(biāo)返回結(jié)果集到一個應(yīng)用程序,必須聲明DYNAMIC RESULT SETS
re: BEGIN
    -- Procedure logic
    DECLARE c_emp CURSOR WITH RETURN FOR         --(2)
        SELECT salary, bonus, comm
        FROM employee
        WHERE job!='PRES';
    OPEN c_emp;                                  --(3)為客戶端保持游標(biāo)打開
END re
此時只返回了所有符合條件的員工的salary, bonus和commission字段。之后,結(jié)果集就可以被另外的存儲過程或客戶端程序調(diào)用
   
    在存儲過程中我們除了數(shù)據(jù)操作語言(Data Manipulation Language (DML):SELECT, DELETE和UPDATE),還可以使用數(shù)據(jù)定義語言(Data Definition Language (DDL)),比如定義一個表。我們可以在存儲過程中定義一個表,然后用游標(biāo)返回結(jié)果集。但是游標(biāo)聲明必須在BEGIN ... END的一開始,但如果這樣的話,此時表還沒有建立,編譯時會報錯。但如果先聲明表,編譯也會報錯。這是我們可以用BEGIN ... END可以嵌套這個特性。我們在存儲過程末尾嵌套一個BEGIN ... END來聲明游標(biāo)
如:
CREATE PROCEDURE create_and_return ( )
    LANGUAGE SQL
    SPECIFIC create_and_return                  
DYNAMIC RESULT SETS 1
cr: BEGIN
    -- Procedure logic
    CREATE TABLE mytable (sid INT);
    INSERT INTO mytable VALUES (1);
    INSERT INTO mytable VALUES (2);
    BEGIN                                        --(1)
    DECLARE c_cur CURSOR WITH RETURN
        FOR SELECT *
            FROM mytable;
    OPEN c_cur;                                  --(2)
    END;                                         --(3)OPEN必須在這個嵌套的BEGIN ... END中,因為游
                                                      標(biāo)的定義只在這個BEGIN ... END中有效
END cr
   
    有時我們不只返回一個結(jié)果集,若返回多個結(jié)果集,要求:
     1.CREATE PROCEDURE中的DYNAMIC RESULT SETS子句寫明想返回的結(jié)果集的數(shù)量
     2.為每一個結(jié)果集聲明含有WITH RETURN的游標(biāo)
     3.保證所有游標(biāo)返回給客戶端是打開的
 例如:
CREATE PROCEDURE read_emp_multi ( )
    LANGUAGE SQL
    SPECIFIC read_emp_multi               
    DYNAMIC RESULT SETS 3                  --(1)
re: BEGIN
    -- Procedure logic
    DECLARE c_salary CURSOR WITH RETURN FOR
        SELECT salary
          FROM employee;
    DECLARE c_bonus CURSOR WITH RETURN FOR
        SELECT bonus
          FROM employee;
    DECLARE c_comm CURSOR WITH RETURN FOR
        SELECT comm
          FROM employee;
    OPEN c_salary;
    OPEN c_bonus;
    OPEN c_comm;
END re
游標(biāo)打開的順序反映了結(jié)果集返回給客戶端的順序
    當(dāng)在存儲過程中使用游標(biāo)的時候,會影響其他應(yīng)用和人們使用這個數(shù)據(jù)庫。鎖的類型取決于游標(biāo)的類型和DB2的隔離級別(isolation level)
 
    鎖模式(Lock Modes):
Table  Row Lock Descriptions

Lock Mode

Applicable Object Type

Description

S (Share)

Rows, blocks, tables

The lock owner and all concurrent applications can read, but not update, the locked data.

U (Update)

Rows, blocks, tables

The lock owner can update data. Other UOW can read the data in the locked object, but cannot attempt to update it.

X (Exclusive)

Rows, blocks, tables, bufferpools

The lock owner can both read and update data in the locked object. Only uncommitted read applications can access the locked object.


除非迫不得已不要用游標(biāo)

        不管是sql server 還是 oracle都提供了游標(biāo),其一般用途就是從查詢結(jié)構(gòu)中遍歷數(shù)據(jù)。游標(biāo)很好用,許多初學(xué)oracle或者sql server的程序員都喜歡用,然而如果濫用游標(biāo)的話,對程序的性能會造成很大的影響。我認(rèn)為:除非不得已,不要使用游標(biāo)。下面是我的一次優(yōu)化經(jīng)歷。
問題:
        在我們的系統(tǒng)中,需要管理產(chǎn)品模型,對PDM了解的人知道,其實產(chǎn)品模型可以當(dāng)作一個特殊的零部件來處理,所以在數(shù)據(jù)表的時候產(chǎn)品模型與零部件使用相同的數(shù)據(jù)表來存放數(shù)據(jù),只不過是用一個flag來標(biāo)示是零部件還是產(chǎn)品模型。產(chǎn)品模型具有版本?,F(xiàn)在的要求的是查出最新的所有的零部件信息,查詢的時候允許用戶自定義查詢條件。
        用戶自定義查詢條件是從界面上通過各種選項來構(gòu)造的,因此傳遞的是動態(tài)SQL語句,為了提高效率數(shù)據(jù)庫上建立了一個存儲過程專門來查詢產(chǎn)品模型。下面是一個初學(xué)oracle不久的編程人員編寫的存儲過程:

 1PROCEDURE GETPRODMODELBYSQL(p_sql        IN VARCHAR2,                 
 2                               cur_mi_prodmodel OUT SYS_BASE.BASECURSOR) AS
 3   sqlstr varchar2(1000);
 4   idstr  varchar2(1000);
 5   
 6   
 7   dis_id number;
 8   maxver number;
 9        cursor disid_curs is
10              select distinct pt_id
11              from mi_part
12              where pt_flag = 2;                               
13   BEGIN
14    open disid_curs;
15         loop
16             fetch disid_curs
17             into dis_id;
18             exit when disid_curs% notfound;
19             
20             select max(pv_id)
21             into maxver
22             from mi_partver
23             where pv_partid = dis_id;
24             
25             if maxver is null then
26                 idstr := idstr;
27             else
28                 idstr := idstr || maxver || ',';
29             end if;
30          end loop;
31
32          idstr  := rtrim(idstr, ',');
33          idstr  := ltrim(idstr, ',');
34          if idstr is null then
35              sqlstr := p_sql || ' ORDER BY PV_PRODUCTID,PT_PARTNUMBER';
36          else
37              sqlstr := p_sql || ' and MI_PARTVER.pv_id in (' || idstr || ')  ORDER BY PV_PRODUCTID,PT_PARTNUMBER';
38           end if;
39
40        open cur_mi_prodmodel for sqlstr;
41   END GETPRODMODELBYSQL;

這個存儲過程可以正常工作,但是其速度執(zhí)行起來非常慢,如果數(shù)據(jù)表中有很多數(shù)據(jù)的話,其速度很可能是直線下降,究其原因是因為使用游標(biāo)來遍歷然后構(gòu)造查詢語句。對這個存儲過程進行優(yōu)化:
 1PROCEDURE GETPRODMODELBYSQL(p_sql        IN VARCHAR2,                 
 2                               cur_mi_prodmodel OUT SYS_BASE.BASECURSOR) AS
 3   s varchar2(300);
 4   sqlstr varchar2(1000);
 5   /*idstr  varchar2(1000);
 6   
 7   
 8   dis_id number;
 9   maxver number;
10        cursor disid_curs is
11              select distinct pt_id
12              from mi_part
13              where pt_flag = 2;  */
                             
14   BEGIN
15    /*open disid_curs;
16         loop
17             fetch disid_curs
18             into dis_id;
19             exit when disid_curs% notfound;
20             
21             select max(pv_id)
22             into maxver
23             from mi_partver
24             where pv_partid = dis_id;
25             
26             if maxver is null then
27                 idstr := idstr;
28             else
29                 idstr := idstr || maxver || ',';
30             end if;
31          end loop;*/

32          s:=' and MI_PARTVER.pv_id in (select a.pv_id from mi_partver a,mi_part c where a.pv_partid = c.pt_id and c.pt_flag =2 and
33a.pv_id in (select e.pv_id from mi_partver e
34where e.pv_id in (select max(b.pv_id) from mi_partver b where e.pv_partid = b.pv_partid)))';
35          /*idstr  := rtrim(idstr, ',');
36          idstr  := ltrim(idstr, ',');
37          if idstr is null then
38              sqlstr := p_sql || ' ORDER BY PV_PRODUCTID,PT_PARTNUMBER';
39          else
40              sqlstr := p_sql || ' and MI_PARTVER.pv_id in (' || idstr || ')  ORDER BY PV_PRODUCTID,PT_PARTNUMBER';
41           end if;*/

42              sqlstr := p_sql || s ||  'ORDER BY PV_PRODUCTID,PT_PARTNUMBER';
43        open cur_mi_prodmodel for sqlstr;
44   END GETPRODMODELBYSQL;

這里的有效語句是使用查詢語句來構(gòu)造查詢,其結(jié)果是速度得到了數(shù)量級的提高,而且避免了一個隱患就是緩沖區(qū)溢出,因為原來的存儲過程中定義了幾個varchar2(1000)的變量,實際上這個變量的上界是不可以預(yù)期的,而緩沖區(qū)溢出也是很多初學(xué)oracle的程序員經(jīng)常忽略甚至想不到的問題。
 

SQLServer 游標(biāo)簡介與使用說明

      游標(biāo)(Cursor)是處理數(shù)據(jù)的一種方法,為了查看或者處理結(jié)果集中的數(shù)據(jù),游標(biāo)提供了在結(jié)果集中一次以行或者多行前進或向后瀏覽數(shù)據(jù)的能力。我們可以把游標(biāo)當(dāng)作一個指針,它可以指定結(jié)果中的任何位置,然后允許用戶對指定位置的數(shù)據(jù)進行處理。

      1.游標(biāo)的組成

      游標(biāo)包含兩個部分:一個是游標(biāo)結(jié)果集、一個是游標(biāo)位置。

      游標(biāo)結(jié)果集:定義該游標(biāo)得SELECT語句返回的行的集合。游標(biāo)位置:指向這個結(jié)果集某一行的當(dāng)前指針。

 

      2.游標(biāo)的分類

      游標(biāo)共有3類:API服務(wù)器游標(biāo)、Transaction-SQL游標(biāo)和API客戶端游標(biāo)。

      其中前兩種游標(biāo)都是運行在服務(wù)器上的,所以又叫做服務(wù)器游標(biāo)。

      API服務(wù)器游標(biāo)

      API服務(wù)器游標(biāo)主要應(yīng)用在服務(wù)上,當(dāng)客戶端的應(yīng)用程序調(diào)用API游標(biāo)函數(shù)時,服務(wù)器會對API函數(shù)進行處理。使用API函數(shù)和方法可以實現(xiàn)如下功能:

      (1)打開一個連接。

      (2)設(shè)置定義游標(biāo)特征的特性或?qū)傩?,API自動將游標(biāo)影射到每個結(jié)果集。

      (3)執(zhí)行一個或多個Transaction-SQL語句。

      (4)使用API函數(shù)或方法提取結(jié)果集中的行。

      API服務(wù)器游標(biāo)包含以下四種:靜態(tài)游標(biāo)、動態(tài)游標(biāo)、只進游標(biāo)、鍵集驅(qū)動游標(biāo)(Primary key)

      靜態(tài)游標(biāo)的完整結(jié)果集將打開游標(biāo)時建立的結(jié)果集存儲在臨時表中,(靜態(tài)游標(biāo)始終是只讀的)。靜態(tài)游標(biāo)具有以下特點:總是按照打開游標(biāo)時的原樣顯示結(jié)果集;不反映數(shù)據(jù)庫中作的任何修改,也不反映對結(jié)果集行的列值所作的更改;不顯示打開游標(biāo)后在數(shù)據(jù)庫中新插入的行;組成結(jié)果集的行被其他用戶更新,新的數(shù)據(jù)值不會顯示在靜態(tài)游標(biāo)中;但是靜態(tài)游標(biāo)會顯示打開游標(biāo)以后從數(shù)據(jù)庫中刪除的行。

      動態(tài)游標(biāo)與靜態(tài)游標(biāo)相反,當(dāng)滾動游標(biāo)時動態(tài)游標(biāo)反映結(jié)果集中的所有更改。結(jié)果集中的行數(shù)據(jù)值、順序和成員每次提取時都會改變。

      只進游標(biāo)不支持滾動,它只支持游標(biāo)從頭到尾順序提取數(shù)據(jù)行。注意:只進游標(biāo)也反映對結(jié)果集所做的所有更改。

      鍵集驅(qū)動游標(biāo)同時具有靜態(tài)游標(biāo)和動態(tài)游標(biāo)的特點。當(dāng)打開游標(biāo)時,該游標(biāo)中的成員以及行的順序是固定的,鍵集在游標(biāo)打開時也會存儲到臨時工作表中,對非鍵集列的數(shù)據(jù)值的更改在用戶游標(biāo)滾動的時候可以看見,在游標(biāo)打開以后對數(shù)據(jù)庫中插入的行是不可見的,除非關(guān)閉重新打開游標(biāo)。

 

      Transaction-SQL游標(biāo)

      該游標(biāo)是基于Declare Cursor 語法,主要用于Transaction-SQL腳本、存儲過程以及觸發(fā)器中。Transaction-SQL游標(biāo)在服務(wù)器處理由客戶端發(fā)送到服務(wù)器的Transaction-SQL語句。

      在存儲過程或觸發(fā)器中使用Transaction-SQL游標(biāo)的過程為:

      (1)聲明Transaction-SQL變量包含游標(biāo)返回的數(shù)據(jù)。為每個結(jié)果集列聲明一個變量。聲明足夠大的變量來保存列返回的值,并聲明變量的類型為可從數(shù)據(jù)類型隱式轉(zhuǎn)換得到的數(shù)據(jù)類型。

      (2)使用Declare Cursor語句將Transaction-SQL游標(biāo)與Select語句相關(guān)聯(lián)。還可以利用Declare Cursor定義游標(biāo)的只讀、只進等特性?!?/p>

      (3)使用Open語句執(zhí)行Select語句填充游標(biāo)。

      (4)使用Fetch Into語句提取單個行,并將每列中得數(shù)據(jù)移至指定的變量中。注意:其他Transaction-SQL語句可以引用那些變量來訪問提取的數(shù)據(jù)值。Transaction-SQL游標(biāo)不支持提取行塊。

      (5)使用Close語句結(jié)束游標(biāo)的使用。注意:關(guān)閉游標(biāo)以后,該游標(biāo)還是存在,可以使用Open命令打開繼續(xù)使用,只有調(diào)用Deallocate語句才會完全釋放。

      客戶端游標(biāo)

      該游標(biāo)將使用默認(rèn)結(jié)果集把整個結(jié)果集高速緩存在客戶端上,所有的游標(biāo)操作都在客戶端的高速緩存中進行。注意:客戶端游標(biāo)只支持只進和靜態(tài)游標(biāo)。不支持其他游標(biāo)。

 

      3.游標(biāo)的生命周期

      游標(biāo)的生命周期包含有五個階段:聲明游標(biāo)、打開游標(biāo)、讀取游標(biāo)數(shù)據(jù)、關(guān)閉游標(biāo)、釋放游標(biāo)。

      聲明游標(biāo)是為游標(biāo)指定獲取數(shù)據(jù)時所使用的Select語句,聲明游標(biāo)并不會檢索任何數(shù)據(jù),它只是為游標(biāo)指明了相應(yīng)的Select 語句。

      Declare 游標(biāo)名稱 Cursor 參數(shù)

      聲明游標(biāo)的參數(shù)

            (1)Local與Global:Local表示游標(biāo)的作用于僅僅限于其所在的存儲過程、觸發(fā)器以及批處理中、執(zhí)行完畢以后游標(biāo)自動釋放。Global表示的是該游標(biāo)作用域是整個會話層。由連接執(zhí)行的任何存儲過程、批處理等都可以引用該游標(biāo)名稱,僅在斷開連接時隱性釋放。

            (2)Forward_only與Scroll:前者表示為只進游標(biāo),后者表示為可以隨意定位。默認(rèn)為前者。

            (3)Static、Keyset與Dynamic: 第一個表示定義一個游標(biāo),其數(shù)據(jù)存放到一個臨時表內(nèi),對游標(biāo)的所有請求都從臨時表中應(yīng)答,因此,對該游標(biāo)進行提取操作時返回的數(shù)據(jù)不反映對基表所作的修改,并且該游標(biāo)不允許修改。Keyset表示的是,當(dāng)游標(biāo)打開時,鍵集驅(qū)動游標(biāo)中行的身份與順序是固定的,并把其放到臨時表中。Dynamic表示的是滾動游標(biāo)時,動態(tài)游標(biāo)反映對結(jié)果集內(nèi)所有數(shù)據(jù)的更改。

            (4)Read_only 、Scroll_Locks與Optimistic:第一個表示的是只讀游標(biāo),第二個表示的是在使用的游標(biāo)結(jié)果集數(shù)據(jù)上放置鎖,當(dāng)行讀取到游標(biāo)中然后對它們進行修改時,數(shù)據(jù)庫將鎖定這些行,以保證數(shù)據(jù)的一致性。Optimistic的含義是游標(biāo)將數(shù)據(jù)讀取以后,如果這些數(shù)據(jù)被更新了,則通過游標(biāo)定位進行的更新與刪除操作將不會成功。

      標(biāo)準(zhǔn)游標(biāo):

            Declare MyCursor Cursor 
                   For Select * From Master_Goods

      只讀游標(biāo)

            Declare MyCusror Cursor

                  For Select * From Master_Goods

                  For Read Only

      可更新游標(biāo)

            Declare MyCusror Cursor

                  For Select * From Master_Goods

                  For UpDate

      打開游標(biāo)使用Open語句用于打開Transaction-SQL服務(wù)器游標(biāo),執(zhí)行Open語句的過程中就是按照Select語句進行填充數(shù)據(jù),打開游標(biāo)以后游標(biāo)位置在第一行。

      打開游標(biāo)

            全局游標(biāo):Open Global MyCursor            局部游標(biāo): Open MyCursor

      讀取游標(biāo)數(shù)據(jù):在打開游標(biāo)以后,使用Fetch語句從Transaction-SQL服務(wù)器游標(biāo)中檢索特定的一行。使用Fetch操作,可以使游標(biāo)移動到下一個記錄,并將游標(biāo)返回的每個列得數(shù)據(jù)分別賦值給聲明的本地變量。

            Fetch [Next | Prior | First | Last | Absolute n | Relative n ]  From MyCursor

            Into @GoodsID,@GoodsName

            其中:Next表示返回結(jié)果集中當(dāng)前行的下一行記錄,如果第一次讀取則返回第一行。默認(rèn)的讀取選項為Next

                   Prior表示返回結(jié)果集中當(dāng)前行的前一行記錄,如果第一次讀取則沒有行返回,并且把游標(biāo)置于第一行之前。

                   First表示返回結(jié)果集中的第一行,并且將其作為當(dāng)前行。

                   Last表示返回結(jié)果集中的最后一行,并且將其作為當(dāng)前行。

                   Absolute n 如果n為正數(shù),則返回從游標(biāo)頭開始的第n行,并且返回行變成新的當(dāng)前行。如果n為負,則返回從游標(biāo)末尾開始的第n行,并且返回行為新的當(dāng)前行,如果n為0,則返回當(dāng)前行。

                   Relative n 如果n為正數(shù),則返回從當(dāng)前行開始的第n行,如果n為負,則返回從當(dāng)前行之前的第n行,如果為0,則返回當(dāng)前行。

      關(guān)閉游標(biāo)調(diào)用的是Close語句,方式如下:Close Global MyCursor               Close MyCursor

      釋放游標(biāo)調(diào)用的是Deallocate語句,方法如下:Deallocate Glboal MyCursor       Deallocate MyCursor

 

      游標(biāo)實例:

            Declare MyCusror Cursor Scroll

                  For Select * From Master_Goods Order By GoodsID

            Open MyCursor

            Fetch next From MyCursor
            Into @GoodsCode,@GoodsName

            While(@@Fetch_Status = 0)
                  Begin

                         Begin
                               Select @GoodsCode = Convert(Char(20),@GoodsCode)
                               Select @GoodsName = Convert(Char(20),@GoodsName)
                               PRINT @GoodsCode + ':' + @GoodsName
                         End

                         Fetch next From MyCursor
                         Into @GoodsCode,@GoodsName

                  End
            Close MyCursor
            Deallocate MyCursor

 

      修改當(dāng)前游標(biāo)的數(shù)據(jù)方法如下:

            UpDate Master_Goods Set GoodsName = 'yangyang8848' Where Current Of MyCursor;
      刪除當(dāng)前游標(biāo)行數(shù)據(jù)的方法如下: 
            Delete From Master_Goods Where Current Of MyCursor

 

      Select @@CURSOR_ROWS 可以得到當(dāng)前游標(biāo)中存在的數(shù)據(jù)行數(shù)。注意:此變量為一個連接上的全局變量,因此只對應(yīng)最后一次打開的游標(biāo)。

游標(biāo)例子

select * from dbo.Products

select * from dbo.Orders

select * from [Order Details]

select * from dbo.Categories Select b.ProductID,datepart(Year,a.OrderDate) as [Year],datepart(Month,a.OrderDate) as [Month],Sum(b.Quantity) as Quantity, c.ProductName,Count(a.OrderID) as Orders from Orders a Inner Join [Order Details] b On a.OrderID = b.OrderID Inner Join Products c On b.ProductID=c.ProductID Group By b.ProductID,a.OrderDate,c.ProductName Order by b.ProductID Asc,a.OrderDate Asc go

--第一題

Alter PROCEDURE USP_Product1 AS BEGIN DECLARE @ID int,@TempID int DECLARE @ProductName NVARCHAR(50) DECLARE @Year NVARCHAR(4) DECLARE @Month NVARCHAR(2) DECLARE @OrderCount int DECLARE @DetailsCount int DECLARE @TotalCount int DECLARE @TotalDetails int DECLARE Cursor_Product CURSOR LOCAL FOR

--STEP1 聲明游標(biāo) Select b.ProductID,datepart(Year,a.OrderDate) as [Year],datepart(Month,a.OrderDate) as [Month],Sum(b.Quantity) as Quantity, c.ProductName,Count(a.OrderID) as Orders from Orders a Inner Join [Order Details] b On a.OrderID = b.OrderID Inner Join Products c On b.ProductID=c.ProductID Group By b.ProductID,a.OrderDate,c.ProductName Order by b.ProductID Asc,a.OrderDate Asc FOR READ ONLY

CREATE TABLE #ProductDetails (產(chǎn)品編號 INT ,產(chǎn)品名稱 NVARCHAR(50),月份 NVARCHAR(10),訂單總數(shù) INT,出貨總量 INT)

--創(chuàng)建臨時表 set @TempID=null --判斷是否為同一個商品 set @TotalCount = 0 set @TotalDetails = 0 OPEN Cursor_Product

--STEP2 打開游標(biāo) WHILE(1=1) BEGIN

--STEP3 從游標(biāo)中提取行 FETCH NEXT FROM Cursor_Product INTO @ID,@Year,@Month,@DetailsCount,@ProductName,@OrderCount IF(@@FETCH_STATUS<>0)BREAK

--跳出循環(huán) if @TempID is null

--判斷是第一次的話為@TempID賦值 BEGIN set @TempID=@ID END if @TempID<>@ID

--判斷是否需要插入小計 BEGIN insert into #ProductDetails Values(null,null,N'小計:',@TotalCount,@TotalDetails) set @TotalCount = 0 set @TotalDetails = 0 set @TempID=@ID Continue

--跳入下次循環(huán) END set @TotalCount = @TotalCount+@OrderCount set @TotalDetails = @TotalDetails+@DetailsCount insert into #ProductDetails Values(@ID,@ProductName,Convert(Nvarchar(50),@Year)+N'年'+Convert(Nvarchar(50),@Month)+N'月',@OrderCount,@DetailsCount) END select * from #ProductDetails CLOSE Cursor_Product

--STEP4 關(guān)閉游標(biāo) DEALLOCATE Cursor_Product

 --STEP5 釋放游標(biāo) END GO EXEC USP_Product1 -- DROP Proc USP_Product1 GO Select c.ProductName,Count(a.OrderID) as Orders,SUM(c.UnitPrice*b.Quantity) as Price from Orders a Inner Join [Order Details] b On a.OrderID = b.OrderID Inner Join Products c On b.ProductID=c.ProductID Where c.CategoryID=1 Group By c.ProductName Order by c.ProductName Asc Go

--第二題 Alter PROCEDURE USP_Product2 AS BEGIN DECLARE @ID int,@Temp int DECLARE @CategoryName NVARCHAR(50) DECLARE @ProductName NVARCHAR(50) DECLARE @POCount int DECLARE @POPrices money --小計 DECLARE @OrderCount int DECLARE @OrderPrices money --總計 DECLARE @Orders int DECLARE @Prices money

-- 聲明游標(biāo)1 DECLARE Cursor_Categories CURSOR LOCAL FOR select CategoryID,CategoryName from Categories FOR READ ONLY CREATE TABLE #ProductDetails (產(chǎn)品類別 INT ,類別名稱 NVARCHAR(50),產(chǎn)品名稱 NVARCHAR(50),下單總次數(shù) INT,下單總價格 INT)

--創(chuàng)建臨時表 --小計 SET @OrderCount = 0 SET @OrderPrices = 0

--總計 SET @Orders = 0 SET @Prices = 0 OPEN Cursor_Categories

-- 打開游標(biāo)1 WHILE(1=1) BEGIN

-- 從游標(biāo)1中提取行 FETCH NEXT FROM Cursor_Categories INTO @ID,@CategoryName IF(@@FETCH_STATUS<>0)BREAK

--跳出循環(huán) DECLARE Cursor_Product CURSOR LOCAL FOR

-- 聲明游標(biāo)2 Select c.ProductName,Count(a.OrderID) as Orders,SUM(c.UnitPrice*b.Quantity) as Price from Orders a Inner Join [Order Details] b On a.OrderID = b.OrderID Inner Join Products c On b.ProductID=c.ProductID Where c.CategoryID=@ID Group By c.ProductName Order by c.ProductName Asc FOR READ ONLY Set @Temp = null OPEN Cursor_Product

-- 打開游標(biāo)2 WHILE(1=1) BEGIN FETCH NEXT FROM Cursor_Product INTO @ProductName,@POCount,@POPrices IF(@@FETCH_STATUS<>0)BREAK

--跳出循環(huán) if(@Temp is Null) -- 第一次進入 BEGIN SET @Temp=1 SET @OrderCount=@POCount SET @OrderPrices=@POPrices Insert Into #ProductDetails Values(@ID,@CategoryName,@ProductName,@POCount,@POPrices) END Else BEGIN SET @OrderCount= @OrderCount + @POCount SET @OrderPrices= @OrderPrices + @POPrices Insert Into #ProductDetails(產(chǎn)品名稱,下單總次數(shù),下單總價格) Values(@ProductName,@POCount,@POPrices) END END Insert Into #ProductDetails(產(chǎn)品名稱,下單總次數(shù),下單總價格) Values('小計:',@OrderCount,@OrderPrices)

--累加總計 SET @Orders = @Orders + @OrderCount SET @Prices = @Prices + @OrderPrices CLOSE Cursor_Product

-- 關(guān)閉游標(biāo)2 DEALLOCATE Cursor_Product

-- 釋放游標(biāo)2 END Insert Into #ProductDetails(產(chǎn)品名稱,下單總次數(shù),下單總價格) Values('總計:',@Orders,@Prices) select * from #ProductDetails CLOSE Cursor_Categories

-- 關(guān)閉游標(biāo)1 DEALLOCATE Cursor_Categories

-- 釋放游標(biāo)1 END GO EXEC USP_Product2 -- DROP PROC USP_Product2

    本站是提供個人知識管理的網(wǎng)絡(luò)存儲空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點。請注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點擊一鍵舉報。
    轉(zhuǎn)藏 分享 獻花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多