2016-08-26 4 views
3

Я пытаюсь реорганизовать один из моих запросов, и я просто не делаю что-то совершенно правильно.рефакторинг postgres join vs except

Я хотел бы объединить два запроса и сделать один, но я смущен тем, как он работает с LEFT JOIN.

Все QuizMasters, которые имеют состояние "активный"

Минус (-)

QuizMasters, которые имеют "активный" событие на определенный день (не все QuizMasters имеют события только ~ 25%).

Определение

  • Событие магазина start_at доу/WDAY для данного события, например, понедельник-воскресенье (хотя как DateTime, только WDAY и время актуальны).
  • События и QuizMasters имеют состояния, которые являются либо «активный» или нет.

Старый запрос (который данные верны)

SELECT first_name, last_name, email 
FROM quiz_masters 
WHERE quiz_masters.state = 'active' # (175 rows) 

EXCEPT 

SELECT first_name, last_name, email 
FROM quiz_masters 
LEFT JOIN events ON events.quiz_master_id = quiz_masters.id 
WHERE quiz_masters.state = 'active' 
AND EXTRACT(dow FROM events.start_at::timestamp::date) = 3 AND events.state = 'active' 
GROUP BY first_name, last_name, email # (- 20 rows) 

Всего 155 строк соответствует запросу.

Комбинированный запрос, который не работает

Я хотел бы объединить их в нечто вроде:

SELECT first_name, last_name, email 
FROM quiz_masters 
LEFT JOIN events ON events.quiz_master_id = quiz_masters.id 
WHERE quiz_masters.state = 'active' 
AND events.quiz_master_id IS null 
OR (EXTRACT(dow FROM events.start_at::timestamp::date) <> 3 AND events.state = 'active') 
GROUP BY first_name, last_name, email 

144 строк (нет 11 строк)

Но я не знаю, как чтобы сохранить все строки от quiz_masters, которые активны, но не имеют никакого события. Он все еще удаляет их. Может быть, мне нужно какое-то другое соединение?

+4

«исключение» обычно может быть переписано как условие «не существует». –

+0

mayby ​​in missing rows condition '(extract ....)' is false И строки в событиях существуют? Вы текущий, где находится '(quiz_masters.state = 'active' И events.quiz_master_id IS null) ИЛИ (EXTRACT ...)' ie _no строки в событиях OR (dow <> 3 и events.state = active) – Mike

+1

BTW ' ... left join events on ... где события. ... 'неправильно. Это фактически «внутреннее соединение», даже если вы выполняете проверку «events.quiz_master_id IS null». – Abelisto

ответ

2

В первом запросе вы исключаете все события, которые активны в среду, поэтому включены неактивные события в любой день. В объединенном запросе вы включаете все события, которые активны в любой день, но среда и неактивные события вообще. Это ваша разница в 11 рядов.

Это вы должны получить обратно до 155 строк:

SELECT DISTINCT first_name, last_name, email 
FROM quiz_masters 
LEFT JOIN (
    SELECT quiz_master_id AS id, state 
    FROM events 
    WHERE EXTRACT(dow FROM events.start_at::timestamp) = 3 
    AND events.state = 'active') ev USING (id) 
WHERE quiz_masters.state = 'active' 
AND ev.state IS NULL; 

Видимо у вас есть несколько записей для мастеров викторины, но вместо того, делать GROUP BY вы должны выбрать DISTINCT строки. GROUP BY должен использоваться только с агрегатными функциями.

+0

Это не работает. Он возвращает 82 строки вместо 155 ;- (Не у всех quiz_masters есть событие, и я думаю, что он удаляет их после добавления AND NOT – ere

+1

Можете ли вы ** отредактировать ** свой вопрос и дать определения таблиц и написать на естественном языке, какие данные вы хотите получить? Это будет работать намного проще. См. также переработанный запрос. – Patrick

+0

Уверен, обновлен. Спасибо! – ere

1

Использование человеческого языка.

Первый запрос:

Удалить из quiz_masters всех записей, которые имеют активные события в dow = 3

Второй запрос:

Выбор записей из quiz_masters, имеющие активные события в dow <> 3

В целом не существует равных условий. Например, если quiz_masters имеют активные события на dow = 3 и dow = 4 одновременно, то он будет отсутствовать в первом запросе, но присутствует во втором. Еще один пример: у quiz_masters вообще нет никаких событий. Затем он будет присутствовать в первом и отсутствовать во втором запросе.

В основном, такое неудобство в том, что неправильное использование этого left (outer) присоединиться: с помощью присоединяемой таблицы левой в пункте where вы трансформирует его в (inner) присоединиться. Если left join работал правильно - первый запрос будет пустым, а второй будет возвращать все активные записи из quiz_masters независимо от событий BTW.