Предложение NOT EXISTS в Postgresql

Кто-нибудь знает, как выполнить такой запрос в Postgresql?

SELECT * 
FROM tabA 
WHERE NOT EXISTS (
    SELECT * 
    FROM tabB 
    WHERE tabB.id = tabA.id
)

Когда я выполняю такой запрос, postgresql жалуется «ERROR: Greenplum Database does not yet support that query».

РЕДАКТИРОВАТЬ: А как насчет этого:

SELECT * 
FROM tabA 
WHERE NOT EXISTS (
    SELECT * 
    FROM tabB WHERE tabB.id = tabA.id AND tabB.id2 = tabA.id2
)

EDIT:
Я проверил в postgresql 8.2.15 4 ответа, предоставленных @ypercube. Выводы такие:

1) Первое не работает в этой версии postgresql, как я уже говорил выше в вопросе. Там же можно найти сообщение об ошибке.

2) Для остальных трех ответов скорость выполнения равна: (3) LEFT JOIN > (4) EXCEPT >> (2) NOT IN.
В частности, для запросов с одинаковым синтаксисом (3) LEFT JOIN занимает около 5580 мс, (4) EXCEPT занимает около 13502 мс и (2) NOT IN занимает более 100000 (на самом деле я не стал ждать, пока он завершится). ).
Есть ли особые причины, по которым предложение NOT IN работает так медленно?
Ченг


person cheng    schedule 28.06.2012    source источник
comment
PostgreSQL 8.2 устарел и больше не поддерживается. С тех пор произошли серьезные изменения в том, как работают запросы EXISTS и NOT EXISTS. postgresql.org/support/versioning   -  person kgrittn    schedule 30.06.2012
comment
Если вы хотите найти наиболее эффективный из запросов, я думаю, вам сначала нужно проверить индексы, которые у вас есть в таблицах. Вы не указываете размер таблиц, но 5 секунд для запроса означают (99%) либо огромные таблицы, либо отсутствие индексов. Я предлагаю вам добавить новый вопрос, включая определения (CREATE TABLE) двух таблиц, запросы и планы выполнения.   -  person ypercubeᵀᴹ    schedule 30.06.2012


Ответы (3)


Есть 3 (основных) способа сделать такой запрос:

  1. NOT EXISTS связанный подзапрос

  2. NOT IN подзапрос

  3. LEFT JOIN с проверкой IS NULL:

Вы обнаружили, что первый способ работает в Greenplum. @Marco и @juergen предоставили второй способ. Вот третий, он может обойти ограничения Greenplum:

SELECT tabA.* 
FROM 
    tabA 
  LEFT JOIN 
    tabB 
      ON  tabB.id = tabA.id 
      AND tabB.id2 = tabA.id2
WHERE tabB.id IS NULL ;

Этот (4-й способ) также работает в Postgres (который поддерживает оператор EXCEPT):

SELECT a.*
FROM a
WHERE id IN
      ( SELECT id
        FROM a
      EXCEPT
        SELECT id
        FROM b
      ) ; 

Протестировано в SQL-Fiddle (все 4 работают в Postgres).

person ypercubeᵀᴹ    schedule 28.06.2012
comment
Нет, это не так. Я думаю, причина в том, что фильтр tabB.id IS NULL применяется до LEFT JOIN, а не после LEFT JOIN. - person cheng; 28.06.2012
comment
Как реализовать этот запрос с NOT EXISTS? Можете ли вы предложить какую-либо помощь? - person cheng; 28.06.2012
comment
Как говорится в ошибке, он еще не поддерживается Greenplum. Никакой помощи оказать нельзя. Поскольку запрос NOT IN работает, используйте его. - person ypercubeᵀᴹ; 28.06.2012
comment
Предположим, у меня есть две таблицы: UserInfo и UserOrder. Я хочу найти пользователей без заказов. Этот запрос SELECT * FROM UserInfo LEFT JOIN UserOrder ON UserInfo.userid = UserOrder.userid AND UserOrder.orderid IS NULL возвращает все строки в UserInfo. В моих таблицах есть ровно одна строка в UserOrder и несколько строк для разных пользователей в UserInfo. - person cheng; 28.06.2012
comment
давайте продолжим это обсуждение в чате - person cheng; 28.06.2012
comment
Я проверил четыре ответа в postgresql 8.2.15. Выводы перечислены в моем вопросе (часть «Редактировать»). Спасибо, что поделились этим со мной и другими. - person cheng; 30.06.2012
comment
Отлично, теперь я использую NOT EXISTS, который дает самые стабильные результаты. И подзапрос NOT IN имеет тенденцию быть катастрофически медленным с низкими настройками work_mem (или большими таблицами). - person denis.peplin; 16.02.2016

Часть ошибки, которую вы упустили, могла указать вам правильное направление. Я думаю, что он сказал: «ДЕТАЛЬ: запрос содержит коррелированный подзапрос». Таким образом, вы должны переписать их с соединениями или некоррелированными подзапросами.

SELECT * FROM tabA WHERE id NOT IN (SELECT id FROM tabB);

Что касается второго запроса, попробуйте

SELECT * FROM tabA WHERE (id, id2) NOT IN (SELECT id, id2 FROM tabB);
person Marco Mariani    schedule 28.06.2012
comment
Спасибо, что так быстро ответили. Я обновил свой вопрос. Что насчет нового запроса? - person cheng; 28.06.2012
comment
Да, он говорит, что запрос содержит коррелированный подзапрос. Этот тип запроса напрямую поддерживается mysql. Я думал, что postgresql также поддерживает это. - person cheng; 28.06.2012
comment
postgres делает это с гораздо более сложными вещами, но greenplum нет, потому что ему приходится отказываться от функциональности из соображений производительности. - person Marco Mariani; 28.06.2012
comment
Во всяком случае, SELECT * FROM tabA WHERE (id, id2) NOT IN (SELECT id, id2 FROM tabB) работает. Спасибо. - person cheng; 28.06.2012

person    schedule
comment
Я обновил свой вопрос, как выполнить обновленный запрос в postgresql? - person cheng; 28.06.2012