2011-10-07 53 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

相關問題