繁体   English   中英

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

[英]SQL join where all joined meet condition

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

现在,我只需要将所有声明都处于指定状态的客户拉出,但是我不知道该怎么做。 如何获得所有声明都具有此状态描述的客户? 我可以为此提供指导或解决方案吗?

这是相关的架构; 索偿表

在此处输入图片说明

联系人表

在此处输入图片说明

客户表

在此处输入图片说明

这是查询返回的图像,其中客户的任何索赔都处于状态。 当前结果

在此处输入图片说明

解决的办法是使用排除原则。 您编写查询以获取所有确实具有该状态的所有客户端至少一次。 好消息:这部分已经完成了:)接下来,编写查询以查找具有其他任何状态的客户端。 拥有两个查询后,将它们组合在一起即可从第一组中排除第二组。 您可以通过几种方式执行此操作: NOT EXISTS()表达式, NOT IN()表达式,排除EXCEPTEXCEPT关键字都可以起作用。

就个人而言,我对排除连接最为满意,但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就像是下标。

类似于(完全未经测试的代码!):

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]

应该做到的。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM