sqlite3上的基本递归查询?

时间:2011-09-17 18:34:33

标签: sql sqlite hierarchical-data

我有一个简单的sqlite3表,如下所示:

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

我需要运行一个递归查询来查找给定SuperPart及其所有子组的所有对。 所以我要说我有eZ00。 eZ00是eZ01的超级部件,eZ01是eZ03的超级部件。结果必须不仅包括对(eZ00,eZ01)和(eZ01和eZ03),还必须包括对(eZ00,eZ03)。

我知道还有其他方法来定义表格,但我在这里别无选择。 我知道如果我知道树的深度,我可以使用几个工会,但我不会总是知道我想去的深度。 它有助于拥有像WITH RECURSIVE这样的东西,甚至只有WITH(,,)AS x,但对于我搜索过的东西,这在sqlite中是不可能的,对吗?

有没有办法在sqlite3中执行此递归查询?

更新:

当提出这个问题时,SQLite不支持递归查询,但是as stated by @lunicon,SQLite现在支持递归CTE,因为3.8.3 sqlite.org/lang_with.html

5 个答案:

答案 0 :(得分:30)

如果您有幸使用SQLite 3.8.3或更高版本,那么您可以使用WITH访问递归和非递归CTE:

enter image description here

感谢lunicon让我们了解此SQLite更新。


在3.8.3之前的 版本中,SQLite不支持递归CTE(或CTE),因此没有WITH in SQLite。由于你不知道它有多深,你不能使用标准的JOIN技巧伪造递归CTE。您必须以艰难的方式完成并在客户端代码中实现递归:

  • 抓取初始行和子部件ID。
  • 抓取子部件的行和子部件ID。
  • 重复直到什么都没有回来。

答案 1 :(得分:7)

在此SQLite Release 3.8.3 On 2014-02-03中增加了对CTE的支持。这是文档WITH clause 例如:

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

答案 2 :(得分:4)

有一个黑客 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

答案 3 :(得分:3)

根据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 :(得分:2)

这是我能想到的最基本的查询,它会生成一个系列,我们从1,2开始并继续添加1直到我们达到20.没有太多用处但是玩一下这将有助于你建立更多复杂递归的

最基本的系列

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