如何获得更好的空集执行计划?

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)
  • 无法将此代码封装在 SP 中
  • 无法改变 HugeView

不幸的是,上述所有限制都是产品设计和/或开发范围的一部分——我对此无能为力——只能在我拥有的范围内工作。

但是,我知道它@var是 1 或 0,并且可以根据需要创建和查询(并加入)临时表或“真实”表。

另请注意,我没有从Col1or的表中进行选择Col2- 我仅将它们用于过滤。

我尝试在空表上创建和加入,或者top(@somevar)根据@var的值 - 但没有帮助。

UNION ALL不会有帮助,因为在任何情况下优化器都不知道@val. 也无法更改查询 - 如果我可以,那么当然所有问题都会得到解决。

Joe*_*ish 7

为了重现您的问题,我创建了三个包含 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. 这是估计的计划:

UNION ALL 估计

起初该计划可能看起来很糟糕,因为它引用了所有三个表。但是,突出显示的启动谓词过滤器很重要:

筛选

这意味着可能不会根据参数的值执行查询计划的分支。如果我得到实际计划,您可以看到计划只执行了一半:

修正实际

这篇文章中所有内容的dbfiddle 链接

请注意,根据视图的复杂性,启动过滤器可能不会出现在最佳位置。但是,它应该可以为您节省一些不必要的工作。