Postgresql UNION занимает в 10 раз больше времени, чем выполнение отдельных запросов - PullRequest
8 голосов
/ 14 июня 2011

Я пытаюсь получить разницу между двумя почти идентичными таблицами в postgresql. Текущий запрос, который я выполняю:

SELECT * FROM tableA EXCEPT SELECT * FROM tableB;

и

SELECT * FROM tableB EXCEPT SELECT * FROM tableA;

Каждый из указанных выше запросов занимает около 2 минут (это большая таблица)

Я хотел объединить два запроса в надежде сэкономить время, поэтому я попытался:

SELECT * FROM tableA EXCEPT SELECT * FROM tableB
UNION
SELECT * FROM tableB EXCEPT SELECT * FROM tableA;

И пока он работает, его запуск занимает 20 минут !!! Я бы предположил, что самое большее потребуется 4 минуты - количество времени для выполнения каждого запроса в отдельности.

Есть ли какая-то дополнительная работа, которую выполняет UNION, что заставляет ее так долго? Или я могу ускорить это (с UNION или без него)?

ОБНОВЛЕНИЕ: Выполнение запроса с помощью UNION ALL занимает 15 минут, почти в 4 раза больше, чем выполнение каждого из них само по себе. Правильно ли я сказал, что UNION (все) вообще не собирается ускорять это? *

Ответы [ 4 ]

15 голосов
/ 14 июня 2011

Относительно вашего вопроса «дополнительной работы». Да. Union не только объединяет два запроса, но также проходит и удаляет дубликаты. Это то же самое, что использовать отдельное утверждение.

По этой причине, особенно в сочетании с вашими утверждениями, кроме "union all", скорее всего, будет быстрее.

Подробнее здесь: http://www.postgresql.org/files/documentation/books/aw_pgsql/node80.html

3 голосов
/ 14 июня 2011

В дополнение к объединению результатов первого и второго запроса, UNION по умолчанию также удаляет дубликаты записей. (см. http://www.postgresql.org/docs/8.1/static/sql-select.html). Дополнительная работа, связанная с проверкой дублирующих записей между двумя запросами, вероятно, ответственна за дополнительное время. В этой ситуации не должно быть никаких дублирующих записей, поэтому дополнительной работы по поиску дубликатов можно избежать указав UNION ALL.

SELECT * FROM tableA EXCEPT SELECT * FROM tableB
UNION ALL
SELECT * FROM tableB EXCEPT SELECT * FROM tableA;
2 голосов
/ 14 июня 2011

Я не думаю, что ваш код возвращает набор результатов, который вы намереваетесь.Я скорее думаю, что вы хотите сделать это:

SELECT * 
  FROM (
        SELECT * FROM tableA 
        EXCEPT 
        SELECT * FROM tableB
       ) AS T1
UNION 
SELECT * 
  FROM (
        SELECT * FROM tableB 
        EXCEPT 
        SELECT * FROM tableA
       ) AS T2;

Другими словами, вам нужен набор взаимоисключающих членов.Если это так, вам нужно прочитать о приоритете реляционных операторов в SQL;) А когда у вас есть, вы можете понять, что вышеприведенное можно рационализировать следующим образом:

SELECT * FROM tableA 
UNION 
SELECT * FROM tableB
EXCEPT 
SELECT * FROM tableA 
INTERSECT
SELECT * FROM tableB;

FWIW, используя подзапросы (производные таблицы)T1 и T2), чтобы явно показать (что в противном случае было бы неявным) приоритет реляционного оператора, ваш исходный запрос выглядит следующим образом:

SELECT * 
  FROM (
        SELECT * 
          FROM (
                SELECT * 
                  FROM tableA 
                EXCEPT 
                SELECT * 
                  FROM tableB
               ) AS T2
        UNION
        SELECT * 
          FROM tableB
       ) AS T1
EXCEPT 
SELECT * 
  FROM tableA;

Вышеприведенное можно соотнести с:

SELECT * 
  FROM tableB 
EXCEPT 
SELECT * 
  FROM tableA;

... и я не думаю, что предназначено.

0 голосов
/ 14 июня 2011

Вы можете использовать tableA FULL OUTER JOIN tableB, которая выдаст то, что вы хотите (с условием propre join) только с 1 сканированием таблицы, вероятно, это будет быстрее, чем 2 запроса выше.

Опубликуйте больше информации, пожалуйста.

...