我称之为我的">空之战",因为我多年来一直在这个问题上挣扎。
我有一个名为People
的大表(250,000+ 行,100+ 列),另一个名为Stuff
,可能包含也可能不包含相应的记录。我可以使用三列来查找可能的匹配项:人员ID,电话号码或电子邮件地址。这些列中可能有也可能没有值,甚至可能包含空值。
我多年前为此编写的原始查询如下:
SELECT *
FROM People
LEFT OUTER JOIN Stuff
ON People.PersonID = Stuff.PersonID
OR People.CellNumber = Stuff.PhoneNumber
OR People.Email = Stuff.WorkEmail;
当我第一次尝试运行此查询时,它在联接表中生成了数百万条记录,根本不是我预期的。 经过几天的故障排除,我最终确定是空值和空单元格的存在导致了结果的巨大增加。 对于那些可能不知道的人,PostgreSQL对待空和空单元格与处理包含数据的单元格一样。 结果是,它采用 People 表中具有空单元格的每条记录,并将其与 Stuff 表中具有空单元格的每条记录联接。 它对空值和所有三个比较执行相同的操作。
我搜索了几个星期,但从未找到一种优雅或简单的解决方法,所以我最终不得不将其分解为一系列单独的查询,如下所示。
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
子句中死亡。
我在这里走在正确的轨道上吗?我怎样才能做到这一点?还是有另一种方法效果更好?
必须有一种方法可以以与空值或空值不匹配的方式运行原始的三条件查询,但我还没有找到它。
狗走了!我要赢得这场虚无之战!(当然,在你的帮助下!
Postgres 不会将">空"单元格与NULL
值匹配。NULL
与任何内容都不匹配,使用典型的比较运算符。 但是,空字符串将与空字符串匹配。
我怀疑你真的想要这样的东西:
SELECT p.*, COALESCE(sp.?, sc.?, se.?) as ?
FROM People p LEFT OUTER JOIN
Stuff sp
ON p.PersonID = sp.PersonID LEFT OUTER JOIN
Stuff sc
ON p.CellNumber = sc.PhoneNumber AND sp.personID IS NULL LEFT OUTER JOIN
stuff se
ON p.Email = se.WorkEmail AND sc.personID is null;
这将从三个表中获取people
中每行的第一个匹配项。
如果布尔表达式中的右侧字段是空字符串,则使用 NULLIF 函数将其视为 null,然后对于左右表至少具有 1 个'' == ''
大小写的行,连接条件将不会返回 true。
SELECT *
FROM People
LEFT OUTER JOIN Stuff
ON People.PersonID = NULLIF(Stuff.PersonID, '')
OR People.CellNumber = NULLIF(Stuff.PhoneNumber, '')
OR People.Email = NULLIF(Stuff.WorkEmail, '');
如果您确保消除空字符串(如您所注意的),并防止插入新的空字符串,则可以使用上面的查询。请参阅:防止在"字符变化"字段中出现空字符串