【问题标题】:MySQL - Select un-matching data in a one to many relationshipMySQL - 在一对多关系中选择不匹配的数据
【发布时间】:2011-04-12 18:55:11
【问题描述】:

首先,这个问题是关于 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 外键。

现在...
我需要找到在过去 30 天内没有针对他们的 note 的所有 people
我现在的做法是:

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

当有很多 PeopleNotes 时,这不是很有效并且会削弱应用程序。

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

感谢观看

【问题讨论】:

    标签: php mysql performance


    【解决方案1】:

    左连接/为空

       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 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.

    【讨论】:

      【解决方案2】:

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

      否则无法避免遍历整个people表,所以在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)
      

      【讨论】:

        【解决方案3】:

        我相信这应该可行:

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

        【讨论】:

        • 这不会将结果限制在过去 30 天。
        【解决方案4】:
        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 时间戳

        【讨论】:

        • 谢谢,看起来很有希望...我使用 unix 时间戳,所以完整的查询应该是:SELECT person_id FROM People WHERE person_id NOT IN (SELECT person_id FROM Note WHERE FROM_UNIXTIME(created_timestamp) > DATE_SUB(CURDATE( ), 间隔 30 天)
        【解决方案5】:

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

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

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

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

        【讨论】:

          猜你喜欢
          • 2015-11-21
          • 1970-01-01
          • 1970-01-01
          • 2012-07-18
          • 1970-01-01
          • 2011-03-01
          • 2021-07-12
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多