ОБЪЕДИНЕНИЕ Postgresql занимает в 10 раз больше времени, чем выполнение отдельных запросов

#sql #postgresql #diff #union

#sql #postgresql #разница #объединение

Вопрос:

Я пытаюсь получить разницу между двумя почти идентичными таблицами в 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 ALL занимает 15 минут, что почти в 4 раза больше времени, чем выполнение каждого из них по отдельности, правильно ли я говорю, что ОБЪЕДИНЕНИЕ (all) вообще не ускорит это?

Комментарии:

1. Будут ли дубликаты в tableA или tableB отдельно, которые нужно опустить? В противном случае попробуйте UNION ALL .

2. @ScrumMeister: Я не думал об этом раньше. Удаляет ли объединение дубликаты из одной таблицы. Я думал, что это только удаляет дубликаты между двумя объединенными таблицами. Возможно, мне придется исследовать это.

3. Можете ли вы опубликовать выходные данные EXPLAIN ANALYZE ?

Ответ №1:

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

По этой причине, особенно в сочетании с вашими инструкциями except «объединить все», вероятно, будет быстрее.

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

Комментарии:

1. Я только что запустил «ВЫБРАТЬ * ИЗ TableA, КРОМЕ SELECT * ИЗ TableB ОБЪЕДИНИТЬ ВСЕ SELECT * ИЗ TableB, КРОМЕ SELECT * ИЗ TableA;» Это заняло 15 минут, так что это все еще далеко не так быстро, как выполнение двух запросов по отдельности.

2. Я выбираю это как правильный ответ, но все еще кажется, что выполнение запросов по отдельности ускоряет его.

3. @RThomas путем добавления union all добавляет избыточное значение также, что, если я хочу разные значения?

Ответ №2:

В дополнение к объединению результатов первого и второго запроса, 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;
  

Комментарии:

1. Кажется, я немного медлил с вводом моего ответа. Слава lazyDBA

2. Значительно увеличьте производительность, я делаю ВЫБОР с ОБЪЕДИНЕНИЕМ стоимостью 2: 47 минут, а с ОБЪЕДИНЕНИЕМ ALL уменьшите время до 14 миллисекунд

Ответ №3:

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

 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;
  

… и я думаю, что это не то, что задумано.

Комментарии:

1. Спасибо за объяснение! Я выполнил первый предоставленный вами запрос, но это все равно занимает намного больше времени, чем выполнение двух отдельных запросов. Два запроса, которые я выполняю, — это именно то, что я хочу; я просто хочу, чтобы это было быстрее :). Второй предоставленный вами запрос занял> 1 часа, поэтому я остановил его (где все остальные, где менее 10 минут)

Ответ №4:

Вы могли бы использовать таблицу ПОЛНОГО ВНЕШНЕГО СОЕДИНЕНИЯ TABLEAB, которая выдала бы то, что вы хотите (с условием надлежащего соединения), всего за 1 сканирование таблицы, вероятно, это было бы быстрее, чем 2 запроса выше.

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