【问题标题】:Recursive CTE and SELECT递归 CTE 和 SELECT
【发布时间】:2017-07-11 09:12:49
【问题描述】:

我写了一个相当简单的递归 CTE 语句。目的是查找一个结构并返回顶层项目

这里是代码

WITH cte_BOM(parent_serial_id, serial_id, serial_batch_no, sort)

AS (SELECT BOM.parent_serial_id, BOM.serial_id, p.serial_batch_no, 1
FROM serial_status AS BOM
INNER JOIN item_serial_nos p ON BOM.parent_serial_id = p.serial_id
WHERE BOM.serial_id = '16320' AND BOM.is_current = 'Y'

UNION ALL

SELECT
BOM1.parent_serial_id, bom1.serial_id, p1.serial_batch_no, cte_BOM.sort + 1
FROM cte_BOM
INNER JOIN serial_status AS BOM1 ON cte_BOM.parent_serial_id = 
BOM1.serial_id
INNER JOIN item_serial_nos p1 ON BOM1.parent_serial_id = p1.serial_id
WHERE BOM1.is_current = 'Y'
)

SELECT TOP 1
cte_BOM.*
FROM
cte_BOM
ORDER BY sort desc

如您所见,我现在只是硬编码serial_id。我现在需要完成的是针对数据子集运行此 cte。我现在被困在如何做到这一点上。 因此,我将通过另一个 select 语句生成一个 serial_ids 列表,然后为每一行使用这个 serial_id 代替当前硬编码的那些并返回第一条记录。重要的是,如果 serial_id 没有仍应返回行的父级

第二个SELECT 是这样的:

SELECT serial_id
FROM
item_serial_nos
WHERE
item_serial_nos.item_id = '15683'

任何建议表示赞赏。 (使用 SQL 2008 R2)

【问题讨论】:

    标签: sql recursion sql-server-2008-r2 common-table-expression


    【解决方案1】:

    如果我正确理解您的目标,您可以从 cte 中删除硬编码的 serial_id 并添加开头 serial_id

    WITH    cte_BOM(parent_serial_id, serial_id, serial_batch_no, sort, Original_Serial_id)
    AS      (
                SELECT      BOM.parent_serial_id
                ,           BOM.serial_id
                ,           p.serial_batch_no
                ,           1
                ,           BOM.serial_id
                FROM        serial_status AS BOM
                INNER JOIN  item_serial_nos p 
                        ON  BOM.parent_serial_id = p.serial_id
                WHERE       BOM.is_current = 'Y'
    
                UNION ALL
    
                SELECT      BOM1.parent_serial_id
                ,           bom1.serial_id
                ,           p1.serial_batch_no
                ,           cte_BOM.sort + 1
                ,           cte_BOM.Original_Serial_id
                FROM        cte_BOM
                INNER JOIN  serial_status AS BOM1 
                        ON  cte_BOM.parent_serial_id = BOM1.serial_id
                INNER JOIN  item_serial_nos p1 
                        ON  BOM1.parent_serial_id = p1.serial_id
                WHERE       BOM1.is_current = 'Y'
    )
    
    SELECT      cte_BOM.*
    FROM        cte_BOM
    INNER JOIN  (
                    SELECT      cte_BOM.Original_Serial_id
                    ,           MAX(sort) sort_max
                    FROM        cte_BOM
                    WHERE       cte_BOM.Original_Serial_id IN (
                                                                    SELECT  serial_id
                                                                    FROM    item_serial_nos
                                                                    WHERE   item_serial_nos.item_id = '15683'
                                                                )
                    GROUP BY    cte_BOM.Original_Serial_id
                ) max_cte
            ON  max_cte.Original_Serial_id = cte_BOM.Original_Serial_id
            AND max_cte.sort_max = cte_BOM.sort
    

    递归 CTE 仅在从最后一个 select 调用时执行,因此它仅对在 IN 查询中选择的那些记录执行。因此,您不应该因此受到性能影响。

    【讨论】:

    • 谢谢,我认为我们在正确的轨道上,除了这一点: item_serial_nos.item_id = '15683' 应该返回 400 行,目前它只返回一行?
    • 好吧,您选择的是top 1。我忽略了这一点,但如果你有超过 1 个serial_id,你应该改变它。
    • 我认为这仍然是必要的,因为前 1 确保 cte 只返回第一个结果,如果我取出它将返回所有以前的级别。
    • 如果您只选择一个serial_id 是正确的,如果您希望为每个选定的serial_id 提供一条记录,则必须找到不同的解决方案。我认为您无法访问row_numberdense_rank 分析函数,因此您必须使用max() 进行子查询。编辑答案以显示您如何做到这一点。
    • 嗨,似乎只是工作,只是进行一些进一步的测试。谢谢。
    猜你喜欢
    • 2017-08-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-12-25
    • 1970-01-01
    • 2015-09-10
    • 1970-01-01
    • 2014-08-13
    相关资源
    最近更新 更多