你真的會玩SQL嗎?表表達式,排名函數


你真的會玩SQL嗎?系列目錄

你真的會玩SQL嗎?之邏輯查詢處理階段

你真的會玩SQL嗎?和平大使 內連接、外連接

你真的會玩SQL嗎?三范式、數據完整性

你真的會玩SQL嗎?查詢指定節點及其所有父節點的方法

你真的會玩SQL嗎?讓人暈頭轉向的三值邏輯

你真的會玩SQL嗎?EXISTS和IN之間的區別

你真的會玩SQL嗎?無處不在的子查詢

你真的會玩SQL嗎?Case也瘋狂

你真的會玩SQL嗎?表表達式,排名函數

你真的會玩SQL嗎?簡單的 數據修改

你真的會玩SQL嗎?你所不知道的 數據聚合

你真的會玩SQL嗎?透視轉換的藝術

你真的會玩SQL嗎?冷落的Top和Apply

你真的會玩SQL嗎?實用函數方法匯總

你真的會玩SQL嗎?玩爆你的數據報表之存儲過程編寫(上)

你真的會玩SQL嗎?玩爆你的數據報表之存儲過程編寫(下)

 

這次講的有些可能是經常用但不會注意到,所以來統一總結一下用法。

我們往往需要臨時存儲某些結果集。除了用臨時表和表變量,還可以使用公用表表達式的方法。

表表達式

    1. 期待單個值的地方可以使用標量子查詢
    2. 期待多個值的地方可以使用多值子查詢
    3. 在期待出現表的地方可用表值子查詢表表達式

1.派生表

是從查詢表達式派生出虛擬結果表的表表達式,派生表的存在范圍只是外部查詢。

使用形式:from 派生表 as 派生表列名

規則:

    1. 所有列必須有名稱
    2. 列名必須唯一
    3. 不允許使用order by(除非指定了top)

不同於標量和多值子查詢,派生表不能是相關的,它必須是獨立的。

 

2.公用表表達式(CTE)

 非遞歸公用表表達式(CTE)是查詢結果僅僅一次性返回一個結果集用於外部查詢調用。

WITH CTE_Test
  AS
  (
      SELECT * FROM Person_1
  )
  SELECT * FROM CTE_Test AS a  --第一次引用
  INNER JOIN  CTE_Test AS b    --第二次引用
  ON a.Id = b.Id
  ORDER BY a.Id DESC
--
SELECT * FROM CTE_Test 再查詢一次會報錯
 
        

遞歸公用表達式

來引用他人的一個示例:

先建一張表欄目表如下,欄目Id,欄目名稱,欄目的父欄目

現在使用CTE查詢其每個欄目是第幾層欄目的代碼如下:

WITH COL_CTE(Id,Name,ParentId,tLevel )
AS
(
    --基本語句
    SELECT Id,Name,ParentId,0 AS tLevel FROM Col
    WHERE ParentId = 0
    UNION ALL
    --遞歸語句
    SELECT c.Id,c.Name,c.ParentId,ce.tLevel+1 AS tLevel FROM COL as c 
    INNER JOIN COL_CTE AS ce   --遞歸調用
    ON c.ParentId = ce.Id
)

SELECT * FROM COL_CTE

結果:

0表示頂級欄目。1就是1級欄目

 

排名函數

四個排名函數:

  1.row_number

  2.rank

  3.dense_rank

  4.ntile 

排名函數order by子句是必需的。我們這里不講定義,直接講實例用法。

利用row_number生成連續行號

SELECT  empid ,
        qty ,
        ROW_NUMBER() OVER ( ORDER BY qty ) AS rownum
FROM    sales
ORDER BY qty

小的分組范圍內排序通過PARTITION BY選項來重新排序,給數據分區或者數據區域唯一的遞增序號

如:LastName以‘A’開頭的作為第一組,在這個組內進行排序。以‘B’開頭的作為第二組,在這個組內排序。以‘C’開頭的作為第三組,在這個組內進行排序,如此等等

select
ROW_NUMBER() over(PARTITION by substring(LastName,1,1) order by LastName) as RowNum,
FirstName+' '+ LastName as FullName
from HumanResources.vEmployee

結果

假設LastName以‘A’開頭的是男子組,這個組有共有三個人,Kim Abercrombie是冠軍,Jay Adams是亞軍,Nancy Anderson是季軍。假設LastName以‘B’開頭的是女子組,這個組只有一個人Bryan Baker,無論如何她都是冠軍。等等如此類推。這樣一眼就能看出他們的小組名次了。

RANK

果有同時撞線的情況發生應該怎么計名次呢?例如A第一個撞線,B和C同時第二個撞線,D第三個撞線,如果我們想把D的名次計為第4名應該怎么處理呢?就是說不計順序名次,只計人數。這時就可以使用RANK函數了。

在order by子句中定義的列上,如果返回一行數據與另一行具有相同的值,rank函數將給這些行賦予相同的排名數值。在排名的過程中,保持一個內部計數值,當值有所改變時,排名序號將有一個跳躍。

SELECT  ROW_NUMBER() OVER ( ORDER BY Department ) AS RowNum ,
        RANK() OVER ( ORDER BY Department ) AS Ranking ,
        FirstName + ' ' + LastName AS FullName ,
        Department
FROM    HumanResources.vEmployeeDepartment
ORDER BY RowNum

rank()函數右面也要跟上一個over子句。為了看到效果我們以Department作為排序字段,可以看到RowNum作為升序連續排名,Ranking作為計同排名,當Department的值相同時,Ranking中的值保持不變,當Ranking中的值發生變化時,Ranking列中的值將跳躍到正確的排名數值。來看結果:

 

從這個結果中我們可以說這次馬拉松賽跑的排名是:Tengiz Kharatishvili,Zainal Arifin,Sean Chai,Karen Berge,Chris Norred並列第1,Michael Sullivan,Sharon Salavaria,Roberto Tamburello,Gail Erickson,Jossef Goldberg並列第6,如此等等。

DENSE_RANK

A第一個撞線,B和C同時第二個撞線,D第三個撞線,如果我們想把B和C的名次計位第2名,D的名次計為第3名應該怎么處理呢?就是說考慮並列名次。這里使用DENSE_RANK函數

SELECT  ROW_NUMBER() OVER ( ORDER BY Department ) AS RowNum ,
        DENSE_RANK() OVER ( ORDER BY Department ) AS Ranking ,
        FirstName + ' ' + LastName AS FullName ,
        Department
FROM    HumanResources.vEmployeeDepartment
ORDER BY RowNum

結果

按照這個結果,我們可以說這次馬拉松賽跑的排名是:Tengiz KharatishviliZainal ArifinSean ChaiKaren BergeChris Norred並列第1Michael SullivanSharon SalavariaRoberto TamburelloGail EricksonJossef GoldbergTerri Duffy並列第2,等等。

NTILE

梭羅是鉛筆的發明者,不過他沒有申請專利。據說他天賦異稟,在父親的鉛筆廠里面打包鉛筆的時候,從一堆鉛筆里面抓取一把,每次都能精確地抓到一打12支。他在森林中目測兩顆樹之間的距離,和護林員用卷尺測量的結果相差無幾。現在如果我們想從一張表中抓取多比數據,每一筆都是相同的數目,並且標明第幾組該怎么辦呢?NTILE函數提供了這個功能。

SELECT  NTILE(30) OVER ( ORDER BY Department ) AS NTiles ,
        FirstName + ' ' + LastName AS FullName ,
        Department
FROM    HumanResources.vEmployeeDepartment

現在我們要抓取30個組的數據,並保證盡可能的保證每組數目相同

這個視圖中共290條數據,290/30=9.7約等於10,所以每組10條數據,如圖每一條數據都有一個組號。這個結果要比索羅精確。

 

練習

用CTE刪除重復數據

創建一個用於測試的表,並在該表里插入幾條數據(包括重復的數據)

/* Create Table with 7 records- 3 are duplicate records*/
CREATE TABLE DeleteDuplicateTest ( Col1 INT, Col2 INT )
INSERT  INTO DeleteDuplicateTest
        SELECT  1 ,
                1
        UNION ALL
        SELECT  1 ,
                1 --duplicate
        UNION ALL
        SELECT  1 ,
                1 --duplicate
        UNION ALL
        SELECT  1 ,
                2
        UNION ALL
        SELECT  1 ,
                2 --duplicate
        UNION ALL
        SELECT  1 ,
                3
        UNION ALL
        SELECT  1 ,
                4
GO

用CTE刪除重復數據4條惟一的記錄

參考SQL

WITH    CTE ( COl1, Col2, DuplicateCount )
          AS ( SELECT   COl1 ,
                        Col2 ,
                        ROW_NUMBER() OVER ( PARTITION BY COl1, Col2 ORDER BY Col1 ) AS DuplicateCount
               FROM     DELETEDUPLICATETEST
             )
    DELETE  FROM CTE
    WHERE   DuplicateCount > 1
GO

/*用SQL SERVER 的CTE,它將重新生成一個相同的但附加了一行編號的表。在此方案中,我們有Col1,Col2以及包含這個兩列重復數的列,對於不同的查詢,這個重復數的列可能有不同的值。另一點需要注意的是,一旦CTE被創建,DELETE語句就可以被運行了。這里我們設置一個條件——當我們讀取到的記錄大於一條(即有重復數據),我們刪除除了第一條的所有其他(這里可能有點繞,簡單的話就是保留一條重復的記錄)*/
View Code

   以上部分示例總結於:T-SQL中的排名函數


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM