带有表变量/临时表的 SQL 查询 - 哪个性能好

问题描述

sql 问题: 这个插入查询性能好吗?


UserIds List 是可变的,它的长度可以是 10、20 ...直到 500。

WITH Users
  AS (SELECT [Id] from [electro].[User] Where [Id] IN (4438,15473,22497,22494,4425,4426,22496))
  INSERT INTO [electro].[ElectronicCorrespondenceInBox] ([UserId],[ElectronicCorrespondenceId],[CreatedAt])
  SELECT [User].[Id],[Corrs].[Id],GETDATE()
FROM [electro].[ElectronicCorrespondence] AS [Corrs],[Users] AS [User]
WHERE [Corrs].[Published] = 0;


创建包含用户 ID 的临时变量是个好主意吗?

带有表变量的选项 2#:

DECLARE @USERS TABLE
([UserId] INT NOT NULL);

INSERT INTO @USERS
VALUES (2350),(4438),(15473),(22497),(22494),(4425),(4426),(22496);

INSERT INTO [electro].[ElectronicCorrespondenceInBox] ([UserId],[CreatedAt])
SELECT [User].[UserId],@USERS AS [User]
WHERE [Corrs].[Published] = 0;

解决方法

首先,停止使用旧式连接语法 - 它不精确且容易出错;使用现代显式 join 语法,请参阅 Bad join habbits

在这种情况下使用 temp table 是不必要的,并且在可能行数未知的大多数情况下是不可取的。 SQL 2019 有一种叫做延迟表变量编译的东西,它可以提高临时查询的表变量性能,但对于缓存执行计划仍然存在问题。

这是因为当您使用表变量时,尤其是在 SQL2019 之前,SQL Server 在任何意义上都将其视为变量,因为变量可以存储单个值,并且 SQL Server 假定只会出现 1 行,无论它实际包含 1 行还是 100 万行。

这可能会导致在联接到表变量时执行计划不佳,从而导致不合适的联接操作、表扫描操作和低估的内存授予,从而导致 tempdb 使用,因为它始终被假定为轻量级操作。

您的查询可能更好地编写如下,因为您要在结果中引入笛卡尔积,所以最好使用正确的连接语法进行明确。这还假设您需要将 ID 值列表与 Users 表中实际存在的值相关联,正如您的第一个查询所暗示的

insert into electro.ElectronicCorrespondenceInbox (UserId,ElectronicCorrespondenceId,CreatedAt)
select u.Id,ec.Id,GetDate()
from electro.ElectronicCorrespondence ec
cross join electro.Users u 
where ec.Published = 0
and u.id in (4438,15473,22497,22494,4425,4426,22496)

如果您实际上并不需要这个,正如您的第二个查询所暗示的那样,那么您可以简单地将 values 的列表直接包含在您的 CTE 中,而无需触及您的 User

 with users as (
    select id from (values(4438),(15473),(22497),(22494),(4425),(4426),(22496))v(Id)
)

最后,如果这打算成为您传递可变数量 ID 值的过程的一部分,例如使用 table-type 参数,您可能会发现最佳性能将将传入的表类型参数中的值插入到正确的 #temporary table 中并在查询中使用它的结果。