再帰 CTE で総積を求める (再帰 CTE の入門)
なんか「SQL かけ算」とかを検索してここに来ている人達がいるようなんだけど、総積でも求めたいのかな?
確かに、SUM はあるのに PRODUCT なんてないしなー。
てことで勝手にそういうことにして、再帰 CTE で総積を書いてみる。
RDBMS はもちろん (?) SQL Server 前提です。
あ、CTE ってのは Common Table Expression の略で、共通表式のことらしいですよ、っと。
WITH Input(n) AS ( SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4 UNION ALL SELECT 5 ) , Input_(i, n) AS ( -- 連番を振る。面倒なのでROW_NUMBER使った。 SELECT ROW_NUMBER() OVER(ORDER BY n), n FROM Input ) , Product(n, i) AS ( SELECT n , i FROM Input_ WHERE -- 1からスタート i = 1 UNION ALL SELECT -- P.nは今までの総積が、I.nには次にかけるべき値が入っている P.n * I.n -- iを進める。I.iでもいいけど、+ 1があった方が分かりやすい気がする , P.i + 1 FROM -- 今かけるべき値をJOINで見つける Product P INNER JOIN Input_ I ON P.i + 1 = I.i -- WHEREなくても対象が空だと止まるっぽい ) , Result(n) AS ( SELECT n FROM Product WHERE i = (SELECT MAX(i) FROM Input_) ) SELECT n FROM Result
これで、Input の n の総積が求まる。
慣れればどうと言うことはない SQL だけど、イメージが掴みにくいかもしれないので、ちょっと Product の処理を追ってみる。
まず最初に、UNION ALL より上の SELECT が実行され、
n | i |
---|---|
1 | 1 |
こんな行が返される。
次に再帰部分、つまり UNION ALL よりも下の SELECT が何度も実行されていくことになる。
再帰の 1 回目では、P.i は上で見ると 1 なので、FROM 句で JOIN された結果は、
P.n | P.i | I.i | I.n |
---|---|---|---|
1 | 1 | 2 | 2 |
こうなる。
これを元に、P.n * I.n と P.i + 1 が計算され、結果、
n | i |
---|---|
2 | 2 |
が返される。
次の再帰で、P.i は上で見ると 2 なので、FROM 句で JOIN された結果は、
P.n | P.i | I.i | I.n |
---|---|---|---|
2 | 2 | 3 | 3 |
となる。
これを元に P.n * I.n と P.i + 1 が計算され、結果、
n | i |
---|---|
6 | 3 |
が返される。
次の再帰で、P.i は上で見ると 3 なので、FROM 句で JOIN された結果は、
P.n | P.i | I.i | I.n |
---|---|---|---|
6 | 3 | 4 | 4 |
となる。
これを元に計算した結果、
n | i |
---|---|
24 | 4 |
が返される。
次の再帰で、
P.n | P.i | I.i | I.n |
---|---|---|---|
24 | 4 | 5 | 5 |
を元に計算した結果、
n | i |
---|---|
120 | 5 |
が返される。
次の再帰で、P.i が 5 なのだが、P.i + 1 = 6 と一致するような I.i は存在しないため、再帰が終了する。
最後に、全ての結果が UNION ALL され(処理中に UNION ALL されていっているのかもしれない)、
n | i |
---|---|
1 | 1 |
2 | 2 |
6 | 3 |
24 | 4 |
120 | 5 |
が Product の結果となる。
欲しいのは最後の行だけなので、Input_ から i の最大値に一致する行のみ抽出したものを Result の結果とし、SELECT * FROM Result とすることで、
n |
---|
120 |
を得ることが出来る。
・・・こんな感じで順を追ってみてみると、再帰 CTE もそんなに難しいものじゃないでしょ?