所有联接均满足条件的SQL连接

时间:2018-11-27 16:30:38

标签: sql sql-server join

我需要一些有关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

enter image description here

Contacts Table

enter image description here

Clients Table

enter image description here

这是查询的图像,其中返回了客户的任何索偿状态。 Current results

enter image description here

2 个答案:

答案 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子句结果中删除了状态,因为这已隐含在要求中。

最后,请注意我如何向查询中添加表别名。在查询中始终使用表别名是一种很好的做法。如果您想在单个查询中多次引用同一张表,则绝对有必要避免歧义,就像我们在此处的两个示例中一样。按照惯例,这些别名通常是表名的缩写,甚至是单个字母。因此,此查询中的cliclient的缩写,我使用了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]

应该做到这一点。