문제

에 따라 MSDN, 중앙값을 사용할 수 없으로 집계에서 기능 Transact-SQL.그러나 내가 좋아하는 것이 가능 여부를 만드는 이 기능을 사용하여( Create Aggregate 기능,사용자 정의 기능,또는 다른 방법).

무엇을 할 수있는 가장 좋은 방법이 될 것이(가능하면)를 이용한 계산의 평균 값이(가정 숫자 데이터 형식)에서 집계 쿼리?

도움이 되었습니까?

해결책

성능이 크게 다양한 방법으로는 여러 가지가 있습니다. 다음은 특히 잘 최적화 된 솔루션입니다 중앙값, ROW_NUMBERS 및 성과. 이는 실행 중에 생성 된 실제 I/O와 관련하여 특히 최적의 솔루션입니다. 다른 솔루션보다 비용이 많이 들지만 실제로는 훨씬 빠릅니다.

이 페이지에는 다른 솔루션 및 성능 테스트 세부 사항에 대한 토론도 포함되어 있습니다. 중앙 열의 값이 동일한 여러 행이있는 경우, Disambiguator로 고유 한 열을 사용하십시오.

모든 데이터베이스 성능 시나리오와 마찬가지로 항상 실제 하드웨어에 대한 실제 데이터로 솔루션을 테스트하십시오. 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;

다른 팁

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

SQL Server 2012에서는 사용해야합니다 백분위 수 _cont:

SELECT SalesOrderID, OrderQty,
    PERCENTILE_CONT(0.5) 
        WITHIN GROUP (ORDER BY OrderQty)
        OVER (PARTITION BY SalesOrderID) AS MedianCont
FROM Sales.SalesOrderDetail
WHERE SalesOrderID IN (43670, 43669, 43667, 43663)
ORDER BY SalesOrderID DESC

또한보십시오 : http://blog.sqlauthority.com/2011/11/20/sql-server-introduction-to-percentile_cont-analytic-functions-intructed-in-sql-server-2012/

나의 원래 빠른 답변은 다음과 같습니다.

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%가 위치 종속 통계를 계산할 때 피할 수없는 데이터를 정렬합니다.

아래의 의견에서 Robert Ševčík-Robajz의 훌륭한 제안을 따르기 위해 답을 수정했습니다.

;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

짝수의 데이터 항목이있을 때 올바른 중앙값 및 백분위 수 값을 계산해야합니다. 다시 말하지만, 전체 백분위 수 분포가 아닌 중앙값 만 원한다면 최종 위치 조항.

더 나은 :

SELECT @Median = AVG(1.0 * val)
FROM
(
    SELECT o.val, rn = ROW_NUMBER() OVER (ORDER BY o.val), c.c
    FROM dbo.EvenRows AS o
    CROSS JOIN (SELECT c = COUNT(*) FROM dbo.EvenRows) AS c
) AS x
WHERE rn IN ((c + 1)/2, (c + 2)/2);

마스터 자신으로부터 Itzik Ben-Gan!

MS SQL Server 2012 (및 이후)에는 정렬 된 값에 대해 특정 백분위 수를 계산하는 백분위 수 _DISC 기능이 있습니다. 백분위 수 _DISC (0.5)는 중앙값을 계산합니다. https://msdn.microsoft.com/en-us/library/hh231327.aspx

간단하고 빠르며 정확합니다

SELECT x.Amount 
FROM   (SELECT amount, 
               Count(1) OVER (partition BY 'A')        AS TotalRows, 
               Row_number() OVER (ORDER BY Amount ASC) AS AmountOrder 
        FROM   facttransaction ft) x 
WHERE  x.AmountOrder = Round(x.TotalRows / 2.0, 0)  

SQL Server에서 집계 기능을 사용하려면 이것이 수행하는 방법입니다. 이렇게하면 깨끗한 쿼리를 작성할 수 있다는 이점이 있습니다. 이 프로세스는 백분위 수 값을 상당히 쉽게 계산하도록 조정할 수 있습니다.

새로운 Visual Studio 프로젝트를 작성하고 대상 프레임 워크를 .NET 3.5로 설정합니다 (SQL 2008의 경우 SQL 2012에서 다를 수 있음). 그런 다음 클래스 파일을 작성하고 다음 코드를 넣거나 C#에 해당합니다.

Imports Microsoft.SqlServer.Server
Imports System.Data.SqlTypes
Imports System.IO

<Serializable>
<SqlUserDefinedAggregate(Format.UserDefined, IsInvariantToNulls:=True, IsInvariantToDuplicates:=False, _
  IsInvariantToOrder:=True, MaxByteSize:=-1, IsNullIfEmpty:=True)>
Public Class Median
  Implements IBinarySerialize
  Private _items As List(Of Decimal)

  Public Sub Init()
    _items = New List(Of Decimal)()
  End Sub

  Public Sub Accumulate(value As SqlDecimal)
    If Not value.IsNull Then
      _items.Add(value.Value)
    End If
  End Sub

  Public Sub Merge(other As Median)
    If other._items IsNot Nothing Then
      _items.AddRange(other._items)
    End If
  End Sub

  Public Function Terminate() As SqlDecimal
    If _items.Count <> 0 Then
      Dim result As Decimal
      _items = _items.OrderBy(Function(i) i).ToList()
      If _items.Count Mod 2 = 0 Then
        result = ((_items((_items.Count / 2) - 1)) + (_items(_items.Count / 2))) / 2@
      Else
        result = _items((_items.Count - 1) / 2)
      End If

      Return New SqlDecimal(result)
    Else
      Return New SqlDecimal()
    End If
  End Function

  Public Sub Read(r As BinaryReader) Implements IBinarySerialize.Read
    'deserialize it from a string
    Dim list = r.ReadString()
    _items = New List(Of Decimal)

    For Each value In list.Split(","c)
      Dim number As Decimal
      If Decimal.TryParse(value, number) Then
        _items.Add(number)
      End If
    Next

  End Sub

  Public Sub Write(w As BinaryWriter) Implements IBinarySerialize.Write
    'serialize the list to a string
    Dim list = ""

    For Each item In _items
      If list <> "" Then
        list += ","
      End If      
      list += item.ToString()
    Next
    w.Write(list)
  End Sub
End Class

그런 다음 컴파일하고 DLL 및 PDB 파일을 SQL Server 시스템에 복사하고 SQL Server에서 다음 명령을 실행하십시오.

CREATE ASSEMBLY CustomAggregate FROM '{path to your DLL}'
WITH PERMISSION_SET=SAFE;
GO

CREATE AGGREGATE Median(@value decimal(9, 3))
RETURNS decimal(9, 3) 
EXTERNAL NAME [CustomAggregate].[{namespace of your DLL}.Median];
GO

그런 다음 쿼리를 작성하여 다음과 같이 중앙값을 계산할 수 있습니다.

중앙값에 대한 세트 기반 솔루션을 찾는 동안 방금이 페이지를 발견했습니다. 여기서 몇 가지 솔루션을 살펴본 후 다음을 생각해 냈습니다. 희망은 도움/일입니다.

DECLARE @test TABLE(
    i int identity(1,1),
    id int,
    score float
)

INSERT INTO @test (id,score) VALUES (1,10)
INSERT INTO @test (id,score) VALUES (1,11)
INSERT INTO @test (id,score) VALUES (1,15)
INSERT INTO @test (id,score) VALUES (1,19)
INSERT INTO @test (id,score) VALUES (1,20)

INSERT INTO @test (id,score) VALUES (2,20)
INSERT INTO @test (id,score) VALUES (2,21)
INSERT INTO @test (id,score) VALUES (2,25)
INSERT INTO @test (id,score) VALUES (2,29)
INSERT INTO @test (id,score) VALUES (2,30)

INSERT INTO @test (id,score) VALUES (3,20)
INSERT INTO @test (id,score) VALUES (3,21)
INSERT INTO @test (id,score) VALUES (3,25)
INSERT INTO @test (id,score) VALUES (3,29)

DECLARE @counts TABLE(
    id int,
    cnt int
)

INSERT INTO @counts (
    id,
    cnt
)
SELECT
    id,
    COUNT(*)
FROM
    @test
GROUP BY
    id

SELECT
    drv.id,
    drv.start,
    AVG(t.score)
FROM
    (
        SELECT
            MIN(t.i)-1 AS start,
            t.id
        FROM
            @test t
        GROUP BY
            t.id
    ) drv
    INNER JOIN @test t ON drv.id = t.id
    INNER JOIN @counts c ON t.id = c.id
WHERE
    t.i = ((c.cnt+1)/2)+drv.start
    OR (
        t.i = (((c.cnt+1)%2) * ((c.cnt+2)/2))+drv.start
        AND ((c.cnt+1)%2) * ((c.cnt+2)/2) <> 0
    )
GROUP BY
    drv.id,
    drv.start

다음 쿼리는 다음을 반환합니다 중앙값 한 열의 값 목록에서. 집계 함수와 함께 사용될 수는 없지만 내부 선택의 WARE 절과 함께 하위 쿼리로 사용할 수 있습니다.

SQL Server 2005+ :

SELECT TOP 1 value from
(
    SELECT TOP 50 PERCENT value 
    FROM table_name 
    ORDER BY  value
)for_median
ORDER BY value DESC

지만 저스틴 그랜트의 솔루션이 나타납 솔리드 나가는 것을 발견할 때의 번호가 중복되는 값에서 특정 파티션 키 행 번호 ASC 중복된 값은 끝까지 순서 그래서 그들은하지 않을 제대로 정렬한다.

여기에서 조각을 내 결과:

KEY VALUE ROWA ROWD  

13  2     22   182
13  1     6    183
13  1     7    184
13  1     8    185
13  1     9    186
13  1     10   187
13  1     11   188
13  1     12   189
13  0     1    190
13  0     2    191
13  0     3    192
13  0     4    193
13  0     5    194

내가 사용하는 저스틴의 코드에 대한 기준으로 이 솔루션입니다.지 않지만으로 효율적 사용의 파생된 여러 테이블은 그것을 해결하는 행 순서 문제가 발생합니다.어떤 개선을 환영받을 것으로 나가지 않는 경험 T-SQL.

SELECT PKEY, cast(AVG(VALUE)as decimal(5,2)) as MEDIANVALUE
FROM
(
  SELECT PKEY,VALUE,ROWA,ROWD,
  'FLAG' = (CASE WHEN ROWA IN (ROWD,ROWD-1,ROWD+1) THEN 1 ELSE 0 END)
  FROM
  (
    SELECT
    PKEY,
    cast(VALUE as decimal(5,2)) as VALUE,
    ROWA,
    ROW_NUMBER() OVER (PARTITION BY PKEY ORDER BY ROWA DESC) as ROWD 

    FROM
    (
      SELECT
      PKEY, 
      VALUE,
      ROW_NUMBER() OVER (PARTITION BY PKEY ORDER BY VALUE ASC,PKEY ASC ) as ROWA 
      FROM [MTEST]
    )T1
  )T2
)T3
WHERE FLAG = '1'
GROUP BY PKEY
ORDER BY PKEY

Justin의 위의 예는 매우 좋습니다. 그러나 그 주요 핵심 요구는 매우 명확하게 언급되어야합니다. 나는 열쇠가없는 야생에서 그 코드를 보았고 결과는 나쁘다.

백분위 수에 대해 얻는 불만은 데이터 세트에서 실제 값을 제공하지 않는다는 것입니다. "중간"에 도달하려면 DataSet의 실제 값 인 Estile_Disc를 사용하십시오.

SELECT SalesOrderID, OrderQty,
    PERCENTILE_DISC(0.5) 
        WITHIN GROUP (ORDER BY OrderQty)
        OVER (PARTITION BY SalesOrderID) AS MedianCont
FROM Sales.SalesOrderDetail
WHERE SalesOrderID IN (43670, 43669, 43667, 43663)
ORDER BY SalesOrderID DESC

UDF에서 다음을 작성하십시오.

 Select Top 1 medianSortColumn from Table T
  Where (Select Count(*) from Table
         Where MedianSortColumn <
           (Select Count(*) From Table) / 2)
  Order By medianSortColumn

SQL의 중간 계산에 대한 다른 솔루션을 참조하십시오. "MySQL로 중앙값을 계산하는 간단한 방법"(솔루션은 대부분 공급 업체 독립적입니다).

연속 변수/측정 'col1'의 경우 '표 1'

select col1  
from
    (select top 50 percent col1, 
    ROW_NUMBER() OVER(ORDER BY col1 ASC) AS Rowa,
    ROW_NUMBER() OVER(ORDER BY col1 DESC) AS Rowd
    from table1 ) tmp
where tmp.Rowa = tmp.Rowd

나는 혼자서 해결책을 제시하고 싶었지만 내 뇌가 넘어져 넘어졌다. 나 생각한다 작동하지만 아침에 설명 해달라고 요청하지 마십시오. :피

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)
--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]

이것은 SQL 2000과 함께 작동합니다.

DECLARE @testTable TABLE 
( 
    VALUE   INT
)
--INSERT INTO @testTable -- Even Test
--SELECT 3 UNION ALL
--SELECT 5 UNION ALL
--SELECT 7 UNION ALL
--SELECT 12 UNION ALL
--SELECT 13 UNION ALL
--SELECT 14 UNION ALL
--SELECT 21 UNION ALL
--SELECT 23 UNION ALL
--SELECT 23 UNION ALL
--SELECT 23 UNION ALL
--SELECT 23 UNION ALL
--SELECT 29 UNION ALL
--SELECT 40 UNION ALL
--SELECT 56

--
--INSERT INTO @testTable -- Odd Test
--SELECT 3 UNION ALL
--SELECT 5 UNION ALL
--SELECT 7 UNION ALL
--SELECT 12 UNION ALL
--SELECT 13 UNION ALL
--SELECT 14 UNION ALL
--SELECT 21 UNION ALL
--SELECT 23 UNION ALL
--SELECT 23 UNION ALL
--SELECT 23 UNION ALL
--SELECT 23 UNION ALL
--SELECT 29 UNION ALL
--SELECT 39 UNION ALL
--SELECT 40 UNION ALL
--SELECT 56


DECLARE @RowAsc TABLE
(
    ID      INT IDENTITY,
    Amount  INT
)

INSERT INTO @RowAsc
SELECT  VALUE 
FROM    @testTable 
ORDER BY VALUE ASC

SELECT  AVG(amount)
FROM @RowAsc ra
WHERE ra.id IN
(
    SELECT  ID 
    FROM    @RowAsc
    WHERE   ra.id -
    (
        SELECT  MAX(id) / 2.0 
        FROM    @RowAsc
    ) BETWEEN 0 AND 1

)

기본 사항을 배우고있는 나와 같은 초보자의 경우, 개인적 으로이 예제를 따라 가기가 더 쉽다는 것을 알게됩니다. 왜냐하면 무슨 일이 일어나고 있는지, 그리고 중간 값이 어디에서 오는지 정확히 이해하는 것이 더 쉽기 때문입니다.

select
 ( max(a.[Value1]) + min(a.[Value1]) ) / 2 as [Median Value1]
,( max(a.[Value2]) + min(a.[Value2]) ) / 2 as [Median Value2]

from (select
    datediff(dd,startdate,enddate) as [Value1]
    ,xxxxxxxxxxxxxx as [Value2]
     from dbo.table1
     )a

위의 일부 코드를 절대적으로 경외하십시오 !!!

이것은 내가 생각해 낼 수있는 것만 큼 간단한 대답입니다. 내 데이터와 잘 작동했습니다. 특정 값을 제외하려면 내부 선택에 WHERE 절을 추가하십시오.

SELECT TOP 1 
    ValueField AS MedianValue
FROM
    (SELECT TOP(SELECT COUNT(1)/2 FROM tTABLE)
        ValueField
    FROM 
        tTABLE
    ORDER BY 
        ValueField) A
ORDER BY
    ValueField DESC

다음과 같은 솔루션을 작동에 이러한 가정:

  • 중복된 값이 없
  • No Null

코드:

IF OBJECT_ID('dbo.R', 'U') IS NOT NULL
  DROP TABLE dbo.R

CREATE TABLE R (
    A FLOAT NOT NULL);

INSERT INTO R VALUES (1);
INSERT INTO R VALUES (2);
INSERT INTO R VALUES (3);
INSERT INTO R VALUES (4);
INSERT INTO R VALUES (5);
INSERT INTO R VALUES (6);

-- Returns Median(R)
select SUM(A) / CAST(COUNT(A) AS FLOAT)
from R R1 
where ((select count(A) from R R2 where R1.A > R2.A) = 
      (select count(A) from R R2 where R1.A < R2.A)) OR
      ((select count(A) from R R2 where R1.A > R2.A) + 1 = 
      (select count(A) from R R2 where R1.A < R2.A)) OR
      ((select count(A) from R R2 where R1.A > R2.A) = 
      (select count(A) from R R2 where R1.A < R2.A) + 1) ; 
DECLARE @Obs int
DECLARE @RowAsc table
(
ID      INT IDENTITY,
Observation  FLOAT
)
INSERT INTO @RowAsc
SELECT Observations FROM MyTable
ORDER BY 1 
SELECT @Obs=COUNT(*)/2 FROM @RowAsc
SELECT Observation AS Median FROM @RowAsc WHERE ID=@Obs

몇 가지 대안으로 시도하지만 데이터 레코드가 반복적으로 값을 얻었 기 때문에 Row_Number 버전은 선택이 아닌 것 같습니다. 그래서 여기에 내가 사용한 쿼리 (ntile이있는 버전) :

SELECT distinct
   CustomerId,
   (
       MAX(CASE WHEN Percent50_Asc=1 THEN TotalDue END) OVER (PARTITION BY CustomerId)  +
       MIN(CASE WHEN Percent50_desc=1 THEN TotalDue END) OVER (PARTITION BY CustomerId) 
   )/2 MEDIAN
FROM
(
   SELECT
      CustomerId,
      TotalDue,
     NTILE(2) OVER (
         PARTITION BY CustomerId
         ORDER BY TotalDue ASC) AS Percent50_Asc,
     NTILE(2) OVER (
         PARTITION BY CustomerId
         ORDER BY TotalDue DESC) AS Percent50_desc
   FROM Sales.SalesOrderHeader SOH
) x
ORDER BY CustomerId;

위의 Jeff Atwood의 답변을 바탕으로 그룹에 의해 그룹과 각 그룹의 중앙값을 얻는 상관 관계가 있습니다.

SELECT TestID, 
(
 (SELECT MAX(Score) FROM
   (SELECT TOP 50 PERCENT Score FROM Posts WHERE TestID = Posts_parent.TestID ORDER BY Score) AS BottomHalf)
 +
 (SELECT MIN(Score) FROM
   (SELECT TOP 50 PERCENT Score FROM Posts WHERE TestID = Posts_parent.TestID ORDER BY Score DESC) AS TopHalf)
) / 2 AS MedianScore,
AVG(Score) AS AvgScore, MIN(Score) AS MinScore, MAX(Score) AS MaxScore
FROM Posts_parent
GROUP BY Posts_parent.TestID

종종 전체 테이블뿐만 아니라 일부 ID에 대한 집계에 대해 중앙값을 계산해야 할 수도 있습니다. 다시 말해, 각 ID에 많은 레코드가있는 표에서 각 ID에 대한 중간 값을 계산하십시오. (@gdoron이 편집 한 솔루션을 기반으로 : 좋은 성능과 많은 SQL에서 작동 함)

SELECT our_id, AVG(1.0 * our_val) as Median
FROM
( SELECT our_id, our_val, 
  COUNT(*) OVER (PARTITION BY our_id) AS cnt,
  ROW_NUMBER() OVER (PARTITION BY our_id ORDER BY our_val) AS rnk
  FROM our_table
) AS x
WHERE rnk IN ((cnt + 1)/2, (cnt + 2)/2) GROUP BY our_id;

도움이되기를 바랍니다.

질문을 위해 Jeff Atwood는 이미 간단하고 효과적인 솔루션을 제공했습니다. 그러나 중앙값을 계산하기위한 대체 접근 방식을 찾고 있다면 SQL 코드 아래에 도움이됩니다.

create table employees(salary int);

insert into employees values(8); insert into employees values(23); insert into employees values(45); insert into employees values(123); insert into employees values(93); insert into employees values(2342); insert into employees values(2238);

select * from employees;

declare @odd_even int; declare @cnt int; declare @middle_no int;


set @cnt=(select count(*) from employees); set @middle_no=(@cnt/2)+1; select @odd_even=case when (@cnt%2=0) THEN -1 ELse 0 END ;


 select AVG(tbl.salary) from  (select  salary,ROW_NUMBER() over (order by salary) as rno from employees group by salary) tbl  where tbl.rno=@middle_no or tbl.rno=@middle_no+@odd_even;

MySQL에서 중간 값을 계산하려는 경우 Github 링크 유용 할 것입니다.

이것은 내가 생각할 수있는 중앙값을 찾는 데 가장 최적의 솔루션입니다. 예제의 이름은 Justin 예제를 기반으로합니다. 테이블 판매에 대한 색인을 확인하십시오. SalesOrderHeader는 INDEX COLUMS COUNTERID 및 TotalDue와 함께 해당 순서대로 존재합니다.

SELECT
 sohCount.CustomerId,
 AVG(sohMid.TotalDue) as TotalDueMedian
FROM 
(SELECT 
  soh.CustomerId,
  COUNT(*) as NumberOfRows
FROM 
  Sales.SalesOrderHeader soh 
GROUP BY soh.CustomerId) As sohCount
CROSS APPLY 
    (Select 
       soh.TotalDue
    FROM 
    Sales.SalesOrderHeader soh 
    WHERE soh.CustomerId = sohCount.CustomerId 
    ORDER BY soh.TotalDue
    OFFSET sohCount.NumberOfRows / 2 - ((sohCount.NumberOfRows + 1) % 2) ROWS 
    FETCH NEXT 1 + ((sohCount.NumberOfRows + 1) % 2) ROWS ONLY
    ) As sohMid
GROUP BY sohCount.CustomerId

업데이트

어떤 방법이 최상의 성능을 가지고 있는지 확실하지 않았으므로 Justin Grants와 Jeff Atwoods의 방법을 비교하여 한 번의 배치로 세 가지 방법을 기반으로 쿼리를 실행하여 각 쿼리의 배치 비용은 다음과 같습니다.

색인없이 :

  • 광산 30%
  • 저스틴은 13%를 부여합니다
  • Jeff Atwoods 58%

그리고 색인으로

  • 광산 3%.
  • 저스틴은 10%를 부여합니다
  • Jeff Atwoods 87%

약 14,000 행에서 더 많은 데이터를 2 배까지 512로 만들어 인덱스가 얼마나 잘 표시되는지 확인하려고 노력했습니다. 이는 결국 약 7,2 백만 행을 의미합니다. 참고 단일 사본을 할 때마다 고유 한 CustomeID 필드를 확인 했으므로 CustomerID의 고유 인스턴스와 비교하여 행의 비율이 일정하게 유지되었습니다. 내가이 작업을 수행하는 동안 나는 나중에 인덱스를 재건 한 곳에서 실행을 실행했고, 결과가 다음 값에 대한 데이터와 함께 약 128의 계수로 안정화되는 것을 발견했습니다.

  • 광산 3%.
  • 저스틴은 5%를 부여합니다
  • Jeff Atwoods 92%

나는 행의 수를 스케일링하지만 고유 한 고객을 일정하게 유지함으로써 성능이 어떻게 영향을받을 수 있는지 궁금했기 때문에 내가 한 곳에서 새로운 테스트를 설정합니다. 이제 안정화 대신, 배치 비용 비율은 계속 분기되었으며, 그러한 고유 한 ID 당 약 10000 줄의 평균 당 고객 ID 당 약 20 행 대신에도 계속 분기되었습니다. 여기서 숫자 :

  • 광산 4%
  • Justins 60%
  • Jeffs 35%

결과를 비교하여 각 방법을 올바르게 구현했는지 확인했습니다. 내 결론은 내가 사용한 방법이 일반적으로 인덱스가 존재하는 한 더 빠릅니다. 또한이 방법은이 기사 에서이 특정 문제에 권장되는 것임을 알았습니다. https://www.microsoftpressstore.com/articles/article.aspx?p=2314819&seqnum=5

이 쿼리에 대한 후속 통화의 성능을 더욱 향상시키는 방법은 보조 테이블에서 카운트 정보를 지속하는 것입니다. CustomerID에 의존하는 SalesOrderheader 행의 수에 관한 정보를 보유하고있는 트리거를 사용하여 유지 관리 할 수도 있습니다. 물론 중앙값도 간단하게 저장할 수 있습니다.

대규모 데이터 세트의 경우이 요점을 시도 할 수 있습니다.

https://gist.github.com/chrisknoll/1b38761ce8c5016ec5b2

세트에서 찾을 수있는 뚜렷한 값 (예 : 연령, 출생 연도 등)을 집계하여 작동하며 SQL 창 함수를 사용하여 쿼리에 지정한 백분위 수 위치를 찾습니다.

중간 발견

이것은 속성의 중앙값을 찾는 가장 간단한 방법입니다.

Select round(S.salary,4) median from employee S where (select count(salary) from station where salary < S.salary ) = (select count(salary) from station where salary > S.salary)
라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top