SQL實例進階-學習sql server2005 step by step(八)

SQL實例進階-學習sql server2005 step by step(八)

1.SQL2005中row_number( )、rank( )、dense_rank( )、ntile( )函數的用法

(1).row_number( )

先來點數據,先建個表

 

代碼
複製代碼
 1 SET NOCOUNT ON
 2 
 3  CREATE TABLE Person(
 4 
 5 FirstName VARCHAR(10),
 6 
 7 Age INT,
 8 
 9 Gender CHAR(1))
10 
11  INSERT INTO Person VALUES ('Ted',23,'M')
12 
13  INSERT INTO Person VALUES ('John',40,'M')
14 
15  INSERT INTO Person VALUES ('George',6,'M')
16 
17  INSERT INTO Person VALUES ('Mary',11,'F')
18 
19  INSERT INTO Person VALUES ('Sam',17,'M')
20 
21  INSERT INTO Person VALUES ('Doris',6,'F')
22 
23  INSERT INTO Person VALUES ('Frank',38,'M')
24 
25  INSERT INTO Person VALUES ('Larry',5,'M')
26 
27  INSERT INTO Person VALUES ('Sue',29,'F')
28 
29  INSERT INTO Person VALUES ('Sherry',11,'F')
30 
31  INSERT INTO Person VALUES ('Marty',23,'F')
32 
33  
複製代碼

 

 

直接用例子說明問題:

SELECT ROW_NUMBER() OVER (ORDER BY Age) AS [Row Number by Age],FirstName,Age

FROM Person

 

出現的數據如下

Row Number by Age                FirstName            Age

--------------------------                 ----------            --------

1                                                Larry                   5

2                                                Doris                   6

3                                                George               6

4                                                Mary                   11

5                                                Sherry                 11

6                                                Sam                    17

7                                                Ted                     23

8                                                Marty                   23

9                                                Sue                     29

10                                              Frank                  38

11                                              John                    40

可以觀察到,是根據年齡升序排列了,並且row_number()是給出了序列號了,這個序列號被重命名爲Row Number by Age,與sql server2000對比:如果在sql server2000中實現相對麻煩一些,我們可以利用IDENTITY()函數實現,但IDENTITY()函數只能用在sql server2000臨時表中,因此需要將數據檢索到臨時表裏。select identity(int,1,1) as [Row Number by Age],FirstName,Age into #A from Person order by Ageselect * from #Adrop table #a如果不想按年齡排序,可以這樣寫

SELECT ROW_NUMBER() OVER (ORDER BY (SELECT 1)) AS [Row Number by Record Set],FirstName,Age

FROM Person另外一個例子SELECT ROW_NUMBER() OVER (PARTITION BY Gender ORDER BY Age) AS [Partition by Gender],FirstName,Age,Gender

FROM Person這裏是按性別劃分區間了,同一性別再按年齡來排序,輸出結果如下

 

Partition by Gender         FirstName         Age                Gender

-------------------- ---------- ----------- ------

1                           Doris             6                  F

2                           Mary              11                 F

3                           Sherry            11                 F

4                           Sue               29                 F

1                           Larry             5                  M

2                           George            6                  M

3                           Sam               17                 M

4                           Ted               23                 M

5                           Marty             23                 M

6                           Frank             38                 M

7                           John              40                 M注意,姓名M開始,序號又從1,2,3開始了

 

 (2).RANK( )函數        

先看例子

SELECT RANK() OVER (ORDER BY Age) AS [Rank by Age],FirstName,Age

FROM Person輸出如下:Rank by Age                 FirstName         Age

-------------------- ---------- -----------

1                           Larry             5

2                           Doris             6

2                           George            6

4                           Mary              11

4                           Sherry            11

6                           Sam               17

7                           Ted               23

7                           Marty             23

9                           Sue               29

10                          Frank             38

11                          John              40

看到了麼,同年嶺的話,將有相同的順序,順序成1,2,2,4了。與sql server2000對比:出現了RANK()函數實在是方便,在sql server2000裏實現排序並列的問題麻煩很多。

select [Rank by Age]=isnull((select count(*) from person where Age>A.Age),0)+1,FirstName,Age from Person A order

 by [Rank by Age] SELECT RANK() OVER(PARTITION BY Gender ORDER BY Age) AS [Partition by Gender],FirstName, Age, Gender FROM Person

輸出爲

Partition by Gender         FirstName         Age                Gender

-------------------- ---------- ----------- ------

1                           Doris             6                  F

2                           Mary              11                 F

2                           Sherry            11                 F

4                           Sue               29                 F

1                           Larry             5                  M

2                           George            6                  M

3                           Sam               17                 M

4                           Ted               23                 M

4                           Marty             23                 M

6                           Frank             38                 M

7                           John              40                 M

可以看到,按性別分組了,每個性別分組裏,繼續是用了rank( )函數

 

(3).DENSE_RANK( )函數

         SELECT DENSE_RANK() OVER (ORDER BY Age) AS [Dense Rank by Age], FirstName, Age

         FROM Person

 

輸出結果爲:

Dense Rank by Age          FirstName        Age

-------------------- ---------- -----------

1                          Larry            5

2                          Doris            6

2                          George           6

3                          Mary             11

3                          Sherry           11

4                          Sam              17

5                          Ted              23

5                          Marty            23

6                          Sue              29

7                          Frank            38

8                          John             40

 

看到了麼,和rank函數區別是,順序始終是連續的,Doris 和George同年,都是排第2位,但之後的mary不象rank函數那樣排第4,而是排第3位了

(4).ntile( )函數

SELECT FirstName,

Age,

NTILE(3) OVER (ORDER BY Age) AS [Age Groups]

FROM Person

 

輸出結果:

FirstName        Age               Age Groups

---------- ----------- --------------------

Larry                5                  1

Doris                6                  1

George            6                  1

Mary                11                1

Sherry             11                 2

Sam                17                 2

Ted                 23                 2

Marty              23                 2

Sue                29                 3

Frank             38                 3

John               40                 3

這個函數按照ntile(n)中的N,把記錄強制分成多少段,11條記錄現在分成3段了,lary到mary是第1段,sherry到maty是第2段,sue到john是第3段了。

2.SQLServer 2005 中的except/intersect和outer apply交併集計算

首先,建立兩個表:

 

代碼
複製代碼
 1 CREATE TABLE #a (ID INT) 
 2  INSERT INTO #a VALUES (1) 
 3  INSERT INTO #a VALUES (2) 
 4  INSERT INTO #a VALUES (null) 
 5 
 6  CREATE TABLE #b (ID INT) 
 7  INSERT INTO #b VALUES (1) 
 8  INSERT INTO #b VALUES (3) 
 9 
10  
複製代碼

 

 


我們的目的是從表#b中取出ID不在表#a的記錄。
如果不看具體的insert的內容,單單看這個需求,可能很多朋友就會寫出這個sql了:

select * from #b where id not in (select id from #a)


但是根據上述插入的記錄,這個sql檢索的結果不是我們期待的ID=3的記錄,而是什麼都沒有返回。原因很簡單:在子查詢select id from #a中返回了null,而null是不能跟任何值比較的。

那麼您肯定會有下面的多種寫法了:

 

代碼
1 select * from #b where id not in (select id from #a where id is not null)
2  select * from #b b where b.id not in (select id from #a a where a.id=b.id)
3  select * from #b b where not exists (select 1 from #a a where a.id=b.id)
4 
5  

 

 


當然還有使用left join/right join/full join的幾種寫法,但是無一例外,都是比較冗長的。其實在SQL Server 2005增加了一種新的方法,可以幫助我們很簡單、很簡潔的完成任務:

select * from #b
except
select * from #a


我不知道在SQL Server 2008裏還有沒有什麼更酷的方法,但是我想這個應該是最簡潔的實現了。當然,在2005裏還有一種方法可以實現:

 

複製代碼
1 select * from #b b
2  outer apply
3 (select id from #a a where a.id=b.id) k
4  where k.id is null
5 
6 
複製代碼

 

 


outer apply也可以完成這個任務。

如果我們要尋找兩個表的交集呢?那麼在2005就可以用intersect關鍵字:

select * from #b
intersect
select * from #a

ID
-----------
1

(1 row(s) affected)

0

3.使用coalesce和nullif的組合來減輕sql的工作

 

代碼
複製代碼
 1 create table tbl (id int, type_a int)
 2 
 3 insert into tbl values (1000,1000)
 4 insert into tbl values (999,999)
 5 insert into tbl values (998,998)
 6 insert into tbl values (997,997)
 7 insert into tbl values (996,996)
 8 insert into tbl values (995,null)
 9 insert into tbl values (994,null)
10 insert into tbl values (993,null)
11 insert into tbl values (992,null)
12 insert into tbl values (991,null)
13 
14 
複製代碼

 

 


邏輯非常簡單:當type_a爲997或null的時候,我們要讓輸出的type_a字段值爲0。
OK,這個SQL語句當然有多種寫法,朋友的sql是這樣寫的:

 

複製代碼
1 select
2     case
3         when (type_a is null or type_a=997) then 0
4         else type_a
5     end as type_a
6 from tbl
7 
8 
複製代碼

 

 


如果需要控制的字段一多,那這個及時已經使用了縮進的select也看起來很複雜了,時間久了想改動這個sp的邏輯就有些吃力了,我們常常在做計劃時會說“半小時搞定這個問題”,但是往往在做的時候都會超過這個時間,原因就在於我們總有從一團亂麻中找到入手點。複雜的代碼和邏輯往往是解決問題中難啃的骨頭。那麼有什麼好辦法優化一下嗎?

select coalesce(nullif(type_a,997),0) as type_a from tbl


Well,上面寫了6行的sql就被這1行所替代了。

nullif接受兩個參數,如果兩個參數相等,那麼返回null,否則返回第一個參數
coalesce接受N個參數,返回第一個不爲null的參數

So,當您遇到處理一個如下所示的計算工資的問題的時候,不妨這樣來解決:

create table salary (e_id uniqueidentifier, byMonth int, byHalfYear int, byYear int)

insert into salary values (newid(),9000,null,null)
insert into salary values (newid(),null,60000,null)
insert into salary values (newid(),null,null,150000)


每個僱員有3種薪資計算方式(按月,按半年,按年)來發放工資,如果我們想統計每個員工的年薪,那這樣一句就夠了:

select e_id,coalesce(byMonth*12,byHalfYear*2,byYear) as salary_amount from salary


結果:

e_id                                                 salary_amount
------------------------------------ -------------
8935330D-2B73-4FEF-941A-768D7A8CCB6C 108000
52A3CE16-74FD-4D5D-BB4F-F5F67A1E9D2F 120000
06B6B924-EAB2-4187-B733-EBB56B62E793 150000


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章