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

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

请耐心听我说——我知道这很复杂。

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

这促使我在 View 中创建这个公共(public)表表达式查询,然后将其与存储过程中的许多其他查询连接起来以获得我需要的结果:

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 子句更好地限制临时表中的查询,而在 View 中却不能。表中的 16 座建筑物有超过 10,000 个租赁,使用 WHERE 进行过滤的功能可能会删除受 90% - 95% 影响的行。

考虑到这一切,我在这里遗漏了什么明显的东西吗?我是否对 View 做了一些错误,可能会导致糟糕的性能,或者只是临时表中较小的结果集击败了 CTE 中不受限制的结果集?

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

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

最佳答案

只是为了向这个鞠躬,这就是我最终所做的:

我没有使用 View 来连接 2 个或 3 个表中的所有可能的行,而是创建了一个基于表的函数来进行相同的基本查询。作为参数之一,我传入建筑物 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 的存储过程更改为使用函数,宾果游戏——巨大的性能提升。

关于 View 中的 SQL CTE 与存储过程中的临时表,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/4843731/

相关文章:

java - 如何从 GWT 访问 SQL 数据库?

sql-server - 使用 SQL FOR XML 语句生成 XML 注释

mysql - 使用 VB.net 的远程 mysql 速度慢 - 需要加速

sql - 在数据库 : relational or multidimensional? 中存储多维数组

c++ - 在 C++ 中将多个数字读入单个有符号字符

mysql - 如何在mysql中编写这个特定的查询?

php - 如何反转 MySQL 查询的输出顺序

php - SQL 外键用法?它到底有什么作用?什么时候需要?

MySQL 到 SQL Server 选择语句

sql-server - SQL Server 是否缓存 View 的执行计划?