Решение заключается в использовании принципа исключения.Вы пишете запрос, чтобы получить всех клиентов, которые имеют статус хотя бы один раз.Хорошие новости: эта часть уже выполнена :) Затем вы пишете запрос, чтобы найти клиентов, которые имеют любой другой статус.Когда у вас есть оба запроса, вы объединяете их, чтобы исключить второй набор из первого.Вы можете сделать это несколькими способами: выражение NOT EXISTS()
, выражение NOT IN()
, исключающее соединение или ключевое слово EXCEPT
могут работать.
Лично яНаиболее удобен для исключающих объединений, но NOT EXISTS()
встречается чаще и имеет тенденцию работать немного лучше:
select cli.id as "Client Ref", cla.clientclaimid as "Claim Number", co.PhoneHome as "Mobile"
from dbo.claims cla
inner join statuses s on cla.statusID = s.ID
inner join clients cli on cla.clientid = cli.id
left join contacts co on cli.contactid = co.id
where s.description = 'client - pack sent to customer'
and (DateAdd(MM, -@joinedpremonthsago, GetDate()) > cli.DateJoined)
and cli.DateJoined > 01/01/2012
and cla.active=1
and NOT EXISTS (
select 1
from clients cli0
inner join claims cla0 on cla0.clientid = cli0.id
inner join statuses s0 on s0.ID = cla0.statusID
WHERE cli0.ID = cli.ID
AND s0.description <> 'client - pack sent to customer'
)
order by [Client Ref], [Claim Number]
Версия исключающего соединения:
select cli.id as "Client Ref", cla.clientclaimid as "Claim Number", co.PhoneHome as "Mobile"
from dbo.claims cla
inner join statuses s on cla.statusID = s.ID AND s.description = 'client - pack sent to customer'
inner join clients cli on cla.clientid = cli.id
left join contacts co on cli.contactid = co.id
-- the "JOIN" part of an exclusion join
left join statuses s2 on cla.statusID = s2.ID AND s2.description <> 'client - pack sent to customer'
where (DateAdd(MM, -@joinedpremonthsago, GetDate()) > cli.DateJoined)
and cli.DateJoined > 01/01/2012
and cla.active=1
-- the "EXCLUSION" part of an exclusion join
and s2.ID IS NULL
order by [Client Ref], [Claim Number]
Обратите внимание, как я выбрал inner
вместо left
для некоторых оригинальных соединений.То, как поля из этих таблиц использовались в предложении WHERE, уже сделало их внутренними объединениями.Честное отношение к типу объединения помогает вам обнаруживать ошибки и может позволить Sql Server построить лучший план выполнения.
Также обратите внимание, что я удалил статус из результатов предложения SELECT, что теперь подразумевается требованиями.
Наконец, обратите внимание, как я добавил псевдонимы таблиц в запрос.Рекомендуется всегда использовать псевдонимы таблиц в своих запросах.Абсолютно необходимо избегать двусмысленности, если вы хотите ссылаться на одну и ту же таблицу более одного раза в одном запросе, как мы делаем в обоих примерах здесь.По соглашению, эти псевдонимы часто являются краткими - даже однобуквенными - мнемониками для имен таблиц.Так что cli
в этом запросе - сокращение от client
, и я использовал 3 целых символа, чтобы его можно было отличить от claims
.cli0
используется во внутреннем запросе для обозначения «простое число клиента» ... думайте об этом, как если бы 0
было индексом.