Sql Server - 外部应用与子查询

时间:2011-03-25 15:47:47

标签: sql sql-server common-table-expression

请在Sql Server中考虑以下两个语句:

这个使用嵌套子查询:

    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

这使用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

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

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

还有几个问题:

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

2 个答案:

答案 0 :(得分:4)

首先,摆脱CTE中的Top 100 Percent。你没有在这里使用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

另一种可能性

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

答案 1 :(得分:1)

也许我会使用托马斯查询的这种变体:

WITH cte AS
(
SELECT *, Row_Number() Over( Partition By SegmentId Order By InvoiceDetailID, SegmentId ) As Num
FROM Segments)
SELECT seg.*, t.DepartureInfo AS ReturnDate, t.DepartureCityCode
FROM Segments seg LEFT JOIN cte t ON seg.InvoiceDetailID = t.InvoiceDetailID AND t.SegmentID > seg.SegmentID AND t.Num = 1