数据库——游标
來源:http://blog.csdn.net/liujiahan629629/article/details/18014051
? ? ? ??一,游標是什么?
?????????????????游標是一段私有的SQL工作區(qū),也就是一段內(nèi)存區(qū)域,用于暫時存放受SQL語句影響到的數(shù)據(jù)。通俗理解就是將受影響的數(shù)據(jù)暫時放到了一個內(nèi)存區(qū)域的虛表中,而這個虛表就是游標。
? ? ? ? 游標是一種能從包括多條數(shù)據(jù)記錄的結(jié)果集中每次提取一條記錄的機制。即游標用來逐行讀取結(jié)果集。游標充當(dāng)指針的作用。盡管游標能遍歷結(jié)果中的所有行,但他一次只指向一行。概括來講,SQL的游標是一種臨時的數(shù)據(jù)庫對象,即可以用來存放在數(shù)據(jù)庫表中的數(shù)據(jù)行副本,也可以指向存儲在數(shù)據(jù)庫中的數(shù)據(jù)行的指針。游標提供了在逐行的基礎(chǔ)上操作表中數(shù)據(jù)的方法。
? ? ? ? 游標的一個常見用途就是保存查詢結(jié)果,以便以后使用。游標的結(jié)果集是由SELECT語句產(chǎn)生,如果處理過程需要重復(fù)使用一個記錄集,那么創(chuàng)建一次游標而重復(fù)使用若干次,比重復(fù)查詢數(shù)據(jù)庫要快的多。
游標cursor,主要用于循環(huán)處理結(jié)果集。??存儲Select的查詢結(jié)果,并用來遍歷。
? ? ? ? 在數(shù)據(jù)庫中,游標是一個十分重要的概念。游標提供了一種對從表中檢索出的數(shù)據(jù)進行操作的靈活手段,就本質(zhì)而言,游標實際上是一種能從包括多條數(shù)據(jù)記錄的結(jié)果集中每次提取一條記錄的機制。游標總是與一條T_SQL 選擇語句相關(guān)聯(lián)因為游標由結(jié)果集(可以是零條、一條或由相關(guān)的選擇語句檢索出的多條記錄)和結(jié)果集中指向特定記錄的游標位置組成。當(dāng)決定對結(jié)果集進行處理時,必須聲明一個指向該結(jié)果集的游標。如果曾經(jīng)用 C 語言寫過對文件進行處理的程序,那么游標就像您打開文件所得到的文件句柄一樣,只要文件打開成功,該文件句柄就可代表該文件。對于游標而言,其道理是相同的。可見游標能夠?qū)崿F(xiàn)按與傳統(tǒng)程序讀取平面文件類似的方式處理來自基礎(chǔ)表的結(jié)果集,從而把表中數(shù)據(jù)以平面文件的形式呈現(xiàn)給程序。
? ? ? ? 由此可見,游標允許應(yīng)用程序?qū)Σ樵冋Z句select 返回的行結(jié)果集中每一行進行相同或不同的操作,而不是一次對整個結(jié)果集進行同一種操作;它還提供對基于游標位置而對表中數(shù)據(jù)進行刪除或更新的能力;而且,正是游標把作為面向集合的數(shù)據(jù)庫管理系統(tǒng)和面向行的程序設(shè)計兩者聯(lián)系起來,使兩個數(shù)據(jù)處理方式能夠進行溝通。
????????二,作用是什么?
????????????????? 1,大家都知道數(shù)據(jù)庫中的事物可以回滾,而游標在其中起著非常重要的作用,由于對數(shù)據(jù)庫的操作我們會暫時放在游標中,只要不提交,我們就可以根據(jù)游標中內(nèi)容進行回滾,在一定意義有利于數(shù)據(jù)庫的安全。
???????????????? 2,另外,在Oracle中PL/SQL只能返回單行數(shù)據(jù),而游標彌補了這個不足。相當(dāng)于ADO.NET中的Data?table吧。
?????????三,類型:
??????????????? 1,隱式游標:增刪改等操作Oracle都會自動創(chuàng)建游標,暫時保存操作結(jié)果,也就是能夠回滾的操作都會引發(fā)游標的創(chuàng)建。
??????????????? 2,顯示游標:由開發(fā)人員通過程序顯式控制,用于從表中取出多行數(shù)據(jù),并將多行數(shù)據(jù)一行一行的單獨進行處理.
?????????四,屬性:
| 屬性 | 注釋 |
| %rowcount? | ?受SQL影響的行數(shù) |
| %found | ?Boolean值,是否還有數(shù)據(jù) |
| %notfound | Boolean值,是否已無數(shù)據(jù) |
| %isopen | 游標是否打開 |
?
????當(dāng)然如果我們想獲得隱式游標的屬性,通過%前邊加上SQL即可得到。例如SQL%rowcount.
?
????????五,游標簡單認識了,我們來看看游標的具體使用:
?????????? 1,先看一下簡單的使用游標四步驟:
| 步驟 | 關(guān)鍵詞 | 說明 |
| 1 | 在DECLARE中cursor | 聲明游標,創(chuàng)建一個命名的查詢語句 |
| 2 | Open | 打開游標 |
| 3 | Fetch | 取出游標中的一條記錄裝入變量 |
| 4 | Close | 釋放游標 |
?
?
????????? 2,當(dāng)然游標中可以存放一條數(shù)據(jù),也可以存放多條數(shù)據(jù),后者是我們用游標,前者我們通過PL/SQL語句即可完成的,這樣我們在這里就必須用到循環(huán)結(jié)構(gòu)了,在Oracle數(shù)據(jù)庫中我們可以使用while……?loop……end loop , for……?loop……end loop,loop……end loop。在這里需要提出的是,for循環(huán)結(jié)構(gòu)在Oracle中被簡化了,我們只需要聲明和使用即可。看下邊這個例子吧:
?????
???????? a,whlie循環(huán)結(jié)構(gòu)的:
declare --定義記錄類型的變量 v_user user%rowtype; --1,利用cursor關(guān)鍵字聲明游標 cursor user_cur is select * from user; begin --2,打開游標 open user_cur; --3,利用fetch讀取數(shù)據(jù) fetch user_cur into v_user; while user_cur%found loop dbms_output.put_line(v_user.userName); fetch user_cur into v_user; end loop; --4,釋放游標 close user_cur; end;??? ? ??b ,簡化的for結(jié)構(gòu)循環(huán):
declare --1,利用cursor關(guān)鍵字聲明游標 cursor user_cur is select * from user; begin --2,直接使用,Oracle會自動打開和關(guān)閉等操作。 for v_user in user_cur loop dbms_output.put_line(v_user.userName); end loop end;
這里就介紹這兩種的類型,loop的和這都差不多。
?
? ? ? ? ? 3,最后在這里再學(xué)習(xí)一下帶參數(shù)的游標,也是就和我們但參數(shù)的類是一樣的,只不過一個用在了數(shù)據(jù)庫中,一個用在了編程語言中。
declare --定義記錄類型的變量 v_User user%rowtype; --1,利用cursor關(guān)鍵字聲明帶參數(shù)的游標 cursor user_Cur(v_UserNo number) is select * from user where userNo=v_UserNo; begin --2,打開帶參數(shù)的游標,使之更加靈活 。 open user_Cur(1012); --3,利用fetch讀取數(shù)據(jù) loop fetch user_Cur into v_User; exit when user_Cur%notfound; dbms_output.put_line(v_user.userName); end loop; --4,釋放游標 close user_Cur; end;游標(cursor)
游標是系統(tǒng)為用戶開設(shè)的一個數(shù)據(jù)緩沖區(qū),存放SQL語句的執(zhí)行結(jié)果
每個游標區(qū)都有一個名字
用戶可以用SQL語句逐一從游標中獲取記錄,并賦給主變量,交由主語言進一步處理
主語言是面向記錄的,一組主變量一次只能存放一條記錄
僅使用主變量并不能完全滿足SQL語句向應(yīng)用程序輸出數(shù)據(jù)的要求
嵌入式SQL引入了游標的概念,用來協(xié)調(diào)這兩種不同的處理方式
在數(shù)據(jù)庫開發(fā)過程中,當(dāng)你檢索的數(shù)據(jù)只是一條記錄時,你所編寫的事務(wù)語句代碼往往使用SELECT INSERT 語句。但是我們常常會遇到這樣情況,即從某一結(jié)果集中逐一地讀取一條記錄。那么如何解決這種問題呢?游標為我們提供了一種極為優(yōu)秀的解決方案。
1.游標和游標的優(yōu)點
在數(shù)據(jù)庫中,游標是一個十分重要的概念。游標提供了一種對從表中檢索出的數(shù)據(jù)進行操作的靈活手段,就本質(zhì)而言,游標實際上是一種能從包括多條數(shù)據(jù)記錄的結(jié)果集中每次提取一條記錄的機制。游標總是與一條SQL 選擇語句相關(guān)聯(lián)因為游標由結(jié)果集(可以是零條、一條或由相關(guān)的選擇語句檢索出的多條記錄)和結(jié)果集中指向特定記錄的游標位置組成。當(dāng)決定對結(jié)果集進行處理時,必須聲明一個指向該結(jié)果集的游標。如果曾經(jīng)用 C 語言寫過對文件進行處理的程序,那么游標就像您打開文件所得到的文件句柄一樣,只要文件打開成功,該文件句柄就可代表該文件。對于游標而言,其道理是相同的。可見游標能夠?qū)崿F(xiàn)按與傳統(tǒng)程序讀取平面文件類似的方式處理來自基礎(chǔ)表的結(jié)果集,從而把表中數(shù)據(jù)以平面文件的形式呈現(xiàn)給程序。
我們知道關(guān)系數(shù)據(jù)庫管理系統(tǒng)實質(zhì)是面向集合的,在MS SQL SERVER 中并沒有一種描述表中單一記錄的表達形式,除非使用where 子句來限制只有一條記錄被選中。因此我們必須借助于游標來進行面向單條記錄的數(shù)據(jù)處理。由此可見,游標允許應(yīng)用程序?qū)Σ樵冋Z句select 返回的行結(jié)果集中每一行進行相同或不同的操作,而不是一次對整個結(jié)果集進行同一種操作;它還提供對基于游標位置而對表中數(shù)據(jù)進行刪除或更新的能力;而且,正是游標把作為面向集合的數(shù)據(jù)庫管理系統(tǒng)和面向行的程序設(shè)計兩者聯(lián)系起來,使兩個數(shù)據(jù)處理方式能夠進行溝通。
2. 游標種類
MS SQL SERVER 支持三種類型的游標:Transact_SQL 游標,API 服務(wù)器游標和客戶游標。
(1) Transact_SQL 游標
Transact_SQL 游標是由DECLARE CURSOR 語法定義、主要用在Transact_SQL 腳本、存儲過程和觸發(fā)器中。Transact_SQL 游標主要用在服務(wù)器上,由從客戶端發(fā)送給服務(wù)器的Transact_SQL 語句或是批處理、存儲過程、觸發(fā)器中的Transact_SQL 進行管理。 Transact_SQL 游標不支持提取數(shù)據(jù)塊或多行數(shù)據(jù)。
(2) API 游標
API 游標支持在OLE DB, ODBC 以及DB_library 中使用游標函數(shù),主要用在服務(wù)器上。每一次客戶端應(yīng)用程序調(diào)用API 游標函數(shù),MS SQL SEVER 的OLE DB 提供者、ODBC驅(qū)動器或DB_library 的動態(tài)鏈接庫(DLL) 都會將這些客戶請求傳送給服務(wù)器以對API游標進行處理。
(3) 客戶游標
客戶游標主要是當(dāng)在客戶機上緩存結(jié)果集時才使用。在客戶游標中,有一個缺省的結(jié)果集被用來在客戶機上緩存整個結(jié)果集。客戶游標僅支持靜態(tài)游標而非動態(tài)游標。由于服務(wù)器游標并不支持所有的Transact-SQL 語句或批處理,所以客戶游標常常僅被用作服務(wù)器游標的輔助。因為在一般情況下,服務(wù)器游標能支持絕大多數(shù)的游標操作。由于API 游標和Transact-SQL 游標使用在服務(wù)器端,所以被稱為服務(wù)器游標,也被稱為后臺游標,而客戶端游標被稱為前臺游標。在本章中我們主要講述服務(wù)器(后臺)游標
游標詳細說明:
RS.OPEN SQL,CONN,A,B
參數(shù)A為設(shè)定游標的類型,其取值為:
0 僅向前游標,只能向前瀏覽記錄,不支持分頁、Recordset、BookMark
1 鍵集游標,其他用戶對記錄說做的修改將反映到記錄集中,但其他用戶增加或刪除記錄不會反映到記錄集中。支持分頁、Recordset、BookMark
2 動態(tài)游標功能最強,但耗資源也最多。用戶對記錄所做的修改增加或刪除記錄都將反映到記錄集中。支持全功能瀏覽。
3 靜態(tài)游標,只是數(shù)據(jù)的一個快照,用戶對記錄所做的修改增加或刪除記錄都不會反映到記錄集中。支持向前或向后移動
參數(shù)B為記錄集的鎖定類型,其取值為:
1 鎖定類型,默認的,只讀,不能作任何修改
2 當(dāng)編輯時立即鎖定記錄,最安全的方式
3 只有在調(diào)用Update方法時才鎖定記錄集,而在此前的其他操作仍可對當(dāng)前記錄進行更改、插入和刪除等
4 當(dāng)編輯時記錄不會被鎖定,而更改、插入和刪除是在批處理方式下完成的
打開數(shù)據(jù)記錄集方法其實不止一種,但是我們用的最多的就是
rs.open sql,1,1的方法,可是后面的數(shù)字參數(shù)很多人不解其意,下面我們來介紹一下。
其實open方法后面有多個參數(shù)
CursorType LockType CommandType
比如 rs.open sql,1,1
也可以寫成
rs.cursorType = 1
rs.LockType = 1
rs.open sql
其中CursorType代表從一個表或者一個SQL查詢結(jié)果返回的記錄。
這個參數(shù)有四個值分別是:
adOpenForwardOnly 表示只允許在記錄集內(nèi)的記錄間往前移動。這個是缺省值。
adOpenKeyset 反映由其它用戶所做的對記錄的改變或者刪除動作,但并不反映由其它用戶做作的添加新記錄的動作。
adOpenDynamic 反映由其它用戶所做的對記錄的改變或者刪除動作,包括添加的新記錄
adOpenStatic 不反映其它用戶對記錄所做的修改,添加,刪除動作。
這四個值VBSCRIPT預(yù)定義位
adOpenForwardOnly = 0
adOpenKeyset = 1
adOpenDynamic = 2
adOpenStatic = 3
lockType 表示當(dāng)打開記錄集時,數(shù)據(jù)提供者用于鎖定數(shù)據(jù)庫的類型:
adLockReadOnly 數(shù)據(jù)不能改變,這是缺省值!
adLockPessimistic 數(shù)據(jù)提供者在開始編輯數(shù)據(jù)的時候鎖定記錄
adLockOptimistic 僅當(dāng)調(diào)用update方法時,數(shù)據(jù)提供者鎖定記錄
adLockBatchOptimistic 用于批處理修改
他們的常量值定義分別是:
adLockReadOnly = 1
adLockPessimistic = 2
adLockOptimistic = 3
adLockBatchOptimistic = 4
rs.open sql,conn,1,1 讀取記錄 select
rs.open sql,conn,1,3 只更新記錄最好 update
rs.open sql,conn,2,3 插入和刪除最好 insert delete
下面的解釋:
1. 什幺是游標?
游標,也有人稱為光標。概括的講,它是基于記錄的。
過去,關(guān)系型數(shù)據(jù)庫沒有象現(xiàn)在這樣被廣泛的應(yīng)用。那時候,人們大多使用 dBase 這樣的小型數(shù)據(jù)庫軟件。這類數(shù)據(jù)庫確切的說應(yīng)為數(shù)據(jù)文件管理軟件。他們是面向記錄的。
不過,這種方式也許更符合人們的習(xí)慣。比如,我們在電話本中查找號碼,在學(xué)生檔案中查找檔案,最終都要歸結(jié)于其中的一個號碼,一個檔案,那就是一條記錄。現(xiàn)實生活中,我們在一張表格中尋找某一項時,可能會用手一條一條逐行的掃過,以幫助我們找到所需的那條記錄。對應(yīng)于數(shù)據(jù)庫來說,這就是游標的模型。所以,你可以這樣想象:表格是數(shù)據(jù)庫中的表,而我們的手好比是游標。
所以,當(dāng)你使用類似 .MoveNext,.MoveLast 這樣的語句時,覺得再自然不過了。
現(xiàn)在,你明白什幺是游標了吧。游標就是數(shù)據(jù)的 ' 定位系統(tǒng) ' 。
這個 ' 定位系統(tǒng) ' 粗分有兩種:服務(wù)器游標和客戶游標。對應(yīng)于 ADO 中的 CursorLocation 。舉例來說:
Set rs = Server.CreateObject("ADODB.RecordSet")
rs.CursorLocation = adUseServer????? '缺省,使用服務(wù)器游標
.....
rs.CursorLocation = adUseClient????? '使用客戶游標
2. 什幺是服務(wù)器游標?
或者稱為 API 服務(wù)器游標。
假設(shè)你要查詢有關(guān)編程語言的書,寫成 SQL 語句就是:SELECT book_id,book_title FROM books WHERE book_catalog = '編程' ORDER BY book_title,同時你指定使用服務(wù)器游標。
這條語句發(fā)送到服務(wù)器,服務(wù)器對數(shù)據(jù)進行檢索,將符合查詢條件的記錄集合放入臨時表(對某些游標類型是這樣)中。每當(dāng)你進行 .MoveNext 操作,服務(wù)器就會發(fā)送一條記錄到客戶端的緩沖區(qū),然后你才可以使用它。
3. 什幺是 CacheSize ?
繼續(xù)上面的例子,假設(shè)符合查詢條件的記錄有100個,也就是說,用 .MoveNext 這種方法遍歷該結(jié)果集需要同服務(wù)器交互100次。我們可以通過設(shè)置 CacheSize 使客戶與服務(wù)器的通信變少。上面的例子其實就是 CacheSize=1 的情況,這是缺省值。
假設(shè) CacheSize=4,當(dāng) RecordSet 對象打開時,服務(wù)器發(fā)送4條記錄到客戶端。前4次的 .MoveNext 操作實際上是在客戶緩沖區(qū)中得到數(shù)據(jù),當(dāng)?shù)?次 .Movenext 時,服務(wù)器才發(fā)送下一個4條記錄。由此,減少了客戶與服務(wù)器間的網(wǎng)絡(luò)通信。
那幺是不是說 CacheSize 越大越好呢?千萬不要想當(dāng)然。萬事都有兩面性,CacheSize 也一樣。客戶端請求數(shù)據(jù),服務(wù)器發(fā)送數(shù)據(jù),這個過程有點象交通管理。CacheSize 過高,會阻塞交通,甚至引起數(shù)據(jù)丟失(比如當(dāng) Cachesize 大于客戶端緩沖區(qū)時)。對于不同的應(yīng)用,所取的值也不同。
另外要指出的是,使用任何形式的游標都不是最有效的訪問數(shù)據(jù)的方法,Cachesize 有的時候并不是瓶頸,盡量將使用游標的程序轉(zhuǎn)換為面向結(jié)果集的程序,性能會提高很多。
3. 什幺是客戶游標?
既然游標是數(shù)據(jù)的 ' 定位系統(tǒng) ',那幺在客戶端也可以完成。
客戶游標的產(chǎn)生由來已久,當(dāng)初是為了彌補服務(wù)器的不足(有些數(shù)據(jù)庫引擎就不支持游標)。隨著服務(wù)器游標的出現(xiàn),客戶游標似乎已經(jīng)過時了,不過還是那句話:萬事都有兩面性,在 internet 上,與數(shù)據(jù)庫的連接并不是永久的,使用客戶游標能使我們獲得同使用服務(wù)器游標一樣的功能。
當(dāng) CursorLoction 屬性設(shè)成 adUseClient 時,微軟的游標服務(wù)( Cursor Service )創(chuàng)建 RecordSet 對象,用前向 / 只讀的游標方式從服務(wù)器將所有查詢結(jié)果檢索出來,并且存儲在客戶緩沖區(qū)中。當(dāng)應(yīng)用程序通過 ADO 請求數(shù)據(jù)時,游標服務(wù)就從客戶緩沖區(qū)中檢取數(shù)據(jù)。這種方式在連接遠程服務(wù)器時非常有用,它會提高應(yīng)用程序的性能。如果你訪問的數(shù)據(jù)庫是 Jet 數(shù)據(jù)庫( Access ),而且在本地,那么用客戶游標非但不提高性能,還會使性能下降。這時候,數(shù)據(jù)將被緩存兩次,數(shù)據(jù)庫一次,游標服務(wù)一次。
如果考慮應(yīng)用的功能,客戶游標功能是很完善的,它能支持某些數(shù)據(jù)庫不能完成的操作( 視數(shù)據(jù)庫的情況而定 )。
4. 什幺是 DisConnected RecordSet ?
我們使用了客戶游標,就可以斷開與數(shù)據(jù)庫的連接,釋放 Connection 對象。這樣的結(jié)果集就是 DisConnected RecordSet。舉例說明:
Dim c As New ADODB.Connection
Dim r As New ADODB.Recordset
On Error GoTo handler
???
c.ConnectionString = connectStr
c.CursorLocation = adUseClient
c.Open
Set r.ActiveConnection = c
r.Open SqlText, , adOpenKeyset, adLockBatchOptimistic, -1
Set r.ActiveConnection = Nothing?? ' This disconnects the recordset.
c.Close
Set c = Nothing
......
......???
' Recordset is now in disconnected state; do something with it.
r.Close
Set r = Nothing
使用游標
這里要做一個聲明,我們所說的游標通常是指顯式游標,因此從現(xiàn)在起沒有特別指明的情況,我們所說的游標都是指顯式游標。要在程序中使用游標,必須首先聲明游標。
聲明游標
語法:CURSOR cursor_name?IS select_statement;?
在PL/SQL中游標名是一個未聲明變量,不能給游標名賦值或用于表達式中。
打開游標
使用游標中的值之前應(yīng)該首先打開游標,打開游標初始化查詢處理。打開游標的語法是:OPEN cursor_name?
cursor_name是在聲明部分定義的游標名。
例:OPEN C_EMP;?
關(guān)閉游標
語法:CLOSE cursor_name?
例:CLOSE C_EMP;?
從游標提取數(shù)據(jù)
從游標得到一行數(shù)據(jù)使用FETCH命令。每一次提取數(shù)據(jù)后,游標都指向結(jié)果集的下一行。
? ? ? ?語法如下:FETCH cursor_name INTO variable[,variable,...]?
對于SELECT定義的游標的每一列,FETCH變量列表都應(yīng)該有一個變量與之相對應(yīng),變量的類型也要相同。
例: SET SERVERIUTPUT ON DECLARE v_ename EMP.ENAME%TYPE; v_salary EMP.SALARY%TYPE; CURSOR c_emp IS SELECT ename,salary FROM emp; BEGIN OPEN c_emp; FETCH c_emp INTO v_ename,v_salary; DBMS_OUTPUT.PUT_LINE('Salary of Employee'|| v_ename ||'is'|| v_salary); FETCH c_emp INTO v_ename,v_salary; DBMS_OUTPUT.PUT_LINE('Salary of Employee'|| v_ename ||'is'|| v_salary); FETCH c_emp INTO v_ename,v_salary; DBMS_OUTPUT.PUT_LINE('Salary of Employee'|| v_ename ||'is'|| v_salary); CLOSE c_emp; END 這段代碼無疑是非常麻煩的,如果有多行返回結(jié)果,可以使用循環(huán)并用游標屬性為結(jié)束循環(huán)的條件,以這種方式提取數(shù)據(jù),程序的可讀性和簡潔性都大為提高,下面我們使用循環(huán)重新寫上面的程序:
SET SERVERIUTPUT ON DECLARE v_ename EMP.ENAME%TYPE; v_salary EMP.SALARY%TYPE; CURSOR c_emp IS SELECT ename,salary FROM emp; BEGIN OPEN c_emp; LOOP FETCH c_emp INTO v_ename,v_salary; EXIT WHEN c_emp%NOTFOUND; DBMS_OUTPUT.PUT_LINE('Salary of Employee'|| v_ename ||'is'|| v_salary); END 記錄變量
定義一個記錄變量使用TYPE命令和%ROWTYPE,關(guān)于%ROWsTYPE的更多信息請參閱相關(guān)資料。
記錄變量用于從游標中提取數(shù)據(jù)行,當(dāng)游標選擇很多列的時候,那么使用記錄比為每列聲明一個變量要方便得多。
當(dāng)在表上使用%ROWTYPE并將從游標中取出的值放入記錄中時,如果要選擇表中所有列,那么在SELECT子句中使用*比將所有列名列出來要安全 例: SET SERVERIUTPUT ON DECLARE R_emp EMP%ROWTYPE; CURSOR c_emp IS SELECT * FROM emp; BEGIN OPEN c_emp; LOOP FETCH c_emp INTO r_emp; EXIT WHEN c_emp%NOTFOUND; DBMS_OUT.PUT.PUT_LINE('Salary of Employee'||r_emp.ename||'is'|| r_emp.salary); END LOOP; CLOSE c_emp; END; %ROWTYPE也可以用游標名來定義,這樣的話就必須要首先聲明游標:SET SERVERIUTPUT ON DECLARE CURSOR c_emp IS SELECT ename,salary FROM emp; R_emp c_emp%ROWTYPE; BEGIN OPEN c_emp; LOOP FETCH c_emp INTO r_emp; EXIT WHEN c_emp%NOTFOUND; DBMS_OUT.PUT.PUT_LINE('Salary of Employee'||r_emp.ename||'is'|| r_emp.salary); END LOOP; CLOSE c_emp; END; 帶參數(shù)的游標
與存儲過程和函數(shù)相似,可以將參數(shù)傳遞給游標并在查詢中使用。這對于處理在某種條件下打開游標的情況非常有用。
它的語法如下:CURSOR cursor_name[(parameter[,parameter],...)] IS select_statement;?
定義參數(shù)的語法如下:Parameter_name [IN] data_type[{:=|DEFAULT} ]?
與存儲過程不同的是,游標只能接受傳遞的值,而不能返回值。參數(shù)只定義數(shù)據(jù)類型,沒有大小。?
另外可以給參數(shù)設(shè)定一個缺省值,當(dāng)沒有參數(shù)值傳遞給游標時,就使用缺省值。
游標中定義的參數(shù)只是一個占位符,在別處引用該參數(shù)不一定可靠。
在打開游標時給參數(shù)賦值,語法如下:OPEN cursor_name[[,]....];?
參數(shù)值可以是文字或變量。
在大多數(shù)時候我們在設(shè)計程序的時候都遵循下面的步驟:
1、打開游標
2、開始循環(huán)
3、從游標中取值
4、檢查那一行被返回
5、處理
6、關(guān)閉循環(huán)
7、關(guān)閉游標
可以簡單的把這一類代碼稱 偽代碼 的 FOR循環(huán),用于FOR循環(huán)的游標按照正常的聲明方式聲明,它的優(yōu)點在于不需要顯式的打開、關(guān)閉、取數(shù)據(jù),測試數(shù)據(jù)的存在、定義存放數(shù)據(jù)的變量等等。游標FOR 循環(huán)的語法如下:
FOR record_name IN
(corsor_name[(parameter[,parameter]...)]
| (query_difinition)
LOOP
statements
END LOOP;?
下面我們用for循環(huán)重寫上面的例子:
DECALRE CURSOR c_dept IS SELECT deptno,dname FROM dept ORDER BY deptno; CURSOR c_emp (p_dept VARACHAR2) IS SELECT ename,salary FROM emp WHERE deptno=p_dept ORDER BY enamev_tot_salary EMP.SALARY%TYPE;BEGINFOR r_dept IN c_dept LOOP DBMS_OUTPUT.PUT_LINE('Department:'|| r_dept.deptno||'-'||r_dept.dname); v_tot_salary:=0; FOR r_emp IN c_emp(r_dept.deptno) LOOP DBMS_OUTPUT.PUT_LINE('Name:'|| v_ename||' salary:'||v_salary); v_tot_salary:=v_tot_salary+v_salary; END LOOP; DBMS_OUTPUT.PUT_LINE('Toltal Salary for dept:'|| v_tot_salary); END LOOP;END; 在游標FOR循環(huán)中使用查詢
在游標FOR循環(huán)中可以定義查詢,由于沒有顯式聲明所以游標沒有名字,記錄名通過游標查詢來定義。
DECALREv_tot_salary EMP.SALARY%TYPE;BEGINFOR r_dept IN (SELECT deptno,dname FROM dept ORDER BY deptno) LOOPDBMS_OUTPUT.PUT_LINE('Department:'|| r_dept.deptno||'-'||r_dept.dname);v_tot_salary:=0;FOR r_emp IN (SELECT ename,salaryFROM empWHERE deptno=p_deptORDER BY ename) LOOPDBMS_OUTPUT.PUT_LINE('Name:'|| v_ename||' salary:'||v_salary);v_tot_salary:=v_tot_salary+v_salary;END LOOP;DBMS_OUTPUT.PUT_LINE('Toltal Salary for dept:'|| v_tot_salary);END LOOP;END; 游標中的子查詢
語法如下:
CURSOR C1 IS SELECT * FROM emp
WHERE deptno NOT IN (SELECT deptno
FROM dept
WHERE dname!='ACCOUNTING');?
可以看出與SQL中的子查詢沒有什么區(qū)別。
游標中的更新和刪除
在PL/SQL中依然可以使用UPDATE和DELETE語句更新或刪除數(shù)據(jù)行。顯式游標只有在需要獲得多行數(shù)據(jù)的情況下使用。PL/SQL提供了僅僅使用游標就可以執(zhí)行刪除或更新記錄的方法。
UPDATE或DELETE語句中的WHERE CURRENT OF子串專門處理要執(zhí)行UPDATE或DELETE操作的表中取出的最近的數(shù)據(jù)。要使用這個方法,在聲明游標時必須使用FOR UPDATE子串,當(dāng)對話使用FOR UPDATE子串打開一個游標時,所有返回集中的數(shù)據(jù)行都將處于行級(ROW-LEVEL)獨占式鎖定,其他對象只能查詢這些數(shù)據(jù)行,不能進行UPDATE、DELETE或SELECT...FOR UPDATE操作。
語法:
FOR UPDATE [OF [schema.]table.column[,[schema.]table.column]..
[nowait]?
在多表查詢中,使用 OF 子句來鎖定特定的表,如果忽略了OF 子句,那么所有表中選擇的數(shù)據(jù)行都將被鎖定。如果這些數(shù)據(jù)行已經(jīng)被其他會話鎖定,那么正常情況下ORACLE將等待,直到數(shù)據(jù)行解鎖。
在 UPDATE 和 DELETE 中使用 WHERE CURRENT OF 子串的語法如下:WHERE{CURRENT OF cursor_name|search_condition}?
例: DELCARE CURSOR c1 IS SELECT empno,salary FROM emp WHERE comm IS NULL FOR UPDATE OF comm;v_comm NUMBER(10,2);BEGINFOR r1 IN c1 LOOPIF r1.salary<500 THEN v_comm:=r1.salary*0.25; ELSEIF r1.salary<1000 THEN v_comm:=r1.salary*0.20; ELSEIF r1.salary<3000 THEN v_comm:=r1.salary*0.15; ELSE v_comm:=r1.salary*0.12; END IF;UPDATE emp; SET comm=v_comm WHERE CURRENT OF c1l;END LOOP; END
一 ) 聲明游標?
在這一步中,需要指定游標的屬性和根據(jù)要求產(chǎn)生的結(jié)果集。有兩種方法可以指定一個游標。?
形式1 (ANSI 92)?
DECLARE cursor_name [INSENSITIVE] [SCROLL] CURSOR?
FOR select_statement?
[FOR {READ ONLY | UPDATE ][OF column_list]}]?
形式2?
DECLARE cursor_name CURSOR?
[LOCAL | GLOBAL]?
[FORWARD_ONLY | SCROLL]?
[STATIC | KEYSET | DYNAMIC]?
[READ_ONLY | SCROLL_LOCKS | OPTIMISTIC]?
FOR select_statement?
[FOR {READ ONLY | UPDATE ][OF column_list]}]?
INSENSITIVE關(guān)鍵字指明要為檢索到的結(jié)果集建立一個臨時拷貝,以后的數(shù)據(jù)從這個臨時拷貝中獲取。如果在后來游標處理的過程中,原有基表中數(shù)據(jù)發(fā)生了改變,那么它們對于該游標而言是不可見的。這種不敏感的游標不允許數(shù)據(jù)更改。?
SCROLL關(guān)鍵字指明游標可以在任意方向上滾動。所有的fetch選項(first、last、next、relative、absolute)都可以在游標中使用。如果忽略該選項,則游標只能向前滾動(next)。?
Select_statement指明SQL語句建立的結(jié)果集。Transact SQL語句COMPUTE、COMPUTE BY、FOR BROWSE和INTO在游標聲明的選擇語句中不允許使用。?
READ ONLY指明在游標結(jié)果集中不允許進行數(shù)據(jù)修改。?
UPDATE關(guān)鍵字指明游標的結(jié)果集可以修改。?
OF column_list指明結(jié)果集中可以進行修改的列。缺省情況下(使用UPDATE關(guān)鍵字),所有的列都可進行修改。?
LOCAL關(guān)鍵字指明游標是局部的,它只能在它所聲明的過程中使用。?
GLOBAL關(guān)鍵字使得游標對于整個連接全局可見。全局的游標在連接激活的任何時候都是可用的。只有當(dāng)連接結(jié)束時,游標才不再可用。?
FORWARD_ONLY指明游標只能向前滾動。?
STATIC的游標與INSENSITIVE的游標是相同的。?
KEYSET指明選取的行的順序。SQL Server將從結(jié)果集中創(chuàng)建一個臨時關(guān)鍵字集。如果對數(shù)據(jù)庫的非關(guān)鍵字列進行了修改,則它們對游標是可見的。因為是固定的關(guān)鍵字集合,所以對關(guān)鍵字列進行修改或新插入列是不可見的。?
DYNAMIC指明游標將反映所有對結(jié)果集的修改。?
SCROLL_LOCK是為了保證游標操作的成功,而對修改或刪除加鎖。?
OPTIMISTIC指明哪些通過游標進行的修改或者刪除將不會成功。?
注意:?
· 如果在SELECT語句中使用了DISTINCT、UNION、GROUP BY語句,且在選擇中包含了聚合表達式,則游標自動為INSENSITIVE的游標。?
· 如果基表沒有唯一的索引,則游標創(chuàng)建成INSENSITIVE的游標。?
· 如果SELECT語句包含了ORDER BY,而被ORDER BY的列并非唯一的行標識,則DYNAMIC游標將轉(zhuǎn)換成KEYSET游標。如果KEYSET游標不能打開,則將轉(zhuǎn)換成INSENSITIVE游標。使用SQL ANSI-92語法定義的游標同樣如此,只是沒有INSENSITIVE關(guān)鍵字而已。?
二 ) 打開游標?
打開游標就是創(chuàng)建結(jié)果集。游標通過DECLARE語句定義,但其實際的執(zhí)行是通過OPEN語句。語法如下:?
OPEN { { [GLOBAL] cursor_name } | cursor_variable_name}?
GLOBAL指明一個全局游標。?
Cursor_name是被打開的游標的名稱。?
Cursor_variable_name是所引用游標的變量名。該變量應(yīng)該為游標類型。?
在游標被打開之后,系統(tǒng)變量@@cursor_rows可以用來檢測結(jié)果集的行數(shù)。@@cursor_rows為負數(shù)時,表示游標正在被異步遷移,其絕對值(如果@@cursor_rows為-5,則絕對值為5)為當(dāng)前結(jié)果集的行數(shù)。異步游標使用戶在游標被完全遷移時仍然能夠訪問游標的結(jié)果。?
三 ) 從游標中取值?
在從游標中取值的過程中,可以在結(jié)果集中的每一行上來回移動和處理。如果游標定義成了可滾動的(在聲明時使用SCROLL關(guān)鍵字),則任何時候都可取出結(jié)果集中的任意行。對于非滾動的游標,只能對當(dāng)前行的下一行實施取操作。結(jié)果集可以取到局部變量中。Fetch命令的語法如下:?
FETCH [NEXT | PRIOR| FIRST | LAST | ABSOLUTE {n | @nvar} | RELATIVE {n | @nvar}]?
FROM [GLOBAL] cursor_name} | cursor_variable_name}?
[INTO @variable_name ][,……n]]?
NEXT指明從當(dāng)前行的下一行取值。?
PRIOR指明從當(dāng)前行的前一行取值。?
FIRST是結(jié)果集的第一行。?
LAST是結(jié)果集的最后一行。?
ABSOLUTE n表示結(jié)果集中的第n行,該行數(shù)同樣可以通過一個局部變量傳播。行號從0開始,所以n為0時不能得到任何行。?
RELATIVE n表示要取出的行在當(dāng)前行的前n行或后n行的位置上。如果該值為正數(shù),則要取出的行在當(dāng)前行前n行的位置上,如果該值為負數(shù),則返回當(dāng)前行的后n行。?
INTO @cursor_variable_name表示游標列值存儲的地方的變量列表。該列表中的變量數(shù)應(yīng)該與DECLARE語句中選擇語句所使用的變量數(shù)相同。變量的數(shù)據(jù)類型也應(yīng)該與被選擇列的數(shù)據(jù)類型相同。直到下一次使用FETCH語句之前,變量中的值都會一直保持。?
每一次FETCH的執(zhí)行都存儲在系統(tǒng)變量@@fetch_status中。如果FETCH成功,則@@fetch_status被設(shè)置成0。@@fetch_status為-1表示已經(jīng)到達了結(jié)果集的一部分(例如,在游標被打開之后,基表中的行被刪除)。@@fetch_status可以用來構(gòu)造游標處理的循環(huán)。?
例如:?
DECLARE @iname char(20), @fname char(20)?
OPEN author_cur?
FETCH FIRST FROM author_cur INTO @iname, @fname?
WHILE @@fetch_status = 0?
BEGIN?
IF @fname = ‘Albert’?
PRINT “Found Albert Ringer”?
ELSE?
Print “Other Ringer”?
FETCH NEXT FROM author_cur INTO @iname, @fname?
END?
四 ) 關(guān)閉游標?
CLOSE語句用來關(guān)閉游標并釋放結(jié)果集。游標關(guān)閉之后,不能再執(zhí)行FETCH操作。如果還需要使用FETCH語句,則要重新打開游標。語法如下:?
CLOSE [GLOBAL] cursor_name | cursor_variable_name?
五 ) 釋放游標?
游標使用不再需要之后,要釋放游標。DEALLOCATE語句釋放數(shù)據(jù)結(jié)構(gòu)和游標所加的鎖。語法如下:?
DEALLOCATE [GLOBAL] cursor_name | cursor_variable_name?
下面給出游標的一個完整的例子:?
USE master?
GO?
CREATE PROCEDURE sp_BuildIndexes?
AS?
DECLARE @TableName sysname, @msg varchar(100), @cmd varchar(100)?
DECLARE table_cur CURSOR FOR?
SELECT name FROM sysobjects WHERE type=’u’?
OPEN table_cur?
FETCH NEXT FROM table_cur INTO @TableName?
?
WHILE @@fetch_status = 0?
BEGIN?
IF @@fetch_status = -2?
CONTINUE?
SELECT @msg = “Building indexes for table”+@TableName+”…”?
PRINT @msg?
SELECT @cmd = “DBCC DBREINDEX (‘”+@TableName+”')”?
EXEC (@cmd)?
PRINT “ “?
FETCH NEXT FROM table_cur INTO @TableName?
END?
DEALLOCATE table_cur?
GO?
下面的腳本將為PUBS數(shù)據(jù)庫執(zhí)行sp_BuildIndexes?
USE pubs?
GO?
EXEC ap_BuildIndexes?
注意:上面也是創(chuàng)建用戶定義的系統(tǒng)存儲過程的示例。?
使用臨時表?
臨時表是在TempDB中創(chuàng)建的表。臨時表的名稱都以“#”開頭。臨時表的范圍為創(chuàng)建臨時表的連接。因為,臨時表不能在兩個連接之間共享,一旦連接關(guān)閉,臨時表就會被丟棄。如果臨時表被創(chuàng)建于存儲過程之中,則臨時表的范圍在存儲過程之中,或者被該存儲過程調(diào)用的任何存儲過程之中。如果需要在連接之間共享臨時表,則需要使用全局的臨時表。全局的臨時表以“##”符號開頭,它將一直存在于數(shù)據(jù)庫中,直到SQL Server重新啟動。一旦這類臨時表創(chuàng)建之后,所有的用戶都可以訪問到。在臨時表上不能明確地指明權(quán)限。 臨時表提供了存儲中間結(jié)果的能力。有時候,臨時表還能通過將一個復(fù)雜的查詢分解成兩個查詢而獲得性能的改善。這可以通過首先將第一個查詢的結(jié)果存在臨時表中,然后在第二個查詢中使用臨時表來實現(xiàn)。當(dāng)一個大表中的某個子集在一個在座過程中使用多次時,建議使用臨時表。在這種情況下,在臨時表中保持數(shù)據(jù)的子集,以在隨后的連接中使用,這樣能大大改善性能。還可以在臨時表中創(chuàng)建索引。
總結(jié)
- 上一篇: 安卓逆向_18 --- APK保护策略【
- 下一篇: adb(Android debug br