2014-01-26 1 views
0

Рассмотрим две таблицы:Выберите Foo с стержневых событий только после определенной даты

Foo: 
    id INT, 
    name VARCHAR 

Bar: 
    id INT, 
    foo_id INT REFERENCES Foo(id), 
    event SET('hit','miss'), 
    ts TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP 

Каждый элемент Foo может иметь несколько событий бар. Как запросить элементы Foo, которые имеют Бар события после определенного времени и времени, но которые также не имеют Бар событий до этого datetime?

Например, рассмотрим:

Foo id=1: 
    hit 2014-01-21 
    hit 2014-01-22 
    hit 2014-01-23 

Foo id=2: 
    hit 2014-01-17 
    hit 2014-01-19 
    hit 2014-01-21 

Foo id=3: 
    hit 2014-01-14 
    hit 2014-01-15 
    hit 2014-01-18 

Foo id=4: 
    hit 2014-01-21 
    hit 2014-01-22 
    hit 2014-01-24 

Я хотел бы запросить только те элементы, Foo с хитами на или после 2014-01-20, так что должен возвращать идентификаторы 1 и 4.

Я попытался присоединиться к Bar таблицу к себе, используя несколько вариантов на следующий:

SELECT 
    b1.id 
FROM 
    Bar b1 JOIN Bar b2 using (foo_id) 
WHERE 
    b1.ts > '2014-01-20'; 

Однако, я не вижу, как фразу ИНЕК, чтобы исключить любые комбинации, для которых любой даты до 2014-01-20.

+0

Итак, стол для бара немного похож на таблицу событий? – Strawberry

+0

@Strawberry: Да, точно. – dotancohen

ответ

2
SELECT Foo.* 
FROM  Foo JOIN Bar ON Bar.foo_id = Foo.id 
GROUP BY Foo.id 
HAVING MIN(Bar.ts) >= '2014-01-20' 

Или:

SELECT Foo.* 
FROM  Foo JOIN (
      SELECT foo_id id, MIN(ts) min_ts 
      FROM  Bar 
      GROUP BY id 
     ) t USING (id) 
WHERE t.min_ts >= '2014-01-20' 

Или:

SELECT Foo.* 
FROM  Foo JOIN Bar ON Bar.foo_id = Foo.id 
GROUP BY Foo.id 
HAVING SUM(Bar.ts >= '2014-01-20') 
AND NOT SUM(Bar.ts < '2014-01-20') 

Или:

SELECT * 
FROM  Foo 
WHERE EXISTS (
      SELECT * 
      FROM Bar 
      WHERE Bar.foo_id = Foo.id 
       AND Bar.ts >= '2014-01-20' 
     ) 
AND NOT EXISTS (
      SELECT * 
      FROM Bar 
      WHERE Bar.foo_id = Foo.id 
       AND Bar.ts < '2014-01-20' 
     ) 

Или:

SELECT b1.foo_id 
FROM  Bar b1 LEFT JOIN Bar b2 
     ON b2.foo_id = b1.foo_id 
    AND b2.ts < '2014-01-20' 
WHERE b1.ts >= '2014-01-20' 
    AND b2.foo_id IS NULL 
GROUP BY b1.foo_id 
+0

Спасибо, здесь есть потрясающие идеи. +1, и я попробую их все, прежде чем выбрать ответ. – dotancohen

+0

@ dotancohen: Честно говоря, я бы пошел с первым решением - это читаемо и показательно. – eggyal

+0

Я вижу это! Однако мне это нужно, чтобы соответствовать разнообразным решениям на сильно индексированной таблице из почти 5 000 000 строк. – dotancohen

Смежные вопросы