table 函数中的条件 UNION ALL

Conditional UNION ALL in table function

所以用例如下 - 有一些参数,我想根据这些参数 select 来自一个 table 或另一个的数据。

create table dbo.TEST1 (id int primary key, name nvarchar(128))
create table dbo.TEST2 (id int primary key, name nvarchar(128))

所以我创建了这样的函数:

create function [dbo].[f_TEST]
(
    @test bit
)
returns table
as
return (
    select id, name from TEST1 where @test = 1

    union all

    select id, name from TEST2 where @test = 0
)

当我 运行 它带有常量时,执行计划很棒 - 只扫描了一个 table

select * from dbo.f_TEST(1)

但是,当我使用变量时,计划并不是那么好 - table 都被扫描了

declare @test bit = 1

select * from dbo.f_TEST(@test)

那么是否有任何提示(或技巧)强制 SQL 服务器理解在某个查询中只应扫描一个 table?

如果您的函数是 inline-TVP(如示例所示),那么您可以使用:

declare @test bit = 1
select * from dbo.f_TEST(@test) OPTION (RECOMPILE);

那么在这两种情况下,您都将进行单聚簇索引扫描。

DBFiddle Demo

来自Option RECOMPILE

When compiling query plans, the RECOMPILE query hint uses the current values of any local variables in the query and, if the query is inside a stored procedure, the current values passed to any parameters.

不妨试试

select top (@test*100) percent id, name from TEST1 

union all

select top ((1-@test)*100) percent id, name from TEST2
当您的函数用于 table 时,

OPTION (RECOMPILE) 不会在这里帮助您。这将查询,例如,将同时扫描 tables

-- 3rd table to test against
create table dbo.TEST3 (id int primary key, test bit);
insert dbo.TEST3 values(1,1),(2,1),(3,0),(4,1);
GO

select TEST3.* 
from TEST3 
CROSS APPLY dbo.f_TEST(test3.test) 
OPTION (RECOMPILE);

不过没关系。我时间不够(否则我会附上屏幕截图)但是如果你 运行 这三个查询和实际的执行计划你会看到优化器认为这些具有相同的成本:

DECLARE @test int = 1

select * from dbo.f_TEST(1)
select * from dbo.f_TEST(@test)
select * from dbo.f_TEST(@test) OPTION (RECOMPILE)

第二个查询看起来是第一个和最后一个查询的两倍,但是,当您将鼠标悬停在 SELECT 运算符上时,您会看到这是因为优化器正在估计两行而不是 1 (与其他两个一样)。

如果您进行一些性能测试,您会发现,在这种情况下,优化器可能是正确的。

您的代码的更大问题是每个 table 都保证进行 table 扫描,因为您对这两个查询都没有过滤器。如果可能的话,添加一个过滤器将使您能够以一种搜索而不是扫描的方式为这两个 table 建立索引。

它按原样运行良好。在更改参数值时查看相关 table 上的 "Number of Executions"。将被排除的table出现在计划中,因为他们必须被考虑,但这并不意味着他们将被扫描。

此外,请查看过滤器上的启动表达式:

使用存储过程而不是 table 函数,但要注意参数嗅探。您可以在存储过程中使用动态 SQL 来产生与使用 table 函数寻求的结果相同的结果。

本文将解释为什么您正在做的事情会以这样的方式运作。 https://docs.microsoft.com/en-us/sql/relational-databases/user-defined-functions/user-defined-functions

我确定您想对该函数做更多的事情,这就是您可能不想创建存储过程的原因。有一种方法可以在查询中使用 sporc 执行的结果。但是,这与您在此处记录的问题不同。