MySQL - 选择一对多关系中的非匹配数据

时间:2010-09-15 16:15:00

标签: php mysql performance

首先,这个问题涉及PHP和MySQL

我有两个数据库表:

People表:

person_id   |   field_1  |  field_2  |  other_fields... 

Notes表:

note_id   |  person_id  |  created_timestamp  |  other_fields... 

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

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

  1. Notes表中获取具有不同person_idcreated_timestamp>的所有备注'time(31*86400)'(不准确......我知道,但适合我的需要)
  2. 循环显示结果并将person_id添加到临时数组$temp
  3. People表格
  4. 获取所有记录
  5. 循环浏览每条记录,并对in_array()person_id
  6. 进行$temp比较

    当有很多PeopleNotes时,这不是非常有效并且使应用程序瘫痪。

    有没有人有更好的解决方案。理想情况下,只需一个SQL查询即可实现。

    感谢您寻找

5 个答案:

答案 0 :(得分:2)

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时间戳

答案 1 :(得分:1)

标准解决方案是使用此表单的子查询:

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

另一个选项是在Notes上执行右外连接,仅针对Notes.PersonID IS NULL进行过滤,它仅为您提供Notes上不匹配的行。

就我个人而言,我更喜欢上面的子查询方法,该方法应该比外连接解决方​​案运行得更高效,更容易理解。

答案 2 :(得分:1)

我相信这应该有效:

SELECT * FROM people WHERE person_id NOT IN(SELECT DISTINCT person_id FROM notes);

答案 3 :(得分:1)

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

否则,没有办法避免遍历整个人员表,所以在note表的(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)

答案 4 :(得分:1)

LEFT JOIN / IS NULL

   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

NOT EXISTS

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 is the most efficient on MySQL when the columns compared are not nullable。如果是the columns compared were nullable, NOT IN and NOT EXISTS are more efficient