我有一个水平分区的表系统,在Date_Key引用上使用check-constraints,其中Date实际上是日期的YYYYMMDD整数版本(因此check-constraints在YYYY0101和YYYY1231之间)。
我有一个View,它为所有表使用UNION ALL。
如果我执行
Select * from MyDatedTable DT
inner join MyDates M on DT.Date_Key = MD.Date_Key and MD.Date_Key = 20120115
优化器“知道”只扫描并读取正确的2012表(或索引),并忽略所有其他联合的表。
无论其
如果我在lookup-value
表格中使用MyDates
(例如Year
),则不会使用check-constraint
上的Key-relationship
,
即:
Select * from MyDatedTable DT
inner join MyDates MD on DT.Date_Key = MD.Date_Key and MD.Year = 2012 and MD.Month = 1 and MD.Day = 15
(优化器“知道”零行将来自范围之外的表,但它确实表明它需要实际检查索引...)
有没有办法让MS-SQL(2012)正确优化?
答案 0 :(得分:0)
假设存在以下对象(两个表和一个视图):
CREATE TABLE dbo.MyDates2013 (
Date_Key INT PRIMARY KEY,
CHECK (Date_Key BETWEEN 20130101 AND 20131231),
[Year] SMALLINT NOT NULL,
[Month] TINYINT NOT NULL,
[Day] TINYINT NOT NULL
);
INSERT INTO dbo.MyDates2013 (Date_Key, [Year], [Month], [Day])
VALUES (20130101, 2013, 1, 1);
CREATE TABLE dbo.MyDates2014 (
Date_Key INT PRIMARY KEY,
CHECK (Date_Key BETWEEN 20140101 AND 20141231),
[Year] SMALLINT NOT NULL,
[Month] TINYINT NOT NULL,
[Day] TINYINT NOT NULL
);
INSERT INTO dbo.MyDates2014 (Date_Key, [Year], [Month], [Day])
VALUES (20140101, 2014, 1, 1);
GO
CREATE VIEW dbo.My Dates
AS
SELECT * FROM dbo.MyDates2013
UNION ALL
SELECT * FROM dbo.MyDates2014;
GO
以下查询
SELECT *
FROM dbo.MyDates MD
WHERE MD.Date_Key = 20140115;
(确实)由SQL Server优化执行计划
只包含一个Index Seek
(在dbo.MyDates2014
的主键上),因为在编译时SQL Server 知道 Date_Key = 20140115
只能在一个基表中: dbo.MyDates2014
。这是可能的,因为在CHECK
上定义了dbo.MyDates2014
约束:CHECK (Date_Key BETWEEN 20140101 AND 20141231)
。
下一个查询
SELECT *
FROM dbo.MyDates MD
WHERE MD.[Year] = 2014 AND MD.[Month] = 1 AND MD.[Day] = 15;
GO
是不同的
,因为:
{1}每个表中的Year
,Month
和Day
列都没有索引(这会导致Clustered Index Scan
个),但也因为
{2} SQL Server将读取dbo.MyDates
视图使用的两个表。发生这种情况是因为它不知道Date_Key
值和[Year], [Month], [Day]
值之间的相关性,并且(我想)它不能从像CHECK (Date_Key BETWEEN 20140101 AND 20141231)
这样的约束和新规则/约束{ {1}}。
解决方案#1:
因此,一种解决方案是在每个表中添加这些约束:
[Year] = 2014
现在,执行计划仅在ALTER TABLE dbo.MyDates2013
ADD CONSTRAINT CK_MyDates2013_Year CHECK ( [Year] = 2013 );
GO
ALTER TABLE dbo.MyDates2014
ADD CONSTRAINT CK_MyDates2014_Year CHECK ( [Year] = 2014 );
GO
上包含一个Scan
:Clustered Index Scan
:
这种方式只能解决问题#2。对于#1,您需要索引。
解决方案#2:
另一种解决方案是将dbo.MyDates2014
谓词简单地翻译为MD.[Year] = 2014 AND MD.[Month] = 1 AND MD.[Day] = 15
。以下示例将使用MD.Date_Key = 20140115
查询提示强制SQL Server生成针对每次执行优化的执行计划(对于参数的当前值):
RECOMPILE
因此,当DECLARE @Year SMALLINT, @Month TINYINT, @Day TINYINT;
SELECT @Year = 2014, @Month = 1, @Day = 15;
SELECT *
FROM dbo.MyDates MD
WHERE MD.Date_Key = (@Year * 100 + @Month) * 100 + @Day
OPTION(RECOMPILE)
GO
= Index Seek
时,SQL Server将删除Index Scan
上不必要的Index Seek
s / dbo.MyTable2013
个运算符(例如@Year
}
即使没有OPTION(RECOMPILE)
2014
您可以获得良好的效果,因为执行计划包含DECLARE @Year SMALLINT, @Month TINYINT, @Day TINYINT;
SELECT @Year = 2014, @Month = 1, @Day = 15;
SELECT *
FROM dbo.MyDates MD
WHERE MD.Date_Key = (@Year * 100 + @Month) * 100 + @Day
运算符,可防止不必要的读取(Filter
/ Index Seek
):
注意#1:在选择其中一个(如果)之前,您应该自己测试这些解决方案。
注意#2:我不得不说我使用的是SQL Server 2012.