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

StackOverflow https://stackoverflow.com/questions/444820

Вопрос

Я использую 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: На самом деле это взгляды, к которым я присоединяюсь.К сожалению, они находятся в базе данных, которую я не могу контролировать, поэтому я не могу (легко) внести какие-либо изменения в индексацию.Хотя я склонен согласиться, что это проблема индексации.Я подожду немного, прежде чем принять ответ, на случай, если есть какой-то волшебный способ настроить этот запрос, о котором я не знаю.В противном случае я приму один из текущих ответов и попытаюсь найти другой способ сделать то, что хочу.Спасибо всем за помощь.

Это было полезно?

Решение

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

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

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

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

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

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

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

Я думаю, вам нужно понять, почему два последних запроса не совпадают с первым. Если вы выполняете левое соединение, а затем добавляете предложение 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.

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

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

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

http://download.oracle .com / документы / CD / B28359_01 / server.111 / b28274 / ex_plan.htm # PFGRF009

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

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

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

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

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

При левом соединении вы будете сканировать 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

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

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

Демс.

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

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

1 квартал

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' )

Этот запрос семантически эквивалентен предыдущему вопросу 2.

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top