Здесь 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));
была попытка, которая неправильно включает "Джим Скотт"
Я сделал бы это так (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;
Имеющий бит просто подсчитывает экземпляры символов состояния, которые исключают людей из желаемого набора результатов и не требуют ни одного.
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
чтобы определить, есть ли у вас совпадение, вы обнаружите, что используете какую-то агрегацию. Лично я думаю, что запрос не так ужасен.
Вот способ, который не использует подзапросы, хотя я бы хотел проверить производительность этого на большом наборе данных:
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 для каждой строки, чтобы указать, соответствует ли оно вашим критериям, а затем берет минимум этого для всех имен, чтобы исключить исключение Джима Скотта - включены только имена, где все присоединившиеся строки соответствуют вашим критериям.
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 |
Несмотря на то, что вы сказали, что Джим Скотт не следует включать, он выглядел так, как будто у него нет 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';