Вычислить медиану столбца в выражении общей таблицы SQL - PullRequest
2 голосов
/ 10 июля 2010

В 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» на всем пути вниз.Я использую подобное выражение для вычисления режима, и оно отлично работает с тем же общим табличным выражением.

Ответы [ 3 ]

3 голосов
/ 10 июля 2010

Вот немного другой способ сделать это:

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
1 голос
/ 10 июля 2010

Проблема с вашим запросом состоит в том, что вы выполняете

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 голосов
/ 22 января 2011

Это не совсем новый ответ, поскольку он в основном расширяет ответ Марка Байера, но есть несколько вариантов для еще большего упрощения запроса.

Первое, что нужно сделать, это действительно использовать 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 для обработки медианы (например, тот, который указанбесплатная библиотека SQL # на http://www.SQLsharp.com/ [автором которой я являюсь]).

;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
...