分层获取所有 parents 而不使用 CTE 查询
Hierarchical get all parents without using CTE query
简介
有很多答案很好地解释了如何从 parent-child 关系中读取分层数据。我正在使用 mySQL 并创建了一个查询,该查询读取给定 id
:
的所有 parents (parent_id
)(通过逗号连接)
create table `menu` (
`id` double ,
`title` varchar (765),
`controller` varchar (765),
`method` varchar (765),
`url` varchar (765),
`parent_id` varchar (765),
`added_date` datetime ,
`updated_date` datetime
);
填充了 table 的完整示例:http://sqlfiddle.com/#!9/48d276f/171。查询应该 运行 没有 CTE,目前看起来像:
SELECT GROUP_CONCAT(T2.id) AS parents
FROM (
SELECT
@r AS _id,
(SELECT @r := parent_id FROM menu WHERE id = _id) AS parent_id,
@l := @l + 1 AS lvl
FROM (SELECT @r := 31, @l := 0) vars, menu m
WHERE @r <> 0) T1
JOIN menu T2
ON T1._id = T2.id
ORDER BY T1.lvl DESC;
查询结果为:
parents
-------------
3,17,31
挑战:
我想创建相同的查询,它允许我读取 多个 id
中的 parents 个。我认为子查询会有所帮助,但在将 id 传递给子查询时出现错误 (Unknown table 'T3' in field list
)。
预期结果应为:
id | parents
-----------------------
25 | 5,25
31 | 3,17,31
23 | 4,23
使用的查询(http://sqlfiddle.com/#!9/48d276f/180):
SELECT T3.id, T4.parents
FROM menu T3, (SELECT T3.id, GROUP_CONCAT(T2.id) AS parents
FROM (
SELECT
@r AS _id,
(SELECT @r := parent_id FROM menu WHERE id = _id) AS parent_id,
@l := @l + 1 AS lvl
FROM (SELECT @r := T3.id, @l := 0) vars, menu m
WHERE @r <> 0) T1
JOIN menu T2
ON T1._id = T2.id
ORDER BY T1.lvl DESC) T4
WHERE T3.id IN (25, 31, 23)
您可以通过使用另一个连接来应用与单个 ID 相同的逻辑,从而基本上重复该评估多次。由于您不能再使用固定的起始值,我对条件进行了编码以重新初始化交叉连接中的变量 ("reset_r")。
尝试以下操作:
SELECT t1.id, GROUP_CONCAT(t1.r ORDER BY t1.lvl DESC) AS parents
FROM (
SELECT
t0.r_init AS id,
@r := IF(t0.reset_r = 1, t0.r_init,
(select parent_id from menu where id = @r)) AS r,
@l := IF(t0.reset_r = 1, 1, @l + 1) AS lvl
FROM
(SELECT m0.id as counter, m1.id AS r_init,
((SELECT min(id) FROM menu) = m0.id) AS reset_r
FROM menu m0, menu m1
WHERE m1.id IN (25, 31, 23)
) t0
ORDER BY t0.r_init, t0.counter
) t1
WHERE t1.r <> 0
-- or instead of "where":
-- JOIN menu t2 ON t2.id = t1.r;
GROUP BY t1.id;
对于大 tables,您应该将 p
限制为树的最大深度,或者使用 different data model。此外,虽然您的 table 结构可能是由于作为示例,但您显然应该使用主键(否则父项和重置条件未明确定义)和 parent_id
和相同的数据类型id
.
Update:MySQL 5.6 的查询版本(应该也适用于 sql-fiddle),使用更多具体化:
SELECT t2.id, GROUP_CONCAT(t2.r ORDER BY t2.lvl DESC)
FROM (
SELECT id, r, lvl
FROM (
SELECT
t0.r_init AS id,
@r := IF(t0.reset_r = 1, t0.r_init,
(select parent_id from menu where id = @r)) AS r,
@l := IF(t0.reset_r = 1, 1, @l + 1) AS lvl
FROM
(SELECT m0.id as counter, m1.id AS r_init,
((SELECT min(id) FROM menu) = m0.id) AS reset_r
FROM menu m0, menu m1
WHERE m1.id IN (25, 31, 23)
ORDER BY r_init, counter
) t0
ORDER BY t0.r_init, t0.counter
) t1
WHERE r <> 0
) t2
GROUP BY t2.id;
简介
有很多答案很好地解释了如何从 parent-child 关系中读取分层数据。我正在使用 mySQL 并创建了一个查询,该查询读取给定 id
:
parent_id
)(通过逗号连接)
create table `menu` (
`id` double ,
`title` varchar (765),
`controller` varchar (765),
`method` varchar (765),
`url` varchar (765),
`parent_id` varchar (765),
`added_date` datetime ,
`updated_date` datetime
);
填充了 table 的完整示例:http://sqlfiddle.com/#!9/48d276f/171。查询应该 运行 没有 CTE,目前看起来像:
SELECT GROUP_CONCAT(T2.id) AS parents
FROM (
SELECT
@r AS _id,
(SELECT @r := parent_id FROM menu WHERE id = _id) AS parent_id,
@l := @l + 1 AS lvl
FROM (SELECT @r := 31, @l := 0) vars, menu m
WHERE @r <> 0) T1
JOIN menu T2
ON T1._id = T2.id
ORDER BY T1.lvl DESC;
查询结果为:
parents
-------------
3,17,31
挑战:
我想创建相同的查询,它允许我读取 多个 id
中的 parents 个。我认为子查询会有所帮助,但在将 id 传递给子查询时出现错误 (Unknown table 'T3' in field list
)。
预期结果应为:
id | parents
-----------------------
25 | 5,25
31 | 3,17,31
23 | 4,23
使用的查询(http://sqlfiddle.com/#!9/48d276f/180):
SELECT T3.id, T4.parents
FROM menu T3, (SELECT T3.id, GROUP_CONCAT(T2.id) AS parents
FROM (
SELECT
@r AS _id,
(SELECT @r := parent_id FROM menu WHERE id = _id) AS parent_id,
@l := @l + 1 AS lvl
FROM (SELECT @r := T3.id, @l := 0) vars, menu m
WHERE @r <> 0) T1
JOIN menu T2
ON T1._id = T2.id
ORDER BY T1.lvl DESC) T4
WHERE T3.id IN (25, 31, 23)
您可以通过使用另一个连接来应用与单个 ID 相同的逻辑,从而基本上重复该评估多次。由于您不能再使用固定的起始值,我对条件进行了编码以重新初始化交叉连接中的变量 ("reset_r")。
尝试以下操作:
SELECT t1.id, GROUP_CONCAT(t1.r ORDER BY t1.lvl DESC) AS parents
FROM (
SELECT
t0.r_init AS id,
@r := IF(t0.reset_r = 1, t0.r_init,
(select parent_id from menu where id = @r)) AS r,
@l := IF(t0.reset_r = 1, 1, @l + 1) AS lvl
FROM
(SELECT m0.id as counter, m1.id AS r_init,
((SELECT min(id) FROM menu) = m0.id) AS reset_r
FROM menu m0, menu m1
WHERE m1.id IN (25, 31, 23)
) t0
ORDER BY t0.r_init, t0.counter
) t1
WHERE t1.r <> 0
-- or instead of "where":
-- JOIN menu t2 ON t2.id = t1.r;
GROUP BY t1.id;
对于大 tables,您应该将 p
限制为树的最大深度,或者使用 different data model。此外,虽然您的 table 结构可能是由于作为示例,但您显然应该使用主键(否则父项和重置条件未明确定义)和 parent_id
和相同的数据类型id
.
Update:MySQL 5.6 的查询版本(应该也适用于 sql-fiddle),使用更多具体化:
SELECT t2.id, GROUP_CONCAT(t2.r ORDER BY t2.lvl DESC)
FROM (
SELECT id, r, lvl
FROM (
SELECT
t0.r_init AS id,
@r := IF(t0.reset_r = 1, t0.r_init,
(select parent_id from menu where id = @r)) AS r,
@l := IF(t0.reset_r = 1, 1, @l + 1) AS lvl
FROM
(SELECT m0.id as counter, m1.id AS r_init,
((SELECT min(id) FROM menu) = m0.id) AS reset_r
FROM menu m0, menu m1
WHERE m1.id IN (25, 31, 23)
ORDER BY r_init, counter
) t0
ORDER BY t0.r_init, t0.counter
) t1
WHERE r <> 0
) t2
GROUP BY t2.id;