查询以在 table 记录 "start" 和 "stop" 日期中查找给定日期的活动记录
Query to find active records on a given date in a table recording "start" and "stop" dates
我有一份 "start / stop" activity 的列表,记录在 table 中,每个都与一个日期相关联。我需要确定哪些用户在特定日期有 "started" - 即正在执行任务。我当前的设置和查询可以用这个简单的视图来表示:
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)
通过更改查询中的日期,此查询 return 是一个用户 ID 列表,告诉我谁在该特定日期参与(即已开始任务)。然而,用户被认为(在现实生活中)在他们停止任务的实际日期参与了任务。此查询不允许这样做,因为如果您要更改查询中的日期以反映 2020-01-19,即用户 1 停止的日期,则查询只会 return 用户 2.
我尝试将 <=
条件更改为严格的 <
,虽然这解决了那部分问题,但用户在他们开始的那一天并不被视为忙碌。使用严格的 <
,只有用户 return 在“2019-01-25”上编辑,而我希望两个用户都出现。
目前我唯一的"viable"解决方案是合并两个版本的查询结果(以DISTINCT
/UNION
查询的形式),但我忍不住想,一定有更有效的方法来获得我需要的结果。
这有帮助吗?
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 |
+-----------+----------------+------------+------------+
一种方法是相关子查询:
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_date end);
也就是说,select 个用户的最新状态日期为 "active"。
我有一份 "start / stop" activity 的列表,记录在 table 中,每个都与一个日期相关联。我需要确定哪些用户在特定日期有 "started" - 即正在执行任务。我当前的设置和查询可以用这个简单的视图来表示:
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)
通过更改查询中的日期,此查询 return 是一个用户 ID 列表,告诉我谁在该特定日期参与(即已开始任务)。然而,用户被认为(在现实生活中)在他们停止任务的实际日期参与了任务。此查询不允许这样做,因为如果您要更改查询中的日期以反映 2020-01-19,即用户 1 停止的日期,则查询只会 return 用户 2.
我尝试将 <=
条件更改为严格的 <
,虽然这解决了那部分问题,但用户在他们开始的那一天并不被视为忙碌。使用严格的 <
,只有用户 return 在“2019-01-25”上编辑,而我希望两个用户都出现。
目前我唯一的"viable"解决方案是合并两个版本的查询结果(以DISTINCT
/UNION
查询的形式),但我忍不住想,一定有更有效的方法来获得我需要的结果。
这有帮助吗?
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 |
+-----------+----------------+------------+------------+
一种方法是相关子查询:
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_date end);
也就是说,select 个用户的最新状态日期为 "active"。