Sql Server - 外部应用与子查询

r_h*_*ney 7 sql sql-server common-table-expression

请考虑Sql Server中的以下2个语句:

这个使用嵌套子查询:

    WITH cte AS
(
    SELECT TOP 100 PERCENT *
    FROM Segments
    ORDER BY InvoiceDetailID, SegmentID
)
SELECT *, ReturnDate =
                (SELECT TOP 1 cte.DepartureInfo
                    FROM cte
                    WHERE seg.InvoiceDetailID = cte.InvoiceDetailID
                        AND cte.SegmentID > seg.SegmentID), 
            DepartureCityCode =
                (SELECT TOP 1 cte.DepartureCityCode
                    FROM cte
                    WHERE seg.InvoiceDetailID = cte.InvoiceDetailID
                        AND cte.SegmentID > seg.SegmentID)
FROM Segments seg
Run Code Online (Sandbox Code Playgroud)

这使用OUTER APPLY运算符:

    WITH cte AS
(
    SELECT TOP 100 PERCENT *
    FROM Segments
    ORDER BY InvoiceDetailID, SegmentID
)
SELECT seg.*, t.DepartureInfo AS ReturnDate, t.DepartureCityCode
FROM Segments seg OUTER APPLY (
                SELECT TOP 1 cte.DepartureInfo, cte.DepartureCityCode
                FROM cte
                WHERE seg.InvoiceDetailID = cte.InvoiceDetailID
                        AND cte.SegmentID > seg.SegmentID
            ) t
Run Code Online (Sandbox Code Playgroud)

考虑到两个Segments表可能有数百万行,这两个中哪一个可能表现更好?

我的直觉是外部应用会表现得更好.

还有几个问题:

  1. 几乎我对此非常肯定,但仍然想确认在第一个解决方案中,CTE将被有效地执行两次(因为它引用了两次,CTE像宏一样内联扩展).
  2. 当在OUTER APPLY运算符中使用CTE时,每行会执行一次吗?当在第一个语句中的嵌套查询中使用时,它也会被执行吗?

Tho*_*mas 4

首先,去掉Top 100 PercentCTE 中的 。此处未使用 TOP,如果您希望对结果进行排序,则应在整个语句的末尾添加 Order By。其次,为了解决您关于性能的问题,如果被迫进行猜测,我的赌注将是第二种形式,因为它有一个子查询而不是两个。第三,您可以尝试的另一种形式是:

With RankedSegments As
    (
    Select S1.SegmentId, ...
        , Row_Number() Over( Partition By S1.SegmentId Order By S2.SegmentId ) As Num
    From Segments As S1
        Left Join Segments As S2
            On S2.InvoiceDetailId = S1.InvoiceDetailId
                And S2.SegmentId > S1.SegmentID
    )
Select ...
From RankedSegments
Where Num = 1
Run Code Online (Sandbox Code Playgroud)

另一种可能

With MinSegments As
    (
    Select S1.SegmentId, Min(S2.SegmentId) As MinSegmentId
    From Segments As S1
        Join Segments As S2
            On S2.InvoiceDetailId = S1.InvoiceDetailId
                And S2.SegmentId > S1.SegmentID
    Group By S1.SegmentId
    )
Select ...
From Segments As S1
    Left Join (MinSegments As MS1
        Join Segments As S2
            On S2.SegmentId = MS1.MinSegmentId)
        On MS1.SegmentId = S1.SegmentId
Run Code Online (Sandbox Code Playgroud)