我需要一些有关SQL查询的帮助。
我有桌子:客户|索赔|状态|联系人
我有一个查询来查找所有客户及其联系方式,其中任何声明为指定状态:
SELECT
clients.id AS "Client Ref"
,claims.clientclaimid AS "Claim Number"
,Contacts.PhoneHome AS "Mobile"
,statuses.description AS Status
FROM
dbo.claims
LEFT JOIN
statuses
ON
dbo.claims.statusID = statuses.ID
LEFT JOIN
clients
ON
dbo.claims.clientid = clients.id
LEFT JOIN
contacts
ON
clients.contactid = Contacts.id
WHERE
statuses.description = 'client - pack sent to customer'
AND (DATEADD(MM, -@joinedpremonthsago, GETDATE()) > clients.DateJoined)
AND clients.DateJoined > 01 / 01 / 2012
AND claims.active = 1
ORDER BY
[Client Ref]
,[Claim Number];
我现在需要这样做,以仅提取其声明的全部处于指定状态的客户,但我不知道该怎么做。如何获得所有声明都具有此状态描述的客户?我可以为此提供指导或解决方案吗?
这里是相关的架构; Claims Table
这是查询的图像,其中返回了客户的任何索偿状态。 Current results
答案 0 :(得分:1)
解决方案是使用排除原理。您编写查询以获取所有确实具有该状态的所有客户端至少一次。好消息:这部分已经完成了:)接下来,编写查询以查找具有其他任何状态的客户端。拥有两个查询后,将它们组合在一起即可从第一组中排除第二组。您可以通过几种方式执行此操作:NOT EXISTS()
表达式,NOT IN()
表达式,排除联接或the EXCEPT
keyword都可以起作用。
我个人最擅长使用排除联接,但是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
是下标一样。
答案 1 :(得分:0)
类似(完全未经测试的代码!):
select clients.id as "Client Ref", claims.clientclaimid as "Claim Number",
Contacts.PhoneHome as "Mobile",statuses.description as Status
from dbo.claims
left join clients on dbo.claims.clientid = clients.id
left join contacts on clients.contactid = contacts.id
where (DateAdd(MM, -@joinedpremonthsago, GetDate()) > clients.DateJoined)
and clients.DateJoined > 01/01/2012
and claims.active=1
and dbo.claims.clientID in (
select dbo.claims.clientID
from dbo.claims
left join statuses on dbo.claims.statusID = statuses.ID
where statuses.description = 'client - pack sent to customer'
)
order by [Client Ref], [Claim Number]
应该做到这一点。