忽略单峰的内部联接
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>)
这可以让您专注于获得正确的自联接,而不受其他表的干扰。
【讨论】:
以上是关于忽略单峰的内部联接的主要内容,如果未能解决你的问题,请参考以下文章