2011-10-07 55 views
1

我有一个简单的中值计算功能:在GROUP BY中使用自定义聚合函数?

IF OBJECT_ID(N'COMPUTEMEDIAN', N'FN') IS NOT NULL 
    DROP FUNCTION dbo.COMPUTEMEDIAN; 
GO 
CREATE FUNCTION dbo.COMPUTEMEDIAN(@VALUES NVARCHAR(MAX)) 
RETURNS DECIMAL 
WITH EXECUTE AS CALLER 
AS 
BEGIN 
    DECLARE @SQL NVARCHAR(MAX) 
    DECLARE @MEDIAN DECIMAL 
    SET @MEDIAN = 0.0; 

    DECLARE @MEDIAN_TEMP TABLE (RawValue DECIMAL); 

    -- This is the Killer! 
    INSERT INTO @MEDIAN_TEMP 
    SELECT s FROM master.dbo.Split(',', @VALUES) OPTION(MAXRECURSION 0) 

    SELECT @MEDIAN = 
    (
    (SELECT MAX(RawValue) FROM 
     (SELECT TOP 50 PERCENT RawValue FROM @MEDIAN_TEMP ORDER BY RawValue) AS BottomHalf) 
    + 
    (SELECT MIN(RawValue) FROM 
     (SELECT TOP 50 PERCENT RawValue FROM @MEDIAN_TEMP ORDER BY RawValue DESC) AS TopHalf) 
    )/2 

    --PRINT @SQL 
    RETURN @MEDIAN; 
END; 
GO 

然而,我的表是以下形式:

CREATE TABLE #TEMP (GroupName VARCHAR(MAX), Value DECIMAL) 
INSERT INTO #TEMP VALUES ('A', 1.0) 
INSERT INTO #TEMP VALUES ('A', 2.0) 
INSERT INTO #TEMP VALUES ('A', 3.0) 
INSERT INTO #TEMP VALUES ('A', 4.0) 
INSERT INTO #TEMP VALUES ('B', 10.0) 
INSERT INTO #TEMP VALUES ('B', 11.0) 
INSERT INTO #TEMP VALUES ('B', 12.0) 

SELECT * FROM #TEMP 

DROP TABLE #TEMP 

什么是使用GROUP BY来调用这个表MEDIAN功能的最佳途径在id列?所以,我期待这样的事情:

SELECT id, COMPUTEMEDIAN(Values) 
FROM #TEMP 
GROUP BY id 

我目前的做法包括使用XMLPATHGROUP BY操作导致成一个大的字符串,然后将它传递给函数的所有值组合,但是这涉及到字符串分割操作对于大字符串,这只会减慢一切。有什么建议么?

回答

1

编辑:我可以证实这工作得非常非常好对大型数据库(30,000值)

嗯......对面this来到所以下面作品完全正常,但不知道它可能是多么昂贵:

SELECT 
    GroupName, 
    AVG(Value) 
FROM 
(
    SELECT 
     GroupName, 
     cast(Value as decimal(5,2)) Value, 
     ROW_NUMBER() OVER (
     PARTITION BY GroupName 
     ORDER BY Value ASC) AS RowAsc, 
     ROW_NUMBER() OVER (
     PARTITION BY GroupName 
     ORDER BY Value DESC) AS RowDesc 
    FROM #TEMP SOH 
) x 
WHERE 
    RowAsc IN (RowDesc, RowDesc - 1, RowDesc + 1) 
GROUP BY GroupName 
ORDER BY GroupName; 
1

由于您使用的是SQL Server 2008,因此我建议将集合函数作为CLR函数编写。

http://msdn.microsoft.com/en-us/library/91e6taax(v=vs.80).aspx

同时,人们也都问过这个问题。也许,他们的回答将是有益的

Function to Calculate Median in Sql Server

+0

是的。我看到了,但并不需要特殊的权限?我将在高度受限的环境中执行此脚本,因此我不确定CLR是否适合我。 – Legend

+0

正确,需要特殊权限才能加载CLR函数: “需要CREATE AGGREGATE权限以及EXTERNAL NAME子句中指定的程序集的REFERENCES权限。” http://msdn.microsoft.com/en-us/library/ms182741.aspx –

+0

是的。这是我的担忧。但为你的时间+1。谢谢。 – Legend

1

无需使用用户定义的功能!下面是我该怎么做:

CREATE TABLE #TEMP (id VARCHAR(MAX), Value DECIMAL) 

INSERT INTO #TEMP VALUES('A', 1.0) 

INSERT INTO #TEMP VALUES('A', 2.0) 
INSERT INTO #TEMP VALUES('A', 3.0) 
INSERT INTO #TEMP VALUES('A', 4.0) 
INSERT INTO #TEMP VALUES('B', 10.0) 
INSERT INTO #TEMP VALUES('B', 11.0) 
INSERT INTO #TEMP VALUES('B', 12.0) 

SELECT 
    (SELECT TOP 1 Value 
     FROM (SELECT TOP(calcs.medianIndex) Value 
       FROM #temp 
       WHERE #temp.ID = calcs.ID ORDER BY Value ASC) AS subSet 
     ORDER BY subSet.Value DESC), ID 
FROM 
(SELECT 
    CASE WHEN count(*) % 2 = 1 THEN count(*)/2 + 1 
     ELSE count(*)/2 
    END AS medianIndex, 
ID 
FROM #TEMP 
GROUP BY ID) AS calcs 

DROP TABLE #TEMP 

可能希望仔细检查有偶数个记录时的行为。

编辑:在您检查您的中位数函数的工作后,我意识到我的答案基本上只是将您的工作移出函数并进入常规查询。那么......为什么你的中位数计算必须在用户定义的函数内?这似乎很多 这种方式更困难。

+0

+1其实,你是对的。我认为它会更优雅,具有聚合功能,但显然我可以在没有这个功能的情况下完成。但是,尽管如此,感谢你的努力。 – Legend

相关问题