はじめに
2009年7月に正式リリースされたPostgreSQL 8.4で、分析関数(window関数)がサポートされました。本連載では、分析関数の衝撃シリーズをPostgreSQL用にアレンジした内容と、OracleやDB2の分析関数をPostgreSQL 8.4で代用する方法を扱います。
本稿では、『分析関数の衝撃6(応用編)』を、PostgreSQL 8.4用にリニューアルした内容を扱います。
対象読者
- PostgreSQLでwindow関数を使ってみたい方
- 分析関数の理解を深めたい方
必要な環境
本稿で扱うSQLは、PostgreSQL 8.4 beta 2で動作確認しました。その他、次の環境でも応用が可能です。
- Oracle
- DB2
- SQL Server
1. 複数列のdistinctなcount
最初は、複数列のdistinct
なcount
を求めるSQLです。まずは、テーブルのデータと、出力結果を考えます。
ID | ColA | ColB |
1 | ab | c |
2 | a | bc |
3 | a | b |
4 | a | b |
5 | a | b |
ColA,ColBの組み合わせのdistinct
なcount
を求めます。(ColA,ColB) = (ab,c),(a,bc),(a,b)
で3通りの組み合わせがあるので、これをdisCntとして下記の形で出力します。
ID | ColA | ColB | disCnt |
1 | ab | c | 3 |
2 | a | bc | 3 |
3 | a | b | 3 |
4 | a | b | 3 |
5 | a | b | 3 |
集約関数で、複数列のdistinct
なcount
を取得する方法として、行コンストラクタを使った下記の方法があります。
with tmp(ID,Val1,Val2) as( values('AA',1,1), ('AA',1,1), ('AA',2,2), ('BB',1,2), ('BB',2,1), ('BB',2,1), ('BB',3,1)) select ID,count(distinct Row(Val1,Val2)) as disCnt from tmp group by ID;
ID | disCnt |
AA | 2 |
BB | 3 |
上記をふまえて、window
関数のcount
関数で同じようにdistinct
オプションを使用すればいいように思えますが、 PostgreSQL 8.4では、window
関数でのdistinct
オプションが実装されていないので、文法エラーになります。
select ID,ColA,ColB, count(distinct Row(ColA,ColB)) over() as disCnt from IDTable; ERROR: DISTINCT is not implemented for window functions LINE 2: count(distinct Row(ColA,ColB)) over() as disCnt
答えは、下記の逆ソートを使う方法となります。
select ID,ColA,ColB, -1+dense_rank() over(order by ColA asc ,ColB asc ) +dense_rank() over(order by ColA desc,ColB desc) as disCnt from IDTable order by ID;
order by ColA asc,ColB asc
でのasc
は省略可能なのですが、desc
と対比させたほうが分かりやすいと考え、省略せずに記述しています。
下記の数式をふまえてdense_rank
関数を使っています。-1を加算しているのは、自分を2回数えているからです。
-1 + 重複を除いた自分以上の組み合わせの数 + 重複を除いた自分以下の組み合わせの数 = 重複を除いた組み合わせの数