具有基于Getdate的自动日期范围的高效SQL视图

时间:2013-12-07 19:12:09

标签: sql sql-server

所以我试图创建一个自动提取过去12个月数据的视图(从上个月末开始。)

当我使用如下的where子句运行它时:

WHERE Visit_Date between 'Dec 1 2012' and 'Dec 1 2013'

它将在~1分钟内运行。

我有一些计算会自动创建这些日期。但是当我在where子句中使用它们时,查询在15分钟后仍在运行。

WHERE Visit_Date between DATEADD(mm,-12,DATEADD(mm,DATEDIFF(mm,12,GETDATE()),0))
                 and Dateadd(dd,-1,DATEADD(mm,DATEDIFF(mm,12,GETDATE()),0))

查询正在一个包含50多万条记录的表上运行。我相信这是一种更有效的方法。我猜测正在发生的事情是它通过每行的Getdate()计算,这显然不是理想的。

有什么建议吗? 请记住,我正在创建一个视图,我通常不会编写存储过程或动态SQL。

2 个答案:

答案 0 :(得分:2)

我认为@Andriy可能是正确的(I've also blogged about it),这是由于基数估计错误(估计时日期相反)。您可以在KB #2481274Connect #630583中查看更多详情,@ Andriy指出的问题:

Query runs slow with date expression, but fast with string literal

对于每月只更改一次的内容,我认为您可以考虑创建一个工作,在每月月初改变视图,将日期范围硬编码到视图中。这可能不是启用trace flag 4199的可怕替代方案,这可能是也可能不是永久修复,如果您全局启用它可能会或可能不会导致其他问题(而不是仅仅运行此问题的会话)查询 - 再次无法保证它总能使这个快速)。这是我正在考虑的那种过程:

CREATE PROCEDURE dbo.AlterThatView
AS
BEGIN
  SET NOCOUNT ON;

  DECLARE @end DATE = DATEADD(DAY, 1-DAY(GETDATE()), GETDATE());

  DECLARE @start DATE = DATEADD(MONTH, -12, @end);

  DECLARE @sql NVARCHAR(MAX) = N'ALTER VIEW dbo.ViewName
    AS
      SELECT ...
      WHERE Visit_Date >= ''' + CONVERT(CHAR(8), @start, 112) + ''' 
        AND Visit_Date <  ''' + CONVERT(CHAR(8), @end,   112) + ''';';

  EXEC sp_executesql @sql;
END
GO

只需创建一个在每个月的每个月1日午夜后运行的作业,然后调用此过程。您可能希望让工作也从视图中运行SELECT

don't use BETWEEN for date range queriesstop using lazy shorthand for dateparts

答案 1 :(得分:0)

因为您可以在&lt;中返回行在50M +行的表中1分钟,我猜你在Visit_Date列上有一个索引。在第一个查询中,SQL查询计划生成器对索引进行搜索,因为它大致了解将返回多少行,因为它知道日期边界。然后,它确定索引上的索引搜索是最佳行动计划。

在你的第二个查询中,它不知道或者不准确地知道可以返回多少行,所以它可能决定进行索引或表扫描而不是搜索。

您可以考虑的一个选项是在查询中使用索引提示。如果这不是生产代码,而且可能只是偶尔执行的即席查询,则索引提示是安全的。问题是如果索引被删除或名称更改,查询将失败。所以记住这一点。

要记住的其他事项是,如果您提供索引提示,SQL Server将使用该索引。如果开始日期和结束日期之间的时间跨度使得表的大部分返回,则搜索可能不如扫描有效(这就是SQL Server有时会选择扫描的原因)。

此处您最好的朋友正在分析生成的估算查询计划。你可以在SSMS中得到这个。我将尝试一些方法,直到您可以对查询执行索引搜索(而不是扫描)。