递归排序 table
Order by on recursive table
我有一个 categories
table,有一些递归。有些类别是子类别,因此在 parent_category_id
字段中有一个值。顶级类别在 parent_category_id
字段中为空。
要求列出的类别应显示为:
Parent category 1
Sub category 1
Sub category 2
Parent category 2
Subcategory 3
Subcategory 4
这是否可以通过单个查询顺序语句实现,或者我是否需要进行单独的查询?
请求的一些示例数据:
在 MYSQL 中,不支持递归 cte:
select id, path(id) from mytable order by 2
,其中 path(id)
由
定义
DELIMITER $$
CREATE FUNCTION path(v_id INT(10)) RETURNS varchar(255)
begin
declare v_path varchar(255);
declare v_parent_id INT(10);
declare MAX_ITERS int;
declare iters int;
set v_path = '';
set MAX_ITERS = 20;
set iters = 0;
if not exists (select * from node where id = v_id) then
return 'no such node';
end if;
if not exists (select * from node where parent_id < 0) then
return 'no root node in table';
end if;
select parent_id into v_parent_id from node where id = v_id;
while (v_parent_id >= 0) do
set iters = iters + 1;
if iters >= MAX_ITERS then
return 'path too long';
end if;
select parent_id, concat(id, '.', v_path) into v_parent_id, v_path from node where id = v_id;
set v_id = v_parent_id;
end while;
return trim(both '.' from v_path);
end$$
DELIMITER ;
;
请注意,在我的示例中,我使用的节点 ID 为 -1,而不是根的空父节点。
为了性能,维护第二个table(使用触发器),它存储每个节点的'path',其中路径(节点)由上面的UDF定义。
这是SQL服务器语法:
DECLARE @category TABLE(category_id INT,parent_category_id INT,caption varchar(100));
INSERT INTO @category VALUES
(1,NULL,'Top1')
,(2,1,'Sub11')
,(3,1,'Sub12')
,(4,2,'Sub111')
,(5,2,'Sub112')
,(6,NULL,'Top2')
,(7,6,'Sub21')
,(8,6,'Sub22')
,(9,8,'Sub221');
WITH rCTE AS
(
SELECT c.category_id
,c.parent_category_id
,c.caption
,CAST(ROW_NUMBER() OVER(ORDER BY c.category_id) AS VARCHAR(MAX)) + '.' AS [Level]
,REPLACE(STR(ROW_NUMBER() OVER(ORDER BY c.category_id),4),' ','0') + '.' AS [Sortable]
FROM @category AS c WHERE parent_category_id IS NULL
UNION ALL
SELECT c.category_id
,c.parent_category_id
,c.caption
,rCTE.[Level] + CAST(ROW_NUMBER() OVER(ORDER BY c.category_id) AS VARCHAR(MAX)) + '.'
,rCTE.Sortable + REPLACE(STR(ROW_NUMBER() OVER(ORDER BY c.category_id),4),' ','0') + '.'
FROM rCTE
INNER JOIN @category AS c ON c.parent_category_id=rCTE.category_id
)
SELECT *
FROM rCTE
ORDER BY Sortable
结果
+-------------+--------------------+---------+--------+-----------------+
| category_id | parent_category_id | caption | Level | Sortable |
+-------------+--------------------+---------+--------+-----------------+
| 1 | NULL | Top1 | 1. | 0001. |
+-------------+--------------------+---------+--------+-----------------+
| 2 | 1 | Sub11 | 1.1. | 0001.0001. |
+-------------+--------------------+---------+--------+-----------------+
| 4 | 2 | Sub111 | 1.1.1. | 0001.0001.0001. |
+-------------+--------------------+---------+--------+-----------------+
| 5 | 2 | Sub112 | 1.1.2. | 0001.0001.0002. |
+-------------+--------------------+---------+--------+-----------------+
| 3 | 1 | Sub12 | 1.2. | 0001.0002. |
+-------------+--------------------+---------+--------+-----------------+
| 6 | NULL | Top2 | 2. | 0002. |
+-------------+--------------------+---------+--------+-----------------+
| 7 | 6 | Sub21 | 2.1. | 0002.0001. |
+-------------+--------------------+---------+--------+-----------------+
| 8 | 6 | Sub22 | 2.2. | 0002.0002. |
+-------------+--------------------+---------+--------+-----------------+
| 9 | 8 | Sub221 | 2.2.1. | 0002.0002.0001. |
+-------------+--------------------+---------+--------+-----------------+
我有一个 categories
table,有一些递归。有些类别是子类别,因此在 parent_category_id
字段中有一个值。顶级类别在 parent_category_id
字段中为空。
要求列出的类别应显示为:
Parent category 1
Sub category 1
Sub category 2
Parent category 2
Subcategory 3
Subcategory 4
这是否可以通过单个查询顺序语句实现,或者我是否需要进行单独的查询?
请求的一些示例数据:
在 MYSQL 中,不支持递归 cte:
select id, path(id) from mytable order by 2
,其中 path(id)
由
DELIMITER $$
CREATE FUNCTION path(v_id INT(10)) RETURNS varchar(255)
begin
declare v_path varchar(255);
declare v_parent_id INT(10);
declare MAX_ITERS int;
declare iters int;
set v_path = '';
set MAX_ITERS = 20;
set iters = 0;
if not exists (select * from node where id = v_id) then
return 'no such node';
end if;
if not exists (select * from node where parent_id < 0) then
return 'no root node in table';
end if;
select parent_id into v_parent_id from node where id = v_id;
while (v_parent_id >= 0) do
set iters = iters + 1;
if iters >= MAX_ITERS then
return 'path too long';
end if;
select parent_id, concat(id, '.', v_path) into v_parent_id, v_path from node where id = v_id;
set v_id = v_parent_id;
end while;
return trim(both '.' from v_path);
end$$
DELIMITER ;
;
请注意,在我的示例中,我使用的节点 ID 为 -1,而不是根的空父节点。
为了性能,维护第二个table(使用触发器),它存储每个节点的'path',其中路径(节点)由上面的UDF定义。
这是SQL服务器语法:
DECLARE @category TABLE(category_id INT,parent_category_id INT,caption varchar(100));
INSERT INTO @category VALUES
(1,NULL,'Top1')
,(2,1,'Sub11')
,(3,1,'Sub12')
,(4,2,'Sub111')
,(5,2,'Sub112')
,(6,NULL,'Top2')
,(7,6,'Sub21')
,(8,6,'Sub22')
,(9,8,'Sub221');
WITH rCTE AS
(
SELECT c.category_id
,c.parent_category_id
,c.caption
,CAST(ROW_NUMBER() OVER(ORDER BY c.category_id) AS VARCHAR(MAX)) + '.' AS [Level]
,REPLACE(STR(ROW_NUMBER() OVER(ORDER BY c.category_id),4),' ','0') + '.' AS [Sortable]
FROM @category AS c WHERE parent_category_id IS NULL
UNION ALL
SELECT c.category_id
,c.parent_category_id
,c.caption
,rCTE.[Level] + CAST(ROW_NUMBER() OVER(ORDER BY c.category_id) AS VARCHAR(MAX)) + '.'
,rCTE.Sortable + REPLACE(STR(ROW_NUMBER() OVER(ORDER BY c.category_id),4),' ','0') + '.'
FROM rCTE
INNER JOIN @category AS c ON c.parent_category_id=rCTE.category_id
)
SELECT *
FROM rCTE
ORDER BY Sortable
结果
+-------------+--------------------+---------+--------+-----------------+
| category_id | parent_category_id | caption | Level | Sortable |
+-------------+--------------------+---------+--------+-----------------+
| 1 | NULL | Top1 | 1. | 0001. |
+-------------+--------------------+---------+--------+-----------------+
| 2 | 1 | Sub11 | 1.1. | 0001.0001. |
+-------------+--------------------+---------+--------+-----------------+
| 4 | 2 | Sub111 | 1.1.1. | 0001.0001.0001. |
+-------------+--------------------+---------+--------+-----------------+
| 5 | 2 | Sub112 | 1.1.2. | 0001.0001.0002. |
+-------------+--------------------+---------+--------+-----------------+
| 3 | 1 | Sub12 | 1.2. | 0001.0002. |
+-------------+--------------------+---------+--------+-----------------+
| 6 | NULL | Top2 | 2. | 0002. |
+-------------+--------------------+---------+--------+-----------------+
| 7 | 6 | Sub21 | 2.1. | 0002.0001. |
+-------------+--------------------+---------+--------+-----------------+
| 8 | 6 | Sub22 | 2.2. | 0002.0002. |
+-------------+--------------------+---------+--------+-----------------+
| 9 | 8 | Sub221 | 2.2.1. | 0002.0002.0001. |
+-------------+--------------------+---------+--------+-----------------+