Получение строк из одной таблицы, у которых нет связанных строк в других таблицах, соответствующих определенным критериям, без подзапроса

0

Здесь SQL скрипка.

У меня есть таблица со списком имен. Я хочу получить список всех имен, которые не имеют записи в связанной таблице с определенным статусом ("S"), или поле в третьей таблице (относящееся ко второй), которая соответствует определенному статусу ("A ").

Я смог выполнить это с подзапросом, показанным в скрипте SQL и ниже, но я хотел бы сделать это без подзапроса, если это возможно.

SELECT * FROM name_table LEFT JOIN 
  (SELECT b.name_id FROM b LEFT JOIN c ON (b.c_id = c.id) WHERE (c.c_status = 'A') OR (b.b_status='S'))
 results ON (name_table.id=results.name_id)
WHERE results.name_id IS NULL;

В моем примере я бы хотел, чтобы строки "Тед Эндрюс" и "Джек Джонсон"

"John Doe" не включен, потому что таблица C имеет строку со статусом "A" "Bill Smith" не включена, поскольку таблица B имеет статус "S" "Джим Скотт" не включен, потому что таблица C имеет строку со статусом "A '(хотя в таблице C есть еще одна строка без статуса "A")

 SELECT * FROM name_table 
  LEFT JOIN b ON (name_table.id = b.name_id)
 LEFT JOIN c ON (b.c_id = c.id) AND (c_status = 'A');
  WHERE (b.b_status IS NULL) OR ((b.b_status <> 'S') AND (c.id IS NULL));

была попытка, которая неправильно включает "Джим Скотт"

Теги:

5 ответов

2
Лучший ответ

Я сделал бы это так (sqlfiddle):

  SELECT nt.first, nt.last
    FROM name_table nt
         LEFT JOIN b ON nt.id  = b.name_id
         LEFT JOIN c ON nt.id  = c.a_id
GROUP BY first, last
  HAVING SUM(IF(b.b_status = 'S',1,0)) = 0
     AND SUM(IF(c.c_status = 'A',1,0)) = 0;

Имеющий бит просто подсчитывает экземпляры символов состояния, которые исключают людей из желаемого набора результатов и не требуют ни одного.

  • 1
    Это в значительной степени то, что я делал. Конечно, не стоит полагаться на уникальность комбинаций имен.
  • 0
    Моя проблема заключалась в группировке по неправильному столбцу идентификатора
Показать ещё 2 комментария
1
SELECT name_table.id, min(first) as first, min(last) as last
FROM name_table
   LEFT JOIN b ON name_table.id = b.name_id
   LEFT JOIN c ON b.c_id = c.id
GROUP BY name_table.id
HAVING
        count(case when b.b_status = 'S' then 1 end) = 0
    and count(case when c.c_status = 'A' then 1 end) = 0;

Поскольку у вас есть требование, чтобы вы просмотрели несколько строк в c чтобы определить, есть ли у вас совпадение, вы обнаружите, что используете какую-то агрегацию. Лично я думаю, что запрос не так ужасен.

0

Вот способ, который не использует подзапросы, хотя я бы хотел проверить производительность этого на большом наборе данных:

SELECT a.*
FROM name_table AS a
LEFT JOIN b ON a.id = b.name_id
LEFT JOIN c ON b.c_id = c.id
GROUP BY a.id
HAVING MIN((b.b_status IS NULL OR b.b_status <> 'S') AND (c.c_status IS NULL OR c.c_status <> 'A'));

Предложение HAVING оценивает 0 или 1 для каждой строки, чтобы указать, соответствует ли оно вашим критериям, а затем берет минимум этого для всех имен, чтобы исключить исключение Джима Скотта - включены только имена, где все присоединившиеся строки соответствуют вашим критериям.

0

not exists, полезно в этих ситуациях

select
      *
from name_table t
where not exists (select null from B
                  where t.id = b.name_id and b.b_status = 'S')
and not exists (select null from C
                where t.id = c.a_id and c.c_status = 'A')

Результат с демо:

| id | first |     last |
|----|-------|----------|
|  3 |   Ted | Anderson |
|  5 |  Jack |  Johnson |
0

Несмотря на то, что вы сказали, что Джим Скотт не следует включать, он выглядел так, как будто у него нет b_status 'S' или c_status 'A'. Это то, что вы ищете?

select * from name_table
join b on name_table.id = b.name_id and b.b_status <> 'S'
join c on b.c_id = c.id and c.c_status <> 'A';

Ещё вопросы

Сообщество Overcoder
Наверх
Меню