次のような単純な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 =
SQLite 3.8.3以降を使用できるほど幸運である場合、 [〜#〜] with [〜#〜]を使用して再帰的および非再帰的CTEにアクセスできます。 :
このSQLiteの更新について知らせてくれた lunicon に感謝します。
バージョン3.8.3より前のバージョンでは、SQLiteは再帰CTE(またはその点ではCTE)をサポートしていなかったため、 WITH SQLite はありませんでした。深さがわからないため、標準のJOINトリックを使用して再帰CTEを偽造することはできません。あなたはそれを難しい方法で行い、クライアントコードに再帰を実装する必要があります:
この中に 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;
ハックがある 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
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)
この場合、親はまったくありませんが、最初のスーパーパートの親も取得します。
これは私が考えることができる最も基本的なクエリです。これは、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