在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 JOINLEFT 表)和子查询(@ 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]的主要内容,如果未能解决你的问题,请参考以下文章

如何正则表达式匹配具有固定长度且其中至少有一个数字的字符串

MySQL数据类型 - 空间数据类型

如果这些行中至少有一个具有给定值,则选择具有给定 ID 的所有行

Java集合框架上机练习题:编写一个Book类,该类至少有name和price两个属性。该类要实现Comparable接口,在接口的compareTo()方法.....

如何将两个 TextViews 定位在 ListView 中彼此相邻,但至少有一定的差距

代理当时不能在多个流程图中。至少有两个流程图块存在冲突: