SQL Server-T-SQL程式設計與遊標設計
本篇主要介紹T-SQL的語法、功能,以及用其進行復雜程式設計的思想和方法。
當需要對多行結果集進行逐行處理時,可以使用SQL Server提供的遊標實現,本篇亦提供了遊標的概念和採用T-SQL定義和使用遊標方法,以便進一步提高使用T-SQL處理資料的能力。
T-SQL程式設計基本知識
Transact-SQL是SQL Server對標準SQL語言的擴充。它引入了程式設計的思想,增加了程式的流程控制語句。Transact-SQL語言最主要的用途是設計伺服器端的能夠在後臺執行的程式塊,如儲存過程、觸發器等。
(1)變數
Transact-SQL中可以使用兩種變數:區域性變數和全域性變數。
- 區域性變數。區域性變數是使用者可自行定義的變數,它的作用範圍是在程式內部,一般用來儲存從表中查詢到的資料,或作為程式執行過程中的暫存變數。區域性變數必須以@開頭,且必須先用DECLARE命令加以說明後才可使用。
- 全域性變數。全域性變數是SQL Server 系統內容使用的變數,其作用範圍並不侷限於某一程式,而是所有程式都可隨時呼叫。全域性變數通常儲存一些SQL Server的配置設定值和效能統計資料。引用全域性變數必須以@@開頭。
(2)流程控制命令
- BEGIN… END
- IF…ELSE …
- CASE
- WHILE … CONTINUE… BREAK
- WAITFOR
- GOTO
- RETURN
(3)其他命令
- BACKUP
- CHECKPOINT
- DBCC
- DECLARE
- EXECUTE
- KILL
- RAISERROR
- READTEXT
- RESTORE
- SELECT
- SET
- SHUTDOWN
- WRITETEXT
- USE
(4)常用函式
- 統計函式
- 算術函式
- 字串函式
- 資料型別轉換函式
- 日期函式
- TEXT函式和IMAGE函式
- 使用者自定義函式
遊標簡介
關係資料庫中的操作會對整個行集起作用。由SELECT語句返回的行集包括滿足該語句的 WHERE 子句中條件的所有行。這種由語句返回的完整行集稱為結果集。應用程式,特別是互動式聯機應用程式,不總能將整個結果集作為一個單元來有效地處理。這些應用程式需要一種機制以便每次處理一行或一部分行。遊標就是提供這種機制的對結果集的一種擴充套件。
(1)遊標的作用
- 允許定位在結果集的特定行。
- 從結果集的當前位置檢索一行或一部分行。
- 支援對結果集中當前位置的行進行資料修改。
- 為由其他使用者對顯示在結果集中的資料庫資料所做的更改提供不同級別的可見性支援。
- 提供指令碼,儲存過程和觸發器中用於訪問結果集中的資料的Transact-SQL語句。
(2)請求遊標的方法
Microsoft SQL Server 2005支援兩種請求遊標的方法:
①Transact-SQL。Transact-SQL語言支援使用根據SQL-92遊標語法制定的遊標的語法。
②資料庫應用程式程式設計介面(API)遊標函式。SQL Server支援以下資料庫API的遊標功能:
- ADO(Microsoft ActiveX資料物件)
- OLEDB
- ODBC(開放式資料庫連線)
(3)遊標的型別
①客戶端遊標。
②伺服器遊標,包括以下兩種。
- Transact-SQL遊標。
- 應用程式設計介面(API)伺服器遊標。
(4)使用T-SQL遊標過程
Transact-SQL遊標主要用於儲存過程、觸發器和Transact-SQL指令碼中,它們使結果集的內容可用於其他Transact-SQL語句。
在儲存過程或觸發器中使用Transact-SQL遊標的典型過程為:
- 宣告Transact-SQL變數包含遊標返回的資料。
- 使用DECLARE CURSOR語句將Transact-SQL遊標與SELECT語句相關聯。另外,DECLARE CURSOR語句還定義遊標的特性,例如遊標名稱以及遊標是隻讀還是隻進。
- 使用OPEN語句執行SELECT語句並填充遊標。
- 使用FETCH INTO語句提取單個行,並將每列中的資料移至指定的變數中。
- 使用CLOSE語句結束遊標的使用。關閉遊標可以釋放某些資源。
(5)FETCH返回值及其狀態
SQL Server中提供了一個全域性變數@@FETCH_STATUS,用於儲存最近執行的FETCH語句返回的狀態,如下表所示。
返回值 | 說明 |
0 | FETCH語句成功 |
-1 | FETCH語句失敗或行不在結果集中 |
-2 | 提取的行不存在 |
T-SQL程式設計邏輯
(1)計算1~100之間所有能被3整除的數的個數和總和。
declare @sum smallint, @i smallint, @nums smallint set @sum = 0 set @i = 1 set @nums = 0 while (@i <= 100) begin if (@i % 3 = 0) begin set @sum = @sum + @i set @nums = @nums + 1 end set @i = @i + 1 end print '個數是:' + ltrim(str(@nums)) print '總和是:' + ltrim(str(@sum))
go
(2)計算1!+2!+...+100!的總和。
declare @factorial float, @sum float, @i smallint set @i = 1 set @factorial = 1 set @sum = 0 while(@i <=100) begin set @factorial = @factorial * @i set @sum = @sum + @factorial set @i = @i +1 end print @factorial print @sum
go
(3)從學生表S中選取SNO、SN、SEX,如果為“男”則輸出M,如果為“女”則輸出F。
select SNO 學號, SN 姓名, 性別 = case SEX when '男' then 'M' when '女' then 'F' end from S go
面向複雜T-SQL程式設計
(1)從教學資料庫jxsk中查詢所有同學選課成績情況:姓名、課程名、成績。
要求:凡成績為空者輸出“未考”;小於60分的輸出“不及格”;60~70分的輸出“及格”;70~80分的輸出“中”;80~90分的輸出“良好”;90~100分的輸出“優秀”。並且輸出記錄按下列要求排序:先按SNO升序,再按CNO升序,最後按成績降序。
select SN 姓名, CN 課程名, 成績 = case when SCORE is null then '未考' when SCORE < 60 then '不及格' when SCORE >= 60 and SCORE <70 then '及格' when SCORE >= 70 and SCORE <80 then '中' when SCORE >= 80 and SCORE <90 then '良好' when SCORE >= 90 then '優' end from SC, S, C where S.SNO = SC.SNO and C.CNO = SC.CNO order by S.SNO asc, C.CNO, SCORE desc go
(2)給教師增加工資。
要求:必須任2門以上課程且漲幅按總收入分成三個級別:4000元以上漲300;3000元以上漲200;3000元以下漲100。只任一門課程的漲50。其他情況不漲。
update T set SAL = SAL + case when T.TNO in (select TC.TNO from T, TC where T.TNO = TC.TNO and (SAL+COMM) >= 4000 group by TC.TNO having count(*) >= 2) then 300 when T.TNO in (select TC.TNO from T, TC where T.TNO = TC.TNO and (SAL+COMM) >= 3000 and (SAL+COMM) < 4000 group by TC.TNO having count(*) >= 2) then 200 when T.TNO in (select TC.TNO from T, TC where T.TNO = TC.TNO and (SAL+COMM) < 3000 group by TC.TNO having count(*) >= 2) then 100 when T.TNO in (select TC.TNO from T, TC where T.TNO = TC.TNO group by TC.TNO having count(*) = 1) then 50 else 0 end go
使用遊標
針對資料庫jiaoxuedb,進行下面的實驗:
(1)定義一個遊標Cursor_Famale。要求該遊標返回所有女同學的基本資訊,在遊標中查詢並顯示牛莉的記錄。
declare @SNO char(6), @SNAME char(8), @SEX char(2), @AGE tinyint, @DEPT char(10) declare Cursor_Female cursor for select Sno, Sname, Sex, Age, Dept from Student open Cursor_Female fetch next from Cursor_Female into @SNO, @SNAME, @SEX, @AGE, @DEPT while @@fetch_status = 0 begin if @SNAME = '牛莉' begin print '找到牛莉的資訊如下:' print @SNO+' '+@SNAME+' '+@SEX+' '+convert(char(2),@AGE)+' '+@DEPT break end fetch next from Cursor_Female into @SNO, @SNAME, @SEX, @AGE, @DEPT end if @@fetch_status != 0 print '很抱歉,沒有找到牛莉的資訊!' close Cursor_Female deallocate Cursor_Female
go
(2)建立一個儲存過程Pro_C,返回一個包含所有選修資料庫課程的學生基本資訊的遊標。利用該儲存過程,通過學生姓名查詢學生王一山是否選修了資料庫課程及其成績。
create procedure Pro_C @C_cursor cursor varying output as set @C_cursor = cursor for select Sname, Score from Student, SC, Course where Student.Sno = SC.Sno and SC.Cno = Course.Cno and Course.Cname = '資料庫' open @C_cursor go declare @MyCursor cursor declare @Name char(10) declare @Name_in char(10) declare @Score int select @Name_in = '王一山' exec Pro_C @C_cursor = @MyCursor output fetch next from @MyCursor into @Name, @Score while (@@fetch_status = 0) begin if @Name = @Name_in begin print rtrim(@Name)+'選修了資料庫課程,成績是:'+convert(char(2),@Score) break end fetch next from @MyCursor into @Name, @Score end if (@@fetch_status != 0) print rtrim(@Name_in)+'沒有選修資料庫課程。' close @MyCursor deallocate @MyCursor go