1. 程式人生 > >SQL Server 2005新功能詳解

SQL Server 2005新功能詳解

今天整理了一下SQL Server 2005的新增功能,自己學習了以後進行了總結,一共有四個新的排序函式(ROW_NUMBER、RANK、DENSE_RANK 和 NTILE)和三個其他新增功能(TOP表示式,Try...Catch,通用表示式CTE)

為了詳細進行講解,這兒建立一張表StuTest:

create table stutest(pid int primary key,name varchar(50) not null,age int not null,sex varchar(10) not null,score float not null)

然後插入一些測試資料:

insert into stutest values('yingzi',23,'female',89.5)
insert into stutest values('zhudan',22,'female',78.5)
insert into stutest values('ludi',24,'male',90.3)
insert into stutest values('yujue',21,'female',84.5)
insert into stutest values('shiche',20,'male',81.5)
insert into stutest values('adfasd',23,'female',45.5)
insert into stutest values('dfad',23,'male',98.5)
insert into stutest values('ertd',23,'female',80.5)
insert into stutest values('fgfg',22,'female',73.5)
insert into stutest values('jkghj',24,'male',94.3)
insert into stutest values('trts',21,'female',85.5)
insert into stutest values('bvbx',20,'male',87.5)
insert into stutest values('adf',23,'female',49.5)
insert into stutest values('ughdgh',23,'male',91.5)
insert into stutest values('lkgj',23,'female',70.5)
insert into stutest values('grt',21,'female',85.5)
insert into stutest values('loiut',20,'male',87.5)
insert into stutest values('erer',23,'female',49.5)
insert into stutest values('rqeq',23,'male',91.5)
insert into stutest values('trwer',23,'female',70.5)

執行SQL語句:select * from stutest

結果如下圖:

(1)ROW_NUMBER()函式。ROW_NUMBER 函式使您可以向查詢的結果行提供連續的整數值。

例子:假設您要返回所有學生的 age、name、sex和 score,同時按照 score 降序向結果行分配從 1 開始的連續值。SQL語句如下:

select row_number() over(order by score desc) as scorerank,age,name,sex,score from stutest                                                    

order by score desc

結果如下:

ROW_NUMBER 總是按照請求的排序為不同的行生成不同的行號。請注意,如果在 OVER() 選項中指定的 ORDER BY 列表不唯一,則結果是不確定的。這意味著該查詢具有一個以上正確的結果;在該查詢的不同調用中,可能獲得不同的結果。如果您指定一個唯一的 ORDER BY 列表,則結果總是確定的。例如,假設在學生之間出現得分相同的情況時,您希望使用最高的age值來分出先後。如果值仍然相同,則使用最低詞典順序sex值來分出先後。最後,如果值仍然相同,則使用最低詞典順序name名字來分出先後。由於 ORDER BY 列表——score、age、sex和 name——是唯一的,因此結果是確定的。SQL語句如下:

select row_number() over(order by score desc,age desc,sex,name) as scorerank,age,name,sex,score from stutest
order by score desc,age desc,sex,name

結果如下圖:

新的排序函式的重要好處之一是它們的效率。SQL Server 的優化程式只需要掃描資料一次,以便計算值。它完成該工作的方法是:使用在排序列上放置的索引的有序掃描,或者,如果未建立適當的索引,則掃描資料一次並對其進行排序。另一個好處是語法的簡單性。

行號的一個典型應用是通過查詢結果分頁。給定頁大小(以行數為單位)和頁號,需要返回屬於給定頁的行。例如,假設您希望按照“score DESC, age”順序從 StuTest表中返回第二頁的行,並且假定頁大小為三行。下面的查詢首先按照指定的排序計算派生表 D 中的行數,然後只篩選行號為 4 到 6 的行(它們屬於第二頁).SQL語句如下:

select * from                                                    (select row_number() over(order by score desc,age desc,sex,name) as scorerank,age,name,sex,score from stutest) as d
where scorerank between 4 and 6

結果如下圖:

用更一般的術語表達就是,給定 @pagenum 變數中的頁號和 @pagesize 變數中的頁大小,以下查詢返回屬於預期頁的行:

declare @pagenum as int,@pagesize as int
set @pagenum=2
set @pagesize=3
select * from (select row_number() over(order by score desc,age desc,sex,name) as scorerank,age,name,sex,score from stutest) as d
where scorerank between (@pagenum-1)*@pagesize+1 and @pagenum*@pagesize

結果同上:

上述方法對於您只對行的一個特定頁感興趣的特定請求而言已經足夠了。但是,當用戶發出多個請求時,該方法就不能滿足需要了,因為該查詢的每個呼叫都 需要您對錶進行完整掃描,以便計算行號。當用戶可能反覆請求不同的頁時,為了更有效地進行分頁,請首先用所有基礎錶行(包括計算得到的行號)填充一個臨時表,並且對包含這些行號的列進行索引:

select row_number() over(order by score desc,age desc,sex,name) as scorerank,*
into #stutestRN from stutest
create unique clustered index idx_uc_scorerank on #stutestRN(scorerank)

然後,對於所請求的每個頁,發出以下查詢:

declare @pagenum as int,@pagesize as int
set @pagenum=2
set @pagesize=3
select * from #stutestRN
where scorerank between (@pagenum-1)*@pagesize+1 AND @pagenum*@pagesize

結果同上:

可以在行組內部獨立地計算排序值,而不是為作為一個組的所有錶行計算排序值。為此,請使用 PARTITION BY 子句,並且指定一個表示式列表,以標識應該為其獨立計算排序值的行組。例如,以下查詢按照“score desc, age desc,name”順序單獨分配每個sex內部的行號:

select sex,row_number() over(
partition by sex order by score desc,age,name) as pos,age,name,score
from stutest order by sex,score desc,age,name

結果如下圖:

(2)RANK、DENSE_RANK函式。RANK 和 DENSE_RANK 函式非常類似於 ROW_NUMBER 函式,因為它們也按照指定的排序提供排序值,而且可以根據需要在行組(分段)內部提供。但是,與 ROW_NUMBER 不同的是,RANK 和 DENSE_RANK 向在排序列中具有相同值的行分配相同的排序。當 ORDER BY 列表不唯一,並且您不希望為在 ORDER BY 列表中具有相同值的行分配不同的排序時,RANK 和 DENSE_RANK 很有用。RANK 和 DENSE_RANK 的用途以及兩者之間的差異可以用示例進行最好的解釋。以下查詢按照 score DESC 順序計算不同學生的行號、排序和緊密排序值:

select name,age,sex,score,                                      row_number() over(order by score desc) as rownum,
rank() over(order by score desc) as rnk,                             dense_rank() over(order by score desc) as drnk
from stutest order by score desc

結果如下圖:

(3)NTILE函式。NTILE使您可以按照指定的順序,將查詢的結果行分散到指定數量的組(tile)中,每個行組都獲得不同的號碼:第一組為1,第二組為2,等等。您可以在函式名稱後面的括號中指定所請求的組號,在OVER選項的ORDER BY子句中指定所請求的排序。組中的行數被計算為total_num_rows/num_groups。如果有餘數n,則前面n個組獲得一個附加行。因此,可能不會所有組都獲得相等數量的行,但是組大小最大隻可能相差一行。例如,以下查詢按照score降序將三個組號分配給不同的行。

select name,age,sex,score,
row_number() over(order by score desc) as rownum,
ntile(3) over(order by score desc) as tile
from stutest order by score desc

結果如下圖:

通過該資訊可以生成直方圖,並且將專案均勻分佈到每個梯級。在我們的示例中,第一個梯級表示具有最高得分的學生,第二個梯級表示具有中等得分的學生,第三個梯級表示具有最低得分的學生。可以使用CASE表示式為組號提供說明性的有意義的備選含義:

select name,age,sex,score,
case ntile(3) over(order by score desc)
when 1 then 'High'
when 2 then 'Medium'
when 3 then 'Low'
end as scoregrade
from stutest

結果如下圖:

(5)TOP表示式。TOP表示式可以取得指定的前N條記錄。例如:取出StuTest表中前10條記錄:select top(10) * from stutest

結果如下圖:

(6)Try...Catch。SQL Server 2005新增了異常處理功能。

例子:                                                           SET XACT_ABORT ON
BEGIN TRY
    begin tran
        insert into stutest values('rtwrt',27,'female',76.5)
    commit tran
    print 'commited'
END TRY
BEGIN CATCH
    rollback    
    print 'rolled back'
END CATCH

結果:

(7)通用表示式CTE。通過表示式可免除你過去建立臨時表的麻煩。

with stutest_temp as(
    select *,ROW_NUMBER() OVER(order by score desc) as row from stutest
)
select name, age from stutest_temp where row between 10 and 20

結果如下圖:

以上是我的總結。通過學習,自己對SQL Server 2005的新功能有了進一步的瞭解,可以使用自如。