为什么 PostgreSQL 不能做这个简单的 FULL JOIN?

Mat*_*att 6 postgresql join

这是一个最小的设置,有 2 个表ab每个表有 3 行:

CREATE TABLE a (
    id SERIAL PRIMARY KEY,
    value TEXT
);
CREATE INDEX ON a (value);

CREATE TABLE b (
    id SERIAL PRIMARY KEY,
    value TEXT
);
CREATE INDEX ON b (value);

INSERT INTO a (value) VALUES ('x'), ('y'),        (NULL);
INSERT INTO b (value) VALUES        ('y'), ('z'), (NULL);
Run Code Online (Sandbox Code Playgroud)

这是一个按预期工作正常的 LEFT JOIN:

SELECT * FROM a
LEFT JOIN b ON a.value IS NOT DISTINCT FROM b.value;
Run Code Online (Sandbox Code Playgroud)

带输出:

 id | value | id | value 
----+-------+----+-------
  1 | x     |    | 
  2 | y     |  1 | y
  3 |       |  3 | 
(3 rows)
Run Code Online (Sandbox Code Playgroud)

将“LEFT JOIN”更改为“FULL JOIN”会出现错误:

SELECT * FROM a
FULL JOIN b ON a.value IS NOT DISTINCT FROM b.value;
Run Code Online (Sandbox Code Playgroud)

错误:FULL JOIN 仅支持可合并连接或可哈希连接的连接条件

有人可以回答吗:

什么是“可合并连接或可哈希连接的连接条件”,为什么加入a.value IS NOT DISTINCT FROM b.value不满足此条件,但a.value = b.value完全没问题?

似乎唯一的区别是如何处理 NULL 值。由于该value列在两个表中都有索引,因此EXPLAINNULL查找上运行与查找非值一样有效NULL

EXPLAIN SELECT * FROM a WHERE value = 'x';
                                QUERY PLAN                                
--------------------------------------------------------------------------
 Bitmap Heap Scan on a  (cost=4.20..13.67 rows=6 width=36)
   Recheck Cond: (value = 'x'::text)
   ->  Bitmap Index Scan on a_value_idx  (cost=0.00..4.20 rows=6 width=0)
         Index Cond: (value = 'x'::text)


EXPLAIN SELECT * FROM a WHERE value ISNULL;
                                QUERY PLAN                                
--------------------------------------------------------------------------
 Bitmap Heap Scan on a  (cost=4.20..13.65 rows=6 width=36)
   Recheck Cond: (value IS NULL)
   ->  Bitmap Index Scan on a_value_idx  (cost=0.00..4.20 rows=6 width=0)
         Index Cond: (value IS NULL)
Run Code Online (Sandbox Code Playgroud)

这已经用 PostgreSQL 9.6.3 和 10beta1 测试过。

关于这个问题已经有过讨论,但并没有直接回答上述问题。

Lau*_*lbe 6

PostgreSQLFULL OUTER JOIN使用散列或合并连接来实现。

要符合此类加入的条件,加入条件必须具有以下形式

<expression using only left table> <operator> <expression using only right table>
Run Code Online (Sandbox Code Playgroud)

现在您的连接条件确实看起来像这样,但是 PostgreSQL 没有特殊的IS NOT DISTINCT FROM运算符,因此它将您的条件解析为:

(NOT ($1 IS DISTINCT FROM $2))
Run Code Online (Sandbox Code Playgroud)

并且这样的表达式不能用于散列或合并连接,因此会出现错误消息。

我可以想出一种方法来解决它:

SELECT a_id, NULLIF(a_value, '<null>'),
       b_id, NULLIF(b_value, '<null>')
FROM (SELECT id AS a_id,
             COALESCE(value, '<null>') AS a_value
      FROM a
     ) x
   FULL JOIN
     (SELECT id AS b_id,
             COALESCE(value, '<null>') AS b_value
      FROM b
     ) y
      ON x.a_value = y.b_value;
Run Code Online (Sandbox Code Playgroud)

如果<null>未出现在value列中的任何位置,则有效。