SSブログ

SQL WITH句 再帰クエリー RECURSIVE SQLポケリ [SQLポケリ]

最近、SQLネタをブログにアップしている。本日もSQLネタである。

WITH句



少し前から、WITH句というものが使えるようになっている。
Oracleだと9iから使えるのか。割と昔からあるじゃない。SQLポケリには載っていない。
(改訂第4版には載ってます)。

WITHを使うと、長ったらしいサブクエリを何回も書かなくてよくなる
これは便利。

CREATE VIEWをSELECT命令内でやってしまうようなもの。

具体例を示しながら解説してみる。

SELECT foo.a, bar.b FROM foo INNER JOIN bar ON foo.a = bar.a


といったSELECT命令をサブクエリで何回も使いたいとする。
ビューを定義してしまえば、話は簡単。

CREATE VIEW vfoobar AS
 SELECT foo.a, bar.b FROM foo INNER JOIN bar ON foo.a = bar.a


vfoobarという名前のビューを定義した。
vfoobarをテーブルのように使用できるから、長ったらしいfooとbarをa列で結合して...といったSELECT命令をvfoobarという名前だけで使用できるようになる。

SELECT * FROM vfoobar
 WHERE a = (SELECT MIN(a) FROM vfoobar)
   OR  a = (SELECT MAX(a) FROM vfoobar)


ほらね。
vfoobarのところを、SELECT foo.a, bar.b FROM foo INNER...というSELECT命令に脳内で置換してみてください。
なんだかよくわからない、複雑なクエリになってしまうことがわかる。

WITH句を使えば、こういったことを、ビューを作らないで一気にやってしまうことができる。

WITH foobar AS (
 SELECT foo.a, bar.b FROM foo INNER JOIN bar ON foo.a = bar.a
)
SELECT * FROM foobar
 WHERE a = (SELECT MIN(a) FROM foobar)
   OR  a = (SELECT MAX(a) FROM foobar)


なるほど。やっぱりこれは便利。


WITH RECURSIVE



便利なWITH句ではあるが、本来は「再帰クエリー」というものを想定して作られたものらしい。再帰クエリーとはいったいどういったものなのであろうか。

と偉そうに書いてみたが、再帰という概念は説明するのがけっこう難しい。自分の中に自分があるような感じ。プログラミングの場合、関数の再帰呼び出し、ということをよく耳にするかと思う。関数の再帰呼び出しなら、ある関数の内部処理で、自分自身を「再帰的に」呼び出す、というもの。

よく考えずに、関数の再帰呼び出しを行うと、無限ループに陥ることになる。なぜかというと、自分が自分を呼び出して、さらに呼び出された自分も自分を呼び出すので...
通常の関数は、引数をスタックに積む。無限ループといっても、スタックが足りなくなった時点で異常終了する。また、よく考えられた再帰呼び出しは、ちゃんといつかは終了するようにして作成する。

さて、SQLに戻って再帰クエリーである。

ビューを作成する際に、SELECT命令を記述することになるのだが、そのSELECT命令に定義中のビューを使用することはできない。
なぜなら、そのビューはまだ作成されていないから。
CREATE VIEWでviewxxを作成中に、ビューのSELECT命令でviewxxを使うことはできない。

CREATE VIEW vfoo AS
 SELECT * FROM vfoo


まぁ、そりゃそうだよね。再帰関数呼び出しで、無限ループに陥ってしまうことと同じ問題である。
試しに、上記のCREATE VIEWをOracleでやってみたら、エラーになった。

 SQLエラー: ORA-01731: 循環ビュー定義が検出されました
 01731. 00000 - "circular view definition encountered"

へー、「循環ビュー」っていうのか。

ビューでは、エラーになるものの、WITH句では、これが許されているのである。
マジか?無限ループにならないのか?
やってみよう。

WITH vfoo AS (
  SELECT a FROM vfoo
)
SELECT * FROM vfoo

 ORA-32039: 再帰的WITH句には列別名リストが必要です
 32039. 00000 -  "recursive WITH clause must have column alias list"


えー、そうなの。めんどくさー

WITH vfoo(a) AS (
  SELECT a FROM vfoo
)
SELECT * FROM vfoo

 ORA-32040: 再帰的WITH句にはUNION ALL操作を使用する必要があります
 32040. 00000 -  "recursive WITH clause must use a UNION ALL operation"


また、エラーになってしまった。
UNION ALLが必要?

WITH vfoo(a) AS (
  SELECT a FROM vfoo
  UNION ALL
  SELECT a FROM vfoo
)
SELECT * FROM vfoo

 ORA-32043: 再帰的WITH句には初期化ブランチが必要です
 32043. 00000 -  "recursive WITH clause needs an initialization branch"


ふえーん。初期化ブランチとはいったい。

要は、再帰呼び出しで無限ループに陥らないように、ちゃんと中のクエリを定義しなさい、ということ。WITHの中で、自分自身を使っても循環ビューのエラーにはならない。

再帰クエリーにする場合は、列指定が必要。再帰クエリーの中には、自分自身を含めてよいが、UNION ALLと初期化ブランチが必要。というところまでわかった。

本日は、ここまでとする。




改訂第4版SQLポケリには、WITH句、WITH再帰クエリが載ってます。

ですが、WITH RECURSIVEのRECURSIVEを書くとエラーになります、とある。これは間違いではないのだが、SQLiteでの話が抜けている。そもそもSQLiteがWITH RECURSIVEに対応していないことになっている。スミマセン。

整理すると以下のような状況である。

PostgreSQL RECURSIVEを書く必要あり
SQLite   RECURSIVEを省略可能
その他   RECURSIVEは書いてはいけない

ということで、訂正は以下のようになる。

77ページ
【誤】
PostgreSQLの場合、再帰クエリーを実行する際には、WITHの後に
RECURSIVEをキーワードを指定する必要があります。他のデータベースでは
RECURSIVEを指定するとエラーになります。
【正】
PostgreSQLの場合、再帰クエリーを実行する際には、WITHの後に
RECURSIVEをキーワードを指定する必要があります。SQLiteでは、RECURSIVE
を省略可能です。他のデータベースではRECURSIVEを指定するとエラーになります。








サイト内を検索

タグ:with SQL
nice!(0)  コメント(0) 
共通テーマ:携帯コンテンツ

nice! 0

コメント 0

コメントを書く

お名前:
URL:
コメント:
画像認証:
下の画像に表示されている文字を入力してください。



Copyright Atsushi Asai Google+朝井淳
[改訂第4版]SQLポケットリファレンス

[改訂第4版]SQLポケットリファレンス

  • 作者: 朝井 淳
  • 出版社/メーカー: 技術評論社
  • 発売日: 2017/02/18
  • メディア: 単行本(ソフトカバー)

イラストで理解 SQL はじめて入門

イラストで理解 SQL はじめて入門

  • 作者: 朝井 淳
  • 出版社/メーカー: 技術評論社
  • 発売日: 2019/05/16
  • メディア: 単行本(ソフトカバー)

[データベースの気持ちがわかる]SQLはじめの一歩 (WEB+DB PRESS plus)

[データベースの気持ちがわかる]SQLはじめの一歩 (WEB+DB PRESS plus)

  • 作者: 朝井 淳
  • 出版社/メーカー: 技術評論社
  • 発売日: 2015/03/03
  • メディア: 単行本(ソフトカバー)

Access クエリ 徹底活用ガイド ~仕事の現場で即使える

Access クエリ 徹底活用ガイド ~仕事の現場で即使える

  • 作者: 朝井 淳
  • 出版社/メーカー: 技術評論社
  • 発売日: 2018/05/25
  • メディア: 大型本

この広告は前回の更新から一定期間経過したブログに表示されています。更新すると自動で解除されます。