Функция для вычисления медианы в SQL Server

Whats, который Вы ищете, jQuery Ajax. И затем просто установите php страницу для обработки запроса.

212
задан Peter Mortensen 12 December 2017 в 13:55
поделиться

7 ответов

ОБНОВЛЕНИЕ 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) функции PERCENTILE_CONT , рекомендованной в одном из других ответов на этот вопрос, потому что в статье, указанной выше, эта встроенная функция работает в 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;
129
ответ дан 23 November 2019 в 04:30
поделиться

Я хотел найти решение самостоятельно, но мой мозг споткнулся и упал на пути. Я думаю , что это работает, но не просите меня объяснять это утром. : P

DECLARE @table AS TABLE
(
    Number int not null
);

insert into @table select 2;
insert into @table select 4;
insert into @table select 9;
insert into @table select 15;
insert into @table select 22;
insert into @table select 26;
insert into @table select 37;
insert into @table select 49;

DECLARE @Count AS INT
SELECT @Count = COUNT(*) FROM @table;

WITH MyResults(RowNo, Number) AS
(
    SELECT RowNo, Number FROM
        (SELECT ROW_NUMBER() OVER (ORDER BY Number) AS RowNo, Number FROM @table) AS Foo
)
SELECT AVG(Number) FROM MyResults WHERE RowNo = (@Count+1)/2 OR RowNo = ((@Count+1)%2) * ((@Count+2)/2)
0
ответ дан 23 November 2019 в 04:30
поделиться

См. Другие решения для вычисления медианы в SQL здесь: « Простой способ вычисления медианы с MySQL » (решения в основном не зависят от производителя).

1
ответ дан 23 November 2019 в 04:30
поделиться

В UDF напишите:

 Select Top 1 medianSortColumn from Table T
  Where (Select Count(*) from Table
         Where MedianSortColumn <
           (Select Count(*) From Table) / 2)
  Order By medianSortColumn
2
ответ дан 23 November 2019 в 04:30
поделиться
--Create Temp Table to Store Results in
DECLARE @results AS TABLE 
(
    [Month] datetime not null
 ,[Median] int not null
);

--This variable will determine the date
DECLARE @IntDate as int 
set @IntDate = -13


WHILE (@IntDate < 0) 
BEGIN

--Create Temp Table
DECLARE @table AS TABLE 
(
    [Rank] int not null
 ,[Days Open] int not null
);

--Insert records into Temp Table
insert into @table 

SELECT 
    rank() OVER (ORDER BY DATEADD(mm, DATEDIFF(mm, 0, DATEADD(ss, SVR.close_date, '1970')), 0), DATEDIFF(day,DATEADD(ss, SVR.open_date, '1970'),DATEADD(ss, SVR.close_date, '1970')),[SVR].[ref_num]) as [Rank]
 ,DATEDIFF(day,DATEADD(ss, SVR.open_date, '1970'),DATEADD(ss, SVR.close_date, '1970')) as [Days Open]
FROM
 mdbrpt.dbo.View_Request SVR
 LEFT OUTER JOIN dbo.dtv_apps_systems vapp 
 on SVR.category = vapp.persid
 LEFT OUTER JOIN dbo.prob_ctg pctg 
 on SVR.category = pctg.persid
 Left Outer Join [mdbrpt].[dbo].[rootcause] as [Root Cause] 
 on [SVR].[rootcause]=[Root Cause].[id]
 Left Outer Join [mdbrpt].[dbo].[cr_stat] as [Status]
 on [SVR].[status]=[Status].[code]
 LEFT OUTER JOIN [mdbrpt].[dbo].[net_res] as [net] 
 on [net].[id]=SVR.[affected_rc]
WHERE
 SVR.Type IN ('P') 
 AND
 SVR.close_date IS NOT NULL 
 AND
 [Status].[SYM] = 'Closed'
 AND
 SVR.parent is null
 AND
 [Root Cause].[sym] in ( 'RC - Application','RC - Hardware', 'RC - Operational', 'RC - Unknown')
 AND
 (
  [vapp].[appl_name] in ('3PI','Billing Rpts/Files','Collabrent','Reports','STMS','STMS 2','Telco','Comergent','OOM','C3-BAU','C3-DD','DIRECTV','DIRECTV Sales','DIRECTV Self Care','Dealer Website','EI Servlet','Enterprise Integration','ET','ICAN','ODS','SB-SCM','SeeBeyond','Digital Dashboard','IVR','OMS','Order Services','Retail Services','OSCAR','SAP','CTI','RIO','RIO Call Center','RIO Field Services','FSS-RIO3','TAOS','TCS')
 OR
  pctg.sym in ('Systems.Release Health Dashboard.Problem','DTV QA Test.Enterprise Release.Deferred Defect Log')
 AND  
  [Net].[nr_desc] in ('3PI','Billing Rpts/Files','Collabrent','Reports','STMS','STMS 2','Telco','Comergent','OOM','C3-BAU','C3-DD','DIRECTV','DIRECTV Sales','DIRECTV Self Care','Dealer Website','EI Servlet','Enterprise Integration','ET','ICAN','ODS','SB-SCM','SeeBeyond','Digital Dashboard','IVR','OMS','Order Services','Retail Services','OSCAR','SAP','CTI','RIO','RIO Call Center','RIO Field Services','FSS-RIO3','TAOS','TCS')
 )
 AND
 DATEADD(mm, DATEDIFF(mm, 0, DATEADD(ss, SVR.close_date, '1970')), 0) = DATEADD(mm, DATEDIFF(mm,0,DATEADD(mm,@IntDate,getdate())), 0)
ORDER BY [Days Open]



DECLARE @Count AS INT
SELECT @Count = COUNT(*) FROM @table;

WITH MyResults(RowNo, [Days Open]) AS
(
    SELECT RowNo, [Days Open] FROM
        (SELECT ROW_NUMBER() OVER (ORDER BY [Days Open]) AS RowNo, [Days Open] FROM @table) AS Foo
)


insert into @results
SELECT 
 DATEADD(mm, DATEDIFF(mm,0,DATEADD(mm,@IntDate,getdate())), 0) as [Month]
 ,AVG([Days Open])as [Median] FROM MyResults WHERE RowNo = (@Count+1)/2 OR RowNo = ((@Count+1)%2) * ((@Count+2)/2) 


set @IntDate = @IntDate+1
DELETE FROM @table
END

select *
from @results
order by [Month]
0
ответ дан 23 November 2019 в 04:30
поделиться

Если вы используете SQL 2005 или лучше, то это хороший, простой расчет медианы для одного столбца в таблице:

SELECT
(
 (SELECT MAX(Score) FROM
   (SELECT TOP 50 PERCENT Score FROM Posts ORDER BY Score) AS BottomHalf)
 +
 (SELECT MIN(Score) FROM
   (SELECT TOP 50 PERCENT Score FROM Posts ORDER BY Score DESC) AS TopHalf)
) / 2 AS Median
194
ответ дан 23 November 2019 в 04:30
поделиться

Мой первоначальный быстрый ответ был:

select  max(my_column) as [my_column], quartile
from    (select my_column, ntile(4) over (order by my_column) as [quartile]
         from   my_table) i
--where quartile = 2
group by quartile

Это даст вам медиану и интерквартильный размах одним махом. Если вам действительно нужен только один ряд, который является медианой, то отмените предложение where.

Когда вы вставляете это в план объяснения, 60% работы приходится на сортировку данных, что неизбежно при вычислении статистики, зависящей от положения, как эта.

Я изменил ответ, чтобы следовать отличному предложению Роберта Шевчика-Робажа в комментариях ниже:

;with PartitionedData as
  (select my_column, ntile(10) over (order by my_column) as [percentile]
   from   my_table),
MinimaAndMaxima as
  (select  min(my_column) as [low], max(my_column) as [high], percentile
   from    PartitionedData
   group by percentile)
select
  case
    when b.percentile = 10 then cast(b.high as decimal(18,2))
    else cast((a.low + b.high)  as decimal(18,2)) / 2
  end as [value], --b.high, a.low,
  b.percentile
from    MinimaAndMaxima a
  join  MinimaAndMaxima b on (a.percentile -1 = b.percentile) or (a.percentile = 10 and b.percentile = 10)
--where b.percentile = 5

Это должно вычислить правильные значения медианы и процентиля, когда у вас четное количество элементов данных. Опять же, откомментируйте последнее предложение where, если вам нужна только медиана, а не все перцентильное распределение.

21
ответ дан 23 November 2019 в 04:30
поделиться
Другие вопросы по тегам:

Похожие вопросы: