Левое внешнее соединение на двух столбцах проблема с производительностью

Я использую SQL-запрос, похожий на следующую форму:

SELECT col1, col2
FROM table1
LEFT OUTER JOIN table2
ON table1.person_uid = table2.person_uid
AND table1.period = table2.period

И это либо слишком медленно, либо что-то заходит в тупик, потому что для возврата требуется не менее 4 минут. Если бы я изменил его на это:

SELECT col1, col2
FROM table1
LEFT OUTER JOIN table2
ON table1.person_uid = table2.person_uid
WHERE table1.period = table2.period

тогда он работает нормально (хотя и не возвращает нужное количество столбцов). Есть ли способ ускорить это?

ОБНОВИТЬ: он делает то же самое, если я переключаю последние две строки последнего запроса:

SELECT col1, col2
FROM table1
LEFT OUTER JOIN table2
ON table1.period = table2.period
WHERE table1.person_uid = table2.person_uid

ОБНОВЛЕНИЕ 2: Это на самом деле мнения, к которым я присоединяюсь. К сожалению, они находятся в базе данных, которую я не контролирую, поэтому я не могу (легко) вносить какие-либо изменения в индексацию. Я склонен согласиться с тем, что это проблема индексации. Я подожду немного, прежде чем принять ответ, на случай, если есть какой-то волшебный способ настроить этот запрос, о котором я не знаю. В противном случае я приму один из текущих ответов и попытаюсь придумать другой способ сделать то, что я хочу сделать. Спасибо за помощь до сих пор.

предоставьте план выполнения для этого запроса

squadette 15.01.2009 00:48
ReactJs | Supabase | Добавление данных в базу данных
ReactJs | Supabase | Добавление данных в базу данных
Это и есть ваш редактор таблиц в supabase.👇
Понимание Python и переход к SQL
Понимание Python и переход к SQL
Перед нами лабораторная работа по BloodOath:
11
1
87 556
8
Перейти к ответу Данный вопрос помечен как решенный

Ответы 8

У вас есть индексы покрытия на person_uid и period для обеих таблиц?

Если нет, добавьте их и попробуйте еще раз.

Взгляните на план выполнения и посмотрите, что на самом деле делает запрос.

Также: каковы типы данных полей? Они одинаковы в обеих таблицах? Неявное приведение действительно может замедлить работу.

Гм, вопрос помечен как oracle, поэтому я не думаю, что он использует SQL Server.

cletus 15.01.2009 00:57

Ах :) Я этого не видел ... Неважно ... План выполнения есть и в оракуле ... Отредактирую ответ.

Andrew Rollings 15.01.2009 00:59

Есть ли в этих таблицах индексы по столбцам, к которым вы присоединяетесь? Установите бесплатный продукт Oracle SQLDeveloper и используйте его для «объяснения» этого запроса и посмотрите, выполняет ли он последовательное сканирование обеих таблиц.

Ответ принят как подходящий

Имейте в виду, что утверждения 2 и 3 отличаются от первого.

Как? Что ж, вы выполняете левое внешнее соединение, и ваше предложение WHERE не принимает это во внимание (как это делает предложение ON). Как минимум попробуйте:

SELECT col1, col2
FROM table1, table2
WHERE table1.person_uid = table2.person_uid (+)
AND table1.period = table2.period (+)

и посмотрите, возникнет ли у вас такая же проблема с производительностью.

Какие индексы у вас есть на этих таблицах? Определяется ли эта связь ограничением внешнего ключа?

Что вам, вероятно, понадобится, так это составной индекс как для person_uid, так и для периода (для обеих таблиц).

Эти ошибки запроса, что я не могу смешивать внешние соединения ANSI и внешние соединения старого стиля.

Jason Baker 15.01.2009 00:58

С вашими исходными запросами с WHERE база данных могла свободно перемещаться из table2, получая записи table1, где person_uid или период совпадают. Исходный запрос ДОЛЖЕН полностью сканировать table1 и будет плохо работать, если будет плохой доступ к table2 (например, нет полезного индекса).

Gary Myers 15.01.2009 02:36

@ igor-db: есть ли шанс, что вы могли бы вставить это в ответ, чтобы я мог проголосовать за него? :-)

Jason Baker 15.01.2009 17:24

Думаю, вам нужно понять, почему последние два запроса не совпадают с первым. Если вы выполняете левое соединение, а затем добавляете предложение where, ссылающееся на поле в таблице с правой стороны соединения (которое не всегда может иметь запись, соответствующую первой таблице), то вы фактически изменили соединение на внутреннее соединение. Есть одно исключение, и это если вы ссылаетесь на что-то вроде

SELECT col1, col2
FROM table1
LEFT OUTER JOIN table2
ON table1.person_uid = table2.person_uid
WHERE table2.person_uid is null

В этом случае вы запрашиваете запись, которой нет во второй таблице. Но кроме этого особого случая, вы меняете левое соединение на внутреннее, если вы ссылаетесь на поле в table2 в предложении where.

Если ваш запрос недостаточно быстрый, я бы посмотрел на вашу индексацию.

В левом соединении вы должны сканировать table1 для каждой уникальной комбинации (person_uid, period), а затем искать table2 для всех соответствующих записей. Если table2 не имеет подходящего индекса, это также может включать сканирование всей этой таблицы.

Мое лучшее предположение, не видя плана выполнения, заключается в том, что первый запрос (единственный, который кажется правильным) должен сканировать таблицу table2, а также table1.

Как вы говорите, что вы не можете изменить индексы, вам нужно изменить запрос. Насколько я могу судить, есть только одна реальная альтернатива ...

SELECT
   col1, col2
FROM
   table2
FULL OUTER JOIN
   table1
      ON table1.person_uid = table2.person_uid
      AND table1.period = table2.period
WHERE
   table1.person_uid IS NOT NULL

Здесь есть надежда, что вы просканируете table2 для каждой уникальной комбинации (person_uid, period), но будете использовать индексы для table1. (В отличие от сканирования table1 и использования индексов в table2, чего я ожидал от вашего запроса.)

Однако, если table1 не имеет подходящих индексов, вы вряд ли вообще заметите какое-либо улучшение производительности ...

Dems.

Все, что кто-либо говорит вам на основе предоставленной вами информации, является предположением.

Посмотрите на план выполнения запроса. Если вы не видите причины медлительности в плане, опубликуйте план здесь.

http://download.oracle.com/docs/cd/B28359_01/server.111/b28274/ex_plan.htm#PFGRF009

@ Дэйв Коста: Совершенно верно! Используйте трассировку Oracle, чтобы узнать план выполнения и то, что ожидает Oracle. В противном случае все только догадываются.

spencer7593 16.05.2009 06:31

В одном из обновлений OP заявляет, что на самом деле он запрашивает представления, а не таблицы. В этом случае производительность вполне может быть увеличена путем прямого запроса необходимых ему таблиц, особенно если представления сложные и присоединяются ко многим другим таблицам, которые не содержат нужной ему информации или представляют собой представления, вызывающие представления.

Синтаксис соединения ANSI обеспечивает очень четкое различие между условиями JOIN и предикатами FILTER; это очень важно при написании внешних объединений. Используя таблицы emp / dept, посмотрите на результаты следующих двух внешних объединений.

Q1

SELECT dname, d.deptno, e.ename, e.mgr, d.loc
FROM dept d
LEFT OUTER JOIN emp e
on  d.deptno = e.deptno
and loc in ('NEW YORK','BOSTON' )
;

DNAME              DEPTNO ENAME             MGR LOC
-------------- ---------- ---------- ---------- -------------
ACCOUNTING             10 CLARK            7839 NEW YORK
ACCOUNTING             10 KING                  NEW YORK
ACCOUNTING             10 MILLER           7782 NEW YORK
RESEARCH               20                       DALLAS
SALES                  30                       CHICAGO
OPERATIONS             40                       BOSTON

====

Q2
SELECT dname, d.deptno, e.ename, e.mgr, d.loc
FROM dept d
LEFT OUTER JOIN emp e
on  d.deptno = e.deptno
where loc in ('NEW YORK','BOSTON' )
;

DNAME              DEPTNO ENAME             MGR LOC
-------------- ---------- ---------- ---------- -------------
ACCOUNTING             10 CLARK            7839 NEW YORK
ACCOUNTING             10 KING                  NEW YORK
ACCOUNTING             10 MILLER           7782 NEW YORK
OPERATIONS             40                       BOSTON

Первый пример, показанный Q1, является примером «соединения по константе». По сути, условие фильтрации применяется до выполнения внешнего соединения. Таким образом, вы удаляете строки, которые впоследствии добавляются обратно как часть внешнего соединения. Это не обязательно неправильно, но действительно ли это тот вопрос, который вы просили? Часто требуются результаты, показанные в Q2, когда фильтр применяется после (внешнего) соединения.

Также есть влияние на производительность для больших наборов данных. Во многих случаях объединение по константе должно быть разрешено оптимизатором внутри путем создания бокового представления, которое обычно можно оптимизировать только с помощью соединения вложенного цикла, а не хеш-соединения.

Для разработчиков, знакомых с синтаксисом внешнего соединения Oracle, запрос, вероятно, был бы записан как

SELECT dname, d.deptno, e.ename, e.mgr, d.loc
FROM dept d
        ,emp e
where  d.deptno = e.deptno(+)
and loc in ('NEW YORK','BOSTON' )

Этот запрос семантически эквивалентен Q2 выше.

Таким образом, чрезвычайно важно понимать разницу между предложением JOIN и предложением WHERE при написании внешних соединений ANSI.

Другие вопросы по теме