以下是初始数据:
CREATE TABLE #data
(
Id integer,
Surname varchar(50),
DOB datetime
)
INSERT INTO #data
values
(1,'smith', null),
(2,'jones', '01 jan 1970'),
(3,'vernon', null),
(4,'smith', '01 jan 1970'),
(5,'jones', '01 jan 1970'),
(6,'vernon', '01 jan 1970'),
(7,null, '01 jan 1970')
以下是排除列表:
CREATE TABLE #exclusions
(
ExcludedSurname varchar(50),
ExcludedDOB datetime
)
INSERT INTO #exclusions
values
('smith', '01 jan 1970'),
('jones', '01 jan 1970'),
('vernon', null),
(null, '01 jan 1970')
这是一个返回我意想不到的结果的查询:
SELECT *
FROM #data a
WHERE
NOT EXISTS
(
SELECT 1
FROM #exclusions e
WHERE
a.DOB = e.ExcludedDOB and
a.Surname = e.ExcludedSurname
)
为了确保排除Id
s 3和7,我可以对脚本进行这种丑陋的更改。生产表中有很多可能的数据(#data的实时版本是1000万条记录) - 这就是为什么我选择了null
这么远的替代品。
SELECT *
FROM #data a
WHERE
NOT EXISTS
(
SELECT 1
FROM #exclusions e
WHERE
ISNULL(a.DOB, '01 JAN 2200') = ISNULL(e.ExcludedDOB, '01 JAN 2200') and
ISNULL(a.Surname,'AAAAAAAAAAAAAAAA') = ISNULL(e.ExcludedSurname,'AAAAAAAAAAAAAAAA')
)
此处位于SQL Fiddle
有更优雅的方式来做上述事情吗?
答案 0 :(得分:6)
在PostgreSQL(SQL Fiddle)中你可以使用
WHERE (a.DOB, a.Surname) IS NOT DISTINCT FROM (e.ExcludedDOB, e.ExcludedSurname)
但是SQL Server缺少两个可以工作的项目。 row value constructors和IS [NOT] DISTINCT FROM
同时您可以使用此处的技术:Undocumented Query Plans: Equality Comparisons
SELECT *
FROM #data a
WHERE NOT EXISTS (SELECT *
FROM #exclusions e
WHERE EXISTS (SELECT a.DOB,
a.Surname
INTERSECT
SELECT e.ExcludedDOB,
e.ExcludedSurname))
上述内容的变体使用EXCEPT
(实例 HERE ):
SELECT *
FROM #data a
WHERE EXISTS (SELECT a.DOB,
a.Surname
EXCEPT
SELECT e.ExcludedDOB,
e.ExcludedSurname
FROM #exclusions e)
答案 1 :(得分:2)
@MartinSmith
看看这个替代方案:
SELECT *
FROM #data a
WHERE EXISTS(SELECT a.Surname,
a.DOB
EXCEPT
SELECT e.ExcludedSurname,
e.ExcludedDOB
FROM #exclusions e)
非常优雅和可读。
虽然为什么在上面的EXISTS中打扰似乎除了做所有的工作?
SELECT Surname,
DOB
FROM #data
EXCEPT
SELECT ExcludedSurname,
ExcludedDOB
FROM #exclusions
[提供了我的一位朋友 - 遗憾的是他没有提供SO
- 我不知道这一点......
两个备选方案都在SQL FIDDLE
上答案 2 :(得分:1)
如果您的排除项没有重复项,则不会将此作为left outer join
至少看起来更清晰:
SELECT *
FROM #data a left outer join
#exclusions e
on a.DOB = e.ExcludedDOB and
a.Surname = e.ExcludedSurname
where e.ExcludedDOB is NULL and e.ExcludedSurname is null
然后,您可以使用coalesce
或逻辑处理NULL(此示例显示两者):
SELECT *
FROM #data a left outer join
#exclusions e
on (a.DOB = e.ExcludedDOB or a.DOB is NULL and e.ExcludedDOB is NULL) and
(coealesce(a.Surname, '<null>') = coalesce(e.ExcludedSurname, '<null>')
where e.ExcludedDOB is NULL and e.ExcludedSurname is null
所有这些方法的缺点是我认为他们不会利用排除表上的索引。 。 。如果桌子很大,这可能是一个好主意。一种方法需要两个连接,但修复了这个问题:
SELECT *
FROM #data a left outer join
#exclusions e
on a.DOB = e.ExcludedDOB and
a.Surname = e.ExcludedSurname left outer join
#exclusions enull
on enull.ExcludedSurname is null and a.Surname is NULL and
enull.ExcludedDOB = a.DOB
where e.ExcludedDOB is NULL and e.ExcludedSurname is null and
enull.ExcludedDOB is NULL and enull.ExcludedSurname is null
然而,马丁的方法可能仍然是表现最好的。
如果您不需要来自#data的id
,那么最简单的方法是:
select Surname, Dob
from #data
except (select ExcludedSurname, ExcludedDB from #exceptions)
我经常使用这个结构进行表格比较。但是,要获取id,您将在连接中留下NULL问题。