Это на Oracle 8i (извините, нет контроля над этим) и PHP 7.
Я строю инструмент поиска. Это простая форма с 3 полями, использующая метод HTTP Post. Затем PHP выполняет некоторые проверки значений 3 полей, определяет, действительны ли они, а затем отправляет значения в запрос SQL. Запрос выглядит примерно так; помните его 8i, так что нет ANSI присоединиться здесь:
SELECT
reports_table.*, documents_table.*, cases_table.*
FROM
reports_table, documents_table, cases_table
WHERE
reports_table.report_id = documents_table.report_id
AND reports_table.report_id = cases_table.report_id(+)
-- Report Number filtering
AND reports_table.report_no =
CASE
WHEN $report_no_isvalid = 1
THEN '$report_no' -- Oracle expects datatype varchar2
ELSE reports_table.report_no
END
-- Document Number filtering
AND documents_table.document_no =
CASE
WHEN $doc_no_isvalid = 1
THEN $doc_no -- Oracle expects datatype number
ELSE documents_table.document_no
END
-- Case Number filtering
AND cases_table.case_no =
CASE
WHEN $case_no_isvalid = 1
THEN '$case_no' -- Oracle expects datatype varchar2
ELSE cases_table.case_no
END
Пользователь должен ввести хотя бы номер отчета или номер дела. Требуются полные номера, то есть поиск по шаблону не допускается.
reports_table
очень большой
При поиске по номеру отчета база данных занимает очень много времени, как будто оценка CASE, которая влияет на достоверность номера отчета, то есть этот раздел кода здесь
AND reports_table.report_no =
CASE
WHEN $report_no_isvalid = 1
THEN '$report_no' -- Oracle expects datatype varchar2
ELSE reports_table.report_no
END
оценивается после операции соединения. Это, кажется, действительно оценивается, потому что, если я добавлю еще один простой предикат в предложении WHERE, чтобы ограничить область действия для номера отчета, база данных ответит очень быстро, с ожидаемым результатом. например скажем, номер отчета, который я ищу, это «R123456», если я добавлю AND reports_table.report_no LIKE 'R1234%'
как предикат вне оператора CASE, производительность хорошая. В противном случае это очень медленно, как будто Oracle сканирует весь reports_table
в попытке сделать соединение.
Я хотел бы найти способ сообщить Oracle, чтобы он смотрел на условный фильтр CASE по номеру отчета при выполнении объединения, но я не знаю, как это сделать.
Или, может быть, мне следует вообще избегать такого рода условных ограничений на объединение, и если да, то какую технику я могу использовать для достижения того, что я пытаюсь сделать?
Прошло некоторое время с тех пор, как мне пришлось поработать с этим: и полный выстрел в темноте … Перераспределяет ли ограничения так, чтобы они налагались до внешнего соединения?
WHERE
(reports_table.report_id = documents_table.report_id
-- Report Number filtering
AND reports_table.report_no =
CASE
WHEN $report_no_isvalid = 1
THEN '$report_no' -- Oracle expects datatype varchar2
ELSE reports_table.report_no
END
-- Document Number filtering
AND documents_table.document_no =
CASE
WHEN $doc_no_isvalid = 1
THEN $doc_no -- Oracle expects datatype number
ELSE documents_table.document_no
END)
AND
(reports_table.report_id = cases_table.report_id(+)
-- Case Number filtering
AND cases_table.case_no =
CASE
WHEN $case_no_isvalid = 1
THEN '$case_no' -- Oracle expects datatype varchar2
ELSE cases_table.case_no
END)
В ANSI я знаю, что если я использую AND в таблице where с помощью внешнего соединения, то мое внешнее соединение ведет себя как внутреннее. Я задаюсь вопросом, переставляя ли двигатель лучше оптимизировать; или если понадобится реальная подсказка SQL.
Других решений пока нет …