Reputation: 9411
I have a categories
table, with some recursion. Some categories are sub categories and so have a value in a parent_category_id
field. Top level categories have null in the parent_category_id
field.
The requirement is to list categories so that they appear as:
Parent category 1
Sub category 1
Sub category 2
Parent category 2
Subcategory 3
Subcategory 4
Is this possible with a single query order statement, or do I need to make separate queries?
Some sample data as requested:
Upvotes: 1
Views: 224
Reputation: 3221
In MYSQL, which doesn't support recursive cte's:
select id, path(id) from mytable order by 2
, where path(id)
is defined by
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 ;
;
Note, in my example, instead of a null parent node for the root, I'm using a node id of -1.
For performance, maintain a second table (using triggers) which stores the 'path' of each node, where path(node) is defined by the above UDF.
Upvotes: 2
Reputation: 67291
This is SQL Server syntax:
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
The result
+-------------+--------------------+---------+--------+-----------------+
| 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. |
+-------------+--------------------+---------+--------+-----------------+
Upvotes: 2