【问题标题】:Display planner events each week from start date until end date从开始日期到结束日期每周显示计划者事件
【发布时间】:2018-12-12 17:30:23
【问题描述】:

过去 2 天我一直在为这个问题挠头——我需要专家的眼光!

基本上,我试图在 1 周内为司机显示计划事件,直到他们关闭。如果司机在 2018 年 12 月 10 日至 2018 年 12 月 21 日期间休息,那么我希望司机翻车到第二天,并在结束日期之前显示他/她的名字。现在结果只显示当天的驱动程序,而不是每天重复显示。

这是一个示例数据集:

CREATE TABLE IF NOT EXISTS planner_events (
  planner_id INT(5) UNSIGNED AUTO_INCREMENT PRIMARY KEY,
  event_name VARCHAR(100) NOT NULL,     
  event_start_date DATE,    
  event_end_date DATE,
  user_id INT(5) NOT NULL,  
  user_type VARCHAR(10) NOT NULL,
  date_created DATETIME DEFAULT CURRENT_TIMESTAMP
);

INSERT INTO planner_events (event_name, event_start_date, event_end_date, user_id, user_type)
VALUES ('Annual Holiday', '2018-12-12', '2018-12-16', 101, 'driver'), 
('Alex on leave', '2018-12-12', '2018-12-15', 102, 'driver'), 
('Reminder', '2018-12-13', '2018-12-13', 103, 'driver');

CREATE TABLE IF NOT EXISTS drivers (
  driver_id INT(5) UNSIGNED AUTO_INCREMENT PRIMARY KEY,
  driver_name VARCHAR(100) NOT NULL, 
  date_created DATETIME DEFAULT CURRENT_TIMESTAMP
);

INSERT INTO drivers (driver_id, driver_name)
VALUES (101, 'Alex'), (102, 'Tom'),(103, 'Trevor');

我创建了一个 SQL Fiddle,但默认情况下 SQL Fiddle 没有存储 seq_0_to_999999 表。我正在使用最新版本的 MariaDB。

SQL Fiddle Link

如果您安装了 MariaDB,您可以在本地计算机上复制上述表格和 SQL 语句。

基本上,在代码中,如果驱动程序在给定日期关闭,我将使用 in_array 函数从可用性框中删除驱动程序。我试图通过 1 个查询来实现这一点,它检索一周的空天数或驱动程序关闭的位置。目前,查询仅在给定日期有条目时才有效,问题是,如果结束日期尚未结束,则查询不会继续。

这是一个执行当前查询的好例子。如果您查看下面的屏幕,马里奥会在 2019 年 1 月 1 日之前关闭,但第 19、20、21、22 和 23 天是空白的,并且没有说出他的名字。他们可能需要在某个地方进行 CROSS JOIN - 逻辑思考!

任何帮助将不胜感激,并感谢您超时!

【问题讨论】:

  • 我担心您将数据存储和检索问题与数据显示问题混为一谈。
  • 如果您仍在苦苦挣扎,那么给定参数集的预期结果可能会很有用
  • 我刚刚上传了我正在处理的当前系统的视图,我已经解释了我的意思。我的目标是对其进行编辑以描述结果。
  • 我刚刚添加了结果集;它正在输出什么以及它应该做什么。
  • 数据集和结果集之间没有任何关系

标签: mysql sql database mariadb


【解决方案1】:

我发现你小提琴中的查询有点难以理解。但我认为你想要的是这样的:

select *
from planner_events e
join drivers d on d.driver_id=e.user_id
where e.event_start <= @enddate and e.event_end >= @startdate
  and e.user_type='driver'

@startdate 是您感兴趣的时间段的第一天,@enddate 是最后一天。如果范围的大小始终相同,一周或其他任何时间,@enddate 可以替换为计算。

这应该会为您提供所有司机的列表,这些司机的活动与所需日期范围重叠。如果您想要的是所有在该日期范围内没有活动的司机,那么只需颠倒日期测试即可。

哦,当然,您应该列出您真正感兴趣的任何字段,而不是“选择 *”。我只是在那里偷懒。

更新

啊,我明白了。您需要每天都有谁有空的列表,而不仅仅是一周中某个时间有空的人。

好的,据我所知,要做到这一点,您需要一张包含每天记录的表格。有多种方法可以创建这样的表,我不知道使用标准 SQL 的方法。如果创建该表是我们可以解决的问题,但让我假设它不是,并且这样的表存在并且称为“工作日”并且每条记录都包含一个日期字段“date1”。

那么查询可以这样完成:

select w.date1, d.driver_id, d.driver_name
from workdays w
cross join drivers d
where w.date1 between @start and @end
 and not exists (select * from planner_event 
   where event_start<=w.date1 and event_end>=w.date1 and user_id=d.driver_id and user_type='driver')
order by w.date1, d.driver_id

这会给出一个包含日期列表和可用驱动程序的结果集,然后您必须使用代码将其格式化为您想要的表格。如果您要寻找的结果集每天有一列,每个驱动程序有一排或类似的,我相信我们可以做到这一点,必须多玩一点。我认为这将是一个混乱的查询,每天都有一个子查询。

更新 2

啊。如果我们知道周期是一周,那么我们就不需要工作日表。我们可以这样做:

select d.driver_id, d.driver_name,
  case when exists (select * from planner_events where '2018-12-31' between event_start_date and event_end_date and user_id=d.driver_id) then 'N' else 'Y' end as Monday,
  case when exists (select * from planner_events where DATE_ADD('2018-12-31', INTERVAL 1 DAY) between event_start_date and event_end_date and user_id=d.driver_id) then 'N' else 'Y' end as Tuesday,
  case when exists (select * from planner_events where DATE_ADD('2018-12-31', INTERVAL 2 DAY) between event_start_date and event_end_date and user_id=d.driver_id) then 'N' else 'Y' end as Wednesday,
  case when exists (select * from planner_events where DATE_ADD('2018-12-31', INTERVAL 3 DAY) between event_start_date and event_end_date and user_id=d.driver_id) then 'N' else 'Y' end as Thursday,
  case when exists (select * from planner_events where DATE_ADD('2018-12-31', INTERVAL 4 DAY) between event_start_date and event_end_date and user_id=d.driver_id) then 'N' else 'Y' end as Friday
from drivers d
order by d.driver_id

【讨论】:

  • 这仅适用于每日视图,而不适用于每周视图。我已经让这个查询每天都在工作。我面临的问题是,我们需要一种方法来显示司机在一周内的上下班情况。
  • 不,这适用于每周视图。给出一周的开始和结束日期。与一周重叠的任何事件都将包含在输出中。试试看或通过一个例子来工作。假设这一周是 12 月 9 日到 15 日。如果一个事件是 12 月 8 日到 12 日,它将通过测试。如果是 12 月 14 日至 20 日,它将通过测试。 12 月 8 日至 16 日将通过测试。但 12 月 7 日至 8 日不会,12 月 20 日至 21 日也不会。
  • 我刚刚更新了帖子,并上传了一张图片 - 你会明白我的意思。
  • 我刚刚添加了结果集;它输出和丢失了什么。
【解决方案2】:

有一位名叫 Andriy (https://dba.stackexchange.com/users/6965/andriy-m) 的绅士,他过去曾帮助过我,他的回答符合我的期望。还要感谢 @Jay 为这个问题发布了另一个答案。

这是正确的 SQL 查询:

SELECT sub.planner_start_date, COALESCE(d.driver_name, 'N/A') AS driver_name, e.planner_id, e.event_name, e.event_start_date, e.event_start_time, e.event_end_date, e.event_end_time
FROM
( SELECT
        '2018-12-30' + INTERVAL seq.seq DAY AS planner_start_date
     FROM
        seq_0_to_999999 AS seq
     WHERE
        seq.seq <= TIMESTAMPDIFF(DAY, '2018-12-30', '2019-01-05')
    ) as sub
LEFT JOIN planner_events AS e ON e.event_start_date <= sub.planner_start_date AND e.event_end_date >= sub.planner_start_date
LEFT JOIN drivers AS d ON e.user_id = d.driver_id AND e.user_type = 'driver'

感谢大家为此提供的所有帮助。

【讨论】:

  • seq 位对我来说似乎有点愚蠢。显然,您在应用程序代码中输出结果,因此这似乎是处理显示方面的合乎逻辑的地方。不过,这是你的派对。
【解决方案3】:

在我看来,这就是您需要的全部查询:

SELECT d.driver_id
     , d.driver_name
     , e.planner_id
     , e.event_name
     , e.event_start_date
     , e.event_end_date
  FROM drivers d 
  JOIN planner_events e 
    ON e.user_id = d.driver_id;

其他一切似乎都是显示问题 - 最好在应用程序代码中解决。

【讨论】:

  • 这是行不通的,因为有 CASE 语句、user_id 和 user_type 是有原因的。
猜你喜欢
  • 2020-03-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-11-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多