2

在MSSQL2008,我試圖採用經典的中位數查詢如下計算一列數的中位數從公共表表達式:計算平均

WITH cte AS 
(
    SELECT number 
    FROM table 
) 

SELECT cte.*, 
(SELECT 
    (SELECT ( 
    (SELECT TOP 1 cte.number 
    FROM  
    (SELECT TOP 50 PERCENT cte.number  
     FROM cte 
     ORDER BY cte.number) AS medianSubquery1 
    ORDER BY cte.number DESC) 
    + 
    (SELECT TOP 1 cte.number 
    FROM  
    (SELECT TOP 50 PERCENT cte.number  
    FROM cte 
    ORDER BY cte.number DESC) AS medianSubquery2 
    ORDER BY cte.number ASC))/2)) AS median 

FROM cte 
ORDER BY cte.number 

的結果集我得到的是以下幾點:

NUMBER MEDIAN 
x1  x1 
x1  x1 
x1  x1 
x2  x2 
x3  x3 

換句話說,「中間」列是一樣的「號碼」一欄時,我希望中位數列是「X1」一路下跌。我使用類似的表達式來計算模式,它在相同的公用表表達式上工作正常。

回答

1

與您的查詢的問題是,你正在做

SELECT TOP 1 cte.number FROM...

但不與子查詢,以便子查詢無關它與外部查詢的關係密切。這就解釋了爲什麼你最終只能得到相同的價值。刪除cte.(如下)給出了CTE的中位數。這是一個恆定的價值。你想做什麼?

WITH cte AS 
    (SELECT NUMBER 
    FROM master.dbo.spt_values 
    WHERE TYPE='p' 
    ) 

SELECT cte.*, 
(SELECT 
    (SELECT ( 
    (SELECT TOP 1 number 
    FROM  
    (SELECT TOP 50 PERCENT cte.number  
     FROM cte 
     ORDER BY cte.number) AS medianSubquery1 
    ORDER BY number DESC) 
    + 
    (SELECT TOP 1 number 
    FROM  
    (SELECT TOP 50 PERCENT cte.number  
    FROM cte 
    ORDER BY cte.number DESC) AS medianSubquery2 
    ORDER BY number ASC))/2)) AS median 
FROM cte 
ORDER BY cte.number 

返回

NUMBER  median 
----------- ----------- 
0   1023 
1   1023 
2   1023 
3   1023 
4   1023 
5   1023 
6   1023 
7   1023 
+0

我在查詢中看到的唯一區別是在公用表表達式中添加了WHERE子句,我不確定WHERE子句應該做什麼。 – Dan 2010-07-09 22:34:20

+0

啊,我不明白指定CTE名稱作爲限定符有這種效果。在實踐中,我使用兩列進行製圖,所以恆定的中值對於在第一個系列的中值處繪製橫跨圖表的第二個系列非常有用。 感謝您的幫助。 – Dan 2010-07-09 22:44:41

+0

從技術上講,輸出不正確。 1023.50應該是中位數。因此,如果您有偶數行並需要進行除法,您是否不需要將Number轉換爲小數或浮點數(例如Number * 1.000)? – Thomas 2010-07-10 01:51:10

3

這裏有一個稍微不同的方式做到這一點:

WITH cte AS 
(
    SELECT number 
    FROM table1 
) 
SELECT T1.number, T3.median 
FROM cte T1, 
(
    SELECT AVG(number) AS median 
    FROM 
    (
     SELECT number, ROW_NUMBER() OVER(ORDER BY number) AS rn 
     FROM cte 
    ) T2 
    WHERE T2.rn = ((SELECT COUNT(*) FROM table1) + 1)/2 
    OR T2.rn = ((SELECT COUNT(*) FROM table1) + 2)/2 
) T3 
+0

謝謝。這當然是計算中位數的更簡單和更簡單的方法。 – Dan 2010-07-09 22:45:47

0

這並不是一個完全新的答案,因爲它主要是在馬克·拜爾的回答擴大,但也有還有一些選項可以進一步簡化查詢。

首先要真正利用CTE的。您不僅可以擁有多個CTE,還可以互相參考。考慮到這一點,我們可以創建一個額外的CTE來根據第一個結果計算中位數。這封裝了中值計算,並使實際的SELECT只做它需要做的事情。請注意,ROW_NUMBER()必須移入第一個CTE。

;WITH cte AS 
(
    SELECT number, ROW_NUMBER() OVER(ORDER BY number) AS rn 
    FROM table1 
), 
med AS 
(
    SELECT AVG(number) AS median 
    FROM cte 
    WHERE cte.rn = ((SELECT COUNT(*) FROM cte) + 1)/2 
    OR cte.rn = ((SELECT COUNT(*) FROM cte) + 2)/2 
) 
SELECT cte.number, med.median 
FROM cte 
CROSS JOIN med 

爲了進一步降低複雜性,你「可以」使用自定義的CLR聚合處理中位數(如在http://www.SQLsharp.com/ [我是筆者在免費的SQL#庫提供的一個)。

;WITH cte AS 
(
    SELECT number 
    FROM table1 
), 
med AS 
(
    SELECT SQL#.Agg_Median(cte.number) AS median 
    FROM cte 
) 
SELECT cte.number, med.median 
FROM cte 
CROSS JOIN med