【发布时间】:2018-03-03 02:26:07
【问题描述】:
我称之为“虚无之战”,因为多年来我一直在为这个问题苦苦挣扎。
我有一个名为People 和另一个名为Stuff 的大表(250,000+ 行,100+ 列),它可能包含也可能不包含相应的记录。我可以使用三列来查找可能的匹配项:个人 ID、电话号码或电子邮件地址。这些列中可能有也可能没有值,甚至可能包含空值。
我多年前为此编写的原始查询如下:
SELECT *
FROM People
LEFT OUTER JOIN Stuff
ON People.PersonID = Stuff.PersonID
OR People.CellNumber = Stuff.PhoneNumber
OR People.Email = Stuff.WorkEmail;
当我第一次尝试运行此查询时,它在连接表中产生了 数百万 条记录,这与我的预期完全不同。 经过几天的故障排除后,我终于确定是空值和空单元格的存在导致了结果的大幅增加。 对于那些可能不知道的人,PostgreSQL 对待 null 和空单元格的方式与对待其中包含数据的单元格相同。 结果是它获取 People 表中每个带有空单元格的记录,并将其与 Stuff 表中带有空单元格的每个记录连接起来。 它对 null 以及所有三个比较都执行相同的操作。
我搜索了数周,但从未找到优雅或简单的解决方法,因此我最终不得不将其分解为一系列单独的查询,如下所示。
SELECT *
FROM People
LEFT OUTER JOIN Stuff
ON People.PersonID = Stuff.PersonID
WHERE (People.PersonID != ''
AND People.PersonID IS NOT NULL);
将匹配的记录转储到临时表中,然后通过第二个查询运行不匹配的记录:
SELECT *
FROM People
LEFT OUTER JOIN Stuff
ON People.CellNumber = Stuff.PhoneNumber
WHERE (People.CellNumber != ''
AND People.CellNumber IS NOT NULL);
将匹配的记录转储到临时表中,然后通过第三个查询运行剩余的不匹配记录:
SELECT *
FROM People
LEFT OUTER JOIN Stuff
ON People.Email = Stuff.WorkEmail
WHERE (People.Email != ''
AND People.Email IS NOT NULL);
将结果(匹配和不匹配)转储到临时表中,然后继续。
多年来,我一直在使用这种非常不优雅的方法,并且它没有任何问题。但是现在我需要修改这个脚本以适应业务需求的变化,并且我试图再次找到一个更简单的解决方案。当前方法的问题是,每当我必须对查询进行更改时,我必须在代码中的多个位置进行更改,这会成为维护的噩梦。
在这次迭代中,我提出了以下几点:
SELECT *
FROM People
LEFT OUTER JOIN Stuff
ON (People.PersonID = Stuff.PersonID
WHERE People.PersonID != ''
AND People.PersonID IS NOT NULL)
OR (People.CellNumber = Stuff.PhoneNumber
WHERE People.CellNumber != ''
AND People.CellNumber IS NOT NULL)
OR (People.Email = Stuff.WorkEmail)
WHERE People.Email != ''
AND People.Email IS NOT NULL);
这看起来应该可以工作,但它在第一个 WHERE 子句处死了。
我在正确的轨道上吗?我怎样才能使这项工作?还是有其他更好的方法?
必须有一种方法来运行原始的三条件查询,它与空值或空值不匹配,但我还没有找到它。
狗走了!我要赢得这场空虚之战! (当然是在你的帮助下!)
【问题讨论】:
-
我与 NULL 的战斗通常通过简单地使用 coalesce(int,0) 或 coalesce(text,'') 来赢得。但肯定还有很多其他方法。
-
“对待空值 [...] 与包含数据的单元格一样” 不。(null = something)为空。 “空单元格”不清楚。 '' 不为空,但在 Oracle 中除外。但是(真或什么)是真的。如果您希望在某些输入为 null 或 '' 时 OR 为假,请这样说。您没有给出约束或您希望在什么条件下返回行,因此很难给出正确的代码或缩短的代码。当 id 不匹配时,您想要带有 = non-ids 的行似乎也很奇怪,因此您可能会遇到其他问题。但在任何事情之前:minimal reproducible example.
标签: postgresql join null