简化递归 mysql 查询
Simplify recursive mysql query
给定MySQLtable
CREATE TABLE taxons (
id int NOT NULL PRIMARY KEY AUTO_INCREMENT,
parent_id int,
name varchar(255)
);
table 有以下条目
INSERT INTO taxons (parent_id, name) VALUES (NULL, "Baby & Kleinkind");
INSERT INTO taxons (parent_id, name) VALUES (1, "Babysicherheit");
INSERT INTO taxons (parent_id, name) VALUES (2, "Babysicherungen & Schutzvorrichtungen");
关卡层出不穷。
我想通过向下遍历树来 select 一个只有一个查询的实体(一个分类单元)。
目前我的查询是手工制作的
SELECT *
FROM taxons
WHERE name = "Babysicherungen & Schutzvorrichtungen"
AND parent_id = (
SELECT id
FROM taxons
WHERE name = "Babysicherheit"
AND parent_id = (
SELECT id
FROM taxons
WHERE name = "Baby & Kleinkind"
)
);
这个查询有没有更好的解决方案?
查看数据库 fiddle 以获得更好的理解:https://www.db-fiddle.com/f/mvMCGdNgjCa9PeNKbbzmRL/0
MySQL v5.7
一种更简洁的表达方式是使用连接:
select t1.* from taxons t1
join taxons t2 on t1.name = "Babysicherungen & Schutzvorrichtungen" and t1.parent_id = t2.id and t2.name = "Babysicherheit"
join taxons t3 on t2.parent_id = t3.id and t3.name="Baby & Kleinkind"
;
鉴于您已定义(或未定义)的键,它也会更有效率。请参阅您的 SQL 和上面的两个 EXPLAIN 分析:
架构(MySQL v5.7)
CREATE TABLE taxons (
id int NOT NULL PRIMARY KEY AUTO_INCREMENT,
parent_id int,
name varchar(255)
);
INSERT INTO taxons (parent_id, name) VALUES (NULL, "Baby & Kleinkind");
INSERT INTO taxons (parent_id, name) VALUES (1, "Babysicherheit");
INSERT INTO taxons (parent_id, name) VALUES (2, "Babysicherungen & Schutzvorrichtungen");
查询#1
EXPLAIN SELECT *
FROM taxons
WHERE name = "Babysicherungen & Schutzvorrichtungen"
AND parent_id = (
SELECT id
FROM taxons
WHERE name = "Babysicherheit"
AND parent_id = (
SELECT id
FROM taxons
WHERE name = "Baby & Kleinkind"
)
);
|编号 | select_type | table |分区 |类型 | possible_keys |钥匙 | key_len |参考 |行 |过滤 |额外 |
| --- | ---------- | ------ | ---------- | ---- | -------------- | --- | ------ | --- |
---- | -------- | ----------- |
| 1 | PRIMARY | taxons | | ALL | | | | | 3 | 33.33 | Using where |
| 2 | SUBQUERY | taxons | | ALL | | | | | 3 | 33.33 | Using where |
| 3 | SUBQUERY | taxons | | ALL | | | | | 3 | 33.33 | Using where |
---
架构(MySQL v5.7)
CREATE TABLE taxons (
id int NOT NULL PRIMARY KEY AUTO_INCREMENT,
parent_id int,
name varchar(255)
);
INSERT INTO taxons (parent_id, name) VALUES (NULL, "Baby & Kleinkind");
INSERT INTO taxons (parent_id, name) VALUES (1, "Babysicherheit");
INSERT INTO taxons (parent_id, name) VALUES (2, "Babysicherungen & Schutzvorrichtungen");
查询#1
EXPLAIN select t1.* from taxons t1
join taxons t2 on t1.name = "Babysicherungen & Schutzvorrichtungen" and t1.parent_id = t2.id and t2.name = "Babysicherheit"
join taxons t3 on t2.parent_id = t3.id and t3.name="Baby & Kleinkind"
;
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
| --- | ----------- | ----- | ---------- | ------ | ------------- | ------- | ------- | ----------------- | ---- | -------- | ----------- |
| 1 | SIMPLE | t1 | | ALL | | | | | 3 | 33.33 | Using where |
| 1 | SIMPLE | t2 | | eq_ref | PRIMARY | PRIMARY | 4 | test.t1.parent_id | 1 | 33.33 | Using where |
| 1 | SIMPLE | t3 | | eq_ref | PRIMARY | PRIMARY | 4 | test.t2.parent_id | 1 | 33.33 | Using where |
如果我没看错你的查询,你想查询顶级记录,但只查询所有子行都存在的记录。例如,如果某些 ID 不存在 "Baby & Kleinkind",那么我们不需要该 ID。
如果是这样,您可以使用动态 SQL 构建一个自动为您检查每个 parent-child 关系的查询。如果您有很多分类单元,这可以节省您的时间。
我们的想法是构建一个如下所示的查询(假设有五个分类单元):
SELECT t1.*
FROM taxons t1
INNER JOIN taxons t2
ON t1.id = t2.parent_id
INNER JOIN taxons t3
ON t2.id = t3.parent_id
INNER JOIN taxons t4
ON t3.id = t4.parent_id
INNER JOIN taxons t5
ON t4.id = t5.parent_id
WHERE NAME = "Babysicherungen & Schutzvorrichtungen"
要构建它,您可以使用循环:
DECLARE totalLevels INT;
DECLARE num INT;
DECLARE queryString VARCHAR(255);
SET @totalLevels = 5
SET @num = 2;
SET @str = 'SELECT t1.*
FROM taxons t1';
build_query: LOOP
IF @num > @totalLevels THEN
LEAVE build_query;
END IF;
SET @queryString = CONCAT(@queryString,
' INNER JOIN taxons t', num, ' ON t', num-1, '.id = t', num, '.parent_id'
);
SET @num = @num + 1;
ITERATE build_query;
END LOOP;
SET @queryString = CONCAT(@queryString, ' WHERE NAME = "Babysicherungen & Schutzvorrichtungen"')
您使用 totalLevels 设置要遍历的分类单元总数。
我没有专门测试该循环中的代码,但我过去做过类似的查询,它应该可以工作,或者只需要稍作更改即可工作。
编辑:我忘了补充,创建查询字符串后,您需要执行它。为此,请参见此处:
Is it possible to execute a string in MySQL?
给定MySQLtable
CREATE TABLE taxons (
id int NOT NULL PRIMARY KEY AUTO_INCREMENT,
parent_id int,
name varchar(255)
);
table 有以下条目
INSERT INTO taxons (parent_id, name) VALUES (NULL, "Baby & Kleinkind");
INSERT INTO taxons (parent_id, name) VALUES (1, "Babysicherheit");
INSERT INTO taxons (parent_id, name) VALUES (2, "Babysicherungen & Schutzvorrichtungen");
关卡层出不穷。
我想通过向下遍历树来 select 一个只有一个查询的实体(一个分类单元)。
目前我的查询是手工制作的
SELECT *
FROM taxons
WHERE name = "Babysicherungen & Schutzvorrichtungen"
AND parent_id = (
SELECT id
FROM taxons
WHERE name = "Babysicherheit"
AND parent_id = (
SELECT id
FROM taxons
WHERE name = "Baby & Kleinkind"
)
);
这个查询有没有更好的解决方案?
查看数据库 fiddle 以获得更好的理解:https://www.db-fiddle.com/f/mvMCGdNgjCa9PeNKbbzmRL/0
MySQL v5.7
一种更简洁的表达方式是使用连接:
select t1.* from taxons t1
join taxons t2 on t1.name = "Babysicherungen & Schutzvorrichtungen" and t1.parent_id = t2.id and t2.name = "Babysicherheit"
join taxons t3 on t2.parent_id = t3.id and t3.name="Baby & Kleinkind"
;
鉴于您已定义(或未定义)的键,它也会更有效率。请参阅您的 SQL 和上面的两个 EXPLAIN 分析:
架构(MySQL v5.7)
CREATE TABLE taxons (
id int NOT NULL PRIMARY KEY AUTO_INCREMENT,
parent_id int,
name varchar(255)
);
INSERT INTO taxons (parent_id, name) VALUES (NULL, "Baby & Kleinkind");
INSERT INTO taxons (parent_id, name) VALUES (1, "Babysicherheit");
INSERT INTO taxons (parent_id, name) VALUES (2, "Babysicherungen & Schutzvorrichtungen");
查询#1
EXPLAIN SELECT *
FROM taxons
WHERE name = "Babysicherungen & Schutzvorrichtungen"
AND parent_id = (
SELECT id
FROM taxons
WHERE name = "Babysicherheit"
AND parent_id = (
SELECT id
FROM taxons
WHERE name = "Baby & Kleinkind"
)
);
|编号 | select_type | table |分区 |类型 | possible_keys |钥匙 | key_len |参考 |行 |过滤 |额外 | | --- | ---------- | ------ | ---------- | ---- | -------------- | --- | ------ | --- |
---- | -------- | ----------- |
| 1 | PRIMARY | taxons | | ALL | | | | | 3 | 33.33 | Using where |
| 2 | SUBQUERY | taxons | | ALL | | | | | 3 | 33.33 | Using where |
| 3 | SUBQUERY | taxons | | ALL | | | | | 3 | 33.33 | Using where |
---
架构(MySQL v5.7)
CREATE TABLE taxons (
id int NOT NULL PRIMARY KEY AUTO_INCREMENT,
parent_id int,
name varchar(255)
);
INSERT INTO taxons (parent_id, name) VALUES (NULL, "Baby & Kleinkind");
INSERT INTO taxons (parent_id, name) VALUES (1, "Babysicherheit");
INSERT INTO taxons (parent_id, name) VALUES (2, "Babysicherungen & Schutzvorrichtungen");
查询#1
EXPLAIN select t1.* from taxons t1
join taxons t2 on t1.name = "Babysicherungen & Schutzvorrichtungen" and t1.parent_id = t2.id and t2.name = "Babysicherheit"
join taxons t3 on t2.parent_id = t3.id and t3.name="Baby & Kleinkind"
;
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
| --- | ----------- | ----- | ---------- | ------ | ------------- | ------- | ------- | ----------------- | ---- | -------- | ----------- |
| 1 | SIMPLE | t1 | | ALL | | | | | 3 | 33.33 | Using where |
| 1 | SIMPLE | t2 | | eq_ref | PRIMARY | PRIMARY | 4 | test.t1.parent_id | 1 | 33.33 | Using where |
| 1 | SIMPLE | t3 | | eq_ref | PRIMARY | PRIMARY | 4 | test.t2.parent_id | 1 | 33.33 | Using where |
如果我没看错你的查询,你想查询顶级记录,但只查询所有子行都存在的记录。例如,如果某些 ID 不存在 "Baby & Kleinkind",那么我们不需要该 ID。
如果是这样,您可以使用动态 SQL 构建一个自动为您检查每个 parent-child 关系的查询。如果您有很多分类单元,这可以节省您的时间。
我们的想法是构建一个如下所示的查询(假设有五个分类单元):
SELECT t1.*
FROM taxons t1
INNER JOIN taxons t2
ON t1.id = t2.parent_id
INNER JOIN taxons t3
ON t2.id = t3.parent_id
INNER JOIN taxons t4
ON t3.id = t4.parent_id
INNER JOIN taxons t5
ON t4.id = t5.parent_id
WHERE NAME = "Babysicherungen & Schutzvorrichtungen"
要构建它,您可以使用循环:
DECLARE totalLevels INT;
DECLARE num INT;
DECLARE queryString VARCHAR(255);
SET @totalLevels = 5
SET @num = 2;
SET @str = 'SELECT t1.*
FROM taxons t1';
build_query: LOOP
IF @num > @totalLevels THEN
LEAVE build_query;
END IF;
SET @queryString = CONCAT(@queryString,
' INNER JOIN taxons t', num, ' ON t', num-1, '.id = t', num, '.parent_id'
);
SET @num = @num + 1;
ITERATE build_query;
END LOOP;
SET @queryString = CONCAT(@queryString, ' WHERE NAME = "Babysicherungen & Schutzvorrichtungen"')
您使用 totalLevels 设置要遍历的分类单元总数。
我没有专门测试该循环中的代码,但我过去做过类似的查询,它应该可以工作,或者只需要稍作更改即可工作。
编辑:我忘了补充,创建查询字符串后,您需要执行它。为此,请参见此处: Is it possible to execute a string in MySQL?