优化日期之间的选择

Posted

技术标签:

【中文标题】优化日期之间的选择【英文标题】:Optimize selection between dates 【发布时间】:2018-08-12 23:38:33 【问题描述】:

我的存储过程执行得非常糟糕。关于如何改进它的任何建议?

 select  t2.ID,
    t2.Transactions,
    t2.StartTime,
    t2.EndTime,
    t2.Good,
    t2.OK,
    t2.Bad,
    t1.PID,
    t1.PName
from(
select distinct ID,
     max([PID]) as MaxPID, 
     min([DATE_S]) as StartTime,
     max([DATE_S]) as EndTime,  
     count([ID_P]) as Transactions,
     coalesce(count(case when [STATUS] = 0 then 1 end), 0)  as Good,
     coalesce(count(case when [STATUS] = 1 then 1 end), 0)  as Warning,
     coalesce(count(case when [STATUS] > 1 then 1 end), 0)  as Bad
 from  [dbo].[Table1]
 where CONVERT(date, [DATE_S]) between CONVERT(date, @StartDate) and 
 CONVERT(date, @EndDate)
 group by ID) t2
 LEFT join [dbo].[Table2] t1 on t2.MaxPID = t1.[PID] 
order by ID desc

非常感谢任何提示。

【问题讨论】:

DISTINCT 超过 GROUPed 已经没有多大意义。另外,我怀疑COUNT() 是否按照您认为的方式工作。为此,您可能希望使用SUM()。 (因为无论如何我都在使用它,所以使用t1 作为Table2 的别名有点令人困惑(反之亦然)) 【参考方案1】:

几个假设 - 您的 @startdate 和 @enddate 是日期(没有时间组件),您的数据库记录是日期时间。还假设 date_s 上有一个索引,但它没有被使用,因为您没有直接在 where 子句中使用它。处理这个问题的方法是保留 date_s 原样,然后在你的 between 子句中使用 @startdate (这将是开始当天的午夜,@endDate+1 将是下一个午夜。

当然,这可能会从第二天开始在午夜开始的一行,如果是这种情况,则使用 next day-1second 或类似的东西。但不太可能成为问题。

where 子句:

where DATE_S between @StartDate and dateadd(d,1,@EndDate)

【讨论】:

实际上,午夜日期很可能是个问题。如果添加了任何没有时间的日期,则它们将显示为午夜。不要减去 1 秒,不要使用 between,而是使用 Where Date_S >= CONVERT(date, @StartDate) and Date_S @DancingFool 是的,取决于场景。就像我说的 - 做了一些假设!如果手动添加,则可能没有时间,如果在添加事务时自动添加,则不太可能。但是换成 >= 和 【参考方案2】:

试试这样的:

declare @StartDateDt as datetime=cast(@StartDate as date);
declare @EndDateDt as datetime=cast(EndDate as date);

With tmp as (
select ID, 
     max(PID) as MaxPID, 
     min(DATE_S) as StartTime,
     max(DATE_S) as EndTime,  
     count(ID_P) as Transactions,
     sum(case when STATUS = 0 then 1 else 0 end)  as Good,
     sum(case when STATUS = 1 then 1 else 0 end)  as Warning,
     sum(case when STATUS > 1 then 1 else 0 end)  as Bad
 from  dbo.Table1
 where cast(DATE_S as date) between @StartDateDt  and @EndDateDt
 group by ID
)
select T2.*, T1.PID, T1.PName  
from tmp T1
LEFT join dbo.Table2 T2 on T1.MaxPID = T2.PID
order by T1.ID desc

【讨论】:

【参考方案3】:
select ID,
     max([PID]) as MaxPID, 
     min([DATE_S]) as StartTime,
     max([DATE_S]) as EndTime,  
     count([ID_P]) as Transactions,
     coalesce(count(case when [STATUS] = 0 then 1 end), 0)  as Good,
     coalesce(count(case when [STATUS] = 1 then 1 end), 0)  as Warning,
     coalesce(count(case when [STATUS] > 1 then 1 end), 0)  as Bad int #tmpResult
 from  [dbo].[Table1]
 where CONVERT(date, [DATE_S]) between CONVERT(date, @StartDate) and 
 CONVERT(date, @EndDate)
 group by ID

SELECT  t2.ID,
    t2.Transactions,
    t2.StartTime,
    t2.EndTime,
    t2.Good,
    t2.OK,
    t2.Bad,
    t1.PID,
    t1.PName
FROM #tmpResult t2
 LEFT join [dbo].[Table2] t1 on t2.MaxPID = t1.[PID] 
order by ID desc

【讨论】:

distinct 和 group 不是必须的 ;)【参考方案4】:

我假设[DATE_S] 包含datetime 值(日期和时间)

@StartDate@EndDate 是日期时间数据类型,但仅包含日期值。

所以在@EndDate 中添加 1 天

set @EndDate=DateADd(day,1,@EndDate)



    select  t2.ID,
        t2.Transactions,
        t2.StartTime,
        t2.EndTime,
        t2.Good,
        t2.OK,
        t2.Bad,
        t1.PID,
        t1.PName
    from(

    select  ID,
         max([PID]) as MaxPID, 
         min([DATE_S]) as StartTime,
         max([DATE_S]) as EndTime,  
         count([ID_P]) as Transactions,
         coalesce(count(case when [STATUS] = 0 then 1 end), 0)  as Good,
         coalesce(count(case when [STATUS] = 1 then 1 end), 0)  as Warning,
         coalesce(count(case when [STATUS] > 1 then 1 end), 0)  as Bad
     from  [dbo].[Table1]
     where [DATE_S]>=@StartDate and [DATE_S]<=@EndDate
     group by ID
    ) t2
     LEFT join [dbo].[Table2] t1 on t2.MaxPID = t1.[PID] 
order by ID desc

Create non Clustered index ix_Date on Table1(DATE_S)include(PID,ID_P,STATUS)

您可以告诉两个表的索引,以便相应地更正我的索引。

【讨论】:

以上是关于优化日期之间的选择的主要内容,如果未能解决你的问题,请参考以下文章

优化两个日期之间的工作日统计查询

选择查询优化

如何使用 jQuery 日期选择器优化引导输入标签

MySQL 索引优化器选择索引的规则是啥?

Oracle 子查询优化思路

Laravel 按日期获取以前的记录(优化)