选择没有任何特定Bar事件的Foo对象

时间:2014-01-26 17:16:01

标签: mysql sql join group-by having

考虑两个表:

Foo:
  id INT,
  name VARCHAR

Bar:
  id INT,
  foo_id INT REFERENCES Foo(id),
  event_type VARCHAR DEFAULT NULL,
  event_duration INT DEFAULT NULL

每个Foo项目可以有多个Bar事件。 如何查询没有满足以下任一条件的任何Bar事件的Foo项目

  1. event_type不是以下值之一:'miss','scratch','scrape'
  2. event_duration非空
  3. 例如,考虑:

    Foo id=1:
        event_type: hit       | event_duration: NULL
        event_type: poke      | event_duration: NULL
        event_type: capture   | event_duration: NULL
    
    Foo id=2:
        event_type: hit       | event_duration: 2
        event_type: poke      | event_duration: NULL
        event_type: capture   | event_duration: NULL
    
    Foo id=3:
        event_type: miss      | event_duration: NULL
        event_type: poke      | event_duration: NULL
        event_type: capture   | event_duration: NULL
    
    Foo id=4:
        event_type: strike    | event_duration: NULL
        event_type: hit       | event_duration: NULL
        event_type: land      | event_duration: NULL
    

    只应返回包含id=1id=4的Foo项目。不应返回带有id=2的项目,因为event_duration之一不是NULL。不应返回包含id=3的项目,因为event_type之一是miss(位于禁止的event_types列表中)。

    我尝试过来自this terrific answer的各种想法,这些想法对我希望从中构建此查询所能学到的情况的概括作出回应。唉,我一直无法将答案概括到足以解决这个问题。这是一个非工作查询的例子,还有其他一些失败的尝试:

    SELECT
        f.name
    FROM
        Foo f JOIN Bar b ON f.id = b.foo_id
    GROUP BY
        b.event_type, b.event_duration
    HAVING
        b.event_type not in ('miss', 'scratch', 'scrape')
      AND
        b.event_duration not null
    

    这是另一个不工作的查询:

    SELECT
        f.name
    FROM
        (
        SELECT
            f.name, b.event_duration
        FROM
            Foo f JOIN Bar b ON f.id = b.foo_id
        GROUP BY
            b.event_type
        HAVING
            b.event_type not in ('miss', 'scratch', 'scrape')
        )
    GROUP BY
        b.event_duration
    HAVING
        b.event_duration not null
    

    还有很多关于JOIN和子查询的其他未处理查询。 请注意,Foo表有近500万行,Bar表有近200万行。这些表在相关字段上编制索引,但在这些大表上根本不可能O(n^2)

4 个答案:

答案 0 :(得分:0)

您可以使用NOT EXISTS来获得所需的结果。

SELECT f.name
FROM foo f
WHERE NOT EXISTS (SELECT 1 FROM bar b
                  WHERE b.foo_id = f.id
                  AND (b.event_type IN ('miss','scratch','scrape')
                       OR b.event_duration IS NOT NULL)
                  )

答案 1 :(得分:0)

试试这个,即使听起来很简单,我认为你的情况也可以。

select f1.id, f1.name from
Foo f1 
left join 

(
     select distinct f.id 
     from Foo f
     join Bar b on f.id = b.foo_id
     where
     ( b.event_type IN ('miss','scratch','scrape') ) OR ( b.event_duration IS NOT NULL )
) f2 on f1.id = f2.id 

WHERE
(f2.id is null)

答案 2 :(得分:0)

您可以在Foo表中创建一个“计数器缓存”字段,该字段仅保存关联的条形图项的计数。

我认为通过两个查询可以更快地解决您的问题:

  1. 查询以更新所有Foo项的计数器缓存。因为这只会看索引列,所以它应该相当快。

  2. 第二个查询不会进行任何连接,但只会查询Foo表中所需的条件,并将“计数器缓存”值设为0.

  3. 通过智能管理“计数器缓存”列,您可能只需运行第一个查询一次。 (当然,你的程序必须保持“计数器缓存”列同步)。

答案 3 :(得分:0)

我会试试这个

SELECT DISTINCT f.Id
FROM Foo f
WHERE NOT EXIST (
                 SELECT DISTINCT b.foo_id
                 WHERE b.foo_id = f.Id
                       AND   (b.event_type IN ('miss','scratch','scrape')                     
                               OR b.event_duration IS NOT NULL)
                 )

你也可以像这样使用Merge

  1. 创建在event_type中有'miss','scratch','scrape'或event_duration不为空的不同Bar ID的列表
  2. 合并Foo和Bar
  3. 使用WHEN NOT MATCHED查找结果