我正在尝试在 mssql 中编写一个查询,将下面的输入表转换为所需的输出
我有一个表,通过使用
parent_id
列的自引用来表示层次结构。
这是涵盖我的案例的输入表示例。
id | current_level | 姓名 | parent_id |
---|---|---|---|
1 | 1 | 老A | 空 |
2 | 1 | 最老的B | 空 |
3 | 2 | 老A | 1 |
4 | 2 | 老B | 2 |
5 | 3 | 小A | 3 |
6 | 3 | 小B | 4 |
7 | 3 | 小C | 4 |
8 | 4 | 老幺A | 5 |
9 | 4 | 最小的B | 6 |
10 | 4 | 最小的C | 7 |
11 | 4 | 老幺D | 7 |
12 | 4 | 最小的E | 7 |
13 | 4 | 老幺F | 7 |
这里的想法是通过添加与级别一样多的列来扩展此表。在这种情况下,我们有 4 个级别,所以我们想向表中添加 4 个新列。
这是期望的输出
id | current_level | 姓名 | parent_id | level_1 | level_2 | level_3 | level_4 |
---|---|---|---|---|---|---|---|
1 | 1 | 老A | 空 | 老A | 空 | 空 | 空 |
2 | 1 | 最老的B | 空 | 最老的B | 空 | 空 | 空 |
3 | 2 | 老A | 1 | 老A | 老A | 空 | 空 |
4 | 2 | 老B | 2 | 最老的B | 老B | 空 | 空 |
5 | 3 | 小A | 3 | 老A | 老A | 小A | 空 |
6 | 3 | 小B | 4 | 最老的B | 老B | 小B | 空 |
7 | 3 | 小C | 4 | 最老的B | 老B | 小C | 空 |
8 | 4 | 老幺A | 5 | 老A | 老A | 小A | 老幺A |
9 | 4 | 最小的B | 6 | 最老的B | 老B | 小B | 最小的B |
10 | 4 | 最小的C | 7 | 最老的B | 老B | 小C | 最小的C |
11 | 4 | 老幺D | 7 | 最老的B | 老B | 小C | 老幺D |
12 | 4 | 最小的E | 7 | 最老的B | 老B | 小C | 最小的E |
13 | 4 | 老幺F | 7 | 最老的B | 老B | 小C | 老幺F |
我在这里面临的两个主要挑战:
parent_id == null
)。例如,要使用
id = 5
填充行的级别,我正在使用这样的逻辑:
if column_level > current_level then SET to NULL
=> 列 level_4 = NULLif column_level == current_level then SET to current_level
=> column level_3 = Young Aif column_level == current_level-1 then SET to parent_level
=> 列 level_2 = 旧 Aif column_level == current_level-2 then SET to parent_of_parent_level
=> 列 level_1 = 最旧的 Aid=5 行的查找链
3(parent)->1(parent_of_parent)->NULL
我试图用递归 cte 来解决这个问题,但是那些不允许递归地附加新列或者至少我不能。
我也开始考虑用 TSQL 解决方案,但也没有运气。
您可以为此使用递归 CTE。 要获得父级别,您只需在递归部分将它们连接在一起,使用 JSON 或 XML,以便您可以将其解析为列表。
由于您希望将它们全部作为单独的列,因此变得更加困难。因此,您需要在
PIVOT
内使用一个巨大的 APPLY
来获得所有三十列(我没有费心重命名它们,它们都只是 1
2
3
等)。
WITH cte AS (
SELECT
t.id,
t.current_level,
t.name,
t.parent_id,
levels = STRING_ESCAPE(t.name, 'json')
FROM YourTable t
WHERE current_level = 1
UNION ALL
SELECT
t.id,
t.current_level,
t.name,
t.parent_id,
cte.levels + '","' + STRING_ESCAPE(t.name, 'json')
FROM cte
JOIN YourTable t ON t.parent_id = cte.id
)
SELECT
cte.id,
cte.current_level,
cte.name,
cte.parent_id,
levels.*
FROM cte
CROSS APPLY (
SELECT *
FROM (
SELECT
[key] = CAST([key] AS int) + 1,
value
FROM OPENJSON('["' + cte.levels + '"]')
) o
PIVOT (
MAX(value) FOR [key] IN (
[1], [2], [3], [4], [5], [6], [7], [8], [9], [10], [11], [12], [13], [14], [15], [16], [17], [18], [19], [20], [21], [22], [23], [24], [25], [26], [27], [28], [29], [30]
)
) p
) levels;
如果你不介意只得到一个大的 JSON blob,你可以删除
APPLY
和 PIVOT
WITH cte AS (
SELECT
t.id,
t.current_level,
t.name,
t.parent_id,
levels = STRING_ESCAPE(t.name, 'json')
FROM YourTable t
WHERE current_level = 1
UNION ALL
SELECT
t.id,
t.current_level,
t.name,
t.parent_id,
cte.levels + '","' + STRING_ESCAPE(t.name, 'json')
FROM cte
JOIN YourTable t ON t.parent_id = cte.id
)
SELECT
cte.id,
cte.current_level,
cte.name,
cte.parent_id,
levels = '["' + cte.levels + '"]'
FROM cte;