PostgreSQL JOIN没有匹配的NULL值

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了PostgreSQL JOIN没有匹配的NULL值相关的知识,希望对你有一定的参考价值。

我称之为“无之战”,因为我多年来一直在努力解决这个问题。

我有一个名为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条款中死亡。

我在这里走在正确的轨道上吗?我怎样才能做到这一点?或者还有另一种方法可以更好地运作吗?

必须有一种方法以一种与空值或空值不匹配的方式运行原始的三条件查询,但我还没有找到它。

狗走了!我将赢得这场无效的战斗! (当然,在你的帮助下!)

答案

如果它们是空字符串,则使用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, '');
另一答案

Postgres与“空”单元格不匹配NULL值。使用典型的比较运算符,NULL与任何东西都不匹配。但是,空字符串将匹配空string.l

我怀疑你真的想要这样的东西:

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中每行的三个表中的第一个匹配。

以上是关于PostgreSQL JOIN没有匹配的NULL值的主要内容,如果未能解决你的问题,请参考以下文章

9.PostgreSQL的Join,Union,Null

SQL-JOIN链接

未找到匹配记录时返回 NULL

Postgresql LEFT JOIN json_agg() 忽略/删除 NULL

高级查询

PostgreSQL:创建索引以快速区分 NULL 和非 NULL 值