sql - несколько слоев коррелированных подзапросов
Я имею table A, B and C
Я хочу вернуть все записи в таблице A, которые не существуют в таблице B, и этого списка не существует в таблице C.
select * from table_A as a
where not exists (select 1 from table_B as b
where a.id = b.id)
это дает мне первый результат записей в A, которых нет в B. Но теперь я хочу только те записи этого результата, которые также не находятся в C.
Я попробовал ароматы:
select * from table_A as a
where not exists (select 1 from table_B as b
where a.id = b.id)
AND
where not exists (select 1 from table_C as c
where a.id = c.id)
Но это не правильная логика. Если есть способ сохранить результаты первого запроса, а затем выбрать * из этого результата, которого нет в таблице C. Но я не уверен, как это сделать. Я ценю помощь.
5 ответов
У вас есть два WHERE
пункты в (внешней части) вашего второго запроса. Это не допустимый SQL. Если вы удалите его, он должен работать как положено:
select * from table_A as a
where not exists (select 1 from table_B as b
where a.id = b.id)
AND
not exists (select 1 from table_C as c -- WHERE removed
where a.id = c.id) ;
Протестировано в SQL-Fiddle (thnx @Alexander)
Попробуй это:
select * from (
select a.*, b.id as b_id, c.id as c_id
from table_A as a
left outer join table_B as b on a.id = b.id
left outer join table_C as c on c.id = a.id
) T
where b_id is null
and c_id is null
Другая реализация заключается в следующем:
select a1.*
from table_A as a1
inner join (
select a.id from table_A
except
select b.id from table_B
except
select c.id from table_c
) as a2 on a1.id = a2.id
Обратите внимание на ограничения в форме подзапроса, как описано здесь. Вторая реализация, наиболее кратко и четко описывающая желаемую операцию для SQL Server, вероятно, будет наиболее эффективной.
Еще один вариант с оператором NOT EXISTS
SELECT *
FROM dbo.test71 a
WHERE NOT EXISTS(
SELECT 1
FROM (SELECT b.ID
FROM dbo.test72 b
UNION ALL
SELECT c.ID
FROM dbo.test73 c) x
WHERE a.ID = x.ID
)
Демо на SQLFiddle
Вариант от @ypercube. Спасибо за подарок;)
SELECT *
FROM dbo.test71 a
WHERE NOT EXISTS(
SELECT 1
FROM dbo.test72 b
WHERE a.ID = b.ID
UNION ALL
SELECT 1
FROM dbo.test73 c
WHERE a.ID = c.ID
);
Демо на SQLFiddle
Как насчет использования LEFT JOIN
SELECT a.*
FROM TableA a
LEFT JOIN TableB b
ON a.ID = b.ID
LEFT JOIN TableC c
ON a.ID = c.ID
WHERE b.ID IS NULL AND
c.ID IS NULL
Я не люблю "не существует", но если по какой-то причине это кажется вам более логичным; тогда вы можете использовать псевдоним для вашего первого запроса. Впоследствии вы можете повторно применить другое условие "не существует". Что-то вроде:
SELECT * FROM
( select * from tableA as a
where not exists (select 1 from tableB as b
where a.id = b.id) )
AS A_NOT_IN_B
WHERE NOT EXISTS (
SELECT 1 FROM tableC as c
WHERE c.id = A_NOT_IN_B.id
)