SQL Server 2012では、列の累積分布を計算するために使用されるCUME_DIST()とPERCENT_RANKが導入されたようです。これを実現するための同等の機能がSQLServer2008にありますか?
4 に答える
SQLでは決して言わないでください。
ステートメント:
select percent_rank() over (partition by <x> order by <y>)
基本的に次と同等です。
select row_number() over (partition by <x> order by <y>) / count(*) over (partition by <x>)
基本的に、データに重複がない場合に機能することを意味します。重複があっても十分に近いはずです。
「本当の」答えは、次と同等であるということです。
select row_number() over (partition by <x> order by <y>) / count(distinct <y>) over (partition by <x>)
しかし、関数として count(distinct) はありません。そして、本当に必要でない限り、2008 年に表現するのは苦痛です。
関数 cume_dist() は、累積合計が必要なため難しく、そのためには自己結合が必要です。重複がないと仮定した近似:
with t as (select <x>, <y>,
row_number() over (partition by <x> order by <y>) as seqnum
from <table>
)
select t.*, sumy*1.0 / sum(sumy) over (partition by <x>)
from (select t.*, sum(tprev.y) as sumy
from t left outer join
t tprev
on t.x = tprev.x and t.seqnum >= tprev.seqnum
) t
2012 年以前に同等の関数は存在しませんでしたが、少なくとも 32767 行未満のデータ セットの場合、再帰 CTE を使用する回避策が考えられます。ここでは、サイコロのペアが 30 回投げられます。
SET NOCOUNT ON;
DECLARE @t TABLE(i INT);
DECLARE @i INT=0;
WHILE @i<30 BEGIN
INSERT INTO @t VALUES (CAST(RAND()*6 AS INT)+1 + CAST(RAND()*6 AS INT)+1);
SET @i+=1;
END
DECLARE @tc INT; SELECT @tc=COUNT(*) FROM @t;
WITH a AS (
SELECT *
, d=CAST(COUNT(1)OVER(PARTITION BY i ORDER BY i) AS DECIMAL(5,2)) / @tc
, r=ROW_NUMBER()OVER(ORDER BY i)
, pr=CAST((RANK()OVER(ORDER BY i)-1)AS DECIMAL(5,2)) / (@tc - 1)
FROM @t
)
, rcte (i, d, r, cd, pr) AS (
SELECT i, d, r, d, pr
FROM a
WHERE r=1
UNION ALL
SELECT a.i, a.d, a.r
, CASE WHEN rcte.i<>a.i THEN CAST(rcte.cd+a.d AS DECIMAL(5,2)) ELSE rcte.cd END
, a.pr
FROM a
INNER JOIN rcte ON rcte.r + 1 = a.r
)
SELECT i,cd,pr FROM rcte
OPTION (MAXRECURSION 32767)
結果:
i cd pr
----------- --------------------------------------- ---------------------------------------
2 0.0333333333333 0.0000000000000
3 0.0700000000000 0.0344827586206
4 0.2400000000000 0.0689655172413
4 0.2400000000000 0.0689655172413
4 0.2400000000000 0.0689655172413
4 0.2400000000000 0.0689655172413
4 0.2400000000000 0.0689655172413
5 0.3100000000000 0.2413793103448
5 0.3100000000000 0.2413793103448
6 0.3800000000000 0.3103448275862
6 0.3800000000000 0.3103448275862
7 0.5100000000000 0.3793103448275
7 0.5100000000000 0.3793103448275
7 0.5100000000000 0.3793103448275
7 0.5100000000000 0.3793103448275
8 0.6100000000000 0.5172413793103
8 0.6100000000000 0.5172413793103
8 0.6100000000000 0.5172413793103
9 0.8400000000000 0.6206896551724
9 0.8400000000000 0.6206896551724
9 0.8400000000000 0.6206896551724
9 0.8400000000000 0.6206896551724
9 0.8400000000000 0.6206896551724
9 0.8400000000000 0.6206896551724
9 0.8400000000000 0.6206896551724
10 0.8700000000000 0.8620689655172
11 0.9700000000000 0.8965517241379
11 0.9700000000000 0.8965517241379
11 0.9700000000000 0.8965517241379
12 1.0000000000000 1.0000000000000
以下は、上記の CTE に相当する SQL 2012 です。
SELECT *
, cd=CUME_DIST()OVER(ORDER BY i)
, pr=PERCENT_RANK()OVER(ORDER BY i)
FROM @t;
これは非常に近いです。最初のサンプルデータ:
USE tempdb;
GO
CREATE TABLE dbo.DartScores
(
TournamentID INT,
PlayerID INT,
Score INT
);
INSERT dbo.DartScores VALUES
(1, 1, 320),
(1, 2, 340),
(1, 3, 310),
(1, 4, 370),
(2, 1, 310),
(2, 2, 280),
(2, 3, 370),
(2, 4, 370);
さて、クエリの 2012 バージョン:
SELECT TournamentID, PlayerID, Score,
pr = PERCENT_RANK() OVER (PARTITION BY TournamentID ORDER BY Score),
cd = CUME_DIST() OVER (PARTITION BY TournamentID ORDER BY Score)
FROM dbo.DartScores
ORDER BY TournamentID, pr;
次の結果が生成されます。
TournamentID PlayerID Score pr cd
1 3 310 0 0.25
1 1 320 0.333333333333333 0.5
1 2 340 0.666666666666667 0.75
1 4 370 1 1
2 2 280 0 0.25
2 1 310 0.333333333333333 0.5
2 3 370 0.666666666666667 1
2 4 370 0.666666666666667 1
2005 年の同等物は非常に近いですが、タイをうまく処理できません。申し訳ありませんが、今夜はガス欠です。それ以外の場合は、原因を突き止めたいと思います。Itzik の新しい High Performance window function bookから学んだことと同じくらい知っています。
;WITH cte AS
(
SELECT TournamentID, PlayerID, Score,
rk = RANK() OVER (PARTITION BY TournamentID ORDER BY Score),
rn = COUNT(*) OVER (PARTITION BY TournamentID)
FROM dbo.DartScores
)
SELECT TournamentID, PlayerID, Score,
pr = 1e0*(rk-1)/(rn-1),
cd = 1e0*(SELECT COALESCE(MIN(cte2.rk)-1, cte.rn)
FROM cte AS cte2 WHERE cte2.rk > cte.rk) / rn
FROM cte;
次の結果が生成されます (同順位の cume_dist 値がわずかに変化することに注意してください)。
TournamentID PlayerID Score pr cd
1 3 310 0 0.25
1 1 320 0.333333333333333 0.5
1 2 340 0.666666666666667 0.75
1 4 370 1 1
2 2 280 0 0.25
2 1 310 0.333333333333333 0.5
2 3 370 0.666666666666667 0.75
2 4 370 0.666666666666667 0.75
クリーンアップすることを忘れないでください:
DROP TABLE dbo.DartScores;
はい、少なくとも percent_rank() 部分については、簡単な解決策があります。使用できます
(rank() over (partition by <x> order by <y>)-1)/(count(*) over (partition by <x>)-1)
これにより、次の場合とまったく同じ結果が得られます
percent_rank() over (partition by <x> order by <y>)
rank() 関数は、SQL Server 2008 に既に存在する数少ない分析関数の 1 つです。