web-dev-qa-db-fra.com

requête récursive de base sur sqlite3?

J'ai une simple table sqlite3 qui ressemble à ceci:

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

J'ai besoin d'exécuter une requête récursive pour trouver toutes les paires d'un SuperPart donné avec tous ses sous-parties. Alors disons que j'ai eZ00. eZ00 est une superpart de eZ01 et eZ01 est une superpart de eZ03. Le résultat doit inclure non seulement les paires (eZ00, eZ01) et (eZ01 et eZ03) mais doit également inclure la paire (eZ00, eZ03).

Je sais qu'il existe d'autres façons de définir la table, mais je n'ai pas le choix ici. Je sais que je peux utiliser plusieurs unions si je connais la profondeur de mon arbre, mais je ne saurai pas toujours à quelle profondeur je veux aller. Il serait utile d'avoir quelque chose comme WITH RECURSIVE ou même simplement AVEC (,) AS x mais pour ce que j'ai recherché, ce n'est pas possible dans sqlite, non?

Existe-t-il un moyen de faire cette requête récursive dans sqlite3?

MISE À JOUR:

Lorsque cette question a été posée, SQLite ne prend pas en charge les requêtes récursives, mais comme indiqué par @lunicon , SQLite prend désormais en charge CTE récursif depuis la version 3.8.3 sqlite.org/lang_with.html =

31
Eric

Si vous avez la chance d'utiliser SQLite 3.8.3 ou supérieur , vous avez accès à des CTE récursifs et non récursifs en utilisant AVEC :

enter image description here

Merci à lunicon de nous avoir informés de cette mise à jour SQLite.


Dans les versions antérieures à 3.8.3 , SQLite ne supportait pas les CTE récursifs (ou CTE du tout d'ailleurs) donc il n'y avait pas AVEC dans SQLite . Comme vous ne savez pas à quelle profondeur il va, vous ne pouvez pas utiliser l'astuce JOIN standard pour simuler le CTE récursif. Vous devez le faire à la dure et implémenter la récursivité dans votre code client:

  • Saisissez la ligne initiale et les ID de sous-partie.
  • Saisissez les lignes et les ID de sous-partie pour les sous-parties.
  • Répétez jusqu'à ce que rien ne revienne.
30
mu is too short

Dans ce SQLite Release 3.8.3 On 2014-02- a été ajouté la prise en charge des CTE. Voici la documentation clause WITH Exemple:

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

il y a un hack 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

Sur la base des exemples trouvés dans sqlite avec documentation , la requête

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
  ;

sortirait

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

comme "il devrait être" prévu

l'enregistrement initial de la table récursive peut être remplacé par

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

afin d'obtenir également le parent de la superpart initiale, bien que dans ce cas il n'y ait pas de parent du tout.

4
Alejadro Xalabarder

C'est la requête la plus simple à laquelle je pourrais penser, elle génère une série où nous commençons par 1,2 et continuons d'ajouter 1 jusqu'à ce que nous atteignions 20. pas très utile mais jouer un peu avec cela vous aidera à en construire des plus récursives complexes

La série la plus basique

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

Impressions

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

Voici un autre exemple simple qui génère des nombres de Fibonacci, nous commençons par a = 0, b = 1 et ensuite nous allons a = b, b = a + b comme vous le feriez dans n'importe quel langage de programmation

Série Fibonacci

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

Impressions

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