web-dev-qa-db-ja.com

sqlite3の基本的な再帰クエリ?

次のような単純なsqlite3テーブルがあります。

Table: Part
Part    SuperPart
wk0Z    wk00
wk06    wk02
wk07    wk02
eZ01    eZ00
eZ02    eZ00
eZ03    eZ01
eZ04    eZ01

特定のSuperPartとそのすべてのsubPartのすべてのペアを検索するには、再帰クエリを実行する必要があります。だから私はeZ00を持っているとしましょう。 eZ00はeZ01のスーパーパートであり、eZ01はeZ03のスーパーパートです。結果には、ペア(eZ00、eZ01)と(eZ01とeZ03)だけでなく、ペア(eZ00、eZ03)も含める必要があります。

テーブルを定義する方法は他にもありますが、ここでは選択の余地はありません。木の深さがわかっていれば、いくつかの共用体を使用できることはわかっていますが、常にどの程度の深さにしたいのかわかりません。 WITH RECURSIVEやWITH(,)AS xのようなものを用意することは役立ちますが、私が検索したものについては、sqliteでは不可能ですよね?

Sqlite3でこの再帰的なクエリを行う方法はありますか?

UPDATE:

この質問が行われたとき、SQLiteは再帰クエリをサポートしていませんでした @ luniconで述べられているように 、SQLiteは3.8.3以降の再帰CTEをサポートしています sqlite.org/lang_with.html =

31
Eric

SQLite 3.8.3以降を使用できるほど幸運である場合、 [〜#〜] with [〜#〜]を使用して再帰的および非再帰的CTEにアクセスできます。

enter image description here

このSQLiteの更新について知らせてくれた lunicon に感謝します。


バージョン3.8.3より前のバージョンでは、SQLiteは再帰CTE(またはその点ではCTE)をサポートしていなかったため、 WITH SQLite はありませんでした。深さがわからないため、標準のJOINトリックを使用して再帰CTEを偽造することはできません。あなたはそれを難し​​い方法で行い、クライアントコードに再帰を実装する必要があります:

  • 最初の行とサブパートIDを取得します。
  • サブパーツの行とサブパーツIDを取得します。
  • 何も戻らないまで繰り返します。
30
mu is too short

この中に SQLiteリリース3.8.3 On 2014-02- がCTEのサポートを追加しました。ここにドキュメントがあります WITH句 例:

WITH RECURSIVE
cnt(x) AS (
 SELECT 1
 UNION ALL
 SELECT x+1 FROM cnt
  LIMIT 1000000
)
SELECT x FROM cnt;
6

ハックがある http://dje.me/2011/03/26/sqlite-data-trees.html

-- A method for storing and retrieving hierarchical data in sqlite3
-- by using a trigger and a temporary table.
-- I needed this but had trouble finding information on it.

-- This is for sqlite3, it mostly won't work on anything else, however 
-- most databases have better ways to do this anyway.

PRAGMA recursive_triggers = TRUE; -- This is not possible before 3.6.18

-- When creating the Node table either use a primary key or some other 
-- identifier which the child node can reference.

CREATE TABLE Node (id INTEGER PRIMARY KEY, parent INTEGER, 
    label VARCHAR(16));

INSERT INTO Node (parent, label) VALUES(NULL, "root");
INSERT INTO Node (parent, label) VALUES(1, "a");
INSERT INTO Node (parent, label) VALUES(2, "b");
INSERT INTO Node (parent, label) VALUES(3, "c1");
INSERT INTO Node (parent, label) VALUES(3, "c2");

-- Create the temp table, note that node is not a primary key
-- which insures the order of the results when Node records are
-- inserted out of order

CREATE TEMP TABLE Path (node INTEGER, parent INTEGER, 
    label VARCHAR(16));

CREATE TRIGGER find_path AFTER INSERT ON Path BEGIN
    INSERT INTO Path SELECT Node.* FROM Node WHERE 
        Node.id = new.parent;
END;


-- The flaw here is that label must be unique, so when creating
-- the table there must be a unique reference for selection
-- This insert sets off the trigger find_path

INSERT INTO Path SELECT * FROM Node WHERE label = "c2";

-- Return the hierarchy in order from "root" to "c2"
SELECT * FROM Path ORDER BY node ASC;

DROP TABLE Path; -- Important if you are staying connected


-- To test this run:
-- sqlite3 -init tree.sql tree.db
4
johndpope

sqlite with documentation にあるサンプルに基づいて、クエリ

DROP TABLE IF EXISTS parts;
CREATE TABLE parts (part, superpart);
INSERT INTO parts VALUES("wk0Z", "wk00");
INSERT INTO parts VALUES("wk06", "wk02");
INSERT INTO parts VALUES("wk07", "wk02");
INSERT INTO parts VALUES("eZ01", "eZ00");
INSERT INTO parts VALUES("eZ02", "eZ00");
INSERT INTO parts VALUES("eZ03", "eZ01");
INSERT INTO parts VALUES("eZ04", "eZ01");

WITH RECURSIVE
  under_part(parent,part,level) AS (
     VALUES('?', 'eZ00', 0)
     UNION ALL
     SELECT parts.superpart, parts.part, under_part.level+1 
        FROM parts, under_part
     WHERE parts.superpart=under_part.part
  )
  SELECT SUBSTR('..........',1,level*3) || "(" || parent || ", " || part || ")" FROM under_part
  ;

出力します

  (?, eZ00)
  ...(eZ00, eZ01)
  ...(eZ00, eZ02)
  ......(eZ01, eZ03)
  ......(eZ01, eZ04)

期待通り

再帰的なテーブルの最初のレコードは、

VALUES ((SELECT superpart FROM parts WHERE part='eZ00'), 'eZ00', 0)

この場合、親はまったくありませんが、最初のスーパーパートの親も取得します。

4

これは私が考えることができる最も基本的なクエリです。これは、1,2から開始し、20に到達するまで1を追加し続けるシリーズを生成します。あまり有用ではありませんが、これを少し試してみると、より複雑な再帰クエリを作成できます

最も基本的なシリーズ

WITH b(x,y) AS 
(
    SELECT 1,2 
    UNION ALL 
    SELECT x+ 1, y + 1 
    FROM b 
    WHERE x < 20
) SELECT * FROM b;

プリント

1|2
2|3
3|4
4|5
5|6
6|7
7|8
8|9
9|10
10|11
11|12
12|13
13|14
14|15
15|16
16|17
17|18
18|19
19|20
20|21

フィボナッチ数列を生成する別の簡単な例を次に示します。a= 0、b = 1で始まり、a = b、b = a + bをプログラミング言語と同じように実行します。

フィボナッチシリーズ

WITH b(x,y) AS 
(
    SELECT 0,1 
    UNION ALL 
    SELECT y, x + y 
    FROM b 
    WHERE x < 10000
) select * FROM b;

プリント

0|1
1|1
1|2
2|3
3|5
5|8
8|13
13|21
21|34
34|55
55|89
89|144
144|233
233|377
377|610
610|987
987|1597
1597|2584
2584|4181
4181|6765
6765|10946
10946|17711
3
PirateApp