我们一直使用User-Defined Table Types将整数列表传递给存储过程。
然后我们使用它们连接到存储过程查询中的其他表。
例如:
CREATE PROCEDURE [dbo].[sp_Name]
(
@Ids [dbo].[OurTableType] READONLY
)
AS
SET Nocount ON
SELECT
*
FROM
SOMETABLE
INNER JOIN @Ids [OurTableType] ON [OurTableType].Id = SOMETABLE.Id
使用更大的数据集时,我们发现这方面的表现很差。
我们用来加快速度的一种方法是将内容转储到临时表中,然后将其加入。
例如:
CREATE PROCEDURE [dbo].[sp_Name]
(
@Ids [dbo].[OurTableType] READONLY
)
AS
SET Nocount ON
CREATE TABLE #TempTable(Id INT)
INSERT INTO #TempTable
SELECT Id from @Ids
SELECT
*
FROM
SOMETABLE
INNER JOIN #TempTable ON #TempTable.Id = SOMETABLE.Id
DROP TABLE #TempTable
这确实显着提高了性能,但我希望对这种方法以及我们未考虑的任何其他后果有所了解。此外,解释为什么这可以提高性能也可能有用。
N.B。有时我们可能需要传递的不仅仅是一个整数,因此我们不使用逗号分隔列表或类似的东西。
答案 0 :(得分:15)
此主题已在之前讨论过。 JOIN性能不佳的主要原因是表值参数(TVP)是表变量。表变量不保留统计信息,并且查询优化器看起来只有1行。因此他们可以做INSERT INTO Table (column_list) SELECT column_list FROM @TVP;
而不是JOIN。
有几件事要试图解决这个问题:
将所有内容转储到本地临时表(您已经在执行此操作)。这里的一个技术缺点是你正在tempdb
复制传入TVP的数据(TVP和临时表都存储了他们的数据)。
也许尝试将用户定义的表类型定义为具有群集主键。您可以在[Id]
字段内联:
[ID] INT NOT NULL PRIMARY KEY
不确定这对性能有多大帮助,但值得一试。
您可以尝试将OPTION (RECOMPILE)
添加到查询中。这是一种让查询优化器查看表变量中有多少行的方法,以便它可以有适当的估计值。
SELECT column_list
FROM SOMETABLE
INNER JOIN @Ids [OurTableType]
ON [OurTableType].Id = SOMETABLE.Id
OPTION (RECOMPILE);
这里的缺点是你有一个RECOMPILE
,每次调用此proc时需要额外的时间。但这可能是整体净收益。
从SQL Server 2014开始,您可以利用内存中OLTP并为用户定义的表类型指定WITH (MEMORY_OPTIMIZED = ON)
。有关详细信息,请参阅Scenario: Table variable can be MEMORY_OPTIMIZED=ON。我听说这绝对有帮助。遗憾的是,在SQL Server 2014和SQL Server 2016 RTM中,此功能仅适用于64位企业版。但是,从SQL Server 2016 SP1开始,此功能可供所有版本使用(可能的例外是SQL Server Express LocalDB)。
SQL Server 2019引入了“Table variable deferred compilation”:
使用表变量延迟编译,引用表变量的语句的编译将延迟到第一次实际执行语句。此延迟编译行为与临时表的行为相同。此更改导致使用实际基数而不是原始的单行猜测。
有关详细信息,请参阅链接文档。
PS。不要SELECT *
。始终指定列列表。除非做IF EXIST(SELECT * FROM)...
。