MS-Sql从水平分区表中选择

时间:2014-01-31 21:09:32

标签: sql-server sql-server-2012

我有一个水平分区的表系统,在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)正确优化?

1 个答案:

答案 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优化执行计划

enter image description here

只包含一个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

是不同的

enter image description here

,因为:

{1}每个表中的YearMonthDay列都没有索引(这会导致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 上包含一个ScanClustered Index Scan

enter image description here

这种方式只能解决问题#2。对于#1,您需要索引。

解决方案#2:

另一种解决方案是将dbo.MyDates2014谓词简单地翻译为MD.[Year] = 2014 AND MD.[Month] = 1 AND MD.[Day] = 15。以下示例将使用MD.Date_Key = 20140115查询提示强制SQL Server生成针对每次执行优化的执行计划(对于参数的当前值):

RECOMPILE

enter image description here

因此,当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):

enter image description here

注意#1:在选择其中一个(如果)之前,您应该自己测试这些解决方案。

注意#2:我不得不说我使用的是SQL Server 2012.