Вычислить медиану столбца В общем табличном выражении SQL
В 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
3 ответа:
Проблема с вашим запросом заключается в том, что вы делаете
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
Вот немного другой способ сделать это:
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
Это не совсем новый ответ, поскольку он в основном расширяет ответ Марка Байера, но есть несколько вариантов для дальнейшего упрощения запроса.
Первое-это действительно использовать CTE. не только у вас может быть несколько CTE, но они могут ссылаться друг на друга. Имея это в виду, мы можем создать дополнительный CTE для вычисления медианы на основе результатов первого. Это инкапсулирует медианное вычисление и оставляет фактический выбор делать только то, что ему нужно делать. Обратите внимание, что 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