好的,使用 MySQL @variables 可能真的会伤到你的脑袋。它们的工作方式类似于内联程序语句,当您通过 := 进行分配时,它们可以用于下一个要查询的 sql 列中,从而简化您的逻辑,而无需一直进行繁重的日期数学运算。
首先,这是整个查询。那我就分解一下……
select
pq.id,
pq.client,
c.name,
sum( pq.UpdHours ) as ResponseHours,
sum( pq.dayHours ) as TotHours,
sum( pq.TimeOnlyOnce ) as TotalTime
from
(select
j.id,
j.client,
j.time_created,
j.time_updated,
if( jdays.DaySeq = 0, time_total, 0 ) as TimeOnlyOnce,
@justDay := date_add( date( j.time_created ), interval jdays.DaySeq day ) as JustTheDay,
@dtS := date_add( @justDay, interval 8 hour ) as StoreOpen,
@dtE := date_add( @justDay, interval 17 hour ) as StoreClosed,
@isWkDay := IF( DAYOFWEEK(@justDay) in ( 1, 7 ), 0, 1 ) as IsWeekDay,
@dtST := greatest( j.time_created, @dtS ) as StartTime,
@dtUpd := least( j.time_updated, @dtE ) as TimeUpdate,
@dtET := least( j.time_closed, @dtE ) as EndTime,
if( @isWkDay, TIMESTAMPDIFF( HOUR, @dtST, @dtUpd ), null ) as UpdHours,
if( @isWkDay, TIMESTAMPDIFF( HOUR, @dtST, @dtET ), null ) as dayHours,
jdays.DaySeq
from
jobs j
JOIN ( select @dayLoop := @dayLoop +1 as DaySeq
from jobs js,
( select @dayLoop := -1 ) sqlvars
limit 10 ) jdays
ON jdays.DaySeq <= TIMESTAMPDIFF( DAY, j.time_created, j.time_closed),
( select
@justDay := '2016-01-01',
@dtS := @justDay,
@dtE := @justDay,
@dtST := @justDay,
@dtET := @justDay,
@dtUpd := @justDay,
@isWkDay := 0) sqlvars2
order by
j.id,
j.client,
jdays.DaySeq) pq
LEFT JOIN clients c
ON pq.client = c.id
group by
pq.id
首先,我从最里面的查询开始
JOIN ( select @dayLoop := @dayLoop +1 as DaySeq
from jobs js,
( select @dayLoop := -1 ) sqlvars
limit 10 ) jdays
这会构建一个子表别名“jdays”来表示从 0 到 10 天的日期序列(如果您需要任何单个活动超过 10 天,只需扩展限制)。我以-1 开始@dayLoop,因此当加入您的工作表时(假设实际上它将有超过10 条记录),它将获取10 行,其值分别为0、1、2、--- 9。这防止需要一些关于多少记录来表示给定作业可能运行的总时间的虚假表格,即时执行。
接下来是JOBS表之间的连接,上面的子查询表示多天,是为了创建笛卡尔结果,以及下一部分
( select
@justDay := '2016-01-01',
@dtS := @justDay,
@dtE := @justDay,
@dtST := @justDay,
@dtET := @justDay,
@dtUpd := @justDay,
@isWkDay := 0) sqlvars2
除了创建一些变量之外什么也不做(响应时间),如果所讨论的日期是否为工作日,则为标志列。这只是在 sql 语句中声明变量,无需外部声明。
现在,我正在使用所有@variables 的下一级查询。将其视为一次分析每一行,并根据 jDays 别名结果获取笛卡尔结果。
我只想查看您的第二张票证 ID
ID Client time_created time_updated time_closed time_total
6412 106 2016-03-04 08:00:00 2016-03-07 08:00:00 2016-03-07 17:00:00 .25
如果您单独运行此 INNER QUERY,对于此 SINGLE ID,与 jDays 表的连接基于从创建到关闭的总天数大于 jDays 值(例如 0、1、2、3、. ...)。为什么要创建多行?因为每一天都需要根据自己的优点来评估。因此,一次获取一个数据元素,我只计算一次 total_time 记录,以便基于 daySeq = 0 的 IF(),因此在针对不同行拆分时不会多次计算。 (3月4日、5日、6日、7日)
if( jdays.DaySeq = 0, time_total, 0 ) as TimeOnlyOnce,
现在日期。只是为了笑,让我们假设我们的 time_created 实际上是一些中午的值,例如 2016-03-04 13:15:00(下午 1:15)。我想要只是一天剥夺时间部分。 Date(j.time_created) 只返回日期部分。
@justDay := date_add( date( j.time_created ), interval jdays.DaySeq day ) as JustTheDay,
结果为“2016-03-04”。现在,我分别添加 8 小时和 17 小时来表示商店的开店和关店时间,结果如下,以及是否是周末的标志。
@dtS := date_add( @justDay, interval 8 hour ) as StoreOpen,
@dtE := date_add( @justDay, interval 17 hour ) as StoreClosed,
@isWkDay := IF( DAYOFWEEK(@justDay) in ( 1, 7 ), 0, 1 ) as IsWeekDay,
JustTheDay StoreOpen (8am) StoreClosed (5pm)
2016-03-04 2016-03-04 8:00:00 2016-03-04 17:00:00
根据给定日期的这些基线值(并将在 3 月 5 日、6 日和 7 日重复),我们现在想知道票证时间何时开始、何时更新和结束(关闭)。因此,开始时间是创建时间或当天开始时间中的较大者。在根据我的示例修改的开始时间中,票的开始时间实际上是下午 1:15 时间,而不是原始数据的上午 8 点,只是为了提供上下文。更新和结束时间基于 LEAST 时间。因此,由于更新和关闭是在周末之后的星期一,我们希望在一天的下午 5 点(3 月 4 日)停止时钟。关闭时间类似。
所以现在对于正在处理的每行,我可以将这些 START、UPDATE 和 END 时间用于 TIMESTAMPDIFF() 的 THE SINGLE DAY。但如果是周末,请使用 Null,因为没有时间适用于计算。
@dtST := greatest( j.time_created, @dtS ) as StartTime,
@dtUpd := least( j.time_updated, @dtE ) as TimeUpdate,
@dtET := least( j.time_closed, @dtE ) as EndTime,
if( @isWkDay, TIMESTAMPDIFF( HOUR, @dtST, @dtUpd ), null ) as UpdHours,
if( @isWkDay, TIMESTAMPDIFF( HOUR, @dtST, @dtET ), null ) as dayHours,
现在,由于一张票跨越多个日期,我将有 4 条记录如下
我有额外的列,因此您可以查看记录的逻辑流程。现在我们有了每张票(没有 where 子句),内部查询创建了多行,每行代表票的一天。现在您只需将总小时数、通知客户之前的小时数和总时间(每个故障只存在第一个条目)和按工单分组。所以这给出了每张票的总响应、关闭、时间。
我知道您已经检查了一个有效的答案,但希望您也喜欢这个选项 :) 可能更容易理解和剖析。
要调整相应的星期几开始/结束时间,只需根据给定的星期几更新 date_add 组件 # 小时,而不是分别固定的 8 和 17。这也考虑了跨越包括周末在内的多天。
所以现在整个事情都根据客户的票证 ID 完成了
select
QryPerID.client,
QryPerID.name,
avg( QryPerID.ResponseHours ) as AvgResponseHours,
avg( QryPerID.TotHours ) as AvgTotHours,
sum( QryPerID.TotalTime ) as TotalTime,
count(*) as ClientTickets
from
( entire previous query ) QryPerID
group by
QryPerID.client,
QryPerID.name