【问题标题】:MySQL - Task Manager - S-Curve - Summarizing tasksMySQL - 任务管理器 - S 曲线 - 总结任务
【发布时间】:2022-01-26 01:46:37
【问题描述】:

我正在使用 PHP 和 MySQL 以及其他语言构建项目管理系统,并且我正在努力查询我的数据以创建 S 曲线图。 在我的数据库中,我有一个名为“Tasks”的表格

ID Task Beginning_Date End_date Status
1 Task-1 2021-12-01 2021-12-06 Pendent
2 Task-2 2021-12-02 2021-12-07 Completed
3 Task-3 2021-12-03 2021-12-07 Completed

要绘制图表,我需要在任务表的最小日期和最大日期之间的每一天都有一行,即使任务表中不存在这个确切的日期(例如:2021-12- 04 - 它没有写在桌子上,但它发生在 2021-12-01 和 2021-12-07 之间)。 而且,对于每一天,我都需要计算日期之前存在的所有任务的持续时间,并且只计算已完成的任务的持续时间(我会尝试用一个例子更好地解释):

Day Days_Tasks Days_Tasks_Completed Observation:
2021-12-01 (first day from the Task Table) 1 0 Counting: Only one day of task-1
2021-12-02 3 1 Days_Task: 2 days of task1 + 1 day of task2 Completed_Taks: 1 day of Task2
2021-12-03 6 3 Days_Task: 3 days of task1 + 2d of task2 + 1d task3 Completed_Taks: 2 day of Task2 and 1d task3 (it does not count the days from task1 becouse it is not "Completed"
2021-12-04 9 5 Days_Task: 4days of task1 plus 3d of task2 plus 2d task3 Completed_Taks: 3d of task2 and 2d task3
2021-12-05 12 7 Days_Task: 5d of task1 plus 4d of task2 plus 3d task3 Completed_Taks: 4d of task2 plus 3d task3
2021-12-06 15 9 Days_Task: 6d of task1 plus 5d of task2 plus 4d task3 Completed_Taks: 5d of task2 plus 4d task3
2021-12-07 (last day) 17 11 Days_Task: 6d of task1 plus 6d of task2 plus 5d task3 Completed_Taks: 6d of task2 plus 5d task3

说明: 最后一天 (2020-12-07) 是显示在任务表上的最后日期。对于那一天,我确实计算了 17 个“天任务”(任务 1 为 6 天,因为所有 6 天的持续时间都发生在 2020 年 12 月 7 日之前 + 任务 2 的所有天数 + 持续时间的所有天数任务 3) 已完成的“天数任务”也是如此,但仅计算已完成任务的天数 - 因此,不考虑任务 1,因为它是“未决”)。

老实说,我不知道如何使用 MySQL 来执行此操作。我尝试了一堆查询,但似乎都没有按我的意愿工作。

我能够使用以下方法获得包含所有日期的表格:

@i<-1;
SELECT DATE(ADDDATE((SELECT MIN(Beginning_Date) from tasks), INTERVAL @i:=@i+1 DAY)) AS date FROM `tasks`
HAVING 
@i < DATEDIFF((SELECT MAX(End_date) from tasks), (SELECT MIN(Beginning_Date) from tasks )))

但是,我不知道如何计算这些日期的任务。

有人知道怎么做吗?

【问题讨论】:

  • 你运行的是什么版本的 MySQL?请更新您的 Observation: 列,因为它似乎不正确。 2020-12-06 Days_Tasks = 15 但 Obs: 6 + 5 + 3。类似2020-12-07 Days_Tasks = 17 但 Obs: 6 + 6 + 4。请为 Days_Tasks_Completed 的每一行添加类似的解释
  • 嗨@nnichols。网络服务器正在运行 MySQL 8.0。你对这个错误是完全正确的。我已经编辑了观察列并为已完成的任务添加了新评论
  • 我认为它仍然是错误的,但我可能误解了您要计算的内容。你看过我的db<>fiddle吗?如果我稍后可以使用计算机,我将尝试更新我的答案,并解释为什么我认为您的示例数据是错误的。
  • @nnichols,别担心。我发现我的错误..只是解释得更好,我们以 2020-12-06 为例。对于这一天,我距离任务 1 有 6 天(从 2020 年 12 月 1 日到 2020 年 12 月 6 日),距离任务 2 有 5 天(从 2020 年 12 月 2 日到 2020 年 12 月 6 日),距离任务 3 有 4 天(从 2020 年-12-03 至 2020-12-06) - 总共 15 天。对于已完成的任务,我只过滤已完成的任务,因此,Task1 的日子消失了,导致 9 天......你的小提琴是对的。这正是我所需要的。非常感谢
  • 如果对您有帮助,请记得点赞并接受答案。如果您需要进一步解释,请评论答案,我会尽力提供帮助。

标签: mysql sql


【解决方案1】:

您的所需输出表中似乎存在一些错误。如果我理解正确,则以下查询有效,但这需要 MySQL 8。第一个 CTE 是您的示例数据。第二个 CTE 是 recursive CTE to provide the date range。然后查询会根据日期和状态进行一些聚合。

WITH RECURSIVE `Tasks` (`ID`, `Task`, `Beginning_Date`, `End_date`, `Status`) AS
(
    SELECT 1, 'Task-1', DATE('2021-12-01'), DATE('2021-12-06'), 'Pendent' UNION ALL
    SELECT 2, 'Task-2', DATE('2021-12-02'), DATE('2021-12-07'), 'Completed' UNION ALL
    SELECT 3, 'Task-3', DATE('2021-12-03'), DATE('2021-12-07'), 'Completed'
),
`dates` (`date`) AS
(
  SELECT MIN(`Beginning_Date`) FROM `Tasks`
  UNION ALL
  SELECT `date` + INTERVAL 1 DAY FROM `dates`
  WHERE `date` + INTERVAL 1 DAY <= (SELECT MAX(`End_date`) FROM `Tasks`)
)
SELECT
    `d`.`date` AS `Day`,
    SUM(DATEDIFF(LEAST(`d`.`date`, `t`.`End_date`), `t`.`Beginning_Date`) + 1) AS `Days_Tasks`,
    SUM(IF(`t`.`Status` = 'Completed', DATEDIFF(LEAST(`d`.`date`, `t`.`End_date`), `t`.`Beginning_Date`) + 1, 0)) AS `Days_Tasks_Completed`
FROM `dates` `d`
INNER JOIN `Tasks` `t` ON `t`.`Beginning_Date` <= `d`.`date`
GROUP BY `d`.`date`
ORDER BY `d`.`date`;

这是db<>fiddle

为了更好地了解正在发生的事情,我们可以查看仅针对 Task-1 的 JOIN 结果

/* The CTEs are as above but left out for brevity */
SELECT
    `d`.`date` AS `Day`, t.*,
    /*
        we need to add one as MAX Days_Tasks is inclusive
        of Beginning_Date and End_date
    */
    DATEDIFF(`d`.`date`, `t`.`Beginning_Date`) + 1 AS `Days_Tasks_1`,
    /*
        we need to use the LEAST of d.date and t.End_date so
        we do not continue to count days beyond the t.End_date
    */
    DATEDIFF(LEAST(`d`.`date`, `t`.`End_date`), `t`.`Beginning_Date`) + 1 AS `Days_Tasks_2`
FROM `dates` `d`
INNER JOIN `Tasks` `t` ON `t`.`Beginning_Date` <= `d`.`date`
WHERE `t`.`ID` = 1
ORDER BY `d`.`date` ASC;

返回 -

Day ID Task Beginning_Date End_date Status Days_Tasks_1 Days_Tasks_2
2021-12-01 1 Task-1 2021-12-01 2021-12-06 Pendent 1 1
2021-12-02 1 Task-1 2021-12-01 2021-12-06 Pendent 2 2
2021-12-03 1 Task-1 2021-12-01 2021-12-06 Pendent 3 3
2021-12-04 1 Task-1 2021-12-01 2021-12-06 Pendent 4 4
2021-12-05 1 Task-1 2021-12-01 2021-12-06 Pendent 5 5
2021-12-06 1 Task-1 2021-12-01 2021-12-06 Pendent 6 6
2021-12-07 1 Task-1 2021-12-01 2021-12-06 Pendent 7 6

Days_Tasks_1 看起来很好,直到我们到达最后一行,即使 Task-1 已经结束,它仍在继续增加。为了纠正这个问题,我们需要使用DayEnd_dateLEAST,这样我们就不会在任务的结束日期之后继续。请参阅 Days_Tasks_2

现在,如果我们查看仅 2021-12-07 日的 JOIN 结果

/* CTEs omitted as above */
SELECT
    `d`.`date` AS `Day`, t.*,
    DATEDIFF(LEAST(`d`.`date`, `t`.`End_date`), `t`.`Beginning_Date`) + 1 AS `Days_Tasks`,
    IF(`t`.`Status` = 'Completed', DATEDIFF(LEAST(`d`.`date`, `t`.`End_date`), `t`.`Beginning_Date`) + 1, 0) AS `Days_Tasks_Completed`
FROM `dates` `d`
INNER JOIN `Tasks` `t` ON `t`.`Beginning_Date` <= `d`.`date`
WHERE `d`.`date` = '2021-12-07'
ORDER BY `t`.`ID` ASC;

返回 -

Day ID Task Beginning_Date End_date Status Days_Tasks Days_Tasks_Completed
2021-12-07 1 Task-1 2021-12-01 2021-12-06 Pendent 6 0
2021-12-07 2 Task-2 2021-12-02 2021-12-07 Completed 6 6
2021-12-07 3 Task-3 2021-12-03 2021-12-07 Completed 5 5

您可以在此处查看示例中Observation: 列中所述的计数。通过DayDays_TasksDays_Tasks_Completed 的分组,您可以看到所需的结果。

# Day Days_Tasks Days_Tasks_Completed
2021-12-01 1 0
2021-12-02 3 1
2021-12-03 6 3
2021-12-04 9 5
2021-12-05 12 7
2021-12-06 15 9
2021-12-07 17 11

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-05-16
    • 1970-01-01
    • 2014-04-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多