【问题标题】:Oracle subquery's SELECT MAX() on large dataset is very slowOracle 子查询在大型数据集上的 SELECT MAX() 非常慢
【发布时间】:2017-11-21 12:23:18
【问题描述】:

以下SQL:

SELECT *
FROM   Transaction_Auth_Series t
WHERE  t.Auth_ID = 
(
  SELECT MAX(p.Session_ID)
  FROM   Clone_Db_Derective p
  WHERE  p.Date = trunc(sysdate)
  AND    p.Regularity = 'THEME'
);

当引用的表包含大约 3 亿行时非常慢。但是,在两个游标中写入 SQL 只是几秒钟的问题,即

  CURSOR GetMaxValue IS
    SELECT MAX(p.Session_ID)
      FROM   Clone_Db_Derective p
      WHERE  p.Date = trunc(sysdate)
      AND    p.Regularity = 'THEME'

  CURSOR GetAllItems(temp VARCHAR2) IS
    SELECT *
    FROM   Transaction_Auth_Series t
    WHERE  t.Auth_ID = temp;

........
  FOR item in GETMAX LOOP
    FOR itemx in GETITEMS(item.aaa) LOOP.......

由于表不相关,联接将不起作用。请问如何优化上面的主SQL?

【问题讨论】:

  • 执行计划可能不是最优的。例如,优化器可能已经在内部对其进行了转换,以根据表统计信息首先执行连接。如果是这样,您也许可以修复统计信息或重新排列或提示查询。
  • 您能否详细说明您的建议@WilliamRobertson?
  • 如果执行计划对于这样的简单查询不好,通常意味着统计信息错误。因此,SQL 调优练习涉及检查计划和查看统计信息。他们看起来怎么样?
  • 统计 ----------------------------------------- ----------------- 1205 递归调用 0 db 块获取 87542484 一致获取 3225786 物理读取 10984 重做大小 833979 字节通过 SQLNet 发送到客户端 1862 字节通过 SQL来自客户端的网络 138 SQL*Net 往返客户端 23 排序(内存) 0 排序(磁盘) 2047 行处理
  • 这很可爱,但不是执行计划或表统计信息。

标签: sql oracle join subquery


【解决方案1】:

对于这个查询:

SELECT t.*
FROM Transaction_Auth_Series t
WHERE t.Auth_ID = (SELECT MAX(p.Session_ID)
                   FROM Clone_Db_Derective p
                   WHERE p.Date = trunc(sysdate) AND p.Regularity = 'THEME'
                  );

我会推荐Clone_Db_Derective(Regularity, Date, Session_ID)Transaction_Auth_Series(Auth_Id) 上的索引。

这个查询的优化(假设表不是视图)看起来很简单。我很惊讶光标版本的速度如此之快。

【讨论】:

  • 是的,游标版本在几秒钟内检索到所需的数据。但是,Transaction_Auth_Series 是一个视图,而 Clone_Db_Derective 是索引的。
  • @MaDi 。 . .该视图可能会使优化器感到困惑。您可以尝试将子查询移至FROM 子句并使用JOIN
  • 感谢您的建议,但连接似乎无济于事。这些是不相关的,所以肯定不会有帮助,不是吗?
  • @MaDi - 在这种情况下,“加入”将是笛卡尔联接。第二个“表”(内联视图)是 MAX 计算的结果 - 它是一个标量“表”(只有一行和一列)。这将迫使 Oracle 首先计算 MAX,而不是尝试其他奇怪的事情。值得一试!
【解决方案2】:
WITH max_session
     AS (SELECT MAX (p.Session_ID) id
           FROM Clone_Db_Derective p
          WHERE p.Date = TRUNC (SYSDATE) AND p.Regularity = 'THEME')
SELECT *
  FROM Transaction_Auth_Series t
 WHERE t.Auth_ID = (SELECT id FROM max_session)

当 WITH 查询的结果在主查询的主体中需要不止一次时,例如需要将一个平均值与两次或三次进行比较的情况下,WITH 子句最有价值。关键是尽量减少对多次连接到单个查询中的表的访问次数。

【讨论】:

  • 我尝试了 WITH 但没有成功。它仍然需要很多时间(大约 160 秒,而 CURSOR 选项只需要大约两秒)
  • 重点是子查询在 OP 的整体查询中只使用一次。在这种情况下,我看不到 WITH 子句会如何改变任何东西,因为优化器会将您的查询(在此答案中)转换为 OP 的原始查询。
猜你喜欢
  • 2017-02-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-10-13
  • 2012-03-31
相关资源
最近更新 更多