我会说您的问题是您正在执行许多 LEFT JOIN 并且在应用所有这些 JOIN 后最终结果集变得太大。也不能以这种方式使用索引以最快的方式计算 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)
为了获得更好的性能,在or上创建索引(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
如果和之间存在 1:1 的关系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 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,这需要很长时间,因为它对 I/O 操作的要求很高. 使用 MIN 或 MAX 时索引的错误使用会导致这样的情况,即您将所有历史表数据缓存在内存中,而除了 MIN 或 MAX 计算之外,其他任何事情都不需要它。
如果没有查询的 CROSS APPLY 部分,服务器将需要将 t5 中的所有单独日期加载到内存中,并从整个加载的结果集中计算 MAX。
标记正确索引表上的 MIN 函数的行为类似于 TOP 1 ORDER BY,这非常快。通过这种方式,您可以立即获得结果。
CROSS APPLY 在 Oracle 12C 中可用,否则您可以使用流水线函数。
检查这个SQL Fiddle,尤其是执行计划的差异。