SQL - 递归查询
SQL - Recursive Query
我有以下 mysql table 示例数据如下:
id location parentid
1 UK 0
2 East Anglia 1
3 Cambridgeshire 2
4 Norfolk 2
5 Suffolk 2
6 East Midlands 1
7 Derbyshire 6
8 Leicestershire 6
9 EU Countries 0
10 Austria 9
11 Belgium 9
我想生成一个查询,借此我可以按位置名称获取位置列表,但该位置应包括任何父位置。例如
搜索 folk
应该 return:
id location
4 Norfolk, East Anglia, UK
5 Suffolk, East Anglia, UK
搜索 East
应该 return:
id location
2 East Anglia, UK
6 East Midlands, UK
搜索 Bel
应该 return:
id location
11 Belgium
在上面我们排除了连接 EU countries
显然以下内容不起作用:
select c.id, CONCAT_WS(', ', c.location, p.location, pp.location) as location
from tbl_locations c
outer left join tbl_locations p on p.id = c.parentid
outer left join tbl_locations pp on pp.id = p.parentid
where c.location like '%whatever%'
如果您只想要父位置,则可以通过自连接完成:
select c.id, c.location, p.id, p.location
from tbl_locations c
outer left join tbl_locations p on p.id = c.parentid
where c.location like '%whatever%'
这可以扩展(通过外连接)到任意数量的级别,但查询会变长。例如。分为三个级别:
select c.id, c.location, p.id, p.location, pp.id, pp.location
from tbl_locations c
outer left join tbl_locations p on p.id = c.parentid
outer left join tbl_locations pp on pp.id = p.parentid
where c.location like '%whatever%'
更一般的递归查询取决于RDBMS的细节。最简单的方法是使用通用 Table 表达式 (CTE)。但是 MySQL 不支持它们(至少现在还不支持)。可以使用其他方法:Generating Depth based tree from Hierarchical Data in MySQL (no CTEs).
1) 没有可以计算"transitive closure"传递关系的标准SQL查询。如果你想嵌套 select 语句,你将始终有一个可以达到的最大深度。
2) 没有标准的 SQL 查询可以 return 具有可变列数的行。所以你必须以某种方式格式化你的结果(例如 csv)。
但是,您可以在 MySQL 中使用存储过程完成此操作:
1 CREATE DATABASE IF NOT EXISTS test;
2 USE test;
3
4
5 DROP TABLE IF EXISTS location;
6 CREATE TABLE location (id INT UNSIGNED PRIMARY KEY, name VARCHAR(30) NOT NULL, parent_id INT UNSIGNED NULL REFERENCES location(id));
7
8 INSERT INTO location VALUES
9 (1,"UK",0),
10 (2,"East Anglia",1),
11 (3,"Cambridgeshire",2),
12 (4,"Norfolk",2),
13 (5,"Suffolk",2),
14 (6,"East Midlands",1),
15 (7,"Derbyshire",6),
16 (8,"Leicestershire",6);
17
18
19
20
21 DROP FUNCTION IF EXISTS location_with_parents;
22 DELIMITER //
23 CREATE FUNCTION location_with_parents(location_id INT UNSIGNED) RETURNS VARCHAR(255) READS SQL DATA
24 BEGIN
25 DECLARE LOC_STR VARCHAR(255) DEFAULT NULL;
26 DECLARE LOC_ADD VARCHAR(255) DEFAULT NULL;
27 DECLARE PAR_ID INT UNSIGNED DEFAULT location_id;
28
29 SELECT name INTO LOC_STR FROM location where id=PAR_ID;
30 loop_label: LOOP
31 SELECT parent_id INTO PAR_ID FROM location where id=PAR_ID;
32
33 IF PAR_ID = 0 THEN
34 LEAVE loop_label;
35 ELSE
36 SELECT name INTO LOC_ADD FROM location where id=PAR_ID;
37 SET LOC_STR = CONCAT(LOC_STR, ', ', LOC_ADD);
38 ITERATE loop_label;
39 END IF;
40 END LOOP loop_label;
41 RETURN LOC_STR;
42
43 END;
44 //
45
46 DELIMITER ;
47
48
49
50 SELECT location_with_parents(id) FROM location WHERE name LIKE "%folk%";
51
52 DROP DATABASE test;
适用于 MySQL 5.6.35
希望对您有所帮助!
下面的查询使用递归方法为您提供了您想要的准确结果。
Select S.ID ,
concat( S.location,',', Group_concat
(distinct A.location ORDER BY A.location SEPARATOR ',' ) ) as location
from
( SELECT distinct @r AS _id ,location,
(
SELECT @r := parentid
FROM tbl_locations
WHERE id = _id
) AS parentid,
@l := @l + 1 AS level
FROM (
SELECT @r := h.ID,
@l := 0,
@cl := 0
from tbl_locations h
where location like '%folk%'
) vars,
tbl_locations h
WHERE @r <> 0
)A , tbl_locations S
where s.location like '%folk%'
group by S.ID
OutPut :
location like '%East%' :
location like '%Folk%'
这是个好问题,如果您有任何疑虑,请检查并询问。
我有以下 mysql table 示例数据如下:
id location parentid
1 UK 0
2 East Anglia 1
3 Cambridgeshire 2
4 Norfolk 2
5 Suffolk 2
6 East Midlands 1
7 Derbyshire 6
8 Leicestershire 6
9 EU Countries 0
10 Austria 9
11 Belgium 9
我想生成一个查询,借此我可以按位置名称获取位置列表,但该位置应包括任何父位置。例如
搜索 folk
应该 return:
id location
4 Norfolk, East Anglia, UK
5 Suffolk, East Anglia, UK
搜索 East
应该 return:
id location
2 East Anglia, UK
6 East Midlands, UK
搜索 Bel
应该 return:
id location
11 Belgium
在上面我们排除了连接 EU countries
显然以下内容不起作用:
select c.id, CONCAT_WS(', ', c.location, p.location, pp.location) as location
from tbl_locations c
outer left join tbl_locations p on p.id = c.parentid
outer left join tbl_locations pp on pp.id = p.parentid
where c.location like '%whatever%'
如果您只想要父位置,则可以通过自连接完成:
select c.id, c.location, p.id, p.location
from tbl_locations c
outer left join tbl_locations p on p.id = c.parentid
where c.location like '%whatever%'
这可以扩展(通过外连接)到任意数量的级别,但查询会变长。例如。分为三个级别:
select c.id, c.location, p.id, p.location, pp.id, pp.location
from tbl_locations c
outer left join tbl_locations p on p.id = c.parentid
outer left join tbl_locations pp on pp.id = p.parentid
where c.location like '%whatever%'
更一般的递归查询取决于RDBMS的细节。最简单的方法是使用通用 Table 表达式 (CTE)。但是 MySQL 不支持它们(至少现在还不支持)。可以使用其他方法:Generating Depth based tree from Hierarchical Data in MySQL (no CTEs).
1) 没有可以计算"transitive closure"传递关系的标准SQL查询。如果你想嵌套 select 语句,你将始终有一个可以达到的最大深度。
2) 没有标准的 SQL 查询可以 return 具有可变列数的行。所以你必须以某种方式格式化你的结果(例如 csv)。
但是,您可以在 MySQL 中使用存储过程完成此操作:
1 CREATE DATABASE IF NOT EXISTS test;
2 USE test;
3
4
5 DROP TABLE IF EXISTS location;
6 CREATE TABLE location (id INT UNSIGNED PRIMARY KEY, name VARCHAR(30) NOT NULL, parent_id INT UNSIGNED NULL REFERENCES location(id));
7
8 INSERT INTO location VALUES
9 (1,"UK",0),
10 (2,"East Anglia",1),
11 (3,"Cambridgeshire",2),
12 (4,"Norfolk",2),
13 (5,"Suffolk",2),
14 (6,"East Midlands",1),
15 (7,"Derbyshire",6),
16 (8,"Leicestershire",6);
17
18
19
20
21 DROP FUNCTION IF EXISTS location_with_parents;
22 DELIMITER //
23 CREATE FUNCTION location_with_parents(location_id INT UNSIGNED) RETURNS VARCHAR(255) READS SQL DATA
24 BEGIN
25 DECLARE LOC_STR VARCHAR(255) DEFAULT NULL;
26 DECLARE LOC_ADD VARCHAR(255) DEFAULT NULL;
27 DECLARE PAR_ID INT UNSIGNED DEFAULT location_id;
28
29 SELECT name INTO LOC_STR FROM location where id=PAR_ID;
30 loop_label: LOOP
31 SELECT parent_id INTO PAR_ID FROM location where id=PAR_ID;
32
33 IF PAR_ID = 0 THEN
34 LEAVE loop_label;
35 ELSE
36 SELECT name INTO LOC_ADD FROM location where id=PAR_ID;
37 SET LOC_STR = CONCAT(LOC_STR, ', ', LOC_ADD);
38 ITERATE loop_label;
39 END IF;
40 END LOOP loop_label;
41 RETURN LOC_STR;
42
43 END;
44 //
45
46 DELIMITER ;
47
48
49
50 SELECT location_with_parents(id) FROM location WHERE name LIKE "%folk%";
51
52 DROP DATABASE test;
适用于 MySQL 5.6.35
希望对您有所帮助!
下面的查询使用递归方法为您提供了您想要的准确结果。
Select S.ID ,
concat( S.location,',', Group_concat
(distinct A.location ORDER BY A.location SEPARATOR ',' ) ) as location
from
( SELECT distinct @r AS _id ,location,
(
SELECT @r := parentid
FROM tbl_locations
WHERE id = _id
) AS parentid,
@l := @l + 1 AS level
FROM (
SELECT @r := h.ID,
@l := 0,
@cl := 0
from tbl_locations h
where location like '%folk%'
) vars,
tbl_locations h
WHERE @r <> 0
)A , tbl_locations S
where s.location like '%folk%'
group by S.ID
OutPut :
location like '%East%' :
location like '%Folk%'
这是个好问题,如果您有任何疑虑,请检查并询问。