什么是Transact-SQL?
標准SQL不支持過程化控制, 不能完成復雜的功能.
T-SQL是過程化SQL語言, 是SQL的擴展
增加了過程化語句 (變量,賦值,分支,循環...)
是數據庫服務器端的編程,不同於客戶端的應用程序
不是標准語言 (ORACLE中稱為PL/SQL)
作用:
編寫批處理、存儲過程,函數,觸發器
一 SQL-SERVER批處理
下面的示例創建兩個批處理。
USE STUDB --這是使用STUDB數據庫 GO DECLARE @RS int /* 定義變量@RS */ SET @RS = (SELECT COUNT(*) /* 變量賦值 */ FROM STUDENT WHERE SDEPT='CS' ) PRINT '計科學院學生人數 ' + CAST(@RS AS char (3)) GO
用GO 命令結束一個批處理
兩種類型的注釋方式
一般保存到批處理文件中,以后打開執行,文件名后綴為.SQL
控制語句
BEGIN...END
BEGIN...END:一組要執行的 T-SQL 語句可以包含在 BEGIN...END 中。相當於C語言的復合語句{ }
語法:
BEGIN { statement | statement_block } END
其中,statement 為語句,statement_block 為語句塊。
IF...ELSE
IF...ELSE:可以根據指定的條件來執行不同的SQL 語句。
語法:
IF Boolean_expression { sql_statement|statement_block } ELSE { sql_statement|statement_block }
IF 語句示例:
IF(SELECT COUNT(*) FROM SC WHERE CNO=1 )>20--單值子查詢可以當做表達式 BEGIN …… END ELSE BEGIN …… END
WHILE 循環
WHILE:可以根據某些條件來執行一條 SQL 語句或一組語句。只要指定的條件為真,則重復執行該語句。
語法:
WHILE Boolean_expression { statement | statement_block } BREAK { statement | statement_block } CONTINUE
例題:
給1號課加分,如果平均分少於 75,每人加1分,如果最高分已經達到95,則停止加分 。
USE STUDB GO WHILE (SELECT AVG(GRADE) FROM SC WHERE CNO=1) < 75 --單值子查詢可以當做表達式 BEGIN --循環體用BEGIN END IF (SELECT MAX(grade) FROM SC WHERE CNO=1) >= 95 BREAK UPDATE SC SET GRADE=GRADE+1 WHERE CNO=1 END PRINT '調整完成!' GO
變量
兩種類型的變量:
全局變量
全局變量是SQL Server系統內部使用的變量,任何程序均可調用。
局部變量
自定義變量, 作用於局部
全局變量:
SQL Server 中的全局變量都用'@@' 標記作為前綴。
可以使用PRINT 輸出全局變量的值。
輸出SQL SERVER版本
PRINT @@VERSION
輸出上一個SQL語句影響的行數
SELECT * FROM SC WHERE CNO=1 PRINT @@ROWCOUNT
局部變量:
局部變量名必須以 '@' 為前綴, 先定義,再使用。用DECLARE定義, 類型同表的列類型。
作用域:聲明局部變量的批處理、存儲過程或語句塊。
DECLARE @姓名 CHAR(20) DECLARE @年齡 INT DECLARE @工資 FLOAT DECLARE @地址 VARCHAR(40) DECLARE @SNO NUMERIC(5), @SNAME CHAR(6), @SAGE INT
注意:變量的初值為空值null !
兩種方法為變量賦值
SET 語句或 SELECT 語句用於給局部變量賦值。
DECLARE @姓名 CHAR(10), @年齡 INT SET @姓名 ='劉晨' --SET一次只能賦值一個變量 --賦值單值子查詢結果 SET @姓名 = (SELECT SNAME FROM STUDENT WHERE SNO=95001)
或:
DECLARE @姓名 CHAR(10), @年齡 INT
SELECT @姓名 ='劉晨',@年齡=20 --SELECT可多 賦值 --賦值單值查詢結果 SELECT @姓名 = SNAME, @年齡=SAGE
FROM STUDENT
WHERE SNO=95001
注意這里給賦值單值子查詢SET SELECT的區別,SELECT已經有了一個SELECT所以可以省去不寫,但SET必須寫,否則會報錯!!
SET @CCOURSE =( SELECT COUNT(*) FROM SC WHERE SNO=@SNO )--SET必須寫SELECT SELECT @AVG =(SELECT AVG(GRADE) FROM SC WHERE SNO=@SNO) SELECT @AVG = AVG(GRADE)
FROM SC
WHERE SNO=@SNO --SELECT可以省去不寫
局部變量的使用實例:
DECLARE @姓名 CHAR(10) SET @姓名 ='劉晨' SELECT * FROM STUDENT WHERE SNAME=@姓名--變量可以用在SQL語句中 GO
變量值的輸出
格式一:單表達式輸出
功能:一次只能輸出一個局部變量值或一個表達式的值。
例句:
print @x+4 print @y
格式二: 多表達式輸出
功能:一次可輸出若干個局部變量值或表達式的值。按查詢表的格式輸出,一般不用。
例句:
select @x, @y+100
T-SQL語法總結
變量定義: DECLARE @變量名 類型 變量賦值: SET @變量=表達式 SET @變量=( 單值子查詢 ) SELECT @變量1=表達式1,@變量2=表達式2 SELECT @變量1=列1,@變量2=列2 FROM 表 WHERE 條件 輸出: PRINT ‘輸出結果是’+CAST( @變量名 AS CHAR(3)) 條件: IF 條件表達式 語句 /*多條語句要用BEGIN END*/ ELSE 語句 /*多條語句要用BEGIN END*/ 循環: WHILE 條件表達式 BEGIN 語句 END
二 SQL-SERVER 存儲過程
什么是存儲過程?
存儲過程(procedure)類似於C語言中的函數
存儲過程可以帶參數。
void sum(int a,int b) { int s; s =a+b; printf("sum=%d\n", s) ; }
存儲過程的分類
系統存儲過程
由系統定義,存放在master數據庫中,類似C語言中的系統函數,系統存儲過程的名稱都以“sp_”開頭
用戶自定義存儲過程
由用戶在自己的數據庫中創建的存儲過程,經編譯和優化后存儲在數據庫服務器中,使用時只要調用即可。類似C語言中的用戶自定義函數。
常用的系統存儲過程:
如何創建存儲過程?
創建存儲過程的語法
CREATE PROCEDURE 存儲過程名 [參數] AS SQL語句 GO
執行存儲過程的語法
EXEC 存儲過程名 [參數]
建立存儲過程, 顯示計科學院的學生人數:
CREATE PROCEDURE CSCOUNT AS DECLARE @RS int /* 定義變量@RS */ SET @RS = (SELECT COUNT(*) /* 變量賦值 */ FROM STUDENT WHERE SDEPT='CS' ) PRINT '計科學院學生人數 ' + CAST(@RS AS char (3)) GO
執行存儲過程語句:
EXEC CSCOUNT
創建不帶參數的存儲過程
問題:計算機等級考試分為筆試成績和機試成績,
請創建存儲過程,查看本次考試平均分以及未通過考試的學員名單
需要定義什么變量?
CREATE PROCEDURE proc_stu --proc_stu為自定義的存儲過程名 AS DECLARE @writtenAvg float,@labAvg float SELECT @writtenAvg=AVG(writtenExam), @labAvg=AVG(labExam) FROM Marks print '筆試平均分:'+CAST(@writtenAvg AS CHAR(3)) print '機試平均分:'+CAST(@labAvg AS CHAR(3)) IF (@writtenAvg>70 AND @labAvg>70) --顯示考試成績的等級 print '本班考試成績:優秀' ELSE print '本班考試成績:較差' print '--------------------------------------------------' print ' 參加本次考試沒有通過的學員:' SELECT stuName,stu.stuNo,writtenExam,labExam--顯示未通過的學員 FROM stu, Marks WHERE stu.stuNo=Marks.stuNo AND (writtenExam<60 OR labExam<60) GO
調用存儲過程
EXEC PROC_STU
帶輸入參數的存儲過程
CREATE PROCEDURE DEPTCOUNT @DEPT CHAR(2) /* 參數變量 注意位置 無關鍵字DECLARE*/ AS DECLARE @RS int /* 定義變量@RS */ SET @RS = (SELECT COUNT(*) /* 變量賦值 */ FROM STUDENT WHERE SDEPT=@DEPT ) PRINT @DEPT+'學院學生人數 ' + CAST(@RS AS char (3)) GO
執行存儲過程語句:
EXEC DEPTCOUNT 'CS'
練習: 建立過程 PSTU, 輸出某學生的姓名和總學分.
過程參數? 變量? 代碼?
DROP PROCEDURE PSTU GO CREATE PROCEDURE PSTU @SNO INT AS DECLARE @NAME CHAR(20) DECLARE @TOTAL INT SELECT @NAME=SNAME FROM STUDENT WHERE SNO=@SNO SELECT @TOTAL=SUM(CCREDIT) FROM COURSE,SC WHERE SC.SNO=@SNO AND SC.CNO=COURSE.CNO PRINT'Name='+@NAME+'Total credit='+CAST(@TOTAL AS CHAR(3)) GO
如何執行?
EXEC PSTU 95001
練習:
建立過程 PSTU2, 輸出某學生的姓名, 選課門數, 平均分, 最高分,最低分, 總學分, 如果總學分<9,則顯示“此學生學分不足!”,否則顯示“此學生學分已足!”,
過程參數? 變量? 代碼?
DROP PROCEDURE PSTU2 GO CREATE PROCEDURE PSTU2 @SNO INT AS DECLARE @NAME CHAR(20),@CCOURSE INT,@TOTAL INT,@AVG INT SELECT @NAME=SNAME FROM STUDENT WHERE SNO=@SNO SELECT @CCOURSE =COUNT(*) FROM SC WHERE SNO=@SNO SELECT @TOTAL= SUM(CCREDIT) FROM COURSE,SC WHERE SC.SNO=@SNO AND SC.CNO=COURSE.CNO SELECT @AVG = AVG(GRADE) FROM SC WHERE SNO=@SNO PRINT'Name='+@NAME+ '選課門數'+CAST(@CCOURSE AS CHAR(5))+'平均分' +CAST(@AVG AS CHAR(5))+'Total credit='+CAST(@TOTAL AS CHAR(3)) IF(@TOTAL<9) PRINT '此學生學分不足!' ELSE PRINT '此學生學分已足!' GO
如何執行?
EXEC PSTU2 95001
總結
存儲過程是一組預編譯的SQL語句,它可以包含數據操縱語句、變量、邏輯控制語句等
定義存儲過程的語法:
CREATE PROCEDURE 存儲過程名 @參數1 數據類型, …… , @參數n 數據類型 AS SQL語句 GO
EXECUTE(執行,調用)存儲過程的語法:
EXEC 過程名 [參數]
存儲過程的優點
經編譯和優化后存儲在數據庫服務器中,運行效率高
客戶端應用程序可以調用
降低客戶機和服務器之間的通信量
有利於集中控制,方便維護
三 SQL-SERVER 游標
什么是游標?
先看一個例子:
設計一個存儲過程,參數是課號,顯示某門課的優、良、中、及格、差的人數。
輸出某門課優良差人數 實現方法1
CREATE PROCEDURE P1 @CNO INT --參數為課號 AS DECLARE @優 int,@良 int,@差 int SET @優=( SELECT COUNT(*) FROM SC WHERE CNO=@CNO AND GRADE>=90 ) SET @良=( SELECT COUNT(*) FROM SC WHERE CNO=@CNO AND GRADE>=80 AND GRADE<90 ) SET @差=( SELECT COUNT(*) FROM SC WHERE CNO=@CNO AND GRADE<80 ) PRINT '優人數'+CAST(@優 AS CHAR(2))+' 良人數'+CAST(@良 AS CHAR(2)) +' 差人數'+CAST(@差 AS CHAR(2)) GO
多遍掃描SC, 效率低
游標(cursor)
SQL查詢結果是記錄的集合
可將SQL語句的查詢結果定義為游標,游標代表一個查詢結果集合
可以逐一從游標中獲取記錄,進行處理。
游標操作分為四步:
1、聲明游標:
DECLARE 游標名 CURSOR FOR
SELECT查詢
2、打開游標
3、循環從游標中提取數據
4、關閉游標
輸出某門課優良差人數 實現方法2,使用游標:
將選某門課的成績(這是一個查詢)定義為一個游標,對游標中的成績循環判斷,統計
DROP PROCEDURE P1 GO CREATE PROCEDURE P1 @CNO INT AS DECLARE @成績 int,@優 int,@良 int,@差 int DECLARE C1 CURSOR FOR /*定義游標*/ SELECT GRADE FROM SC WHERE CNO=@CNO SELECT @優=0,@良=0,@差=0 /*計數變量清零(原初值為null) */ OPEN C1 /*打開游標—執行游標中的SELECT*/ WHILE 1=1 --建立循環 BEGIN --循環體開始 FETCH NEXT FROM C1 INTO @成績 /*讀游標*/ IF @@fetch_status<>0 /*判斷提取結束*/ BREAK IF @成績 IS NOT NULL IF @成績 >=90 SET @優=@優+1 ELSE IF @成績 >=80 SET @良=@良+1 ELSE SET @差=@差+1 END --循環體結束 CLOSE C1 /*關閉游標*/ DEALLOCATE C1 /*釋放游標*/ PRINT '優人數'+CAST(@優 AS CHAR(2))+' 良人數'+CAST(@良 AS CHAR(2)) +' 差人數'+CAST(@差 AS CHAR(2)) GO
游標語法框架:
/*定義有關變量*/ DECLARE 游標名 CURSOR FOR /*(1)定義游標*/ SELECT子查詢 OPEN 游標名 /*(2)打開游標—執行游標中的SELECT*/ WHILE 1=1 --建立循環 BEGIN --循環體開始 /*(3)讀游標的一行到變量, 變量的個數類型要與游標中子查詢一致 */ FETCH NEXT FROM 游標名 INTO @變量1, @變量2 IF @@fetch_status<>0 /*判斷提取結束*/ BREAK 處理 END --循環體結束 CLOSE 游標名 /*(4)關閉游標*/ DEALLOCATE 游標名 /*釋放游標*/
練習:
使用游標, 統計輸出男生人數,女生人數,年齡>20的人數,年齡<=20的人數。
CREATE PROCEDURE P2
AS
--定義變量存放男生人數,女生人數,年齡>20的人數,年齡<=20的人數
--定義變量存放性別,年齡
DECLARE C1 CURSOR FOR /*定義游標*/
SELECT SSEX,SAGE FROM STUDENT
......
DROP PROCEDURE P2 GO CREATE PROCEDURE P2 AS DECLARE @MC INT,@WC INT,@UP INT,@DOWN INT DECLARE @SSEX CHAR(2),@AGE INT SELECT @MC=0,@WC=0,@UP=0,@DOWN=0 DECLARE C1 CURSOR FOR SELECT SSEX,SAGE FROM STUDENT OPEN C1 WHILE 1=1 BEGIN FETCH NEXT FROM C1 INTO @SSEX,@AGE IF @@fetch_status<>0 BREAK IF @SSEX IS NOT NULL IF @SSEX ='男' SET @MC=@MC+1 ELSE IF @SSEX='女' SET @WC=@WC+1 IF @AGE IS NOT NULL IF @AGE >20 SET @UP=@UP+1 ELSE IF @AGE<=20 SET @DOWN=@DOWN+1 END CLOSE C1 /*關閉游標*/ DEALLOCATE C1 /*釋放游標*/ PRINT '男生人數'+CAST(@MC AS CHAR(2))+'女生人數'+CAST(@WC AS CHAR(2)) + '年齡>20的人數'+ CAST(@UP AS CHAR(2))+ '年齡<=20的人數'+CAST(@DOWN AS CHAR(2)) GO
思考題:
使用游標, 統計每門課的課名,成績前三名的學生名。
定義幾個游標?什么游標?
定義什么變量?
如何處理?
DROP PROCEDURE P3 GO CREATE PROCEDURE P3 @CNO INT AS DECLARE @SNO INT,@SNAME CHAR(20),@SDEPT CHAR(5),@GRADE INT DECLARE @COUNT INT DECLARE C1 CURSOR FOR SELECT STUDENT.SNO,SNAME,SDEPT,GRADE FROM STUDENT,SC WHERE STUDENT.SNO=SC.SNO AND @CNO=SC.CNO ORDER BY GRADE DESC SET @COUNT=0 OPEN C1 WHILE 1=1 BEGIN FETCH NEXT FROM C1 INTO @SNO,@SNAME,@SDEPT,@GRADE IF @@fetch_status<>0 BREAK IF @COUNT=3 BREAK SET @COUNT=@COUNT+1 PRINT '學號'+CAST(@SNO AS CHAR(10))+'姓名'+@SNAME+ '系名' + @SDEPT+ '成績'+CAST(@GRADE AS CHAR(5)) END CLOSE C1 DEALLOCATE C1 GO
四 SQL-SERVER 觸發器
WHAT IS TRIGGER?
觸發器是一種特殊的存儲過程,它不能被顯式地調用,
而是在往表中插入記錄、更改記錄或者刪除記錄時,才被自動地調用。
SQL-SERVER觸發器的類型
對每個表, 可以建立
DELETE 觸發器
INSERT 觸發器
UPDATE 觸發器
創建觸發器的語法:
CREATE TRIGGER 觸發器名 ON 表名 FOR [DELETE, INSERT, UPDATE] AS T-SQL語句 GO
DELETE, INSERT, UPDATE指定觸發器的類型
兩個臨時表: INSERTED 和DELETED
觸發器觸發時,系統自動在內存中創建兩個臨時表: INSERTED和DELETED
INSERTED 表臨時保存了新記錄行(插入或更新后的記錄行)
DELETED 表臨時保存了舊記錄行(刪除或更新前的記錄行)
結構與觸發器對應表相同
觸發器執行完成后,自動刪除這兩個表
只讀,用於檢查操作是否滿足業務需求
查看DELETED, INSERTED內容
編寫觸發器, 當選課表修改數據時, 顯示修改前后的數據
編寫觸發器:
哪個表?
哪種觸發語句?
修改前的行,修改后的行存放在哪里?
CREATE TRIGGER trig_SC ON SC FOR UPDATE AS --SC表的UPDATE觸發器 SELECT * FROM deleted --deleted存舊數據 SELECT * FROM inserted -- inserted存新數據 GO
UPDATE觸發器
編寫觸發器, 使學生的年齡修改只能增1
編寫觸發器:
哪個表?
哪種觸發語句?
修改前的行,修改后的行存放在哪里?
修改前的年齡? 修改后的年齡?
-----使學生的年齡修改只能增1---- DROP TRIGGER trig_update_sage --為防止重建時報錯, 先刪一下 GO CREATE TRIGGER trig_update_sage ON STUDENT FOR UPDATE AS --STUDENT表的UPDATE觸發器 DECLARE @beforeSage int,@afterSage int --定義變量 SET @beforeSage=(SELECT TOP 1 SAGE FROM deleted) --舊數據 SET @afterSage=(SELECT TOP 1 SAGE FROM inserted) -- 新數據 IF @afterSage-@beforeSage<>1 AND @afterSage-@beforeSage<>0 BEGIN PRINT ‘年齡修改只能增1’ --報錯 ROLLBACK TRANSACTION --回滾,撤銷引起觸發器的UPDATE語句 END GO
DELETE觸發器
編寫觸發器, 在deletedsc中記錄SC表刪除的行 (deletedsc表已建立, 結構同SC)
編寫觸發器:
哪個表?
哪種觸發語句?
刪除的行存放在哪里?
-------關鍵代碼------ CREATE TRIGGER trig_delete_SC ON SC FOR DELETE AS INSERT INTO DELETEDSC SELECT * FROM deleted GO
INSERT觸發器---實時統計
實時統計:在學生表中增加選課門數列CCOUNT和總成績列GTOTAL
CREATE TABLE STUDENT
(SNO NUMERIC(5) CONSTRAINT P_STUDENT PRIMARY KEY,
。。。。。。
CCOUNT NUMERIC(3), --選課門數
GTOTAL NUMERIC(5) --總成績
);
編寫SC的插入、刪除和修改觸發器, 自動更新學生表中選課門數和總成績.
-------關鍵代碼------ --當插入SC數據時,自動更新學生表中選課門數列和總成績列. CREATE TRIGGER trig_insert_sc ON SC FOR insert AS DECLARE @GRADE INT, @SNO INT /* 從INSERTED中取插入的學號和成績 */ SELECT @SNO=SNO,@GRADE=GRADE FROM INSERTED IF @GRADE IS NOT NULL UPDATE STUDENT SET CCOUNT=CCOUNT+1, GTOTAL=GTOTAL+@GRADE WHERE SNO=@SNO GO
DELETE觸發器 ---實時統計
DELETE可能刪除多行
要對刪除的多行逐行進行處理
如何進行?
CREATE TRIGGER trig_delete_sc ON SC FOR DELETE AS --SC表DELETE觸發器 DECLARE @sno int ,@grade int --定義變量 DECLARE C1 CURSOR FOR --定義游標,存放被刪的數據 SELECT SNO,GRADE FROM DELETED OPEN C1 --打開游標 WHILE 1=1 --循環 BEGIN FETCH NEXT FROM C1 INTO @SNO, @GRADE --讀游標 IF @@fetch_status<>0 --如果讀完了 BREAK IF @GRADE IS NOT NULL UPDATE STUDENT
SET CCOUNT=CCOUNT-1,GTOTAL=GTOTAL-@GRADE
WHERE SNO=@sno END CLOSE C1 DEALLOCATE C1 GO
當對SC表UPDATE時,如何自動更新學生表中選課門數列和總成績列?
對SC表建立UPDATE觸發器
UPDATE可能修改多行
修改前的數據,修改后的數據?
要對修改的多行進行處理,如何進行?
/*建立兩個游標C1,C2,分別對應 deleted inserted*/ /*循環處理游標,發現修改,同步更新STUDENT*/ DROP TRIGGER trig_update_sc GO CREATE TRIGGER trig_update_sc ON SC FOR UPDATE AS DECLARE @sno1 int,@grade1 int DECLARE @sno2 int,@grade2 int DECLARE C1 CURSOR FOR SELECT SNO,GRADE FROM deleted DECLARE C2 CURSOR FOR SELECT SNO,GRADE FROM inserted OPEN C1 OPEN C2 WHILE 1=1 BEGIN FETCH NEXT FROM C1 INTO @SNO1,@GRADE1 FETCH NEXT FROM C2 INTO @SNO2,@GRADE2 IF @@fetch_status<>0 BREAK IF @SNO1=@SNO2/*學號未變*/ BEGIN IF @GRADE1 IS NOT NULL AND @GRADE2 IS NOT NULL AND @GRADE1<>@GRADE2/*更新前后均不為空*/ UPDATE STUDENT SET GTOTAL=GTOTAL-@GRADE1+@GRADE2 WHERE SNO=@sno1 ELSE IF @GRADE1 IS NULL AND @GRADE2 IS NOT NULL/*更新前為空,更新后不為空*/ UPDATE STUDENT SET CCOUNT=CCOUNT+1, GTOTAL=GTOTAL+@GRADE2 WHERE SNO=@sno1 ELSE IF @GRADE1 IS NOT NULL AND @GRADE2 IS NULL/*更新前不為空,更新后為空*/ UPDATE STUDENT SET CCOUNT=CCOUNT-1, GTOTAL=GTOTAL-@GRADE1 WHERE SNO=@sno1 END ELSE/*學號變了*/ BEGIN IF @GRADE1 IS NOT NULL/*對舊學號相當於刪選課*/ UPDATE STUDENT SET CCOUNT=CCOUNT-1,GTOTAL=GTOTAL-@GRADE1 WHERE SNO=@sno1 IF @GRADE2 IS NOT NULL/*對新學號相當於插入選課*/ UPDATE STUDENT SET CCOUNT=CCOUNT+1,GTOTAL=GTOTAL+@GRADE2 WHERE SNO=@sno2 END END/*where*/ CLOSE C1 CLOSE C2 DEALLOCATE C1 DEALLOCATE C2 GO
查看觸發器信息
觸發器作為一種特殊的存儲過程,在用戶創建以后,其名字存放在系統表sysobjects中,其創建源代碼存放在syscomments系統表中。
查看指定表中所定義的觸發器及它們的類型:
EXEC sp_helptrigger 表名
查看指定觸發器的定義文本:
EXEC sp_helptext 觸發器名
刪除觸發器:
DROP TRIGGER 觸發器名
總結
觸發器是在對表進行插入、更新或刪除操作時自動執行的存儲過程
觸發器作用: 增強數據完整性和安全性,實時統計
觸發器一般都需要使用臨時表:deleted表和inserted表,它們存放了被刪除或插入的記錄行副本
創建觸發器的語法:
CREATE TRIGGER 觸發器名 ON 表名 FOR [DELETE, INSERT, UPDATE] AS T-SQL語句 GO