Mysql
 sql >> Datenbank >  >> RDS >> Mysql

Erstellen Sie ein dynamisches Menü mit verschachtelten Sets

Die folgende Abfrage ermöglicht es Ihnen, einen beliebigen Pfad (oder eine Reihe von Pfaden) zu öffnen, indem Sie die SQL-Having-Klausel und MySQLs group_concat Funktion.

Das Folgende ist die Tabellendefinition und Beispieldaten, die ich verwendet habe:

drop table nested_set;

CREATE TABLE nested_set (
 id INT,
 name VARCHAR(20) NOT NULL,
 lft INT NOT NULL,
 rgt INT NOT NULL
);

INSERT INTO nested_set (id, name, lft, rgt) VALUES (1,'HEAD',1,28);
INSERT INTO nested_set (id, name, lft, rgt) VALUES (2,'A',2,3);
INSERT INTO nested_set (id, name, lft, rgt) VALUES (3,'B',4,17);
INSERT INTO nested_set (id, name, lft, rgt) VALUES (4,'B1',5,10);
INSERT INTO nested_set (id, name, lft, rgt) VALUES (5,'B1.1',6,7);
INSERT INTO nested_set (id, name, lft, rgt) VALUES (6,'B1.2',8,9);
INSERT INTO nested_set (id, name, lft, rgt) VALUES (7,'B2',11,16);
INSERT INTO nested_set (id, name, lft, rgt) VALUES (8,'B2.1',12,13);
INSERT INTO nested_set (id, name, lft, rgt) VALUES (9,'B2.2',14,15);
INSERT INTO nested_set (id, name, lft, rgt) VALUES (10,'C',18,25);
INSERT INTO nested_set (id, name, lft, rgt) VALUES (11,'C1',19,20);
INSERT INTO nested_set (id, name, lft, rgt) VALUES (12,'C2',21,22);
INSERT INTO nested_set (id, name, lft, rgt) VALUES (13,'C3',23,24);
INSERT INTO nested_set (id, name, lft, rgt) VALUES (14,'D',26,27);

Die folgende Abfrage gibt Ihnen den gesamten Baum (mit Ausnahme des HEAD):

SELECT
  node.id
, node.lft
, node.rgt
, node.name
,  GROUP_CONCAT(parent.name ORDER BY parent.lft  SEPARATOR "/" ) AS path
,  (COUNT(parent.lft) - 1) AS depth
FROM nested_set AS node
inner join nested_set AS parent
on node.lft BETWEEN parent.lft AND parent.rgt
where parent.lft > 1
GROUP BY node.id

Mit einer Ausgabe von Folgendem, wenn es mit den Beispieldaten ausgeführt wird:

+------+-----+-----+------+-----------+-------+
| id   | lft | rgt | name | path      | depth |
+------+-----+-----+------+-----------+-------+
|    2 |   2 |   3 | A    | A         |     0 |
|    3 |   4 |  17 | B    | B         |     0 |
|    4 |   5 |  10 | B1   | B/B1      |     1 |
|    5 |   6 |   7 | B1.1 | B/B1/B1.1 |     2 |
|    6 |   8 |   9 | B1.2 | B/B1/B1.2 |     2 |
|    7 |  11 |  16 | B2   | B/B2      |     1 |
|    8 |  12 |  13 | B2.1 | B/B2/B2.1 |     2 |
|    9 |  14 |  15 | B2.2 | B/B2/B2.2 |     2 |
|   10 |  18 |  25 | C    | C         |     0 |
|   11 |  19 |  20 | C1   | C/C1      |     1 |
|   12 |  21 |  22 | C2   | C/C2      |     1 |
|   13 |  23 |  24 | C3   | C/C3      |     1 |
|   14 |  26 |  27 | D    | D         |     0 |
+------+-----+-----+------+-----------+-------+

Die folgenden Ergänzungen zur obigen Abfrage geben Ihnen die Kontrolle, die Sie zum Öffnen der verschiedenen Abschnitte benötigen:

having
depth = 0
or ('<PATH_TO_OPEN>' =  left(path, length('<PATH_TO_OPEN>'))
   and depth = length('<PATH_TO_OPEN>') - length(replace('<PATH_TO_OPEN>', '/', '')) + 1)

Die Having-Klausel wendet Filter auf die Ergebnisse der Gruppieren-nach-Abfrage an. Der Teil "Tiefe =0" soll sicherstellen, dass wir immer unsere Basismenüknoten (A, B, C und D) haben. Der nächste Teil ist der Teil, der steuert, welche Knoten geöffnet sind. Es vergleicht den Pfad der Knoten mit einem festgelegten Pfad, den Sie öffnen möchten (''), um zu sehen, ob er übereinstimmt, und stellt auch sicher, dass er nur auf der Ebene des Pfads öffnet. Der gesamte oder Abschnitt mit der ''-Logik kann nach Bedarf dupliziert und hinzugefügt werden, um bei Bedarf mehrere Pfade zu öffnen. Stellen Sie sicher, dass '' nicht mit einem nachgestellten Schrägstrich (/) endet.

Im Folgenden finden Sie einige Ausgabebeispiele, die Ihnen zeigen, wie Sie Abfragen erstellen würden, um die gewünschten Ausgaben zu erhalten:

=========Open B==========

SELECT
  node.id
, node.lft
, node.rgt
, node.name
,  GROUP_CONCAT(parent.name ORDER BY parent.lft  SEPARATOR "/" ) AS path
,  (COUNT(parent.lft) - 1) AS depth
FROM nested_set AS node
inner join nested_set AS parent
on node.lft BETWEEN parent.lft AND parent.rgt
where parent.lft > 1
GROUP BY node.id
having
depth = 0
or ('B' =  left(path, length('B'))
   and depth = length('B') - length(replace('B', '/', '')) + 1)

+------+-----+-----+------+------+-------+
| id   | lft | rgt | name | path | depth |
+------+-----+-----+------+------+-------+
|    2 |   2 |   3 | A    | A    |     0 |
|    3 |   4 |  17 | B    | B    |     0 |
|    4 |   5 |  10 | B1   | B/B1 |     1 |
|    7 |  11 |  16 | B2   | B/B2 |     1 |
|   10 |  18 |  25 | C    | C    |     0 |
|   14 |  26 |  27 | D    | D    |     0 |
+------+-----+-----+------+------+-------+

=========Open B and B/B1==========

SELECT
  node.id
, node.lft
, node.rgt
, node.name
,  GROUP_CONCAT(parent.name ORDER BY parent.lft  SEPARATOR "/" ) AS path
,  (COUNT(parent.lft) - 1) AS depth
FROM nested_set AS node
inner join nested_set AS parent
on node.lft BETWEEN parent.lft AND parent.rgt
where parent.lft > 1
GROUP BY node.id
having
depth = 0
or ('B' =  left(path, length('B'))
   and depth = length('B') - length(replace('B', '/', '')) + 1)
or ('B/B1' =  left(path, length('B/B1'))
   and depth = length('B/B1') - length(replace('B/B1', '/', '')) + 1)

+------+-----+-----+------+-----------+-------+
| id   | lft | rgt | name | path      | depth |
+------+-----+-----+------+-----------+-------+
|    2 |   2 |   3 | A    | A         |     0 |
|    3 |   4 |  17 | B    | B         |     0 |
|    4 |   5 |  10 | B1   | B/B1      |     1 |
|    5 |   6 |   7 | B1.1 | B/B1/B1.1 |     2 |
|    6 |   8 |   9 | B1.2 | B/B1/B1.2 |     2 |
|    7 |  11 |  16 | B2   | B/B2      |     1 |
|   10 |  18 |  25 | C    | C         |     0 |
|   14 |  26 |  27 | D    | D         |     0 |
+------+-----+-----+------+-----------+-------+

=========Open B and B/B1 and C==========

SELECT
  node.id
, node.lft
, node.rgt
, node.name
,  GROUP_CONCAT(parent.name ORDER BY parent.lft  SEPARATOR "/" ) AS path
,  (COUNT(parent.lft) - 1) AS depth
FROM nested_set AS node
inner join nested_set AS parent
on node.lft BETWEEN parent.lft AND parent.rgt
where parent.lft > 1
GROUP BY node.id
having
depth = 0
or ('B' =  left(path, length('B'))
   and depth = length('B') - length(replace('B', '/', '')) + 1)
or ('B/B1' =  left(path, length('B/B1'))
   and depth = length('B/B1') - length(replace('B/B1', '/', '')) + 1)
or ('C' =  left(path, length('C'))
   and depth = length('C') - length(replace('C', '/', '')) + 1)

+------+-----+-----+------+-----------+-------+
| id   | lft | rgt | name | path      | depth |
+------+-----+-----+------+-----------+-------+
|    2 |   2 |   3 | A    | A         |     0 |
|    3 |   4 |  17 | B    | B         |     0 |
|    4 |   5 |  10 | B1   | B/B1      |     1 |
|    5 |   6 |   7 | B1.1 | B/B1/B1.1 |     2 |
|    6 |   8 |   9 | B1.2 | B/B1/B1.2 |     2 |
|    7 |  11 |  16 | B2   | B/B2      |     1 |
|   10 |  18 |  25 | C    | C         |     0 |
|   11 |  19 |  20 | C1   | C/C1      |     1 |
|   12 |  21 |  22 | C2   | C/C2      |     1 |
|   13 |  23 |  24 | C3   | C/C3      |     1 |
|   14 |  26 |  27 | D    | D         |     0 |
+------+-----+-----+------+-----------+-------+

Das ist alles. du duplizierst einfach diesen oder Abschnitt für jeden Pfad, den du öffnen musst.

Siehe http://mikehillyer.com/articles/managing-hierarchical-data -in-mysql/ falls Sie allgemeine Informationen zum Arbeiten mit verschachtelten Sätzen in MySQL benötigen.

Lassen Sie mich wissen, wenn Sie Fragen haben.

HTH,

-Dipin