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