【问题标题】:poorly performing query on order lines table对订单行表的查询性能不佳
【发布时间】:2020-04-12 07:21:59
【问题描述】:

我在订单行表上有这个查询。它是一张相当大的桌子。我正在尝试获取过去 365 天内按项目发货的数量。查询有效,但返回结果非常慢。我应该为此使用基于函数的索引吗?我读了一些关于他们的文章,但根本没有和他们一起工作。

我怎样才能使这个查询更快?

select OOL.INVENTORY_ITEM_ID
    ,SUM(nvl(OOL.shipped_QUANTITY,0)) shipped_QUANTITY_Last_365
from oe_order_lines_all OOL
where ool.actual_shipment_date>=trunc(sysdate)-365
    and cancelled_flag='N'
    and fulfilled_flag='Y'
group by ool.inventory_item_id;

解释计划:

统计数据是最新的,我们每周重聚一次。

查询需要 30 多分钟才能完成。

更新

添加此索引后:

解释计划显示查询现在正在使用索引:

查询运行得更快,但不是“快”。大约 6 分钟后完成。

更新2

我按照 Matthew 和 Gordon 的建议创建了一个覆盖索引:

查询现在在不到 1 秒的时间内完成。

解释计划:

我仍然想知道为什么或是否基于函数的索引也是一个可行的解决方案,但我现在没有时间玩它。

【问题讨论】:

  • 解释计划是什么?有哪些可用的索引?你的统计数据是最新的吗?
  • 请了解here如何以文本形式发布完整的执行计划

标签: sql oracle indexing


【解决方案1】:

对于这个查询:

select OOL.INVENTORY_ITEM_ID,
       SUM(OOL.shipped_QUANTITY) as shipped_QUANTITY_Last_365
from oe_order_lines_all OOL
where ool.actual_shipment_date >= trunc(sysdate) - 365 and
      cancelled_flag = 'N' and
      fulfilled_flag = 'Y'
group by ool.inventory_item_id;

我建议从oe_order_lines_all(cancelled_flag, fulfilled_flag, actual_shipment_date) 上的索引开始。这应该可以很好地识别行。

您也可以将额外的列 inventory_item_idquantity_shipped 添加到索引中。

【讨论】:

  • 谢谢。我正在尝试……创建索引需要很长时间。
【解决方案2】:

通常,使用访问表中“显着”百分比的行的索引比全表扫描要慢。根据您的系统,“显着”可能低至 5% 或 10%。

所以,考虑一下您的数据...

  • OE_ORDER_LINES_ALL 中有多少行被取消? (希望不是很多...)
  • 完成了多少行? (希望几乎所有人...)
  • 去年出货了多少行? (除非您的表中有超过 10 年的历史,否则其中超过 10%...)

将所有这些放在一起,您的查询可能必须读取表中至少 10% 的行。这非常接近索引将比全表扫描差的阈值(或者,至少不会比全表扫描好多少)。

现在,如果您需要大量运行此查询,您有几个选择。

  1. 物化视图,可能是前 11 个月的数据,以及当前当月至今对 OE_ORDER_LINES_ALL 的实时查询。
  2. 覆盖索引(见下文)。

您可以提高索引的性能,即使是访问大部分表行的索引,也可以通过使其包含查询所需的所有信息——让 Oracle 完全避免访问表。

CREATE INDEX idx1 ON OE_ORDER_LINES_ALL
  ( actual_shipment_date,
    cancelled_flag,
    fulfilled_flag,
    inventory_item_id,
    shipped_quantity ) ONLINE;

有了这样的索引,Oracle 只需读取索引就可以满足查询(速度比表小很多)。

【讨论】:

  • 为什么不推荐基于函数的索引?
  • @alexherm 因为您没有任何WHERE 子句条件可以与将OE_ORDER_LINES 列作为输入的函数进行比较。
【解决方案3】:

让我们回顾一下事实:

a) 您从表中访问了大约 300K 行(请参阅执行计划第 3 行中的基数)

b) 你使用FULL TABLE SCAN 获取数据

c) 查询很慢

第一件事是检查为什么FULL TABLE SCAN非常慢 - 如果表非常大(检查user_segments 中的BYTES)您需要优化对数据的访问。

但请记住没有索引可以帮助您从 30M 总行中获得 300K 行

如果索引没有太多使用并且大部分在磁盘上,对 300K 行的索引访问可能需要 1/4 小时甚至更多时间。

您需要的是分区 - 在您的情况下,actual_shipment_date 上的 范围分区 - 每月或每年的数据大小。

这将消除扫描旧数据的需要(分区修剪)并使查询更加有效。

其他可能性 - 如果行数很少,但表非常大 - 您需要重新组织表以获得更好的全扫描时间。

【讨论】:

  • 这是 Oracle EBS 的标准表,因此不能重新组织表。我也不确定范围分区,我感觉安装补丁时可能会破坏。
  • 表有大约 1000 万条记录。使用覆盖索引,查询在不到 1 秒的时间内完成。
  • @alexherm 请注意乐观的时间(
  • 我通过打开一个新会话并运行查询来检查这一点。它也在不到 1 秒的时间内完成。我想更好的测试将是在下一次收集统计数据发生之后。
  • 我将索引移入生产环境并在那里运行查询,但几天内根本没有运行。同样在不到 1 秒的时间内完成。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2016-09-13
  • 1970-01-01
  • 1970-01-01
  • 2013-09-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多