Function to Calculate Median in SQL Server

前端 未结 30 3151
孤独总比滥情好
孤独总比滥情好 2020-11-22 04:03

According to MSDN, Median is not available as an aggregate function in Transact-SQL. However, I would like to find out whether it is possible to create this functionality (u

30条回答
  •  滥情空心
    2020-11-22 04:22

    If you want to use the Create Aggregate function in SQL Server, this is how to do it. Doing it this way has the benefit of being able to write clean queries. Note this this process could be adapted to calculate a Percentile value fairly easily.

    Create a new Visual Studio project and set the target framework to .NET 3.5 (this is for SQL 2008, it may be different in SQL 2012). Then create a class file and put in the following code, or c# equivalent:

    Imports Microsoft.SqlServer.Server
    Imports System.Data.SqlTypes
    Imports System.IO
    
    
    
    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
    

    Then compile it and copy the DLL and PDB file to your SQL Server machine and run the following command in 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
    

    You can then write a query to calculate the median like this: SELECT dbo.Median(Field) FROM Table

提交回复
热议问题