#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 запроса выше.
Пожалуйста, опубликуйте больше информации.