【问题标题】:SQL CTE in a View vs Temp Table in a Stored Procedure视图中的 SQL CTE 与存储过程中的临时表
【发布时间】:2011-06-18 03:23:56
【问题描述】:

请多多包涵——我知道这很复杂。

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

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

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% 受影响的行。

考虑到所有这些,我在这里有什么明显的遗漏吗?我是否对可能导致可怕性能的视图做错了什么,还是只是临时表中较小的结果集击败了 CTE 中的无限制结果集?

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

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

【问题讨论】:

  • 请问什么版本的 SQL Server? 2008 年以上的 ROW_NUMBER 更好
  • 1.您如何从视图中选择数据(是否通过建筑物 ID 进行过滤)? 2.请发布表结构和索引 3.请发布两个查询(坏和好)的执行计划
  • 不幸的是,它是 SQL Server 2005。由托管公司管理,因此我无法控制升级。
  • 数据是从带有 BuildingID @oryol 过滤器的视图中选择的。执行计划的图形版本非常庞大。有没有更好的展示方式?
  • 执行 set showplan_text on 和之后的查询。另请添加用于创建数据库结构(表和索引)的帖子脚本

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


【解决方案1】:

只是对这个鞠躬,这就是我最终做的事情:

我没有使用视图来连接 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)

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

然后我将所有依赖视图的存储过程改为使用函数,然后宾果游戏——巨大的性能提升。

【讨论】:

    【解决方案2】:

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

    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 案例没有边界。

    与表值函数相比,视图在并行执行计划方面的问题更少(尽管这个可能无论如何都会被内联,使它们实际上是相同的)

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2014-12-22
      • 2011-10-29
      • 2010-10-14
      • 2011-09-14
      • 2013-09-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多