#sql #postgresql #subquery #inner-join #union
#sql #postgresql #подзапрос #внутреннее соединение #объединение
Вопрос:
У меня есть 3 таблицы:
Table columns
---------------------
items id, name
variations id, item_id
images id, variation_id, type
image
имеет a variation
и variation
имеет item
Я хочу найти items
те, которые не имеют images
типа 4 или 5 (типы представляют собой любое целое число 0 ..5 и не могут быть нулевыми)
Приведенный ниже запрос работает, однако он использует подзапрос ОБЪЕДИНЕНИЯ с ограничением NOT IN, которое я считаю неэффективным — поэтому вопрос в том, какой более эффективный способ написать этот запрос.
SELECT DISTINCT i.id, i.name
FROM items i
INNER JOIN variations v
ON v.item_id = i.id
INNER JOIN images vi
ON vi.variation_id = v.id
WHERE i.id NOT IN (SELECT i.id FROM items i
INNER JOIN variations v
ON v.item_id = i.id
INNER JOIN images vi
ON vi.variation_id = v.id
WHERE vi.type = 4
UNION
SELECT i.id FROM items i
INNER JOIN variations v
ON v.item_id = i.id
INNER JOIN images vi
ON vi.variation_id = v.id
WHERE vi.type = 5)
Решение
На основе приведенного ниже ответа Гордона окончательное решение:
select i.*
from items i
where not exists (select 1
from variations v join
images im
on v.image_id = im.id
where v.item_id = i.item_id and i.type in (4, 5)
)
and exists (select 1
from variations v join
images im
on v.id = im.variation_id
where v.item_id = i.id)
Ответ №1:
Я хочу найти элементы, у которых нет изображений типа 4 или 5
Исходя из вашего описания, это звучит как not exists
:
select i.*
from items i
where not exists (select 1
from variations v join
images im
on v.image_id = im.id
where v.item_id = i.item_id and i.type in (4, 5)
);
Комментарии:
1. Спасибо, Гордон, моя единственная проблема с этим решением заключается в том, что оно отличается от исходных результатов запроса, поскольку включает элементы, у которых вообще нет изображений — их тоже следует опустить (описание могло быть более четким)
2. @mb.Brunel . , , В вашем вопросе нет ничего об этом требовании, и я даже включил соответствующую часть в ответ. Вы можете, конечно, добавить
exists
предложение, аналогичноеnot exists
, чтобы гарантировать, что изображение существует.
Ответ №2:
Альтернатива ответу Гордона с использованием агрегации:
SELECT DISTINCT
i.id,
i.name
FROM items i
INNER JOIN variations v
ON v.item_id = i.id
INNER JOIN images vi
ON vi.variation_id = v.id
GROUP BY
i.id
HAVING
COUNT(*) FILTER (WHERE vi.type IN (4, 5)) = 0;
Обратите внимание, что вышеизложенное предполагает, что id
это столбец первичного ключа в items
таблице. Если нет, то, возможно, нам следует использовать GROUP BY i.id, i.name
вместо этого.
Комментарии:
1. Это решение работает, однако оно медленнее, чем решение, использующее
exists
(протестировано на 8000 записях) время выполнения 37,935 мс против 21,742 мс2. @mb.Brunel Если вы добавите соответствующие индексы, мой ответ может быть выполнен значительно быстрее.