Man*_*man 1 sql-server optimization execution-plan
我有一个要优化的查询,如下所示:
SELECT 1 FROM HugeView
WHERE (Col1 = 'a' AND @val = 1) OR (Col2 = 'a' AND @Val = 0)
(where Col1 and Col2 are on different tables)
Run Code Online (Sandbox Code Playgroud)
如果我对@Val
值 (1/0) 进行硬编码- SQL Server 知道构建一个执行计划,在该计划中它只访问Col1
或的相关表Col2
。
但是当使用一个变量时,所有的表都会HugeView
被访问。
您能否建议一种可以帮助 SQL Server 不访问不必要表的方法?
限制:
option (recompile)
HugeView
不幸的是,上述所有限制都是产品设计和/或开发范围的一部分——我对此无能为力——只能在我拥有的范围内工作。
但是,我知道它@var
是 1 或 0,并且可以根据需要创建和查询(并加入)临时表或“真实”表。
另请注意,我没有从Col1
or的表中进行选择Col2
- 我仅将它们用于过滤。
我尝试在空表上创建和加入,或者top(@somevar)
根据@var
的值 - 但没有帮助。
UNION ALL
不会有帮助,因为在任何情况下优化器都不知道@val
. 也无法更改查询 - 如果我可以,那么当然所有问题都会得到解决。
为了重现您的问题,我创建了三个包含 1000 行的表。视图定义在表的主键上进行左外连接,这应该允许消除连接。这是代码:
CREATE TABLE dbo.BASE_TABLE (ID BIGINT NOT NULL, PRIMARY KEY (ID));
INSERT INTO dbo.BASE_TABLE WITH (TABLOCK)
SELECT TOP (1000) ROW_NUMBER() OVER (ORDER BY (SELECT NULL))
FROM master..spt_values;
CREATE TABLE dbo.COL1_TABLE (ID BIGINT NOT NULL, COL1 VARCHAR(1), PRIMARY KEY (ID));
INSERT INTO dbo.COL1_TABLE WITH (TABLOCK)
SELECT TOP (1000) ROW_NUMBER() OVER (ORDER BY (SELECT NULL)), 'A'
FROM master..spt_values;
CREATE TABLE dbo.COL2_TABLE (ID BIGINT NOT NULL, COL2 VARCHAR(1), PRIMARY KEY (ID));
INSERT INTO dbo.COL2_TABLE WITH (TABLOCK)
SELECT TOP (1000) ROW_NUMBER() OVER (ORDER BY (SELECT NULL)), 'A'
FROM master..spt_values;
GO
CREATE VIEW HugeView AS
SELECT b.ID AS B_ID, c1.COL1, c2.COL2
FROM dbo.BASE_TABLE b
LEFT OUTER JOIN dbo.COL1_TABLE c1 ON b.ID = c1.ID
LEFT OUTER JOIN dbo.COL2_TABLE c2 ON b.ID = c2.ID;
GO
Run Code Online (Sandbox Code Playgroud)
以下查询在不需要时访问所有三个表:
DECLARE @Val BIGINT = 1;
SELECT 1 FROM dbo.HugeView
WHERE (Col1 = 'A' AND @val = 1) OR (Col2 = 'A' AND @Val = 0);
Run Code Online (Sandbox Code Playgroud)
注意实际计划中箭头的粗细:
如果我添加一个RECOMPILE
提示,那么我只会根据需要在实际计划中获得两个表:
可能最直接的解决方法是使用UNION ALL
. 这是估计的计划:
起初该计划可能看起来很糟糕,因为它引用了所有三个表。但是,突出显示的启动谓词过滤器很重要:
这意味着可能不会根据参数的值执行查询计划的分支。如果我得到实际计划,您可以看到计划只执行了一半:
这篇文章中所有内容的dbfiddle 链接。
请注意,根据视图的复杂性,启动过滤器可能不会出现在最佳位置。但是,它应该可以为您节省一些不必要的工作。