#sql #self-join
#sql #самосоединение
Вопрос:
У меня есть 2 таблицы с отношениями родительского внешнего ключа Table1 — (id1, pIntID, fid) id1-первичный ключ, таблица внешнего ключа fid2 — (fid, значение) первичный ключ fid
значения будут примерно такими table1
id1, pIntID, fid
1 IR1 AC
2 IR1 RJ
3 IR2 AC
4 IR2 AC
таблица2
fid, value
AC accept
RJ reject
Я должен выбрать те PIN-коды из таблицы, у которой tablle2.value = Принять, но не отклонить. Я могу сделать это с помощью подзапроса, но это будет плохо работать с огромными данными, есть ли оптимальный способ сделать это?
это рабочий запрос:
select t1.pIntid
from table1 t1 join
table2 t2
on t1.fid = t2.fid
where t2.value = 'accept' and
t1.pIntid not in (select tx1.pintid
from table1 tx1 join
table2.tx2
on tx1.fid = tx2.fid
where tx2.value = 'reject'
)
Комментарии:
1. Прежде всего, в общем случае производительность не определяется тем, какой метод вы используете для выполнения операции. Во всех базах данных поставщиков базовые процессоры запросов оптимизируют запрос, используя более или менее одинаковые базовые алгоритмы объединения, независимо от того, как вы структурируете SQL. Итак, сначала, для ясности и удобства обслуживания, вы должны написать SQL так, чтобы он представлял функциональную цель, которую вы намереваетесь, а затем только исследовать альтернативные подходы, когда проблема с производительностью становится очевидной.
Ответ №1:
Это может быть немного запутанным, поскольку в предложении where есть ограничение, но я считаю, что это сработает:
select t1.pintid
from table1 t1
join table2 t2 on t1.fid = t2.fid
where t2.value in ('accept', 'reject')
group by t1.pintid
having count(distinct t2.value) = 1
and t2.value = 'accept'
Другим решением (более чистым, я думаю) было бы использовать NOT EXISTS
для исключения тех, pintid
которые содержат accept
, но также reject
и состояние:
select t1.pintid
from table1 t1
join table2 t2 on t1.fid = t2.fid
where t2.value = 'accept'
and not exists (
select 1
from table1 t1x
join table2 t2x on t1x.fid = t2x.fid
where t2x.value = 'reject'
and t1.pintid = t1x.pintid
)
group by t1.pintid
Ответ №2:
В качестве другой альтернативы вы могли бы использовать шаблон предотвращения объединения.
SELECT t1.pIntid
FROM table1 t1
JOIN table2 t2
ON t2.fid = t1.fid
AND t2.value = 'accept'
LEFT
JOIN table2 tx2
ON tx2.fid = t1.fid
AND tx2.value = 'reject'
WHERE tx2.fid IS NULL
Шаблон антисоединения — это внешнее объединение с условием в предложении WHERE, которое исключает строки, которые нашли совпадение.
Для оптимальной производительности мы хотим, чтобы в table2 был доступен соответствующий индекс покрытия. С fid
и value
в качестве ведущих столбцов в индексе.
Если в table2 есть несколько строк со значением=’accept’ для одного и того же fid, то операция объединения приведет к созданию «повторяющихся» строк.