Вот минимальная установка с 2 таблицами a и b каждая с 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);

Вот левое соединение, которое работает нормально, как ожидалось:

SELECT * FROM a
LEFT JOIN b ON a.value IS NOT DISTINCT FROM b.value;

С выходом:

 id | value | id | value 
----+-------+----+-------
  1 | x     |    | 
  2 | y     |  1 | y
  3 |       |  3 | 
(3 rows)

Изменение «LEFT JOIN» на «FULL JOIN» дает ошибку:

SELECT * FROM a
FULL JOIN b ON a.value IS NOT DISTINCT FROM b.value;

ОШИБКА: FULL JOIN поддерживается только с условиями объединения с присоединением или с присоединением хеша

Может кто-нибудь, пожалуйста, ответьте:

Что такое «условие объединения с присоединением слиянием или объединения с хеш-соединением» и почему объединение a.value IS NOT DISTINCT FROM b.value не удовлетворяет этому условию, но a.value = b.value совершенно нормально?

Кажется, единственное отличие состоит в том, как обрабатываются значения NULL. Поскольку столбец value индексируется в обеих таблицах, запуск EXPLAIN при поиске NULL так же эффективен, как поиск значений, не являющихся 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)

Это было протестировано с PostgreSQL 9.6.3 и 10beta1.

обсуждался этот вопрос, но это не так т прямо ответить на поставленный выше вопрос.

5
Matt 28 Май 2017 в 23:10

2 ответа

Лучший ответ

PostgreSQL реализует FULL OUTER JOIN с помощью хеш-функции или объединения слиянием.

Чтобы иметь право на такое объединение, условие объединения должно иметь форму

<expression using only left table> <operator> <expression using only right table>

Теперь ваше условие соединения выглядит так, но PostgreSQL не имеет специального оператора IS NOT DISTINCT FROM, поэтому он разбирает ваше условие на:

(NOT ($1 IS DISTINCT FROM $2))

И такое выражение не может использоваться для хеш-соединений или объединений слиянием, следовательно, сообщение об ошибке.

Я могу придумать способ обойти это:

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;

Это работает, если <null> не отображается нигде в столбцах value.

5
Laurenz Albe 29 Май 2017 в 09:30

Я только что решил такой случай, заменив условие ON на «TRUE» и переместив исходное условие «ON» в предложение WHERE. Я не знаю, как это влияет на производительность.

1
David 11 Апр 2018 в 13:57