Я бы сказал, что ваша проблема заключается в том, что вы делаете много ЛЕВЫХ СОЕДИНЕНИЙ, и окончательный набор результатов становится слишком большим после применения всех этих СОЕДИНЕНИЙ. Также индексы нельзя использовать таким образом для расчета MIN или MAX самым быстрым способом. При правильном использовании индексов вы сможете очень быстро рассчитать MIN или MAX.
Я бы написал запрос примерно так:
SELECT t1.id,
(SELECT MIN(t5.date) FROM t5 JOIN t4 ON t5.p_id = t4.p_id WHERE t4.id = t1.id) AS first_pri_date,
(SELECT MIN(date) FROM t3 WHERE t3.id = t1.id) AS first_pub_date,
(SELECT MAX(date) FROM t3 WHERE t3.id = t1.id) AS last_publ_date,
(SELECT MIN(date) FROM t2 WHERE t2.id = t1.id) AS first_exp_date
FROM t1
ORDER BY t1.id;
Для лучшей производительности создайте индексы на (id, date)
или (p_id, date)
. Таким образом, ваши индексы будут такими:
CREATE INDEX ix2 ON T2 (id,date);
CREATE INDEX ix3 ON T3 (id,date);
CREATE INDEX ix5 ON T5 (p_id,date);
CREATE INDEX ix4 ON T4 (id);
Но все еще остается проблема с соединением между t4
и t5
. В случае, если между t1
и t4
отношение 1:1, было бы даже лучше написать что-то вроде этого во второй строке:
(SELECT MIN(t5.date) FROM t5 WHERE t5.p_id = (SELECT p_id FROM t4 WHERE t4.id=t1.id)) AS first_pri_date,
Если это 1:N, а также если CROSS APPLY и OUTER APPLY работают с вашей версией Oracle, вы можете переписать вторую строку следующим образом:
(SELECT MIN(t5min.PartialMinimum)
FROM t4
CROSS APPLY
(
SELECT PartialMinimum = MIN(t5.date)
FROM t5
WHERE t5.p_id = t4.p_id
) AS t5min
WHERE t4.id = t1.id)
AS first_pri_date
Все это направлено на максимально возможное использование индексов при расчете MIN или MAX. Таким образом, весь SELECT можно было бы переписать так:
SELECT t1.id,
(SELECT MIN(t5min.PartialMinimum)
FROM t4
CROSS APPLY
(
SELECT TOP 1 PartialMinimum = date
FROM t5
WHERE t5.p_id = t4.p_id
ORDER BY 1 ASC
) AS t5min
WHERE t4.id = t1.id) AS first_pri_date,
(SELECT TOP 1 date FROM t2 WHERE t2.id = t1.id ORDER BY 1 ASC) AS first_exp_date,
(SELECT TOP 1 date FROM t3 WHERE t3.id = t1.id ORDER BY 1 ASC) AS first_pub_date,
(SELECT TOP 1 date FROM t3 WHERE t3.id = t1.id ORDER BY 1 DESC) AS last_publ_date
FROM t1
ORDER BY 1;
Я считаю, что это наиболее оптимальный способ получить MIN или MAX из таблицы исторических данных.
Дело в том, что использование MIN с большим количеством неиндексированных значений заставляет сервер загружать все данные в память, а затем вычислять MIN или MAX из неиндексированных данных, что занимает много времени, поскольку предъявляет высокие требования к операциям ввода-вывода. . Плохое использование индексов при использовании MIN или MAX может привести к ситуации, когда все ваши исторические данные таблицы кэшируются в памяти, и они не нужны ни для чего другого, кроме расчета MIN или MAX.
Без части запроса CROSS APPLY серверу пришлось бы загружать в память все отдельные даты из t5 и вычислять MAX из всего загруженного набора результатов.
Обратите внимание, что функция MIN в правильно проиндексированной таблице ведет себя как TOP 1 ORDER BY, что очень быстро. Таким образом, вы можете получить свои результаты мгновенно.
CROSS APPLY доступен в Oracle 12C, в противном случае вы можете использовать конвейерную функции.
Проверьте этот SQL Fiddle, особенно различия в планах выполнения.
person
Vojtěch Dohnal
schedule
02.11.2014
{ }
). Пожалуйста, опубликуйте план выполнения в своем запросе. - person peter.hrasko.sk   schedule 31.10.2014p.date
в вашем вопросе? В предложенииFROM
нет таблицы с именемp
. - person Brian Camire   schedule 31.10.2014