查看分区表的视图上的执行计划

时间:2015-05-08 17:31:03

标签: tsql sql-server-2008-r2 partitioning check-constraints

我目前有按年份分区的表格。我们的销售交易月份。例如,我们的销售表看起来像这样:

  • factdailysales_201501
  • factdailysales_201502
  • factdailysales_201503等...

通常,我总是执行动态SQL来捕获开始日期,结束日期,找出它们是什么分区,然后遍历每个分区......但它开始变得如此麻烦而且我'我们了解到,这可能不仅仅是维护,故障排除和性能方面的最佳方式。

我决定构建一个UNION所有销售分区的视图。但是,我不希望从视图中选择必须在执行时扫描所有分区,这将剥夺分区表的整个目的。因此,我在每个销售表上添加了日期检查约束。这样,当我从视图中选择时,它将知道要访问哪些表而不是扫描每个表。

以下是以下示例:

    SELECT SUM([retail])  
    FROM Sales_Orig 
    WHERE [Date] >= '2015-03-01' 

此查询的执行计划仅从我需要的分区中提取。

我现在面临的问题是,在我的团队编写存储过程的大部分时间里,他们很可能在将日期变量传递给where语句的情况下编写查询。

DECLARE @SD DATE = '2015-03-01' 

SELECT SUM([retail])  
FROM Sales_Orig 
WHERE [Date] >= @SD 

但是,当传入一个变量时,执行计划现在扫描视图中的所有分区,导致性能比我在日期中硬编码的时间长得多

我想我可以再次执行动态SQL并将日期字符串插入SELECT语句,但它会让我回到尝试摆脱动态SQL的开始,这个简单的销售查询。

所以我的问题是,我设置错了吗?我是在正确的轨道上吗?似乎视图不能接受检查约束的变量并最终扫描每个表。有没有人会推荐的其他方法?也许我通过动态SQL循环遍历分区的原始解决方案是最好的方法吗?

**编辑** http://sqlsunday.com/2014/08/31/partitioned-views/

这篇文章实际上是我最初看到这个想法的地方!似乎在使用完全相同的解决方案时,我仍然经历着同样的挣扎!

谢谢!

2 个答案:

答案 0 :(得分:0)

好的,这可能会奏效。它是一个表值函数,只能根据你的@start和@end参数访问表,所以只访问你的"分区"它需要的。我想你可以采用这个概念并编写一些动态SQL来创建所有的if语句。

现在当然每天都会添加新表格,所以这是如何配合的。我认为最好的方法是每天更改添加下一个销售表的功能。这样查询它很简单。你可以使用你用来创建函数的相同动态sql来改变它,这应该相对简单。

注意:我添加了默认值,即数据类型DATE的最小值和最大值。这样你可以查询20140101及之后的所有内容,反之亦然。

你的桌子

SELECT CAST('20150101' AS DATE) datesVal INTO factDailySales_20150101;
SELECT CAST('20150102' AS DATE) datesVal INTO factDailySales_20150102;
SELECT CAST('20150103' AS DATE) datesVal INTO factDailySales_20150103;

功能

CREATE FUNCTION ufn_factTotalSales (@Start DATE = '17530101', @End DATE = '99991231')
RETURNS @factTotalSales TABLE
(
    datesVal DATE
)
AS
BEGIN
    IF(CAST('20150101' AS DATE) BETWEEN @Start AND @End)
    BEGIN
        INSERT INTO @factTotalSales
            SELECT datesVal
            FROM factDailySales_20150101
    END
    IF(CAST('20150102' AS DATE) BETWEEN @Start AND @End)
    BEGIN
        INSERT INTO @factTotalSales
            SELECT datesVal
            FROM factDailySales_20150102
    END
    IF(CAST('20150103' AS DATE) BETWEEN @Start AND @End)
    BEGIN
        INSERT INTO @factTotalSales
            SELECT datesVal
            FROM factDailySales_20150103
    END
    RETURN;
END
GO

所有表格

SELECT *
FROM ufn_factTotalSales(default,default)

所有表格大于或等于20150102

SELECT *
FROM ufn_factTotalSales('20150102',default)

**所有表格小于或等于20150102

SELECT *
FROM ufn_factTotalSales(default,'20150102')

特定范围之间的所有表格

SELECT *
FROM ufn_factTotalSales('20150101','20150102')

这是理想的解决方案吗?不是。理想情况是将所有表合并为一个并具有良好的索引。我知道你说因为编写其他代码的方式不会起作用。听我说。现在也许这已经不在了,让我们说你把这些表结合在一起但显然有旧的脚本在寻找特定的每日销售表。也许您可以使用访问factTotalSales的dailySales名称创建视图。 OR 您可以为factTotalSales创建与每个factDailySales对应的同义词。

也许你可以调查一下。这并不容易,但我认为让SQL Server按照设计方式优化查询是一种更好的方法,而不是用动态SQL强制它。

只是我的两分钱。希望这可以帮助。至少,我希望它能给你一些想法。

答案 1 :(得分:0)

5年后:option(recompile)

计划者需要访问常量,以从查询计划中完全删除表 。对于变量,无需强制重新编译,将使用通用计划。 (相关:参数嗅探。)

虽然这意味着查询计划更大,因为它必须包含所有表,但不是并不意味着实际上已扫描了所有表:请查看 IO统计信息,即使在查询计划中显示了消除表扫描

当不扫描表时,查询计划中的“执行次数”将为0:不幸的是,在查询计划和UI中,这些分支仍被报告为非零百分比成本的“表扫描”节点。如果查询速度很快,将按比例显示较高。随着从实际使用的基表返回的数据量的增加,这些额外的“表扫描”节点显示的成本百分比接近零。

当视图不是分区视图时(例如,基本表缺少PK中的分区列),但基础表在过滤列上具有合适的检查约束时,也会发生相同的优化/消除。当视图选择一个常量值来建立分区时,也会发生这种情况,否则该分区将不会存储在表中。对于查询中的常量重新编译的计划,表将被完全删除。使用变量时,表仍将实际被扫描,从而在查询执行期间被逻辑删除。

使用适当的分区视图仅对直接插入和更新真正有好处,主要警告是它要求分区列位于每个表的PK中,并且不允许使用标识列(将分区列查看大部分没用的IMOHO)。对于其他准分区视图案例,SQL Server处理优化的方法非常相似。

(这是在SQL Server 2014上;早期版本可能没有有效地优化不同的模式。)

相关问题