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


У вас есть индексы покрытия на person_uid и period для обеих таблиц?
Если нет, добавьте их и попробуйте еще раз.
Взгляните на план выполнения и посмотрите, что на самом деле делает запрос.
Также: каковы типы данных полей? Они одинаковы в обеих таблицах? Неявное приведение действительно может замедлить работу.
Гм, вопрос помечен как oracle, поэтому я не думаю, что он использует SQL Server.
Ах :) Я этого не видел ... Неважно ... План выполнения есть и в оракуле ... Отредактирую ответ.
Есть ли в этих таблицах индексы по столбцам, к которым вы присоединяетесь? Установите бесплатный продукт 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 и внешние соединения старого стиля.
С вашими исходными запросами с WHERE база данных могла свободно перемещаться из table2, получая записи table1, где person_uid или период совпадают. Исходный запрос ДОЛЖЕН полностью сканировать table1 и будет плохо работать, если будет плохой доступ к table2 (например, нет полезного индекса).
@ igor-db: есть ли шанс, что вы могли бы вставить это в ответ, чтобы я мог проголосовать за него? :-)
Думаю, вам нужно понять, почему последние два запроса не совпадают с первым. Если вы выполняете левое соединение, а затем добавляете предложение 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. В противном случае все только догадываются.
В одном из обновлений 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.
предоставьте план выполнения для этого запроса