Медианные значения в T-SQL - PullRequest
6 голосов
/ 29 августа 2011

Для четных строк формула для медианы равна (104,5 + 108) / 2 для таблицы ниже, а для нечетных строк - 108 для таблицы ниже:

Total       Total

100         100
101         101
104.5       104.5
108         108
108.3       108.3
112         112
            114

Я написал этот запрос, и онвычисляет правильную медиану, когда число строк нечетное:

WITH    a AS ( SELECT   Total ,
                        ROW_NUMBER() OVER ( ORDER BY CAST(Total AS FLOAT) ASC ) rownumber
               FROM     [Table] A
             ),
        b AS ( SELECT TOP 2
                        Total ,
                        isodd
               FROM     ( SELECT TOP 50 PERCENT
                                    Total ,
                                    rownumber % 2 isodd
                          FROM      a
                          ORDER BY  CAST(Total AS FLOAT) ASC
                        ) a
               ORDER BY CAST(total AS FLOAT) DESC
             )
    SELECT  *
    FROM    b

Каков общий запрос T-SQL для поиска медианы в обеих ситуациях?Например, когда число строк нечетное, а также когда число строк четное?

Может ли мой запрос быть искажен, чтобы он мог работать для медианы как в четном, так и в нечетном количестве строк?

Ответы [ 5 ]

10 голосов
/ 29 августа 2011

Я написал блог о Mean, Median и Mode пару лет назад.Я рекомендую вам прочитать его.

Расчет среднего значения, медианы и режима с помощью SQL Server

SELECT ((
        SELECT TOP 1 Total
        FROM   (
                SELECT  TOP 50 PERCENT Total
                FROM    [TABLE] A
                WHERE   Total IS NOT NULL
                ORDER BY Total
                ) AS A
        ORDER BY Total DESC) +
        (
        SELECT TOP 1 Total
        FROM   (
                SELECT  TOP 50 PERCENT Total
                FROM    [TABLE] A
                WHERE   Total IS NOT NULL
                ORDER BY Total DESC
                ) AS A
        ORDER BY Total ASC)) / 2
8 голосов
/ 07 января 2015

Я знаю, что вы искали решение, которое работает с SQL Server 2008, но в случае, если кто-то ищет агрегатную функцию MEDIAN() в SQL Server 2012, он может эмулировать ее, используя PERCENTILE_CONT()* 1004. * функция обратного распределения:

WITH t(value) AS (
  SELECT 1   UNION ALL
  SELECT 2   UNION ALL
  SELECT 100 
)
SELECT DISTINCT
  percentile_cont(0.5) WITHIN GROUP (ORDER BY value) OVER (PARTITION BY 1)
FROM
  t;

Эта эмуляция MEDIAN() через PERCENTILE_CONT() также задокументирована здесь . К сожалению, SQL Server поддерживает эту функцию только как оконную функцию, а не как обычную агрегатную функцию с упорядоченным набором, такую ​​как Oracle или PostgreSQL.

3 голосов
/ 25 мая 2017

Пример проблемы, упомянутой в моем комментарии к принятому ответу:

select avg(Total) median from
(
select Total, 
rnasc = row_number() over(order by Total),
rndesc = row_number() over(order by Total desc)
 from [Table] 
) b
where rnasc between rndesc - 1 and rndesc + 1

Этот фрагмент не гарантированно сработает, если во входном наборе данных есть повторяющиеся значения - поэтому row_number () не предоставит ожидаемых значений.

Например, для ввода:

DROP TABLE #b
CREATE TABLE #b (id INT IDENTITY, Total INT)
INSERT INTO #b 
SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT  5 
UNION ALL SELECT  5 UNION ALL SELECT  5

Внутренний запрос возвращает (я думаю, он может отличаться на разных серверах):

Total   rnasc   rndesc
5       3      1
5       4      2
5       5      3
1       1      4
1       2      5

Внешний запрос Runnig приведет к NULL (так как нет строки, где rnasc между rndesc - 1 и rndesc + 1 )

Простое решение - добавить некоторый суррогатный ключ (я использовал столбец идентификаторов) в набор данных и включить этот столбец в предложение OVER ():

SELECT avg(Total) median from
(
SELECT Total, 
rnasc = row_number() over(order by Total, id),
rndesc = row_number() over(order by Total DESC, id desc)
 from #b
) b
WHERE rnasc between rndesc - 1 and rndesc + 1

Теперь порядок сортировки гарантирован, и внутренний запрос возвращает:

Total   rnasc   rndesc
5       5       1
5       4       2
5       3       3
1       2       4
1       1       5

И результат правильный:)

3 голосов
/ 12 августа 2014

Ответ t-clausens, к сожалению, не работает правильно, когда в списке много повторяющихся значений. Тогда номера строк, сгенерированные различными предложениями OVER, не могут быть предсказуемы, так как этот запрос работает.

В моем случае хорошо работали:

WITH SortedTable AS
    (
        SELECT Total, 
               rnasc, 
               rndesc = ROW_NUMBER() OVER(ORDER BY rnasc DESC)
        FROM ( 
               SELECT Total, 
                      rnasc = ROW_NUMBER() OVER(ORDER BY Total)
               FROM   [Table]
             ) SourceTable
    )
SELECT DISTINCT AVG(Total) median 
FROM   SortedTable
WHERE  rnasc = rndesc OR ABS(rnasc-rndesc) = 1

Предложение WHERE теперь также четко различает четное и нечетное количество записей.

1 голос
/ 26 августа 2018

Я знаю, что это древний вопрос, но ради других людей я все равно публикую это. Производительность PERCENTILE_COUNT(0.5) тупо медленная. У меня есть таблица с 4,9 миллионами записей, и PERCENTILE_COUNT(0.5) заняло 52 секунды. G Mastros ответ выше, лучше (и мой любимый, кроме моего), но это все еще заняло 35 секунд на моем столе. Я настроил его решение, чтобы сделать следующее, и оно запустилось за 7 секунд без индекса в столбце. Когда я добавил индекс, он упал до 2 секунд. Все, что я сделал, это заменил 50 PERCENT целочисленным делением числа записей в таблице.

DECLARE @Cnt int = (SELECT COUNT(*) FROM [TABLE]);

SELECT ((
    SELECT TOP 1 Total
    FROM   (
            SELECT  TOP (@Cnt/2) Total
            FROM    [TABLE] A
            WHERE   Total IS NOT NULL
            ORDER BY Total
            ) AS A
    ORDER BY Total DESC) +
    (
    SELECT TOP 1 Total
    FROM   (
            SELECT  TOP (@Cnt/2) Total
            FROM    [TABLE] A
            WHERE   Total IS NOT NULL
            ORDER BY Total DESC
            ) AS A
    ORDER BY Total ASC)) / 2
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...