У меня есть тривиальные таблицы post
, tag
и post_tags
в тривиальных отношениях Много-ко-многим. Я хочу выбрать некоторые сообщения, включив и исключив некоторые теги. Я пробовал много вариантов SQL-запросов, но ни один из них не работает для , за исключением тегов.
Я начал из запроса, как это:PostgreSQL NOT IN работает некорректно с JOIN
SELECT post.* FROM post
INNER JOIN post_tags ON post.id = post_tags.post_id
INNER JOIN tag ON post_tags.tag_id = tag.id
WHERE tag.name IN ('Science','Culture')
AND tag.name NOT IN ('War', 'Crime')
GROUP BY post.id
HAVING COUNT(post_tags.id) > 1
ORDER BY post.rating DESC
LIMIT 50;
Но, к сожалению, это не работает. Я вижу сообщения с тегом «Война» в наборе результатов. Тогда я попытался переместить условие NOT IN
в отдельный подзапрос на post_tags
и присоединиться к нему:
SELECT post.* FROM post
INNER JOIN post_tags ON post.id = post_tags.post_id
INNER JOIN (SELECT * FROM tag WHERE name NOT IN ('War', 'Crime')) AS tags
ON post_tags.tag_id = tags.id
WHERE tags.name IN ('Science','Culture')
GROUP BY post.id
HAVING COUNT(post_tags.id) > 1
ORDER BY post.rating DESC
LIMIT 50;
Даже пытались исключить некоторые посты в первом JOIN
так:
SELECT post.* FROM post
INNER JOIN post_tags ON post.id = post_tags.post_id
AND post_tags.tag_id NOT IN (SELECT id FROM tag WHERE name IN ('War', 'Crime'))
INNER JOIN tag ON post_tags.tag_id = tag.id
WHERE tag.name IN ('Science','Culture')
GROUP BY post.id
HAVING COUNT(post_tags.id) > 1
ORDER BY post.rating DESC
LIMIT 50;
Но ни одна из этих работ , Меня особенно смущает вопрос о втором запросе (соединение с фильтрованным набором результатов вместо таблицы).
Использование PostgreSQL версии 9.3, OS Ubuntu 14.04.
Любые мысли?
Я должен сказать, что ваше решение в 1,5 раза быстрее, на моей базе данных, чем первое решение. Благодаря! –
@VoidFloyd: Итак, что заставило вас снова принять менее эффективное решение? –
На больших объемах данных это решение становится все более медленным. Первое решение выполняется быстрее. Но самое быстрое решение состоит в том, чтобы дважды присоединиться к таблице post_tags - в первый раз это внутреннее соединение с условием IN, а во втором - внешнее соединение с условием NOT IN. Затем мы можем подсчитать pt.id и pt2.id и сделать условие HAVING для этого числа - 2 и 0 соответственно. Кроме того, конечно, лучше не присоединяться к самому тегу tabe, лучше получить необходимые идентификаторы от него (или от запроса, зависит от приложения) и использовать их в joiningconditions post_tags. –