查询以查找记录了“开始”和“停止”日期的表中给定日期的活动记录

问题描述 投票:2回答:3

我在一个表中记录了一个“开始/停止”活动的列表,每个活动都与一个日期相关联。我需要确定在特定日期“启动”了哪些用户-即正在进行任务。我当前的设置和查询可以通过以下简单视图表示:

CREATE TABLE `registration_statuses` (
    `status_id` INT(11) NOT NULL AUTO_INCREMENT,
    `status_user_id` INT(10) UNSIGNED NOT NULL DEFAULT '0',
    `status_activity` ENUM('start','stop') DEFAULT 'start',
    `status_date` DATE NULL DEFAULT NULL,
    PRIMARY KEY (`status_id`),
    INDEX `status_user_id` (`status_user_id`)
);

INSERT INTO `registration_statuses` (`status_user_id`, `status_activity`, `status_date`)
VALUES (1, 'start', '2020-01-01'),
       (2, 'start', '2020-01-02'),
       (1, 'stop', '2020-01-19'),
       (1, 'start', '2020-01-25'),
       (2, 'stop', '2020-01-31'),
       (1, 'stop', '2020-01-31');

然后我正在运行此查询:

SELECT `rs`.`status_user_id`
FROM `registration_statuses` `rs`
  INNER JOIN (
    SELECT `status_user_id`, MAX(status_date) `last_date`
    FROM `registration_statuses`
    WHERE `status_date` < '2020-01-03'
    GROUP BY `status_user_id`
  ) `srs` ON `rs`.`status_user_id` = `srs`.`status_user_id`
            AND `rs`.`status_date` = `srs`.`last_date`
WHERE `status_activity` = 'start';

(请参阅http://sqlfiddle.com/#!9/c8d371/1/0

通过更改查询中的日期,此查询返回一个用户ID列表,该ID告诉我在该特定日期谁在从事(即开始一项任务)。但是,用户(在现实生活中)被认为在他们停止任务的实际日期参与了该任务。此查询不允许这样做,因为如果您要更改查询中的日期以反映2020-01-19(用户1停止的日期),则查询将仅返回用户2。

我曾尝试将<=条件更改为严格的<,虽然这解决了部分问题,但用户在开始的那一天就不会被视为忙碌。使用严格的<,仅在'2019-01-25'上返回用户,而我希望两个用户都出现。

目前,我唯一的“可行”解决方案是合并两个版本的查询结果(以DISTINCT / UNION查询的形式),但我不禁认为必须有一种更有效的方式来获得我需要的结果。

mysql sql temporal-database
3个回答
0
投票

有帮助吗?

SELECT a.status_id
     , a.status_user_id 
     , a.status_date start
     , MIN(b.status_date) stop
  FROM registration_statuses a
  LEFT
  JOIN registration_statuses b
    ON b.status_user_id = a.status_user_id
   AND b.status_id > a.status_id
   AND b.status_activity = 'stop'
 WHERE a.status_activity = 'start'
 GROUP 
    BY a.status_id;

+-----------+----------------+------------+------------+
| status_id | status_user_id | start      | stop       |
+-----------+----------------+------------+------------+
|         1 |              1 | 2020-01-01 | 2020-01-19 |
|         2 |              2 | 2020-01-02 | 2020-01-31 |
|         4 |              1 | 2020-01-25 | 2020-01-31 |
+-----------+----------------+------------+------------+

0
投票

假设每个用户的状态序列始终是开始/停止(即,没有两个相邻的记录具有相同的状态),则可以使用窗口函数(在MySQL 8.0中可用:]

select status_user_id, status_date start_date, lead_status_date end_date
from (
    select 
        t.*,
        lead(status_date) over(partition by status_user_id order by status_date) lead_status_date
    from registration_statuses t
) t
where 
    status_activity = 'start' 
    and status_date <= '2020-01-03'
    and lead_status_date > '2020-01-03'

Demo on DB Fiddle

status_user_id |开始日期|结束日期-------------:| :--------- | :---------1 | 2020-01-01 | 2020-01-192 | 2020-01-02 | 2020-01-31

0
投票

一种方法是相关子查询:

select rs.*
from registration_statuses rs
where rs.status_date = (select max(rs2.status_date)
                        from registration_statuses rs2
                        where rs2.status_user_id = rs.status_user_id and
                              rs2.status_date <= ?
                       ) and
      rs.status_activity = 'active';

为了提高性能,您需要在registration_statuses(status_user_id, status_date)上建立索引。

还有其他有趣的方法。如果只需要user_id,这是仅使用聚合的方法:

select rs.user_id
from registration_statuses rs
where rs.status_date <= ?
group by rs.user_id
having max(rs.status_date) = max(case when rs.status_activity = 'active' then status_end end);

即,选择截至特定日期的最新状态日期为“活动”的用户。

© www.soinside.com 2019 - 2024. All rights reserved.