假设我有一个这样的层次结构。
| 父亲ID|子ID|
| - ------|- ------|
| 1个|第二章|
| 1个|三个|
| 1个|七|
| 三个|四个|
| 三个|五个|
| 三个|六个|
在我的例子中,这些是生产订单,它们相互(父项)需求或相互(子项)供应。我需要将这些订单转换为如下结构:
| 神父大人|层级|儿童|
| - ------|- ------|- ------|
| 1个|001.001|第二章|
| 1个|001.002|三个|
| 1个|001年002月001日|四个|
| 1个|001年002月002日|五个|
| 1个|001年002月003日|六个|
| 1个|001.003|七|
之后,我必须将组件连接到子订单,这些子订单用于创建主订单。目标是跟踪组件的来源,这些组件用于创建特定产品。
最大的问题是,我无法预测这些树形结构在未来会变得多深,但我想创建一个报告,当它们变得更深时,它不会中断。
到目前为止,我手动计算了深度。如果主父级是0,那么在我的示例中,我有2个级别。转换的(伪)代码如下所示:
Select
L.Father_ID as Level0
, '001' as Hierarchy0
, L.Child_ID as Level1
, Dense_Rank() over (partition by L.Father_ID) as Hierarchy1
, R1.Child_ID as Level2
, Dense_Rank() over (partition by L.Father_ID, L.Child_ID) as Hierarchy2
Into #exploded_table
From Table as L
Left Join Table as R1
on L.Child_ID = R1.Father_ID
Select distinct
Level0 as Master_Father
, Concat(Hierarchy0,'.',format(Hirarchy1, '000')) as Hierarchy
, Level1 as Child
From #exploded_table
Union all
Select distinct
Level0 as Master_Father
, Concat(Hierarchy0,'.',format(Hirarchy1, '000'),'.',format(Hirarchy2, '000')) as Hierarchy
, Level2 as Child
From #exploded_table
这个代码有两个问题。
1.每关都会变长
1.它将打破,如果更多的水平将在未来添加
因此我开始编写动态代码,它首先计算最深树的深度,然后动态地创建所需级别的代码。
执行此代码时,列数为(据我所知)称为“非确定性”。而MS SQL讨厌“非确定性”的东西。要被允许将其存储为临时表,我必须在X1 M0 N1 X函数的作用域之外创建一个临时表。然后,我必须动态修改该表的列,以完全适合传递给Exec函数的动态SQL语句的结果。不要使用全局临时表,因为当多个报表的临时表名称相同时,这将导致混乱。
上面描述的方法很复杂,而且不可读!我可以做到,但感觉与良好的实践背道而驰。
所以我想问一下社区,这是否可以用一种更简单的方式来完成。我来自一家使用PySpark的公司,在那里这很容易做到。现在我在这里,我所拥有的只是SQL Server和SQL Server Reporting Services。
2条答案
按热度按时间nfeuvbwi1#
我相信你的思路基本上是对的,但是,由于你已经定义了列(例如,Master_Father_ID、Hierarchy和Child_ID),所以你不需要动态SQL。
下面的代码不是递归CTE,而是WHILE循环,以帮助理解发生了什么。转换为递归CTE很容易,但我认为(特别是对许多熟悉其他编程语言的人来说)WHILE循环更熟悉。
下面的逻辑如下
1.找到任何"大师级人物"并将其插入工作表
1.对于在上一步中插入的每个值,查找其直接子项并将其插入到工作表中
1.重复上面的步骤2(例如,第3轮找到子节点的子节点;轮4找到子的子的子)直到不再插入数据
然后,最终报告只需忽略插入的初始Master_Father行(例如,上表中的001、002等)。
下面的SQL也可以在this db<>fiddle中找到-注意,我添加了额外的数据用于演示。
初始数据
现在来看工作表--我将其命名为#summary_table。Current_ID表示该行的当前Child_ID(例如,在层次结构的最底层)。insert_round是该行被插入的舍入数(循环迭代)。
我还有两个变量:@round表示我们到达了哪一轮/周期,@n表示当前轮中插入的行数。
主父亲是那些本身不是子的父亲,例如,Father_Id1不是子,因此它被包括; Father_ID 3是子对象,因此不是主对象。
注意,我使用
RIGHT(N'000' + LTRIM(STR(int value)), 3)
将任何整数转换为3位数格式的字符串。(Also第3步:重复,直到不再发生)
从最后一轮插入(由#summary_table. insert_round标识)中获取数据,标识任何子代并将其数据插入工作表。
继续执行此操作,直到不再插入任何行。
最后的报告
数据如下
注意-这假设你的原始数据设置正确,没有循环/等(例如,如果凯尔里斯是约翰康纳的孩子以及他的父亲......呃对不起,'spolier alert')。如果你有这些,你需要添加额外的检查-这些检查将取决于你实际上想如何处理这些循环。
rbl8hiat2#
您应该能够使用Recursive CTE完成此操作。
我还提供了一个构建SQL Server层次结构数据类型(
HIERARCHYID
)的示例。有关详细信息,请参见代码中的注解
结果