0

每个员工已经在 AvailSlots 中有一个可用时隙表,如下所示:

    Staff_ID  Avail_Slots_Datetime

    1         2015-1-1 09:00:00
    1         2015-1-1 10:00:00
    1         2015-1-1 11:00:00
    2         2015-1-1 09:00:00
    2         2015-1-1 10:00:00
    2         2015-1-1 11:00:00
    3         2015-1-1 09:00:00
    3         2015-1-1 12:00:00
    3         2015-1-1 15:00:00

我需要找出哪些员工在每个时间段有 2 个(或 3、4 个等)连续可用时间段。作为一个新手,如果查询是针对 2 个连续的时隙,我只知道要编写下面的 INNER JOIN 代码。

    SELECT a.start_time, a.person
    FROM a_free a, a_free b
    WHERE (b.start_time = addtime( a.start_time, '01:00:00' )) and (a.person = b.person)

但是,显然,这样做,我将不得不添加更多的 INNER JOIN 代码 - 对于每种情况 - 取决于查询是针对给定日期/小时的 3 或 4 或 5 等连续可用时隙。因此,我想学习一种更有效、更灵活的方法来做同样的事情。具体来说,我需要的查询代码(用自然语言)是这样的:

  • 对于 AvailSlots 中的每个时间段,列出从该日期时间开始具有 X(其中 X 可以是我为每个查询指定的任何数字,从 1 到 24)连续日期时间段的员工。如果不止一名员工可以满足该标准,则抢七是他们的“等级”,该等级保存在下面的单独表格中:

    Ranking Table (lower number = higher rank) 
    Staff_ID  Rank
    1          3
    2          1
    3          2
    

如果答案是使用“mysql variables”、“views”等,请解释一下这些东西是如何工作的。同样,作为一个 mysql 新手,到目前为止,我只知道“select”、“join”、“where”、“group by”。我渴望了解更多,但到目前为止无法理解更高级的 mysql 概念。提前谢谢了。

4

1 回答 1

1

使用比您发布的更多的数据,我发现了一个可能满足您需求的查询。它确实使用了您预测的变量:) 但我希望它是不言自明的。让我们从表格开始:

CREATE TABLE a_free
    (`Staff_ID` int, `Avail_Slots_Datetime` datetime)
;

INSERT INTO a_free
    (`Staff_ID`, `Avail_Slots_Datetime`)
VALUES
    (1, '2015-01-01 09:00:00'),
    (1, '2015-01-01 10:00:00'),
    (1, '2015-01-01 11:00:00'),
    (1, '2015-01-01 13:00:00'),
    (2, '2015-01-01 09:00:00'),
    (2, '2015-01-01 10:00:00'),
    (2, '2015-01-01 11:00:00'),
    (3, '2015-01-01 09:00:00'),
    (3, '2015-01-01 12:00:00'),
    (3, '2015-01-01 15:00:00'),
    (3, '2015-01-01 16:00:00'),
    (3, '2015-01-01 17:00:00'),
    (3, '2015-01-01 18:00:00')
;

然后有一个查询来查找连续的插槽。它列出了每对的开始时间,并用唯一的数字标记每组连续的插槽。case 表达式是魔术发生的地方,请参阅评论:

select
Staff_ID,
Avail_Slots_Datetime as slot_start, 
case
  when @slot_group is null then @slot_group:=0 -- initalize the variable
  when @prev_end <> Avail_Slots_Datetime then @slot_group:=@slot_group+1  -- iterate if previous slot end does not match current one's start
  else @slot_group -- otherwise just just keep the value
end as slot_group,
@prev_end:= Avail_Slots_Datetime + interval 1 hour as slot_end -- store the current slot end to compare with next row
from a_free
order by Staff_ID, Avail_Slots_Datetime asc;

识别出带有槽组的列表后,我们可以将上面的查询包装在另一个列表中,以获取每个槽组的长度。第一个查询的结果被视为任何其他表:

select
  Staff_ID,
  slot_group,
  min(slot_start) as group_start,
  max(slot_end) as group_end,
  count(*) as group_length
from (

  select
    Staff_ID,
    Avail_Slots_Datetime as slot_start, 
    case
      when @slot_group is null then @slot_group:=0
      when @prev_end <> Avail_Slots_Datetime then @slot_group:=@slot_group+1
      else @slot_group
    end as slot_group,
    @prev_end:= Avail_Slots_Datetime + interval 1 hour as slot_end
  from a_free
  order by Staff_ID, Avail_Slots_Datetime asc
) groups
group by Staff_ID, slot_group;

注意:如果再次使用同一个 DB 连接执行查询,变量不会被重置,所以 slot_groups 编号会继续增长。这通常不应该是一个问题,但为了安全起见,您需要在之前或之后执行以下操作:

select @prev_end:=null;

如果您愿意,可以玩小提琴:http ://sqlfiddle.com/#!2/0446c8/15

于 2015-01-10T19:59:57.863 回答