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
)
Другие вопросы по тегам