存储过程中视图与临时表中的SQL CTE

时间:2011-01-30 15:38:13

标签: sql sql-server performance common-table-expression temp-tables

请耐心等待 - 我知道这很复杂。

我有一张包含公寓的桌子,另一张包含这些公寓的租约。我的任务是从列表中选择“最相关”的租约。一般来说,这意味着最近的租约,但有一些怪癖使得它比按日期订购更复杂。

这导致我在View中创建这个公共表表达式查询,然后我在存储过程中与其他一些人一起加入以获得我需要的结果:

WITH TempTable AS (
    SELECT  l.BuildingID, l.ApartmentID, l.LeaseID, l.ApplicantID,
                ROW_NUMBER() OVER (PARTITION BY l.ApartmentID ORDER BY s.Importance DESC, MovedOut, MovedIN DESC, LLSigned DESC, Approved DESC, Applied DESC) AS 'RowNumber'
    FROM    dbo.NPleaseapplicant AS l INNER JOIN
            dbo.NPappstatus AS s ON l.BuildingID = s.BuildingID AND l.AppStatus = s.Code

)

SELECT  BuildingID, ApartmentID, LeaseID, ApplicantID
FROM    TempTable
WHERE   RowNumber = 1

这可以工作并返回正确的结果。我面临的挑战是表现非常缓慢。

作为测试,我在存储过程中创建了一个临时表而不是使用View,并获得了更好,更好的性能:

CREATE TABLE #Relevant (
    BuildingID int,
    ApartmentID int,
    LeaseID int,
    ApplicantID int,
    RowNumber int
)

INSERT INTO #Relevant (BuildingID, ApartmentID, LeaseID, ApplicantID, RowNumber)
SELECT  l.BuildingID, l.ApartmentID, l.LeaseID, l.ApplicantID,
            ROW_NUMBER() OVER (PARTITION BY l.ApartmentID ORDER BY s.Importance DESC, MovedOut, MovedIN DESC, LLSigned DESC, Approved DESC, Applied DESC) AS 'RowNumber'
FROM    dbo.NPleaseapplicant AS l INNER JOIN
        dbo.NPappstatus AS s ON l.BuildingID = s.BuildingID AND l.AppStatus = s.Code
WHERE   (l.BuildingID = @BuildingID)

DROP TABLE #Relevant

乍一看,这并不等于我。我听说临时表对于性能来说非常糟糕。皱纹是我能够更好地限制临时表中的查询与WHERE子句,我不能在视图中。在表中的16个建筑物中有超过10,000个租约,使用WHERE过滤的能力可能会使受影响的行减少90% - 95%。

铭记所有这些,有什么明显的东西我在这里失踪了吗?我是否在View中做错了可能会导致可怕的性能,或者仅仅是Temp Table中较小的结果集击败了CTE中不受限制的结果集?

编辑:我应该补充一点,选择“最相关的租约”的业务逻辑是系统中许多报告的关键。这就是为什么它被放置在View中以开始。 View为我们提供了“一次写入,多次使用”功能,而存储过程中的临时表则需要为系统中的每个其他存储过程重新创建。丑陋。

编辑#2:我可以使用基于表格的功能而不是视图吗?这是否允许我限制前面受影响的行,并仍然使用JOIN中的结果数据集与其他表?如果它有效 - 并且具有良好的性能 - 这将允许我将业务逻辑保持在一个位置(该功能),而不是在几十个存储过程中复制它。

2 个答案:

答案 0 :(得分:4)

只是为了鞠躬,这就是我最终做的事情:

我没有使用View来连接2或3个表中的所有可能行,而是创建了一个基于表的函数,它可以生成相同的基本查询。作为我在Building ID中传递的参数之一,并在WHERE子句中使用它,如下所示:

SELECT  l.BuildingID, l.ApartmentID, l.LeaseID, l.ApplicantID,
            ROW_NUMBER() OVER (PARTITION BY l.ApartmentID ORDER BY s.Importance DESC, MovedOut, MovedIN DESC, LLSigned DESC, Approved DESC, Applied DESC) AS 'RowNumber'
FROM    dbo.NPleaseapplicant AS l INNER JOIN
        dbo.NPappstatus AS s ON l.BuildingID = s.BuildingID AND l.AppStatus = s.Code
WHERE  (l.BuildingID = @BuildingID)

结果是彻底减少了所需的连接数,并且它极大地加快了查询速度。

然后我改变所有依赖于View的存储过程来代替使用函数,以及宾果 - 巨大的性能提升。

答案 1 :(得分:0)

您还可以使用子查询语法重写视图:

SELECT  BuildingID, ApartmentID, LeaseID, ApplicantID
FROM
(
SELECT  l.BuildingID, l.ApartmentID, l.LeaseID, l.ApplicantID,
                ROW_NUMBER() OVER (PARTITION BY l.ApartmentID ORDER BY s.Importance DESC, MovedOut, MovedIN DESC, LLSigned DESC, Approved DESC, Applied DESC) AS 'RowNumber'
    FROM    dbo.NPleaseapplicant AS l INNER JOIN
            dbo.NPappstatus AS s ON l.BuildingID = s.BuildingID AND l.AppStatus = s.Code

)subquery
WHERE   RowNumber = 1

执行此操作将允许将边界Where(使用视图)应用于子查询,而CTE情况不受限制。

关于并行执行计划的视图比表值函数更少(虽然这可能会被内联,使它们实际上相同)