SQL Server 2005的检查约束不使用变量
问题描述:
当执行时被应用于下面是一些SQL示例代码:SQL Server 2005的检查约束不使用变量
--Create 2 Sales tables with constraints based on the saledate
create table Sales1(SaleDate datetime, Amount money)
ALTER TABLE dbo.Sales1 ADD CONSTRAINT
CK_Sales1 CHECK (([SaleDate]>='01 May 2010'))
GO
create table Sales2(SaleDate datetime, Amount money)
ALTER TABLE dbo.Sales2 ADD CONSTRAINT
CK_Sales2 CHECK (([SaleDate]<'01 May 2010'))
GO
--Insert some data into Sales1
insert into Sales1 (SaleDate, Amount)
values ('02 May 2010', 50)
insert into Sales1 (SaleDate, Amount)
values ('03 May 2010', 60)
GO
--Insert some data into Sales2
insert into Sales2 (SaleDate, Amount)
values ('30 Mar 2010', 10)
insert into Sales2 (SaleDate, Amount)
values ('31 Mar 2010', 20)
GO
--Create a view that combines these 2 tables
create VIEW [dbo].[Sales]
AS
SELECT SaleDate, Amount FROM Sales1
UNION ALL
SELECT SaleDate, Amount FROM Sales2
GO
--Get the results
--Query 1
select * from Sales where SaleDate < '31 Mar 2010' -- if you look at the execution plan this query only looks at Sales2 (Which is good)
--Query 2
DECLARE @SaleDate datetime
SET @SaleDate = '31 Mar 2010'
select * from Sales where SaleDate < @SaleDate -- if you look at the execution plan this query looks at Sales1 and Sales2 (Which is NOT good)
望着执行计划,你会看到,这两个查询是不同的充。对于查询1,唯一被访问的表是Sales1(这很好)。对于查询2,两个表都被访问(哪个是坏的)。为什么这些执行计划有所不同,以及如何在使用变量时让Query 2仅访问相关表?
我试图为SaleDate列添加索引,这似乎没有帮助。
答
查询计划被缓存以供重用,因此它是为一般情况创建的。缓存的计划必须对所有可能的值@SalesDate
有效。这意味着查询计划必须在两个视图中查看。
可避免通过运行动态SQL:
declare @sql varchar(max)
set @sql = 'select * from Sales where SaleDate < ' + @SaleDate
exec (@sql)
有优化查询一个变量的特定值,就像一个选项:
SELECT *
FROM Sales
WHERE SaleDate < ' + @SaleDate
OPTION (OPTIMIZE FOR (@SalesDate = '2010-01-01'))
但SQL Server仍然坚持查看两个表,因为即使它试图优化2010-01-01
,它也必须返回2009-01-01
的正确结果。
答
这是预期的。该计划现在可以与变量中的任何值一起重用。
具有字面值,它不会打扰可恢复性,因为如果字面更改,计划会更改。现在
,我描述了这种行为一个很好的链接。关键词是有 ...
编辑:
- Conor's article是关于参数设置有关。
- SO:TSQL Constants… Use Variable or Literal?