iri*_*ias 2 sql-server-2008 sql-server t-sql
我有一个像(SQLServer 2008)的表:
CREATE TABLE [dbo].[my_test_table](
[productId] [int] NOT NULL,
[purchaseId] [bigint] NOT NULL,
(some other columns....),
CONSTRAINT [PK_my_test_table] PRIMARY KEY CLUSTERED
(
[productId] ASC,
[purchaseId] ASC
))
Run Code Online (Sandbox Code Playgroud)
大约有 1000 万行。
我想要一个查询,返回产品的总行数,或者如果产品未指定,则返回所有产品的总行数。就像是:
declare @productId int
set @productId = 320
select count(*)
from my_test_table t with(nolock)
where productId = @productId
or @productId is null
Run Code Online (Sandbox Code Playgroud)
问题是查询比等效查询花费的时间要多得多:
select count(*)
from my_test_table t with(nolock)
where productId = 320
or 320 is null
Run Code Online (Sandbox Code Playgroud)
我们如何解释这种行为?
考虑这个查询:
select count(*)
from my_test_table t
where null is null;
Run Code Online (Sandbox Code Playgroud)
优化器在那里进行搜索是否有意义?没有什么可以反对的。试图用提示强制它会引发错误:
select count(*)
from my_test_table t with (forceseek)
where null is null;
Run Code Online (Sandbox Code Playgroud)
消息 8622,级别 16,状态 1,第 15 行
由于此查询中定义的提示,查询处理器无法生成查询计划。在不指定任何提示且不使用 SET FORCEPLAN 的情况下重新提交查询。
对于您现在拥有的查询,您可能需要根据参数值进行索引查找。但是,索引搜索并非对所有可能的参数值都有效,因此您的缓存计划不能包含搜索。此外,当查询优化器构建计划时,它不会首先检查局部变量的值。
您指出查询很复杂,因此最好的解决方案可能是升级到至少 SQL Server 2008 R2 SP2,以便您可以利用参数嵌入优化 (PEO)。结合OPTION (RECOMPILE)提示允许查询优化器在执行计划之前嗅探局部变量的值。查询计划不能被另一个查询重用。
让我们看看它的实际效果。以下查询的实际查询计划显示了索引查找:
declare @productId int = 320;
select count(*)
from my_test_table t
where productId = @productId
or @productId is null
OPTION (RECOMPILE);
Run Code Online (Sandbox Code Playgroud)
下面的实际查询计划显示了一个扫描:
declare @productId int = NULL;
select count(*)
from my_test_table t
where productId = @productId
or @productId is null
OPTION (RECOMPILE);
Run Code Online (Sandbox Code Playgroud)
如果升级不是一种选择,我想你可以试试这个:
select *
from my_test_table t
where productId = @productId
and @productId is not null
UNION ALL
select *
from my_test_table t
where @productId is null;
Run Code Online (Sandbox Code Playgroud)
这可以根据局部变量的值进行查找或扫描,但可能会产生意想不到的副作用。
如果这也不可接受,我认为您唯一的选择是使用另一个答案中已经涵盖的动态 SQL。