繁体   English   中英

MySQL-以一对多关系选择不匹配的数据

[英]MySQL - Select un-matching data in a one to many relationship

首先,这个问题是关于PHP和MySQL的

我有两个数据库表:

People表:

person_id   |   field_1  |  field_2  |  other_fields... 

Notes表:

note_id   |  person_id  |  created_timestamp  |  other_fields... 

People表与Notes表具有一对多关系...
每次在Notes表中创建一个Notes ,都会为其附加一个时间戳,并分配一个person_id外键。

现在...
我需要找到所有people谁没有过一note对他们在过去的30天。
我现在的方式是:

  1. 使用person_idcreated_timestamp > 'time(31*86400)'Notes表中获取所有笔记( person_id 。我知道,但适合我的需要)
  2. 遍历结果并将person_id添加到临时数组$temp
  3. People表中获取所有记录
  4. 遍历每条记录,并使用$tempperson_id进行in_array()比较

这不是很有效,并且在有很多PeopleNotes时会破坏应用程序。

有谁对此有更好的解决方案。 理想情况下,仅使用一个SQL查询就可以实现。

谢谢看

SELECT person_id FROM People WHERE person_id NOT IN 
    (SELECT person_id FROM Note 
        WHERE created_timestamp > DATE_SUB(CURDATE(), INTERVAL 30 DAY))

假定create_timestamp的类型为“ DATE”,“ TIMESTAMP”或“ DATETIME”。 如果您在此处使用unix时间戳,请使用FROM_UNIXTIME(created_timestamp)将其转换为MySQL时间戳。

标准解决方案是使用这种形式的子查询:

Select * from people where PersonID NOT in 
  (select PersonID from Notes where Created_Timestamp>...)

另一种选择是在Notes上进行右外部联接,并仅对Notes.PersonID IS NULL进行过滤,这只会为您提供在Notes上不匹配的行。

就我个人而言,我更喜欢上面的子查询方法,该方法应该可以高效运行,并且比外部联接解决方案更容易理解。

我相信这应该有效:

SELECT * FROM不在person_id中的人(从注释中选择DISTINCT person_id);

如果这很关键,则可以考虑进行非规范化:将最后一个音符的时间戳存储在用户表中,并对该列建立索引。

否则,无法避免遍历整个人员表,因此请在注释表的(person_id,timestamp)对上添加索引,并使用左联接或子查询:

SELECT * FROM people 
         LEFT JOIN notes ON people.person_id = notes.person_id
                        AND notes.created_timestamp < NOW() - INTERVAL 30 DAY
WHERE notes.person_id IS NULL

SELECT * FROM people
WHERE person_id NOT IN (SELECT person_id FROM notes
                        WHERE created_timestamp < NOW() - INTERVAL 30 DAY)

左联接/为空

   SELECT p.*
     FROM PEOPLE p
LEFT JOIN NOTES n ON n.person_id = p.person_id
                 AND n.created_timestamp >= DATE_SUB(NOW(), INTERVAL 30 DAY)
    WHERE n.note_id IS NULL

不存在

SELECT p.*
  FROM PEOPLE p
 WHERE NOT EXISTS(SELECT NULL
                    FROM NOTES n
                   WHERE n.person_id = p.person_id
                     AND n.created_timestamp >= DATE_SUB(NOW(), INTERVAL 30 DAY))

不在

SELECT p.*
  FROM PEOPLE p
 WHERE p.person_id NOT (SELECT n.person_id
                          FROM NOTES n
                         WHERE n.created_timestamp >= DATE_SUB(NOW(), INTERVAL 30 DAY))

结论

当比较的列不可为空时,LEFT JOIN IS NULL在MySQL上效率最高 如果比较的列可以为空,则NOT IN和NOT EXISTS更为有效

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM