【问题标题】:Optimizing the Oracle group on queries in very large data of table在非常大的表数据中优化 Oracle 组的查询
【发布时间】:2020-11-13 21:18:36
【问题描述】:

我有一个要优化的查询。这是查询:

SELECT "c"."NETSW_ACQEREF" AS "BANK",
       count("c"."NETSW_ACQEREF") AS "QTY",
       sum("c"."TRAN_AMNT") / 100 AS "AMOUNT",
       count(distinct "c"."TERM_ID") as "terminals"
  FROM "CSCLWH"."CLWH_COMMON_DATA" "c"
 WHERE ("c"."TRAN_DATE" between 20201101 AND 20201111)
   AND ("TRAN_TYPE" IN
       ('00', '01', '10', '12', '19', '20', '26', '29', '50', '51', '52'))
   AND ("RESP_CODE" IN ('0', '00', '000', '400'))
   AND ("MTI" IN ('1100', '1200', '1240', '1400', '1420'))
 GROUP BY "c"."NETSW_ACQEREF"
 ORDER BY "BANK"

这些是成本巨大的解释计划结果:

Cost 5102095 Time 00:03:20

它有我按索引创建的 300 万行日期,但它的用处不大。你能告诉我一个降低成本的方法吗?

【问题讨论】:

  • 为什么要将交易日期与数字(20201101 和 20201111)进行比较?交易日期列是否为 NUMBER 数据类型?这可能是一个重大问题。即使您对该列有索引,如果数据类型错误,索引也不会有太大帮助——即使它会帮助很大(如果数据类型是 DATE)。

标签: sql oracle optimization


【解决方案1】:

聚合操作COUNTSUM 不能优化太多,也没有HAVING 子句,所以你最好的选择可能是添加一个覆盖整个@987654324 的多列索引@子句:

CREATE INDEX idx ON "CSCLWH"."CLWH_COMMON_DATA" (TRAN_DATE, TRAN_TYPE, RESP_CODE, MTI);

如果使用此索引,至少可以让 Oracle 丢弃许多与 where 过滤器不匹配的记录。索引中使用的列的确切顺序取决于每列中数据的基数。通常,您希望将限制较多的列放在最前面,将限制较少的列放在最后。

【讨论】:

  • 谢谢我将被创建,但我想优化这个查询如何优化它?
  • 除了添加上述索引之外,您无能为力。添加索引正在优化查询。
  • 例如,你能给我一些例子吗?我为单列创建了索引实际上它没用
  • can you give me some examples ... 我认为我的回答 一个例子。如果你不能花一些时间至少尝试我的答案,我不知道我还能在这里做什么......
  • 问OP的第一个问题应该是:“表中总共有多少行,有多少行通过WHERE过滤器”。我强烈怀疑很多行都会这样做;如果是这样,那么索引将无济于事。更有可能的是,聚合和排序会占用大部分时间。从表中读取是 O(n)(n = 基数);分组和排序是 O(n log n),而“n”似乎刚好大到足以使分组和排序成为可能的瓶颈。
【解决方案2】:

我可以在您的查询中看到两个潜在的缓慢来源。您可以运行几个测试,看看哪个更糟。有一种简单的方法可以修复其中一个;我不认为你可以对对方做太多。

您不仅在整体查询级别拥有group by 聚合;你也有一个计数(distinct {something})。该计数 distinct 是一个昂贵的嵌套聚合。如果你去掉那里的“distinct”这个词会发生什么?意思是,执行时间如何变化? (当然,这不会给您所需的结果;但它会告诉您“独特”是多么昂贵。)

不幸的是,如果这是最大的瓶颈,那么您将无能为力。

另一个缓慢的原因是查询末尾的 ORDER BY 子句。一点背景知识:基本上有两种排序方式。一种是对您“分组依据”的表达进行排序;另一种是散列它们。在过去,Oracle 使用“排序”分组方式——这很昂贵。作为副作用,即使没有明确的 ORDER BY 子句,结果也是按 GROUP BY 表达式排序的;这就是开发人员养成非常糟糕的习惯的原因。

在某些时候,Oracle“了解到”“散列”分组更快。然而,他们掉进了一个陷阱:当你有 GROUP BY 后跟 ORDER BY 相同的表达式时,Oracle 认为(在大多数情况下是错误的)他们可以通过简单地使用旧的“排序”组来一次性完成这两项来节省时间.当输入结果中有 300 万行可能分 300 组时,这是非常浪费的。最好对 300 万行进行散列分组,然后执行(额外但微不足道的)步骤,即对 300 个输出行进行排序。为什么甲骨文如此愚蠢以至于没有看到这一点,我不知道 - 它就是这样。

不过,这个问题有一个非常简单的解决方案。您可以使用 use_hash_aggregation 提示强制哈希组。 (首先,您可以简单地从查询中删除 ORDER BY 子句,看看这是否是问题所在;如果您没有发现任何改进,那么添加有关哈希聚合的提示将无济于事。)

我不知道我描述的两个问题中哪个更糟。如果它是“排序分组依据”(您唯一可以做某事的),不要指望奇迹。您可能会看到执行时间从 3 分 20 秒下降到 2 分或 2 分 30 秒或诸如此类;不是一个数量级的改进。

【讨论】:

  • 考虑还建议在 TRAN_DATE 上放置索引,因为该表可能涵盖许多日期,而相关查询仅针对其中两个日期。
  • @TimBiegeleisen - 更重要的是,从查询的编写方式来看,“日期”似乎存储为数字。如果是这样,即使有索引,优化器也会得出错误的结论。在任何情况下——即使没有索引,从表中读取所有内容的时间也只会随着基数线性增长,而聚合和排序则比这更糟糕。我将从这些开始,尽管确实解决有关tran_date 的问题可能也很重要。
  • @TimBiegeleisen - 在我看来,查询的目标是 11 天,从 11 月 1 日到 11 月 11 日。索引仍然有可能提供帮助,尤其是在数据类型为 DATE 的情况下。
  • Tran_date 是 varchar2(8) 并且我在查询中使用 group by 所以创建简单的单一(创建索引 IDX1 CLWH_COMMON_DATA(tran_date)) 索引它是无用的 也许其他方法有因此这个查询用于报告每天每个月或在哪几天内,例如 01.11.2020 和 12.11.2020 。我想用子查询优化,但我做不到
  • @Ma'rufJavliyev - 好的,那么 (1) 为什么要将日期与查询中的数字进行比较,而不是字符串? (2) 更重要的是——你能改变现实生活表中的数据类型吗?对于应该是日期的事物,将其作为字符串进行索引是没有用的。 (3) 你有没有尝试我的建议,如果你尝试了,你从中学到了什么?即:在没有最终 ORDER BY 的情况下运行查询。它跑得更快吗?使用 count(terminals) 而不是 count(distinct terminal) 运行查询。它跑得更快吗?
【解决方案3】:

我想知道具有适当索引的两个级别的聚合是否会有所帮助:

SELECT bank, SUM(qty) as qty, SUM(amount) as amount,
       count(*) as terminals
FROM (SELECT "c"."NETSW_ACQEREF" AS bank, "c"."TERM_ID",
             count(*) AS qty,
             sum("c"."TRAN_AMNT") / 100 AS "AMOUNT",
      FROM "CSCLWH"."CLWH_COMMON_DATA" "c"
      WHERE "c"."TRAN_DATE" between 20201101 AND 20201111 AND
            "TRAN_TYPE" IN ('00', '01', '10', '12', '19', '20', '26', '29', '50', '51', '52') AND
            "RESP_CODE" IN ('0', '00', '000', '400') AND
            "MTI" IN ('1100', '1200', '1240', '1400', '1420')
      GROUP BY "c"."NETSW_ACQEREF", "c"."TERM_ID"
     ) c
GROUP BY bank
ORDER BY BANK;

这假定tran_typeresp_codeMTI 都是字符串。如果它们是数字,则将比较更改为使用数字。

那么你需要一个WHERE 子句的索引。目前还不清楚最好的可能性是什么,但像(tran_date, mti, tran_type, resp_code) 这样的东西应该是最有选择性的。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2023-04-05
    • 2013-08-09
    • 2021-11-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多