无匹配空值的PostgreSQL联接

无匹配空值的PostgreSQL联接,postgresql,join,null,Postgresql,Join,Null,我把这称为我的“零号之战”,因为我已经为这个问题奋斗了多年 我有一个名为People的大表(250000多行,100多列),还有一个名为Stuff,其中可能包含也可能不包含相应的记录。我可以使用三列查找可能的匹配项:个人ID、电话号码或电子邮件地址。这些列中可能有值,也可能没有值,甚至可能包含空值 我几年前写的最初查询是这样的: SELECT * FROM People LEFT OUTER JOIN Stuff ON People.PersonID = Stuff.Person

我把这称为我的“零号之战”,因为我已经为这个问题奋斗了多年

我有一个名为
People
的大表(250000多行,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
子句中它就死了

我走对了吗?我怎样才能做到这一点?还是有其他更有效的方法

必须有一种方法来运行原始的三个条件查询,这种方法在null或空值上不匹配,但我还没有找到它

狗跑了!我要赢这场零号之战!(当然是在您的帮助下!)

Postgres无法将“空”单元格与
NULL
值匹配<使用典型的比较运算符,code>NULL与任何内容都不匹配。但是,空字符串将与空字符串匹配。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

中的每一行获取第一个匹配项,如果布尔表达式中的右侧字段是空字符串,则使用函数将其视为null,然后,对于左表和右表中至少有一个
'==''
案例的行,联接条件将不会返回true


如果确保消除空字符串(如您所注意的),并防止插入新的空字符串,则可以使用上面的查询。看:

天哪,那看起来不错,我一定要试试。您对null和空值的评论给了我另一个想法,即在运行导入脚本时将所有空单元格设置为null。非常感谢,我明天会继续做这件事,并让你知道它是如何进行的。我与NULL的斗争通常是通过简单地使用coalesce(int,0)或coalesce(text“”)来赢得的。但是肯定还有很多其他的方法。“以与包含数据的单元格相同的方式对待空[…]不(null=something)是空的。“空单元格”不清楚不为null,但在Oracle中除外。但是(真的或什么的)是真的。如果希望在某些输入为null或“”时OR为false,请这样说。您没有给出约束条件或希望返回行的条件,因此很难给出正确的代码或缩短的代码。当ID不匹配时,您希望行具有=非ID,这似乎也很奇怪,因此您可能会遇到其他问题。但在任何事情之前:。
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, '');