【问题标题】:How Oracle execute a select on a partitioned table with a date range criteriaOracle 如何在具有日期范围条件的分区表上执行选择
【发布时间】:2015-09-17 09:35:47
【问题描述】:

目前我们有一个工作查询,它​​从一个大型分区表中进行选择以进行处理。该表按每日范围进行分区,1 个分区中有 1 天的数据。

查询是:

SELECT /*+parallel(auto)*/ a.*
          FROM TBL_EXCLUDED a
          WHERE (not exists(
                select 1 from C_HISTORY history 
                inner join C_MATRIX_EXT ext on  ext.from_type = (CASE WHEN UPPER(history.TYPE_CD)<>'B' THEN 'C' ELSE 'B' END) and ext.from_channel = history.channel
                where history.mid= a.mid
                and ext.to_channel = a.channel and ext.to_type = (CASE WHEN UPPER(a.TYPE_CD)<>'B' THEN 'C' ELSE 'B' END)
                and history.channel_sent_dt>=TRUNC (SYSDATE-ext.duration+1)
                and history.channel_sent_dt<=TRUNC (SYSDATE)+1
                )   )

解释计划有点长,所以我只会展示带分区的部分。请注意PARTITION_START1PARTITION_STOPKEY

现在,如果我更改从固定日期搜索的条件,而不是从 sysdate 派生,成本将会改变:

SELECT /*+parallel(auto)*/ a.*
          FROM TBL_EXCLUDED a
          WHERE (not exists(
                select 1 from C_HISTORY history 
                inner join C_MATRIX_EXT ext on  ext.from_type = (CASE WHEN UPPER(history.TYPE_CD)<>'B' THEN 'C' ELSE 'B' END) and ext.from_channel = history.channel
                where history.mid= a.mid
                and ext.to_channel = a.channel and ext.to_type = (CASE WHEN UPPER(a.TYPE_CD)<>'B' THEN 'C' ELSE 'B' END)
                and history.channel_sent_dt>=TRUNC ( TO_DATE('10-09-2015','dd-mm-yy')  )
                and history.channel_sent_dt<=TRUNC ( TO_DATE('17-09-2015','dd-mm-yy')  )+1
                )   )

现在的解释显示成本要低得多:

实际执行会与此计划不同吗?另外,虽然在sysdate 的查询中成本很高,但实际执行会少得多,因为它应该只能从目标分区中进行选择?

最后,使用第二种查询范围分区表而不是第一种查询会有实际好处吗?

感谢大家的回复和指导,谢谢。

【问题讨论】:

    标签: oracle partitioning sql-execution-plan


    【解决方案1】:

    解释很简单——Oracle 直到执行时才知道 sysdate 的值是多少,所以它使用“KEY”表示这将在执行时进行评估。在执行时,成本将与输入固定日期相同。

    但是,在第一个查询中,

    history.channel_sent_dt>=TRUNC (SYSDATE-ext.duration+1)
    

    ...优化器推断它无法修剪要扫描的分区的下限,因为它依赖于解析时未知的连接数据——它根据另一个表中的数据而变化.因此,下分区界限设置为 1。

    您可能可以通过使用动态 SQL 来计算分区的实际下限来优化这一点,但如果在实践中下限实际上是 1,那么就没有意义了。

    编辑:如果您知道 ext.duration 的最大值将是 7,那么您可以尝试帮助优化器:

    and history.channel_sent_dt>=TRUNC (SYSDATE-ext.duration+1)
    and history.channel_sent_dt>=TRUNC (SYSDATE-7+1)
    and history.channel_sent_dt<=TRUNC (SYSDATE)+1
    

    有兴趣知道这是否有效。

    【讨论】:

    • 在实际执行中也不应该是 1。查询的目的是选择最后“7”天(7是可变值)记录,因此涉及的分区应该是最新的分区,+后面的7。不需要从一开始就看。
    • 我应该补充一点,这看起来不像我要寻找的表分区修剪的那种查询。如果它依赖于基于索引的访问方法,它可能会优化得更好。
    • 谢谢。将尝试进一步探索并查看如何对其进行优化。在表上创建了一些索引,但到目前为止,在这种情况下它们都没有用..
    【解决方案2】:

    大卫完整回答的两个小点

    1) 如果你的持续时间很短并且“没有偷看”

     select max(duration) from C_MATRIX_EXT ext
    

    你可以概括而不是

     and history.channel_sent_dt>=TRUNC (SYSDATE-7+1)
    

    通过添加

     and history.channel_sent_dt>=TRUNC (SYSDATE- (select max(duration) -1 from C_MATRIX_EXT))
    

    您将再次看到 KEY - KEY 范围,但效果与使用 sysdate 硬限制相同。

    2) 由于内表中的记录数较少(估计为 82 条),您可能会受益于 NESTED LOOP 连接。您将需要通过索引访问外部表(分区表)。优点是只使用正确的分区范围(对当前行有效的范围)。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-03-29
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多