简体   繁体   English

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

[英]SQL join where all joined meet condition

I need a little help for an SQL query. 我需要一些有关SQL查询的帮助。

I have tables: clients | 我有桌子:客户| claims | 索赔| statuses | 状态| contacts 往来

I have a query to find all clients and their contact details where any of their claims is a specified status: 我有一个查询来查找所有客户及其联系方式,其中任何索偿均为指定状态:

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];

I now need this to only pull clients where ALL of their claims are in specified status, but I don't know how to do this. 现在,我只需要将所有声明都处于指定状态的客户拉出,但是我不知道该怎么做。 How can I get clients where all of the claims have this status description? 如何获得所有声明都具有此状态描述的客户? Could I have guidance or solutions for this? 我可以为此提供指导或解决方案吗?

Here is relevant schema; 这是相关的架构; Claims Table 索偿表

在此处输入图片说明

Contacts Table 联系人表

在此处输入图片说明

Clients Table 客户表

在此处输入图片说明

Here is an image of the query returning where ANY of the client's claims are in status; 这是查询返回的图像,其中客户的任何索赔都处于状态。 Current results 当前结果

在此处输入图片说明

The solution is to use the principle of exclusion. 解决的办法是使用排除原则。 You write a query to get all clients that do have the status at least once. 您编写查询以获取所有确实具有该状态的所有客户端至少一次。 Great news: that part is already done :) Next you write a query to find clients that have any other status. 好消息:这部分已经完成了:)接下来,编写查询以查找具有其他任何状态的客户端。 Once you have both queries, you put them together to exclude the 2nd set from the first. 拥有两个查询后,将它们组合在一起即可从第一组中排除第二组。 You can do this in several ways: a NOT EXISTS() expression, a NOT IN() expression, an exclusion join, or the EXCEPT keyword can all work. 您可以通过几种方式执行此操作: NOT EXISTS()表达式, NOT IN()表达式,排除EXCEPTEXCEPT关键字都可以起作用。

Personally I'm most comfortable with exclusion joins, but NOT EXISTS() is more common and tends to perform a little better: 就个人而言,我对排除连接最为满意,但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]

Exclusion join version: 排除联接版本:

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]

Note how I chose inner rather then left for some of the original joins. 请注意,我是如何选择“ inner而不是“ left来进行某些原始连接的。 The way fields from those tables were used in the WHERE clause already made them effectively inner joins. 在WHERE子句中使用这些表的字段的方式已经使它们有效地成为内部联接。 Being honest about the join type helps you spot bugs and may allow Sql Server to build a better execution plan. 对联接类型诚实可以帮助您发现错误,并可以使Sql Server建立更好的执行计划。

Also note I removed the status from the SELECT clause results, as that is now implied by the requirements. 还要注意,我从SELECT子句结果中删除了状态,因为现在要求已隐含了这一点。

Finally, note how I added table aliases to the query. 最后,请注意如何将表别名添加到查询中。 It's good practice to always use table aliases with your queries. 最好在查询中始终使用表别名。 It's absolutely necessary to avoid ambiguity if you want to reference the same table more than once in a single query, as we do in both examples here. 如果您想在单个查询中多次引用同一张表,则绝对有必要避免歧义,就像我们在此处的两个示例中一样。 By convention, these aliases are often short — even single letter — mnemonics for the table names. 按照惯例,这些别名通常是表名的缩写,甚至是单个字母。 So cli in this query is short for client , and I used 3 whole characters so it can be distinguished from claims . 因此,此查询中的cliclient缩写,我使用了3个完整字符,以便将其与claims区分开。 cli0 is used in the inner query to mean "client prime"... think of it as if the 0 were a subscript. 内部查询中使用cli0表示“客户素数” ...认为0就像是下标。

Something like (totaly untested code!): 类似于(完全未经测试的代码!):

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]

Should do the trick. 应该做到的。

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

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