忽略单峰的内部联接

Posted

技术标签:

【中文标题】忽略单峰的内部联接【英文标题】:Inner join that ignore singlets 【发布时间】:2012-10-29 19:59:01 【问题描述】:

我必须在表上进行自联接。我正在尝试返回几列的列表,以查看在同一天(MM/DD/YYYY)执行了多少每种类型的药物测试,其中至少完成了两项测试,其中至少一项导致了'UN'的结果代码。

我正在加入其他表格以获取如下信息。问题是我不太明白如何排除那些只有一个结果行的人,他们在一天中确实有一个“联合国”结果,但当天没有进行任何其他测试。

查询结果(列)

县、DrugTestID、ID、名称、CollectionDate、DrugTestType、结果、计数(DrugTestType)

我有几行 ID 12345 是正确的。但是 ID 12346 是单行,其中显示他们的行结果为 count (1)。他们在这一天得到了“联合国”的结果,但那天他们没有进行任何其他测试。我想排除这个。

我尝试了以下查询

选择 c.desc 为“县”, dt.pid 作为“PID”, dt.id 作为 'DrugTestID', p.id 作为“ID”, bio.FullName 为“参与者”, 转换(varchar,dt.CollectionDate,101)为'CollectionDate', dtt.desc 作为“药物测试类型”, dt.result 作为结果, 计数(dt.dru_drug_test_type)作为“测试类型计数” 从 dbo.Test as dt with (nolock) 在 dt.pid = h.id 上以 h 身份加入 dbo.History 在 h.pid = p.id 上以 p 身份加入 dbo.Participant 在 bio.id = p.id 上加入 BioData 作为 bio 在 p.CountyCode = c.code 上加入 County as c with (nolock) 在 dt.DrugTestType = dtt.code 上加入 DrugTestType 作为 dtt 与 (nolock) 内部联接 ( 选择不同的 dt2.pid, CONVERT(varchar, dt2.CollectionDate, 101) as 'CollectionDate' 从 dbo.DrugTest as dt2 with (nolock) 在 dt2.pid = h2.id 上加入 dbo.History 作为 h2 在 h2.pid = p2.id 上以 p2 身份加入 dbo.Participant 在哪里 dt2.result = '联合国' 和 '11-01-2011' 和 '10-31-2012' 之间的 dt2.CollectionDate 和 p2.DrugCourtType = 'AD' ) 作为派生的 在 dt.pid = 派生.pid 和转换(varchar,dt.CollectionDate,101)=转换(varchar,派生的.CollectionDate,101) 通过...分组 c.desc、dt.pid、p.id、dt.id、bio.fullname、dt.CollectionDate、dtt.desc、dt.result 订购 c.desc ASC、参与者 ASC、dt.CollectionDate ASC

【问题讨论】:

【参考方案1】:

这有点复杂,因为您的查询对于每个测试都有一个单独的行。您需要使用窗口/分析函数来获取所需的信息。这些允许您计算聚合函数,但将值放在每一行。

以下查询以您的查询开头。然后它计算每个参与者在每个日期的联合国结果数量和测试总数。它应用适当的过滤器来获得您想要的:

with base as (<your query here>)
select b.*
from (select b.*,
             sum(isUN) over (partition by Participant, CollectionDate) as NumUNs,
             count(*) over (partition by Partitipant, CollectionDate) as NumTests
      from (select b.*,
                  (case when result = 'UN' then 1 else 0 end) as IsUN
            from base
           ) b
     ) b
where NumUNs <> 1 or NumTests <> 1

没有with 子句或窗口函数,您可以创建一个特别丑陋的查询来做同样的事情:

select b.*
from (<your query>) b join
     (select Participant, CollectionDate, count(*) as NumTests,
             sum(case when result = 'UN' then 1 else 0 end) as NumUNs
      from (<your query>) b
      group by Participant, CollectionDate
     ) bsum
     on b.Participant = bsum.Participant and
        b.CollectionDate = bsum.CollectionDate
where NumUNs <> 1 or NumTests <> 1

【讨论】:

我还发现我查询的 sql server 是 Sql Server 2000。我差点死了。感谢您的回答。其他服务器我会参考它。【参考方案2】:

如果我理解这个问题,这种查询的基本模式只是在您的联接中包含否定或排除条件。即,列 A 匹配但列 B 和 C 不匹配的自联接:

select
  [columns]

from
  table t1
  join table t2 on (
    t1.NonPkId = t2.NonPkId
    and t1.PkId != t2.PkId
    and t1.category != t2.category
  )

如果基准测试更好,请将条件放在 WHERE 子句中:

select
  [columns]

from
  table t1
  join table t2 on (
    t1.NonPkId = t2.NonPkId
  )

where
    t1.PkId != t2.PkId
    and t1.category != t2.category

从自联接开始通常最容易,将其视为联接所有相关信息的“基表”:

select
  [columns]

from
  (select
    [columns]

  from
    table t1
    join table t2 on (
      t1.NonPkId = t2.NonPkId
    )

  where
      t1.PkId != t2.PkId
      and t1.category != t2.category
  ) bt

  join [othertable] on (<whatever>)
  join [othertable] on (<whatever>)
  join [othertable] on (<whatever>)

这可以让您专注于获得正确的自联接,而不受其他表的干扰。

【讨论】:

以上是关于忽略单峰的内部联接的主要内容,如果未能解决你的问题,请参考以下文章

单峰排列

内部联接与内部联接(SELECT . FROM)

使用多个内部联接时,与使用单个内部联接时相比,我得到不同的结果

内部联接和简单联接之间是不是有任何性能差异? [复制]

外部联接中的内部联接导致性能不足,有啥不同的方法?

使用内部联接删除一行数据[重复]