【问题标题】:SQL - How can I query for all objects who have COUNT(0) of related objects in a reverse relationship?SQL - 如何查询所有具有 COUNT(0) 个反向关系的相关对象的对象?
【发布时间】:2012-02-28 18:54:08
【问题描述】:

这是我所拥有的:

Person
    name = varchar

Helmet
    person = foreignkey -> Person
    is_safe = boolean

现在,对于批处理作业,我需要查询(没有 ORM,只是原始 SQL)所有具有 0 个安全的 HelmetPerson。我显然可以遍历数据库中的每个 Person 等,但我需要在单个查询中执行此操作并一次将其限制为 100 个(数据库中有这些吸盘的 novemdecillions),并删除每个Person。我不需要将每个Helmet 记录附加到结果中。我只需要Person 记录(自然删除会级联),但我不能简单地发出DELETE 来代替我的SELECT,因为在删除它们之前我需要在其他地方做一些事情。

我正在使用 Postgres,但如果可能的话,我更喜欢使用或多或少与数据库无关的查询。

这是我抽象的想法:

SELECT * FROM person
    WHERE (SELECT COUNT(*) FROM helmet
        WHERE person_id = person.id AND is_safe = false) = 0
    LIMIT 100

这显然不是有效的 SQL,但我希望有一个功能等效但有效的版本。

【问题讨论】:

    标签: sql select count


    【解决方案1】:
    select * 
    from person
    where person_id not in
       (
        select person_id 
        from helmet
        where is_safe = false
       )
    

    【讨论】:

      【解决方案2】:
      SELECT *
      FROM (
        SELECT p.*
        FROM Person p
        INNER JOIN Helmet h ON p.id = h.person
        GROUP BY p.id
        HAVING SUM(h.is_safe) = 0
      ) inner_select
      LIMIT 100
      

      所以,这个查询由两部分组成:

      1. 查询的主力是内部查询。这个查询将每个人与他所有的头盔连接在一起。然后它使用GROUP BY 两个将特定人的所有行关联在一起。一旦我们有了一个组,我们就可以对每个组使用聚合函数,在这种情况下,我们使用SUM 来计算安全头盔的数量。 SUMHAVING 子句用于仅选择安全头盔的总和(即安全头盔的数量)为零的组。
      2. 外部查询确保将LIMIT 应用于内部查询的结果,而不是计算准确结果所需的表行。

      【讨论】:

      • 非常感谢。你能解释一下这是如何工作的(尤其是其余部分的包装器SELECT)?
      • 这个查询的内部查询不是真的会使用整个表,即使外部会将它限制为 100? 100 的限制是为了提高性能,因为Person 表中有数百万行。如果我的假设是真的,当它找到 100 个匹配的行时,是否有任何可能的方法让它停止?我想我明白你不能简单地将LIMIT 应用于内部查询,否则一旦处理了前 100 行,查询将永远不会再次返回匹配项,对吧?有没有办法解决这个问题,或者这是否正确?
      • 有一些方法可以解决,但我不是查询优化方面的专家。你真的应该看看@Quassnoi 寻求帮助,因为这对他来说可能很容易 =) 你也可以找到他的博客在这里,充满了好东西:explainextended.com
      • 顺便说一句,当我尝试这个时,我得到了一个错误:subquery in FROM must have an alias at character 20 - HINT: For example, FROM (SELECT ...) [AS] foo. from Postgres
      • 可能是因为 INNER 是保留字。尝试将inner 重命名为foo
      【解决方案3】:
      SELECT person.*
      FROM person
      LEFT JOIN (SELECT DISTINCT person_id FROM helmet) AS T2 ON person.id = T2.person_id
      WHERE T2.person_id IS NULL
      LIMIT 100
      

      【讨论】:

        【解决方案4】:

        我最终使用了:

        SELECT *
        FROM person p
        WHERE NOT EXISTS
            (
                SELECT h.person_id
                FROM helmet h
                WHERE h.person_id = p.id
                    AND is_safe = true
            )
        LIMIT 100
        

        一旦找到 100 个匹配的结果,就会停止扫描表。

        【讨论】:

          猜你喜欢
          • 2020-05-25
          • 1970-01-01
          • 2011-06-06
          • 1970-01-01
          • 2017-07-10
          • 1970-01-01
          • 2019-04-23
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多