ОБНОВЛЕНИЕ 2019: За 10 лет, прошедших с тех пор, как я написал этот ответ, было обнаружено больше решений, которые могут дать лучшие результаты. Кроме того, с тех пор выпуски SQL Server (особенно SQL 2012) представили новые функции T-SQL, которые можно использовать для вычисления медиан. В выпусках SQL Server также улучшен оптимизатор запросов, что может повлиять на производительность различных медианных решений. Net-net, мой исходный пост 2009 года все еще в порядке, но могут быть лучшие решения для современных приложений SQL Server. Взгляните на эту статью 2012 года, которая является отличным ресурсом: https://sqlperformance.com/2012/08/t-sql-queries/median.
В этой статье было обнаружено, что следующий шаблон намного, намного быстрее, чем все другие альтернативы, по крайней мере, на простой схеме, которую они тестировали. Это решение было в 373 раза быстрее (!!!), чем самое медленное (PERCENTILE_CONT
) протестированное решение. Обратите внимание, что для этого трюка требуются два отдельных запроса, что может оказаться нецелесообразным во всех случаях. Также требуется SQL 2012 или новее.
DECLARE @c BIGINT = (SELECT COUNT(*) FROM dbo.EvenRows);
SELECT AVG(1.0 * val)
FROM (
SELECT val FROM dbo.EvenRows
ORDER BY val
OFFSET (@c - 1) / 2 ROWS
FETCH NEXT 1 + (1 - @c % 2) ROWS ONLY
) AS x;
Конечно, только потому, что один тест на одной схеме в 2012 году дал отличные результаты, ваш опыт может отличаться, особенно если вы используете SQL Server 2014 или более поздней версии. Если производительность важна для расчета медианы, я настоятельно рекомендую попробовать и протестировать несколько вариантов, рекомендованных в этой статье, чтобы убедиться, что вы нашли лучший вариант для своей схемы.
Я также был бы особенно осторожен при использовании функции (новая в SQL Server 2012) _ 3_, который рекомендуется в одном из других ответов на этот вопрос, поскольку в статье, приведенной выше, обнаружено, что эта встроенная функция в 373 раза медленнее, чем самое быстрое решение. Возможно, это несоответствие улучшилось за 7 лет, прошедших с тех пор, но лично я не стал бы использовать эту функцию на большой таблице, пока не проверил ее производительность по сравнению с другими решениями.
ОРИГИНАЛЬНАЯ ЗАПИСЬ 2009 ГОДА НАХОДИТСЯ НИЖЕ:
Есть много способов сделать это с сильно различающейся производительностью. Вот одно особенно хорошо оптимизированное решение от Медианы, ROW_NUMBER и производительность. Это особенно оптимальное решение, когда речь идет о фактических операциях ввода-вывода, генерируемых во время выполнения - оно выглядит более затратным, чем другие решения, но на самом деле намного быстрее.
Эта страница также содержит обсуждение других решений и детали тестирования производительности. Обратите внимание на использование уникального столбца в качестве средства устранения неоднозначности в случае наличия нескольких строк с одинаковым значением среднего столбца.
Как и во всех сценариях производительности базы данных, всегда старайтесь протестировать решение с реальными данными на реальном оборудовании - вы никогда не знаете, когда изменение оптимизатора SQL Server или особенность вашей среды замедлит работу обычно работающего решения.
SELECT
CustomerId,
AVG(TotalDue)
FROM
(
SELECT
CustomerId,
TotalDue,
-- SalesOrderId in the ORDER BY is a disambiguator to break ties
ROW_NUMBER() OVER (
PARTITION BY CustomerId
ORDER BY TotalDue ASC, SalesOrderId ASC) AS RowAsc,
ROW_NUMBER() OVER (
PARTITION BY CustomerId
ORDER BY TotalDue DESC, SalesOrderId DESC) AS RowDesc
FROM Sales.SalesOrderHeader SOH
) x
WHERE
RowAsc IN (RowDesc, RowDesc - 1, RowDesc + 1)
GROUP BY CustomerId
ORDER BY CustomerId;
person
Justin Grant
schedule
14.10.2009