【问题标题】:Oracle SQL query slight alteration in query makes large result return time differenceOracle SQL查询 查询稍有改动,结果返回时差大
【发布时间】:2012-04-19 15:25:03
【问题描述】:

H 我试图弄清楚为什么两个相似但略有不同的 SQL 查询在运行它们所花费的时间之间存在如此大的差异。 我希望根据以下两个示例和报告的时间提供意见。

第一个查询如下,运行大约需要 115 - 154 秒。

SELECT * FROM 
(
    SELECT a.*, ROWNUM rnum 
    FROM 
    (
        SELECT ERR_ID, ERR_CREATED_BY,TO_CHAR(ERR_CREATED_DATE, 'DD/MM/YYYY H24:MI'),  
               ERR_SOURCE, ERR_DETAIL  
        FROM tdsys_errors  err   
        WHERE ERR_SOURCE = 'Online Transaction'  
        ORDER BY ERR_ID DESC 
    ) a 
    WHERE ROWNUM <= 25
) 
WHERE rnum > 0;

第二个查询在“ORDER BY ERR_ID DESC”块的位置方面略有变化,运行大约需要0.07秒

SELECT * FROM 
(
    SELECT a.*, ROWNUM rnum 
    FROM 
    (
        SELECT ERR_ID, ERR_CREATED_BY,TO_CHAR(ERR_CREATED_DATE, 'DD/MM/YYYY H24:MI'),  
               ERR_SOURCE, ERR_DETAIL  
        FROM tdsys_errors  err   
        WHERE ERR_SOURCE = 'Online Transaction'    
    ) a 
    WHERE ROWNUM <= 25
) 
WHERE rnum > 0
ORDER BY ERR_ID DESC;

我猜第二个查询是在结果到达之后排序的,第一个查询尝试一次完成所有操作。 我想知道这是一个 SQL 最佳实践案例吗?为什么会有这样的差异?

谢谢

【问题讨论】:

  • 看起来您在那里有不必要的嵌套级别。在第一个(正确的)代码块中,您可以删除外部查询。此外,WHERE rownum &gt; 0 总是评估为 true 并且是多余的。
  • 我也几乎总是选择按创建日期而不是 id 排序。据推测您正在寻找最新的错误。
  • 整个声明 WHERE rnum &gt; 0 是不必要的,因为我很怀疑是否有可能获得 negative 行号。一个我不太确定的“0”数字(到目前为止我看到的所有内容都从 1 开始,但我不使用 mySQL),但无论如何你可能想要那一行。如其他地方所述,在第二个查询中,您将获得 25 个 random 行(与内部 WHERE 子句匹配),这可能不是您想要的。
  • 嗨,下面的大卫写了一个很好的查询来解决这个问题,我测试了它并且效果很好。我接受了 Alex'x 的回答,因为它回答了我最初的疑问并为我解决了问题。

标签: sql oracle


【解决方案1】:

您自己的总结是正确的,第一个查询按 err_id 对 tdsys_errors 中的行进行排序,取其中的前 25 个,然后返回那些。第二个查询只输出 25 行(不保证顺序),然​​后对这些随机的 25 行进行排序。

【讨论】:

    【解决方案2】:

    在第一种情况下,您选择前 25 行 - lowest 最高 err_id 的行。它必须从您的查询中找到所有结果,然后在知道要使用哪个 25 之前将它们全部排序,这显然需要一段时间。

    在第二种情况下,您将提取无序查询返回的前 25 行,这可能是快速的,然后只对这 25 行进行排序。

    您可能会从这两个查询中得到不同的结果 - 您当然不应该假设它们总是相同的,即使它们碰巧有时也是如此。

    【讨论】:

    • 谢谢,我没有意识到与第一个查询相比,第二个查询会返回随机的不想要的结果。现在我知道为什么时差了。我将尝试调整查询以更快地返回我想要的结果。如果可以的话,我会在这里发帖
    【解决方案3】:

    原因是第一个查询必须对tdsys_errors 表中的所有行进行排序,而第二个查询只对从内部查询返回的 25 行进行排序。

    请注意,两个查询的输出可能不同。

    【讨论】:

    • 谢谢。既然我知道了,我会尝试调整查询以更好地执行
    【解决方案4】:

    假设您使用的是 Oracle 9i 或更高版本,您可以使用窗口/分析函数ROW_NUMBER(),因此您无需使用多个子查询:

    SELECT * FROM (
        SELECT ERR_ID, ERR_CREATED_BY, TO_CHAR(ERR_CREATED_DATE, 'DD/MM/YYYY H24:MI')
             , ERR_SOURCE, ERR_DETAIL, ROW_NUMBER() OVER (ORDER BY ERR_ID DESC) AS rnum
           FROM tdsys_errors  err   
          WHERE ERR_SOURCE = 'Online Transaction'
    ) WHERE rnum <= 25
     ORDER BY ERR_ID DESC;
    

    希望这会有所帮助。

    【讨论】:

    • 嗨大卫,我在阅读之前重写了查询,我的查询如下,两者都将时间从 154 秒缩短到 10 多秒,做得很好,我必须现在在两者之间进行选择:select * from (SELECT ERR_ID, ERR_CREATED_BY, TO_CHAR(ERR_CREATED_DATE, 'DD/MM/YYYY HH24:MI'), ERR_SOURCE, ERR_DETAIL FROM tdsys_errors where err_id in (SELECT ERR_ID FROM tdsys_errors WHERE ERR_SOURCE = 'Online Transaction ' ) ORDER BY ERR_ID DESC ) where rownum
    • @David.. 但是使用 row_number() 分析函数可以为您节省 0 个子查询。当然您可以在子查询上进行 ORDER BY。
    • @Stephen.. 您的内部子查询是不必要的,只需将 WHERE ERR_SOURCE = .. 添加到围绕它的查询中。同样,如果您想要最新的错误,请按创建日期而不是 id 排序。如果您担心速度,我建议向 err_source 添加索引(如果您使用它,则创建日期)。
    • @Arth 向 err_source 添加索引正是我所做的,它最终将其加速到 0.04 秒。我最终走上了那条路
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-05-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多