表a中有id, parent_id两个字段,parent_id存id的上一级id。请帮我写一个SQL语句,递归查询出一个id的所有parent_id,输出为一行数据多个列。
ChatGPT said:
非常好的问题 👍
这是一个典型的树形结构递归查询,你希望从一个 id 出发,逐级向上找所有父节点(parent_id),并且把这些父节点展开成一行多列。
下面我分别给出 PostgreSQL / Greenplum / MySQL 8+ 的写法。
🟢 以 PostgreSQL / Greenplum 为例
假设表结构如下:
CREATE TABLE a (id INT PRIMARY KEY,parent_id INT );
你要查某个节点(比如 id = 10)的所有父节点:
🔹 SQL:
WITH RECURSIVE parent_tree AS (SELECT id, parent_id, 1 AS levelFROM aWHERE id = 10UNION ALLSELECT a.id, a.parent_id, pt.level + 1FROM aJOIN parent_tree pt ON a.id = pt.parent_id ) SELECT MAX(CASE WHEN level = 1 THEN id END) AS level1_id,MAX(CASE WHEN level = 2 THEN id END) AS level2_id,MAX(CASE WHEN level = 3 THEN id END) AS level3_id,MAX(CASE WHEN level = 4 THEN id END) AS level4_id FROM parent_tree;
🧠 说明:
-
WITH RECURSIVE从目标 id 开始,不断向上关联a.id = parent_tree.parent_id。 -
递归层数用
level控制。 -
最后用
CASE WHEN+聚合函数MAX()将多行“旋转”为一行多列。
✅ 结果示例:
| level1_id | level2_id | level3_id | level4_id |
|---|---|---|---|
| 10 | 7 | 3 | 1 |
🟡 如果不知道最大层级怎么办?
可以自动生成列名的动态 SQL:
DO $$ DECLAREsql TEXT; BEGINSELECT string_agg(format('MAX(CASE WHEN level = %s THEN id END) AS level%s_id', level, level),', ')INTO sqlFROM generate_series(1, 10) AS level; -- 假设最大10层 sql := format('WITH RECURSIVE parent_tree AS (SELECT id, parent_id, 1 AS level FROM a WHERE id = 10UNION ALLSELECT a.id, a.parent_id, pt.level + 1FROM a JOIN parent_tree pt ON a.id = pt.parent_id)SELECT %s FROM parent_tree;', sql);EXECUTE sql; END $$;
🔵 MySQL 8.0+ 写法(略有不同)
MySQL 8 也支持递归 CTE:
WITH RECURSIVE parent_tree AS (SELECT id, parent_id, 1 AS levelFROM aWHERE id = 10UNION ALLSELECT a.id, a.parent_id, pt.level + 1FROM aJOIN parent_tree pt ON a.id = pt.parent_id ) SELECTMAX(CASE WHEN level = 1 THEN id END) AS level1_id,MAX(CASE WHEN level = 2 THEN id END) AS level2_id,MAX(CASE WHEN level = 3 THEN id END) AS level3_id,MAX(CASE WHEN level = 4 THEN id END) AS level4_id FROM parent_tree;
如果你告诉我:
-
你使用的是哪种数据库(PostgreSQL / Greenplum / MySQL / Oracle / SQL Server 等)
-
最大层级大约是多少(例如 <=10)
我可以帮你写出一个完全可执行的版本,包括动态列生成的方案。
要我帮你生成对应数据库的最终版吗?
You said:
MySQL
ChatGPT said:
非常好 👍 那我们来写 MySQL 8.0+ 版本的完整可执行 SQL。
你的需求是:
从表
a(id, parent_id)中递归查找某个节点的所有上级parent_id,
并把结果输出成“一行多列”(例如 parent1, parent2, parent3...)。
✅ 一、示例表结构与数据
CREATE TABLE a (id INT PRIMARY KEY,parent_id INT );INSERT INTO a (id, parent_id) VALUES (1, NULL), (2, 1), (3, 2), (4, 3), (5, 3), (6, 4);
层级结构示意:
1 └── 2└── 3├── 4│ └── 6└── 5
✅ 二、递归查询:从某个 id 往上找所有父节点
比如要查 id = 6 的所有父级:
WITH RECURSIVE parent_tree AS (SELECT id, parent_id, 1 AS levelFROM aWHERE id = 6UNION ALLSELECT a.id, a.parent_id, pt.level + 1FROM aJOIN parent_tree pt ON a.id = pt.parent_id ) SELECT * FROM parent_tree;
输出:
| id | parent_id | level |
|---|---|---|
| 6 | 4 | 1 |
| 4 | 3 | 2 |
| 3 | 2 | 3 |
| 2 | 1 | 4 |
| 1 | NULL | 5 |
✅ 三、把父节点展开为“一行多列”
我们可以用 MAX(CASE WHEN level=... THEN parent_id END) 转置:
WITH RECURSIVE parent_tree AS (SELECT id, parent_id, 1 AS levelFROM aWHERE id = 6UNION ALLSELECT a.id, a.parent_id, pt.level + 1FROM aJOIN parent_tree pt ON a.id = pt.parent_id ) SELECT MAX(CASE WHEN level = 2 THEN id END) AS parent_1,MAX(CASE WHEN level = 3 THEN id END) AS parent_2,MAX(CASE WHEN level = 4 THEN id END) AS parent_3,MAX(CASE WHEN level = 5 THEN id END) AS parent_4 FROM parent_tree;
结果:
| parent_1 | parent_2 | parent_3 | parent_4 |
|---|---|---|---|
| 4 | 3 | 2 | 1 |
✅ 四、如果不确定层级深度(动态列)
MySQL 不支持动态列生成的 pivot,因此可以用 存储过程 动态生成 SQL:
DELIMITER //CREATE PROCEDURE get_parents(IN start_id INT) BEGINDECLARE max_level INT DEFAULT 10; -- 假设最多10层DECLARE sql_text TEXT;SET @base_cte = 'WITH RECURSIVE parent_tree AS (SELECT id, parent_id, 1 AS levelFROM aWHERE id = ';SET @rest_cte = 'UNION ALLSELECT a.id, a.parent_id, pt.level + 1FROM aJOIN parent_tree pt ON a.id = pt.parent_id)SELECT ';SELECT GROUP_CONCAT(CONCAT('MAX(CASE WHEN level = ', n, ' THEN id END) AS parent_', n - 1)ORDER BY n SEPARATOR ', ')INTO @colsFROM (SELECT 2 AS n UNION ALL SELECT 3 UNION ALL SELECT 4 UNION ALL SELECT 5UNION ALL SELECT 6 UNION ALL SELECT 7 UNION ALL SELECT 8 UNION ALL SELECT 9 UNION ALL SELECT 10) t;SET sql_text = CONCAT(@base_cte, start_id, @rest_cte, @cols, ' FROM parent_tree;');PREPARE stmt FROM sql_text;EXECUTE stmt;DEALLOCATE PREPARE stmt; END //DELIMITER ;
调用:
CALL get_parents(6);
输出:
| parent_1 | parent_2 | parent_3 | parent_4 |
|---|---|---|---|
| 4 | 3 | 2 | 1 |
✅ 总结
| 目标 | 解决方案 |
|---|---|
| 递归查找所有上级 | WITH RECURSIVE |
| 输出多行转多列 | MAX(CASE WHEN level=...) |
| 动态层级 | 存储过程生成动态 SQL |