在mysql中选择至少有两个[activity]且间隔时间至少为24小时的[sth]
Posted
技术标签:
【中文标题】在mysql中选择至少有两个[activity]且间隔时间至少为24小时的[sth]【英文标题】:Select [sth] with at least two [activity] with gap time of at least 24 hours in mysql 【发布时间】:2022-01-16 01:43:43 【问题描述】:获得了一个开发大流行病爆发监测系统数据库的最终项目。其他的东西我想得差不多了,但是这个select语句我不知道怎么写:
列出所有进行过两次病毒测试的公民的电话号码,时间窗口为 2021 年 10 月 3 日 00:00 至 2021 年 10 月 5 日 00:00。两次病毒测试的间隔时间必须至少为 24 小时(至少相隔 24 小时)。
这是我图表的一部分,并且可能是这个特定问题所需的全部(忽略此处不需要的医生图表概率)
我最初在想也许每个 SSN 计算有多少个测试 ID,然后检查是否大于 2,但是如何计算 24 小时时间......如果 3 个测试有 20 小时和 8 小时然后第一和第三之间超过 24 ......无论如何,你可以希望看到我的前进方向以及我的想法有多错误哈哈。任何帮助将不胜感激:)
【问题讨论】:
如果他们有三个测试,每个测试相隔 13 小时,它们是包含还是排除? @ysth 最有可能包括 为什么不通过简单地将社会保险号作为外键字段添加到 Test 中来将 Patient 表吸收到 Test 表中? @toonice 是的,好点。患者表有更多变量 b4 我一定错过了你的观点。谢谢 【参考方案1】:可能是这个:
SELECT a.ssn, MAX(a.phone) phone
FROM citizens a
JOIN patients b ON a.ssn = b.ssn
JOIN test c ON b.test_id = c.test_id
WHERE c.test_time BETWEEN '2021-10-03 00:00:00' AND '2021-10-05 00:00:00'
GROUP BY a.ssn
HAVING TIMESTAMPDIFF(HOUR, MIN(c.test_time), MAX(c.test_time)) >= 24
测试的样本数据
CREATE TABLE citizens (
ssn VARCHAR(20),
phone VARCHAR(20)
);
CREATE TABLE patients (
ssn VARCHAR(20),
test_id INT
);
CREATE TABLE test (
test_id INT,
test_time DATETIME
);
INSERT INTO citizens VALUES ( 'A', '123' ), ( 'B', '456' ), ( 'C', '789' );
INSERT INTO patients VALUES ( 'A', 1 ), ( 'A', 2 ), ( 'A', 3 );
INSERT INTO patients VALUES ( 'B', 4 ), ( 'B', 5 ), ( 'B', 6 );
INSERT INTO patients VALUES ( 'C', 7 );
INSERT INTO test VALUES
( 1, '2021-10-03 10:00:00' ),
( 2, '2021-10-03 20:00:00' ),
( 3, '2021-10-04 12:00:00' ),
( 4, '2021-10-03 10:00:00' ),
( 5, '2021-10-03 12:00:00' ),
( 6, '2021-10-03 19:00:00' ),
( 7, '2021-10-03 10:00:00' );
【讨论】:
看起来不错,谢谢队友 :) 但我能问一下 max(a.phone) 是多少吗?我第一次看到它,所以不知道它是做什么的,哈哈 @CurryFlurry 因为a.phone
不在GROUP BY
子句中,它必须聚合这些列以符合ONLY_FULL_GROUP_BY
dev.mysql.com/doc/refman/8.0/en/…
@ProGu ssn 它是一个主键,因此 phone 是一个功能相关的列,并且可以在没有 MAX 的情况下包含在 mysql 5.7.5 中(尽管还没有在 mariadb 中)
@toonice 你能用 dbfiddle 链接演示你看到的问题吗?
差异是由拼写错误引起的。我为混乱道歉。我会删除我的反对票,但已经超过 18 小时。我已删除评论以避免进一步混淆。【参考方案2】:
首先,我的回答是基于Patient
应该被吸收到Test
中的假设,正如我之前的评论。我可以更新我的答案以包含列出的表结构的代码,或者根据要求。
(以下答案版本已针对以下测试数据进行了测试)
回答查询(如果第三次测试可能发生在相隔至少 24 小时的两次测试之间)
我首先将Test
的两个副本内部连接到自己,然后将结果内部连接到Citizen
,以便对于每个Citizen
,返回它们之间至少有24 小时的所有记录,无论如果第三条记录出现在它们之间且距离它们不到 24 小时。
生成的每条记录都应包含该匹配的测试时间(和测试 ID)。我在这里选择了主题来演示如何使用它们来通知用户,并作为语句扩展可能性的建议(如果用户希望扩展它)。
不应有重复记录。
SELECT Citizen.ssn AS 'Social Security Number',
Citizen.phone_number AS 'Phone Number',
greater_than_first_table.test_id AS 'First Test ID',
greater_than_first_table.test_time AS 'First Test Time',
greater_than_second_table.test_id AS 'Second Test ID',
greater_than_second_table.test_time AS 'Second Test Time'
FROM Test AS greater_than_first_table
INNER JOIN Test AS greater_than_second_table ON greater_than_first_table.ssn = greater_than_second_table.ssn AND
TIMESTAMPDIFF( HOUR,
greater_than_first_table.test_time,
greater_than_second_table.test_time ) >= 24
INNER JOIN Citizen ON greater_than_first_table.ssn = Citizen.ssn
WHERE greater_than_first_table.test_time BETWEEN '2021-10-03 00:00:00' AND '2021-10-05 00:00:00' AND
greater_than_second_table.test_time BETWEEN '2021-10-03 00:00:00' AND '2021-10-05 00:00:00';
回答查询(如上,但只有电话号码)
如果用户只对上面的电话号码感兴趣,那么可以使用下面的。
GROUP BY
用于消除重复结果。
SELECT Citizen.phone_number AS 'Phone Number'
FROM Test AS greater_than_first_table
INNER JOIN Test AS greater_than_second_table ON greater_than_first_table.ssn = greater_than_second_table.ssn AND
TIMESTAMPDIFF( HOUR,
greater_than_first_table.test_time,
greater_than_second_table.test_time ) >= 24
INNER JOIN Citizen ON greater_than_first_table.ssn = Citizen.ssn
WHERE greater_than_first_table.test_time BETWEEN '2021-10-03 00:00:00' AND '2021-10-05 00:00:00' AND
greater_than_second_table.test_time BETWEEN '2021-10-03 00:00:00' AND '2021-10-05 00:00:00'
GROUP BY Citizen.ssn;
回答查询(如果在下次测试前至少 24 小时)
以下是上述第一个语句的修改版本,它消除了第三个值出现在第一个值之后不到 24 小时的结果,即它只列出在下一个按时间顺序出现之前有 24 小时或更长时间的记录测试。
子查询用于为Citizen
生成相隔不到 24 小时的两个测试的记录。然后在主查询的第一个 INNER JOIN
(LEFT
表)和子查询(@ 987654333@表)。主查询中的最后一个 WHERE
子句从子查询标识的结果中删除了这些记录。
SELECT Citizen.ssn AS 'Social Security Number',
Citizen.phone_number AS 'Phone Number',
greater_than_first_table.test_id AS 'First Test ID',
greater_than_first_table.test_time AS 'First Test Time',
greater_than_second_table.test_id AS 'Second Test ID',
greater_than_second_table.test_time AS 'Second Test Time'
FROM Test AS greater_than_first_table
INNER JOIN Test AS greater_than_second_table ON greater_than_first_table.ssn = greater_than_second_table.ssn AND
TIMESTAMPDIFF( HOUR,
greater_than_first_table.test_time,
greater_than_second_table.test_time ) >= 24
INNER JOIN Citizen ON greater_than_first_table.ssn = Citizen.ssn
LEFT JOIN
(
SELECT less_than_first_table.ssn AS less_than_ssn,
less_than_first_table.test_time AS less_than_test_time
FROM Test AS less_than_first_table
INNER JOIN Test AS less_than_second_table ON less_than_first_table.ssn = less_than_second_table.ssn AND
TIMESTAMPDIFF( HOUR,
less_than_first_table.test_time,
less_than_second_table.test_time ) > 0 AND
TIMESTAMPDIFF( HOUR,
less_than_first_table.test_time,
less_than_second_table.test_time ) < 24
WHERE less_than_first_table.test_time BETWEEN '2021-10-03 00:00:00' AND '2021-10-05 00:00:00' AND
less_than_second_table.test_time BETWEEN '2021-10-03 00:00:00' AND '2021-10-05 00:00:00'
) AS less_than_table ON greater_than_first_table.ssn = less_than_table.less_than_ssn AND
greater_than_first_table.test_time = less_than_table.less_than_test_time
WHERE greater_than_first_table.test_time BETWEEN '2021-10-03 00:00:00' AND '2021-10-05 00:00:00' AND
greater_than_second_table.test_time BETWEEN '2021-10-03 00:00:00' AND '2021-10-05 00:00:00' AND
less_than_table.less_than_ssn IS NULL;
测试数据语句
DROP TABLE IF EXISTS Citizen;
DROP TABLE IF EXISTS Test;
CREATE TABLE Citizen
(
ssn VARCHAR( 50 ) NOT NULL,
phone_number VARCHAR( 50 ) NOT NULL,
CONSTRAINT pkc_citizen PRIMARY KEY ( ssn ),
CONSTRAINT unc_citizen_phone_number UNIQUE ( phone_number )
);
CREATE TABLE Test
(
test_id INT NOT NULL AUTO_INCREMENT,
ssn VARCHAR( 50 ) NOT NULL,
test_time DATETIME,
CONSTRAINT pkc_test PRIMARY KEY( test_id ),
CONSTRAINT fkc_test_citizen FOREIGN KEY ( ssn ) REFERENCES Citizen( ssn )
);
INSERT INTO Citizen VALUES ( 'THX-1138', '555-555-555' );
INSERT INTO Citizen VALUES ( 'TK4218', '555-111-111' );
INSERT INTO Citizen VALUES ( 'R2-D2', '555-222-222' );
INSERT INTO Test ( ssn, test_time ) VALUES ( 'THX-1138', '2021-10-02 22:34:51' );
INSERT INTO Test ( ssn, test_time ) VALUES ( 'THX-1138', '2021-10-03 09:00:00' );
INSERT INTO Test ( ssn, test_time ) VALUES ( 'THX-1138', '2021-10-03 10:00:00' );
INSERT INTO Test ( ssn, test_time ) VALUES ( 'THX-1138', '2021-10-04 11:34:51' );
INSERT INTO Test ( ssn, test_time ) VALUES ( 'TK4218', '2021-10-03 10:34:51' );
INSERT INTO Test ( ssn, test_time ) VALUES ( 'TK4218', '2021-10-04 11:34:51' );
【讨论】:
这似乎是不必要的复杂?如果他们想排除他们有两个以上测试的情况,则需要左连接,没有 >= 24 小时相隔,但非相邻测试之间有 24 小时,但从 cmets 到他们做的问题 不想排除这种情况;一个简单的 group by 并比较 having 子句中的 min 和 max 时间就足够了 问题的措辞方式模棱两可,后续评论表明提问者不确定选择哪种解释。我已经更新了我的答案以涵盖这两种可能性。以上是关于在mysql中选择至少有两个[activity]且间隔时间至少为24小时的[sth]的主要内容,如果未能解决你的问题,请参考以下文章
如果这些行中至少有一个具有给定值,则选择具有给定 ID 的所有行
Java集合框架上机练习题:编写一个Book类,该类至少有name和price两个属性。该类要实现Comparable接口,在接口的compareTo()方法.....