所以用例如下 - 有一些参数,根据这些参数,我想从一个或另一个表中选择数据。
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
)
当我使用常量运行它时,执行计划很棒 - 只扫描一个表
select * from dbo.f_TEST(1)
但是,当我使用变量时,计划不是那么好 - 两个表都被扫描
declare @test bit = 1
select * from dbo.f_TEST(@test)
那么是否有任何提示(或技巧)迫使SQL Server理解在某个查询中只应扫描一个表?
答案 0 :(得分:13)
如果您的功能是内联TVP(如示例所示),那么您可以使用:
declare @test bit = 1
select * from dbo.f_TEST(@test) OPTION (RECOMPILE);
然后在这两种情况下,您将获得单个聚簇索引扫描。
<强> DBFiddle Demo 强>
编译查询计划时,RECOMPILE查询提示使用查询中任何局部变量的当前值,如果查询位于存储过程中,则将当前值传递给任何参数。
答案 1 :(得分:4)
答案 2 :(得分:2)
您可以尝试
select top (@test*100) percent id, name from TEST1
union all
select top ((1-@test)*100) percent id, name from TEST2
答案 3 :(得分:1)
使用存储过程而不是表函数,但要注意参数嗅探。您可以在存储过程中使用动态SQL来生成使用表函数查找的相同结果。
本文将解释为什么你所做的工作方式。 https://docs.microsoft.com/en-us/sql/relational-databases/user-defined-functions/user-defined-functions
我确定您希望使用该功能执行更多操作,这就是您可能不想创建存储过程的原因。有一种方法可以在查询中使用sporc执行的结果。但是,这与您在此处记录的内容不同。
答案 4 :(得分:-2)
当您的功能用于表格时,OPTION(RECOMPILE)不会帮助您。例如,这将查询将扫描两个表
-- 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(与其他两个一样)。
如果你进行了一些性能测试,你会看到在这种情况下,优化器可能是正确的。
您的代码更大的问题是每个表都保证表扫描,因为您在任一查询中都没有过滤器。如果可能,添加过滤器将使您能够以搜索发生而不是扫描的方式为这两个表编制索引。