вы можете использовать подзапрос NOT EXISTS:
select *
from Projects P
where NOT EXISTS (select 1 from Tasks where Tasks.ProjectID = P.ID and TaskStatus <> 'COMPLETE')
Изменить до завершения
Вы также можете использовать подзапрос NOT IN:
select *
from Projects
where ID NOT IN (select ProjectID from Tasks where TaskStatus = 'COMPLETE')
Будет ли использоваться один или другой, это будет зависеть от объема данных вашей таблицы и индексов:
Первый параметр будет запускать подзапрос для каждой строки родительского запроса, но получит доступ (если существует,вероятно, да) по индексу ProjectID.
Второй вариант сначала запускает подзапрос, только один раз, а затем запускает родительский запрос, используя результаты подзапроса.Но подзапрос, вероятно, не будет использовать какой-либо индекс, поскольку вряд ли он будет иметь индекс в поле TaskStatus (это не имеет особого смысла).
Но, опять же, если у вас их немногострок в таблице «Задачи», тогда никакой индекс не будет использоваться в любом случае, так как было бы дешевле сделать полное сканирование таблицы.Поэтому я рекомендую проверить план выполнения, сравнить затраты и, если возможно, выполнить несколько тестов, чтобы решить, какой вариант лучше подходит для этого или любого другого случая.