【问题标题】:Is it possible to use a value from a result to union another result into the previous resultset?是否可以使用结果中的值将另一个结果合并到前一个结果集中?
【发布时间】:2017-04-21 03:52:02
【问题描述】:

假设我有一张桌子,BMST。我使用此查询来查找结果集:

SELECT PARENT,
       CHILD,
       LEVEL,
       QTY
FROM   BMST
WHERE  PARENT = '111'          

在结果集中是父部件号,以及儿童部件号,例如:

PARENT | CHILD  | LEVEL | QTY 
-----------------------------
111    | 222    | 0     | 2
111    | 333    | 0     | 1
111    | 444    | 0     | 1

该表提供了有关用于制作 PARENT 零件的所有 CHILD 零件的信息,以及每个 PARENT 零件中使用的 CHILD 零件的数量。 LEVEL 列的值为“0”,因为“111”部分是我们关心的原始部分。我们不关心“111”部分是否是另一个更大的 PARENT 部分的 CHILD 部分。

如果 CHILD 部件由较小的 CHILD 部件组成,那么它们也可以是 PARENT 部件。例如,这个查询:

SELECT PARENT,
       CHILD,
       LEVEL,
       QTY
FROM   BMST
WHERE  PARENT = '222'

会返回:

PARENT | CHILD  | LEVEL | QTY 
-----------------------------
222    | 555    | 1     | 1
222    | 666    | 1     | 1
222    | 777    | 1     | 1

这个新表中的 LEVEL 值为 '1',因为部分 '222' 是 LEVEL = '0' PARENT 部分的 CHILD 部分。

更进一步,'222' 部分的 CHILD 部分本身可能有 CHILD 部分,因此对于 '777' 部分的类似查询将返回:

PARENT | CHILD  | LEVEL | QTY 
-----------------------------
777    | 999    | 2     | 2   

我的问题是,是否可以创建一个返回第一个结果集的查询,然后检查该结果集中的所有 CHILD 部分值以查看它们是否有任何 CHILD 部分,然后检查结果CHILD 零件用于更多的 CHILD 零件等,直到没有更多的 CHILD 零件,然后将这些零件合并到第一个结果集中,如下所示:

PARENT | CHILD  | LEVEL | QTY 
-----------------------------
111    | 222    | 0     | 2
222    | 555    | 1     | 1
222    | 777    | 1     | 1
777    | 999    | 2     | 2
222    | 888    | 1     | 1
111    | 333    | 0     | 1
111    | 444    | 0     | 1

随着查询的深入,LEVEL 值需要增加,最终结果集应该显示进入请求的父部分的每个部分。

有没有办法在 SQL 中完成所有这些工作?还是我必须使用 VB6 或其他程序来遍历循环?感谢您提供任何和所有反馈。

【问题讨论】:

  • 使用Recursive CTE。一定要查找该主题
  • 您能否为上述输出提供准确的输入数据。
  • 您可以使用 CTE 解决您的问题。谢谢
  • 我一定会阅读 CTE,感谢您为我指明正确方向的朋友!
  • 问这个问题的方式呈现了 XY 问题的某些方面...meta.stackexchange.com/questions/66377/what-is-the-xy-problem。您可能想阅读它并尝试将其归结为您真正需要完成的事情,而不是预设解决方案。

标签: sql sql-server sql-server-2008 tsql vb6


【解决方案1】:

您应该研究 SELF JOIN 而不是联合。您可以将表连接到自身,从而链接父 ID 和子 ID。它将需要一个子句来消除与其自身连接的行。 child ID 本质上是您示例中的主键,parent ID 充当某种外键。

这是一个使用 Microsoft Access 完成的示例,因为它很方便:

表 BMST:

PARENT  CHILD   LEVEL   QTY
111 222 0   2
111 333 0   1
111 444 0   1
222 555 1   1
222 666 1   1
222 777 1   1
555 aaa 2   11
555 aab 2   12
aaa xxx 3   100
aab www 3   111
aaa UUU 3   121

查询:

SELECT c.PARENT, c.CHILD, c.[LEVEL], c.QTY
FROM BMST AS P, BMST AS C
WHERE P.child = C.parent
and P.parent <> C.parent
ORDER BY c.level;

结果:

PARENT  CHILD   LEVEL   QTY
222 777 1   1
222 666 1   1
222 555 1   1
555 aab 2   12
555 aaa 2   11
aaa UUU 3   121
aab www 3   111
aaa xxx 3   100

请注意,我发明了一些额外的记录来证明这涵盖了层次结构的所有级别。该查询不完善,因为排除了最顶层的父级(本身没有父级),这可能可以通过使用外连接来解决。

奇怪的是,这个特定示例查询的结果与表本身非常相似,但这是在应用任何其他条件之前,例如您真正感兴趣的父元素。

这个问题可以提供更多信息:Explanation of self-joins

【讨论】:

  • SELF JOIN 的“问题”是您不会得到“下一个”结果。例如,如果您将 tableA 与 tableA 连接,您将获得 1 的递归级别,其中 CTE 将为您提供 N 的递归级别,其中 N 是可能的最深(但在 SQL Server 的情况下限制为 100)。
  • @Mark Kremers 感谢您的评论,但我不明白为什么自加入无法达到任何级别。我刚刚用一个例子更新了我的答案,表明它就是这样做的。
  • 哇,你让我学到了一些新东西。我不知道这个。太好了!!
【解决方案2】:

要做你想做的事,你需要一种叫做递归的东西。当然,您可以逐行解析它(使用 T-SQL,或使用 VB,或任何您熟悉的语言),但是,这个问题(递归)很容易用称为 Common Table ExpressionsCTE 的东西来解决.

使用CTE,您可以结合您的结果,因此在这种情况下,可以解决 CHILD 可以是 PARENT 的 PARENT-CHILD 关系。

我创建了这个脚本来向您展示如何操作。首先我填充一些临时表,然后我使用CTE进行查询

if object_id('tempdb..#BMST') is not null
begin
  drop table #BMST
end

create table #BMST (
  PARENT varchar(5)
, CHILD varchar(5)
, LEVEL varchar(5)
, QTY varchar(5)
)

insert into #BMST
          select    '111', '222', 0, 2
union all select    '111', '333', 0, 1
union all select    '111', '444', 0, 1

union all select    '222', '555', 1, 1
union all select    '222', '666', 1, 1
union all select    '222', '777', 1, 1

union all select    '777', '999', 2, 2

Blow 是CTECommon Table Expression 始终必须是第一条语句,因此为此使用分号。之后,with xxx as () 构造开始。 results 是一个虚构的名称,可以是任何名称。 (在这个例子中,我使用了一个新的 colom SECONDLEVEL 来向你展示新的关卡)

;with results as (
select  *
      , 0 as SECONDLEVEL
from    #BMST b
union all
select  b.*
      , r.SECONDLEVEL + 1 as SECONDLEVEL
from    #BMST b
        inner join results r
          on  r.CHILD = b.PARENT
          and b.LEVEL > r.LEVEL

)
select  *
from    results

如您所见,我使用的是UNION ALL 运算符。上半部分是查询#temp 表,下半部分是用来连接已经获取的结果。

就是这样。你现在有递归了。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-03-24
    • 2014-05-25
    • 2017-02-17
    • 2013-07-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多