一、游標
*什么是游標
游標實際上是一種能從包括多條數據記錄的結果集(結果集是select查詢之后返回的所有行數據的集合)中每次提取一條記錄的機制充當指針的作用,遍歷結果中的所有行,但他一次只指向一行。游標的結果集是由SELECT語句產生,如果處理過程需要重復使用一個記錄集,那么創建一次游標而重復使用若干次,比重復查詢數據庫要快的多。
也可以說,SQL的游標是一種臨時的數據庫對象,可以用來存放在數據庫表中的數據行副本,也可以指向存儲在數據庫中的數據行的指針。游標提供了在逐行的基礎上操作表中數據的方法。
一般復雜的存儲過程,都會有游標的出現,他的用處主要有:
1.定位到結果集中的某一行。
2.對當前位置的數據進行讀寫。
3.可以對結果集中的數據單獨操作,而不是整行執行相同的操作。
4.是面向集合的數據庫管理系統和面向行的程序設計之間的橋梁。
*不足:
數據量小時才使用游標,因為:
1.游標使用時會對行加鎖,系統上跑的不只我們一個業務,這就會影響其他業務的正常進行;
2.數據量大時其效率也較低效;
3.游標其實是相當於把磁盤數據整體放入了內存中,如果游標數據量大則會造成內存不足,
書寫格式:
DECLARE mycursor Cursor --定義游標
FOR SELECT EmployeeID FROM ... --查詢語句
OPEN mycursor --打開游標
DECLARE @id int --根據查詢語句相應地定義變量
FETCH NEXT FROM mycursor INTO @id --逐行提取游標集中的行
WHILE @@FETCH_STATUS=0 --通過檢查全局變量@@FETCH_STATUS來判斷是否已讀完游標集中所有行
BEGIN
*此處書寫要執行的Sql語句*
FETCH NEXT FROM mycursor INTO @id --移動游標
END
CLOSE mycursor --關閉游標
DEALLOCATE mycursor --釋放游標
實例:根據產品名稱(名稱一樣視為同一產品)統計該產品的銷售數量,如果在統計表(ProductStatistics)中能找到這個產品名稱的數據,則插入這個產品的統計結果,如果不能找到這個產品名稱的數據,則修改統計結果。
注意:
@@FETCH_STATUS的值有:
0:FETCH 語句成功,表示已經從游標集中獲取了元組值
1:FETCH 語句失敗或此行不在結果集中
2:被提取的行不存在
游標不使用了,必須關閉,關閉后再使用要重新打開
關閉游標,並沒有釋放游標所占用的內存和外存空間,必須釋放游標
游標可以放在觸發器和存儲過程中
擴充(以上是標准游標):
語法:
參數說明:
(1)Local與Global:
1.Local表示游標的作用於僅僅限於其所在的存儲過程、觸發器以及批處理中、執行完畢以后游標自動釋放。
2.Global表示的是該游標作用域是整個會話層。由連接執行的任何存儲過程、批處理等都可以引用該游標名稱,僅在斷開連接時隱性釋放。
(2)Forward_only與Scroll:前者表示為只進游標,后者表示為可以隨意定位。默認為前者。
(3)Static、Keyset與Dynamic、 FAST_FORWARD: 這四個關鍵字是游標所在數據集所反應的表內數據和游標讀取出的數據的關系
1. STATIC意味着,當游標被建立時,將會創建FOR后面的SELECT語句所包含數據集的副本存入tempdb數據庫中,任何對於底層表內數據的更改不會影響到游標的內容.
2. DYNAMIC是和STATIC完全相反的選項,當底層數據庫更改時,游標的內容也隨之得到反映,在下一次fetch中,數據內容會隨之改變
3. KEYSET可以理解為介於STATIC和DYNAMIC的折中方案。將游標所在結果集的唯一能確定每一行的主鍵存入tempdb,當結果集中任何行改變或者刪除時,@@FETCH_STATUS會為-2,KEYSET無法探測新加入的數據
4. FAST_FORWARD可以理解成FORWARD_ONLY的優化版本.FORWARD_ONLY執行的是靜態計划,而FAST_FORWARD是根據情況進行選擇采用動態計划還是靜態計划,大多數情況下FAST_FORWARD要比FORWARD_ONLY性能略好.
(4) READ_ONLY SCROLL_LOCKS OPTIMISTIC 三選一 :
1. READ_ONLY意味着聲明的游標只能讀取數據,游標不能做任何更新操作
2. SCROLL_LOCKS是另一種極端,將讀入游標的所有數據進行鎖定,防止其他程序進行更改,以確保更新的絕對成功
3. OPTIMISTIC是相對比較好的一個選擇,OPTIMISTIC不鎖定任何數據,當需要在游標中更新數據時,如果底層表數據更新,則游標內數據更新不成功,如果,底層表數據未更新,則游標內表數據可以更新
二、存儲過程
什么是存儲過程
存儲過程是一組命名了的SQL語句集合,是為了完成特定功能匯集而成的。該集合編譯后存放在數據庫中,可根據實際情況重新編譯,可直接運行,也可遠程運行且存儲過程直接在服務器端運行。
優點:
1. 將業務操作封裝
(a) 可為復雜的業務操作編寫存儲過程,放在數據庫中;
(b)用戶可調用存儲過程執行,而業務操作對用戶是不可見的;
(c)若存儲過程僅修改了執行體,沒有修改接口(即調用參數),則用戶程序不需要修改,達到業務封裝的效果。
2. 便於事務管理
(a)事務控制可以用在存儲過程中;
(b)用戶可依據業務的性質定義事務,並對事務進行相應級別的操作
3. 實現一定程度的安全性保護
(a)存儲過程存放在數據庫中,且在服務器端運行;
(b)對於不允許用戶直接操作的表或視圖,可通過調用存儲過程來間接地訪問這些表或視圖,達到一定程度的安全性;
(c)這種安全性緣於用戶對存儲過程只有執行權限,沒有查看權限;
(d)擁有存儲過程的執行權限,自動獲取了存儲過程中對相應表或視圖的操作權限;
(e)這些操作權限僅能通過執行存儲過程來實現,一旦脫離存儲過程,也就失去了相應操作權限。
4. 特別適合統計和查詢操作
(a)一般統計和查詢,尤其是期末統計,往往涉及數據量大、表多,若在客戶端實現,數據流量和網絡通信量較大;
(b)很多情況下,管理信息系統的設計者,將復雜的查詢和統計用存儲過程來實現,免去客戶端的大量編程
5. 減少網絡通信量
(a)存儲過程僅在服務器端執行,客戶端只接收結果;
(b)由於存儲過程與數據一般在一個服務器中,可減少大量的網絡通信量
缺點:
1.如果更改范圍大到需要對輸入存儲過程的參數進行更改,或者要更改由其返回的數據,則仍需要更新程序集中的代碼以添加參數、更新 GetValue() 調用,等等,這時候估計比較繁瑣了。
2.可移植性差,由於存儲過程將應用程序綁定到 SQL Server,因此使用存儲過程封裝業務邏輯將限制應用程序的可移植性。如果應用程序的可移植性在您的環境中非常重要,則將業務邏輯封裝在不特定於 RDBMS 的中間層中可能是一個更佳的選擇。
書寫格式:
實例:1. 創建一個存儲過程, 實現判斷某員工(輸入姓名)的總訂單數,超過100輸出優,在[35,100]中為良,[25,34]為及格,其他為差。
–輸出員工姓名及結果
實例2:(和游標一起使用)
輸入某學院名稱,統計該學院每個班級同學的選課信息,返回班級編號、班級名稱、課程名稱、課程選課人數、課程平均分。
CREATE PROCEDURE proInstitute( @institute varchar(30) )
AS
BEGIN
DECLARE @className varchar(30), @courseName varchar(30)
DECLARE @classNo char(6), @count tinyint, @avg numeric(5, 1)
/*定義一個臨時表,存放每個班級的班級編號、班級名稱、課程
名稱、課程選課人數、課程平均分*/
CREATE TABLE #myTemp (
classNo char(6),
className varchar(30),
courseName varchar(30),
classCount tinyint,
classAvg numeric(5, 1)
)
--定義游標curClass,依據輸入參數@institute,查找班級編號和班級名稱
DECLARE curClass CURSOR FOR
SELECT classNo, className
FROM Class
WHERE institute=@institute
OPEN curClass
FETCH curClass INTO @classNo, @className
WHILE (@@FETCH_STATUS=0)
BEGIN
--定義游標curCourse,查找@classNo班選課的課程名稱、選課人數、平均分
DECLARE curCourse CURSOR FOR
SELECT courseName, count(*), avg(score)
FROM Student a, Score b, Course c
WHERE a.studentNo=b.studentNo AND b.courseNo=c.courseNo
AND classNo=@classNo
GROUP BY courseName
OPEN curCourse
FETCH curCourse INTO @courseName, @count, @avg
WHILE (@@FETCH_STATUS=0)
BEGIN
/* 將班級編號、班級名稱、課程名稱、課程選課人數、課程平均分
插入到臨時表#myTemp中 */
INSERT INTO #myTemp VALUES( @classNo, @className, @courseName,
@count, @avg )
-- 獲取下一游標值,取該班下一門課程的課程名、選課人數和平均分
FETCH curCourse INTO @courseName, @count, @avg
END
CLOSE curCourse
DEALLOCATE curCourse
--獲取游標curClass的下一個值,即取下一個班級
FETCH curClass INTO @classNo, @className
END
CLOSE curClass
DEALLOCATE curClass
--顯示臨時表的內容,同時將臨時表的內容返回給調用者
SELECT * FROM #myTemp
END
注意:
1.OUTPUT:說明該參數是輸出參數,被調用者獲取使用。缺省時表示是輸入參數。
2.使用存儲過程時,必須執行命令EXECUTE/EXEC,且EXECUTE參數必須與對應的PROCEDURE的參數相匹配。
3.刪除存儲過程
語法:
DROP PROCEDURE <procedureName>
4.如果存儲過程的輸出參數取集合值,則該輸出參數不在存儲過程的參數中定義,而是在存儲過程中定義一個臨時表來存儲該集合值。
(a)臨時表的表名前加一個#符號,如#myTemp
(b)在存儲過程尾部,使用語句:
SELECT * FROM #myTemp
1
(c) 將結果集合返回給調用者。
(d)存儲過程結束后,臨時表自動被刪除。
5.用戶定義的存儲過程只能在當前數據庫中創建
6.修改存儲過程只需將CREATE改ALTER
三、觸發器
什么是觸發器
1.觸發器是一種特殊類型的存儲過程,它不同於之前的我們介紹的存儲過程。觸發器主要是通過事件進行觸發被自動調用執行的。而存儲過程可以通過存儲過程的名稱被調用。
2.觸發器對表進行插入、更新、刪除的時候會自動執行的特殊存儲過程,對於每條SQL語句,觸發器僅執行一次。觸發器一般用在check約束更加復雜的約束上面。觸發器和普通的存儲過程的區別是:觸發器是當對某一個表進行操作。諸如:update、insert、delete這些操作的時候,系統會自動調用執行該表上對應的觸發器。
觸發器的分類
1、 after觸發器(之后觸發)
after觸發器要求只有執行某一操作insert、update、delete之后觸發器才被觸發,且只能定義在表上。
(a)創建觸發器的語法:
CREATE TRIGGER <triggerName>
ON <tableName>
FOR { INSERT | UPDATE | DELETE }
AS <SQL-Statement>
(b)實例: 創建一個Order Details表的修改觸發器,當字段Quantity修改時重新修改統計表(ProductStatistics)中的銷售數量
CREATE TRIGGER update_mount
ON [Order Details]
FOR UPDATE
AS
BEGIN
IF UPDATE(Quantity)
BEGIN
DECLARE @Oldquantity int
DECLARE @Newquantity int
DECLARE @ProductID int
SELECT @Newquantity=Quantity FROM inserted
SELECT @Oldquantity=Quantity FROM deleted
SELECT @ProductID=ProductID FROM deleted
UPDATE ProductStatistics
SET Salesvolumes=Salesvolumes-@Oldquantity+@Newquantity
WHERE ProductName IN (Select ProductName from Products WHERE ProductID=@ProductID)
END
END
--******************觸發**********************
UPDATE [Order Details]
SET Quantity=10
WHERE OrderID=10248 AND ProductID=11
2、 instead of 觸發器 (之前觸發)
instead of觸發器表示並不執行其定義的操作(insert、update、delete)而僅是執行觸發器本身。既可以在表上定義instead of觸發器,也可以在視圖上定義。
(a)創建觸發器的語法:
CREATE TRIGGER <triggerName>
ON <tableName>
instead of { INSERT | UPDATE | DELETE }
AS <SQL-Statement>
(b)實例: 創建一個員工表(Employees)的刪除觸發器,判斷是否存在有當前這條員工數據的主碼為外碼的信息,存在則提示錯誤不能刪除(加rollback回滾),不存在正常刪除
CREATE TRIGGER employee_delete
ON Employees
INSTEAD OF DELETE
AS
BEGIN
DECLARE @EmployeeID int
SELECT @EmployeeID=EmployeeID FROM deleted
IF EXISTS(SELECT * FROM Orders WHERE EmployeeID=@EmployeeID)
BEGIN
PRINT('刪除失敗')
ROLLBACK
END
ELSE
BEGIN
DELETE FROM Employees
WHERE EmployeeID=@EmployeeID
PRINT('刪除成功')
END
END
GO
--******************觸發**********************
DELETE FROM Employees
WHERE EmployeeID=4
注意:
1.觸發器本身就是一個事務,所以在觸發器里面可以對修改數據進行一些特殊的檢查。如果不滿足可以利用事務回滾,撤銷操作。
2.表后面加with encrypion表示加密觸發器
3.觸發器有兩個特殊的表:插入表(instered表)和刪除表(deleted表)。這兩張是邏輯表也是虛表。有系統在內存中創建者兩張表,不會存儲在數據庫中。而且兩張表的都是只讀的,只能讀取數據而不能修改數據。這兩張表的結果總是與被改觸發器應用的表的結構相同。當觸發器完成工作后,這兩張表就會被刪除。Inserted表的數據是插入或是修改后的數據,而deleted表的數據是更新前的或是刪除的數據。
4.觸發器僅在當前數據庫中生成
(a)觸發器有三種類型,即插入、刪除和更新;
(b)插入、刪除和更新可作為一種類型的觸發器;
(c)查詢操作不會產生觸發動作,沒有查詢觸發器類型。
5.規定一次僅能修改一個記錄,即如果插入的記錄數大於1條, 則回滾
IF ( SELECT count(*) FROM inserted )>1
ROLLBACK
6.觸發器不需要時可以刪除,刪除語法:
DROP TRIGGER <triggerName>
7.原則上並不限制一張表上定義的觸發器的數量,一張表可以有多個觸發器,且同一類型觸發器也可以有多個,但是,由於觸發器是自動執行的,為一張表建立了多個觸發器,必然加大系統的開銷。且如果觸發器設計得不好,會帶來不可預知的后果。
8.觸發器常常用於維護復雜的完整性約束,不用於業務處理。凡是可以用一般約束限制的,就不要使用觸發器,如限制性別僅取男和女,可以使用檢查約束CHECK實現,用戶的業務處理常常使用存儲過程實現。
---------------------
作者:xueym
原文:https://blog.csdn.net/qq_41571267/article/details/79776043