Poor performance of SQL query with Table Variable or User Defined Type

mik*_*kus 4 sql t-sql sql-server dynamic-sql table-variable

I have a SELECT query on a view, that contains 500.000+ rows. Let's keep it simple:

SELECT * FROM dbo.Document WHERE MemberID = 578310
Run Code Online (Sandbox Code Playgroud)

The query runs fast, ~0s

Let's rewrite it to work with the set of values, which reflects my needs more:

SELECT * FROM dbo.Document WHERE MemberID IN (578310)
Run Code Online (Sandbox Code Playgroud)

This is same fast, ~0s

But now, the set is of IDs needs to be variable; let's define it as:

    DECLARE @AuthorizedMembers TABLE
    (
        MemberID BIGINT NOT NULL PRIMARY KEY,    --primary key 
        UNIQUE NONCLUSTERED (MemberID) -- and index, as if it could help...
    );

    INSERT INTO @AuthorizedMembers SELECT 578310
Run Code Online (Sandbox Code Playgroud)

The set contains the same, one value but is a table variable now. The performance of such query drops to 2s, and in more complicated ones go as high as 25s and more, while with a fixed id it stays around ~0s.

SELECT * 
FROM dbo.Document 
WHERE MemberID IN (SELECT MemberID FROM @AuthorizedMembers)
Run Code Online (Sandbox Code Playgroud)

is the same bad as:

SELECT * 
FROM dbo.Document 
WHERE EXISTS (SELECT MemberID 
              FROM @AuthorizedMembers 
              WHERE [@AuthorizedMembers].MemberID = Document.MemberID)
Run Code Online (Sandbox Code Playgroud)

or as bad as this:

SELECT * 
FROM dbo.Document 
INNER JOIN @AuthorizedMembers AS AM ON AM.MemberID = Document.MemberID
Run Code Online (Sandbox Code Playgroud)

The performance is same for all the above and always much worse than the one with a fixed value.

动态SQL带有帮助,因此很容易,因此创建类似的nvarchar (id1,id2,id3)并使用它构建固定的查询可以节省查询时间~0s。但是我想尽可能避免使用Dynamic SQL,如果这样做,无论值如何(使用参数-上面的方法不允许),我都希望它始终保持相同的字符串。

有什么主意如何获得类似于固定值数组的表变量的性能,或者避免为每次运行构建不同的动态SQL代码?

PS我已经用用户定义的类型尝试了上面的结果相同

编辑: 带有临时表的结果,定义为:

CREATE TABLE #AuthorizedMembers
    (
        MemberID BIGINT NOT NULL PRIMARY KEY
    );

    INSERT INTO #AuthorizedMembers SELECT 578310
Run Code Online (Sandbox Code Playgroud)

缩短了3倍的执行时间。(13s-> 4s)。它仍然显着高于动态SQL <1s。

TT.*_*TT. 5

您的选择:

  • 使用临时表而不是TABLE变量
  • 如果您坚持使用TABLE变量,请OPTION(RECOMPILE)在查询末尾添加

说明:

当编译器编译您的语句时,该TABLE变量中没有行,因此没有适当的基数。这导致执行计划效率低下。OPTION(RECOMPILE)强制语句在运行时重新编译。那时TABLE变量中有行,并且编译器具有更好的基数来生成执行计划。

一般的经验法则是在对大型数据集进行操作时使用临时表,对具有频繁更新的小型数据集使用表变量。我个人很少使用TABLE变量,因为它们通常表现不佳。

我可以在“ SQL Server中的临时表和表变量之间有什么区别?”的问题上推荐此答案。如果您想深入分析差异。