Я бы сказал, что ваша проблема в том, что вы делаете много ЛЕВЫХ ПРИСОЕДИНЕНИЙ, и конечные результаты становятся слишком большими после применения всех этих ОБЪЕДИНЕНИЙ. Также индексы не могут использоваться таким образом, чтобы максимально быстро вычислить 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;
Для лучшего Performace создания индексов на (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
. В случае, если есть 1: 1 соотношение между t1
и t4
, это может быть даже лучше, чтобы написать что-то вроде этого на второй линии:
(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 ОТНОСИТЬСЯ и 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 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, в противном случае вы можете использовать pipelined functions.
Проверьте это SQL Fiddle, особенно различия в планах исполнения.
Пожалуйста, используйте ссылку «изменить» под своим вопросом и используйте возможности форматирования кода редактора вопросов, чтобы правильно форматировать ваш запрос как часть кода (это панель инструментов с двумя фигурками - '{}'). Отправьте план выполнения и в свой запрос. – nop77svk
Если это реальный запрос, вы не ссылаетесь на T4 или T5 в предложении select, так почему вы присоединяетесь к ним? – Sparky
Откуда берутся 'p.date' в вашем вопросе? В разделе 'FROM' нет таблицы с именем' p'. –