慢速运行查询比较日期

时间:2017-04-29 21:08:49

标签: tsql

有人可以提供一些建议。我有以下查询使用大约200,000条记录。我需要评估一个' DateTime'字段,用于评估收入是否在正确的时间段内发生。我目前正在使用CASE语句来评估DateTime字段,它是一个绝对的猪,它运行超过5分钟。有更快更有效的方法吗?请注意,变量@cur_date,@ end_date,@ prev_yr_qtr_start,@cur_date_yr_prev等都是字符串,r.pw_ship_date的类型为DATETIME。所以本质上我将r.pw_ship_date与字符串进行比较即:' 2017-01-01 00:00'

注意:当我添加' SELECT TOP(500)'时,需要4:00分钟才能运行此查询。对于200,000条记录,这将永远需要。

提前致谢

DECLARE @total TABLE
(
    acct_number VARCHAR(50),
    pro_nbr VARCHAR(50),
    sales_rep VARCHAR(50),
    bill_to_name VARCHAR(50),
    billing_addr1 VARCHAR(50),
    billing_addr2 VARCHAR(50),
    billing_city CHAR(50),
    billing_state CHAR(2),
    billing_zip CHAR(10),
    cur_month_bills INT,
    cur_month_rev DECIMAL(30, 6),
    cur_qtr_bills INT,
    cur_qtr_rev DECIMAL(30, 6),
    prev_yr_qtr_bills INT,
    prev_yr_qtr_rev DECIMAL(30, 6),
    cur_ytd_bills INT,
    cur_ytd_rev DECIMAL(30, 6),
    prev_ytd_bills INT
)


INSERT  INTO @total
SELECT  TOP(50000) f.acct_number ,
        r.pro_nbr ,
        r.sales_rep ,
        r.bill_to_name ,
        r.billing_addr1 ,
        r.billing_addr2 ,
        r.billing_city ,
        r.billing_state ,
        r.billing_zip ,
        'cur_month_bills' = MAX(( CASE WHEN r.pw_ship_date BETWEEN @cur_date AND @end_date THEN 1 ELSE 0 END )) ,
        'cur_month_rev' = MAX(ROUND(( CASE WHEN r.pw_ship_date BETWEEN @cur_date AND @end_date THEN f.tot_revenue ELSE 0 END ), 2)) ,
        'cur_qtr_bills' = MAX((CASE WHEN r.pw_ship_date BETWEEN @cur_date AND @end_date  THEN 1 ELSE 0 END )) ,
        'cur_qtr_rev' = MAX(ROUND(CASE WHEN r.pw_ship_date BETWEEN @cur_date AND @end_date THEN f.tot_revenue ELSE 0 END, 2)) ,
        'prev_yr_qtr_bills' = MAX(CASE WHEN r.pw_ship_date BETWEEN @prev_yr_qtr_start AND @cur_date_yr_prev THEN 1 ELSE 0 END ) ,
        'prev_yr_qtr_rev' = MAX(ROUND(CASE WHEN r.pw_ship_date BETWEEN @prev_yr_qtr_start AND @cur_date_yr_prev THEN f.tot_revenue ELSE 0 END , 2)) ,
        'cur_ytd_bills' = MAX(CASE WHEN r.pw_ship_date BETWEEN @first_day_cur_yr AND @end_date  THEN 1 ELSE 0 END ),
        'cur_ytd_rev' = MAX(ROUND(CASE WHEN r.pw_ship_date BETWEEN @first_day_cur_yr AND @end_date THEN f.tot_revenue ELSE 0 END , 2)) ,
        'prev_ytd_bills' = MAX(CASE WHEN r.pw_ship_date BETWEEN @first_day_prev_yr AND @end_date THEN 1    ELSE 0 END )
FROM    @summed f
INNER JOIN @raw r ON f.acct_number = r.acct_number AND f.pro_nbr = r.pro_nbr
GROUP BY f.acct_number ,
        r.pro_nbr ,
        r.sales_rep ,
        r.bill_to_name ,
        r.billing_addr1 ,
        r.billing_addr2 ,
        r.billing_city ,
        r.billing_state ,
        r.billing_zip;

1 个答案:

答案 0 :(得分:0)

将表变量@raw@summed更改为临时表。表变量没有统计信息,并且在索引方面极其受限(您只能有一个)。因此,SQL Server假定您的表变量只有一行(2012年及以前)或100行(2014年以上)。这意味着你几乎肯定会为你的查询得到一个糟糕的执行计划,这会毁了你。

@raw@summed更改为#raw#summed后,请在其上添加索引 - 至少为您的外键编制索引(您加入的字段,acct_numberpro-nbr。它可能值得创建聚集索引和/或主键,但这是您需要尝试以找到所需性能的东西。

另一件让你的表现失败的事情就是将datetime与字符串进行比较。这导致类型转换,可能会显着拖累你。如果您正在使用日期/时间,使用适当的数据类型 - 而不是看起来像日期的字符串。

如果仍然没有足够快地运行,请将CASE语句移出集合函数。

MAX(( CASE WHEN r.pw_ship_date BETWEEN @cur_date AND @end_date THEN 1 ELSE 0 END ))

CASE语句移动到填充#raw.pw_ship_date的查询中,这样当您执行聚合时,您只需要一直查看整数。