【问题标题】:Oracle SQL - Finding the last date effective change between two dates efficientlyOracle SQL - 有效地查找两个日期之间的最后一个有效日期变化
【发布时间】:2017-07-24 09:08:34
【问题描述】:

我经常使用以下 Oracle SQL 查询来查找两个日期之间的最后一个有效更改日期,但效率不高(全表扫描)。 per_all_people_f 的主键是 person_id, effective_start_date, effective_end_date

基本上对于用户名(不存储有效日期更改),我想找到与该用户一起使用的员工的详细信息。然而,员工更改是有效存储日期,因此我需要在 from 和 to date 参数之间找到最后一个有效更改的日期。

是否有适用于两个日期之间的 Oracle 索引?有什么技巧可以用来将现有的主键索引与 from 和 to 日期一起使用?如何编写查询以提高效率?我写的几乎所有查询都会使用这个逻辑。

select fu.user_name, papf.employee_number
from   fnd_user fu
left   outer join
(
   select papf2.person_id,
          max(papf2.effective_start_date) max_effective_start_date
   from   per_all_people_f papf2
   where  papf2.effective_start_date between :P_FROM and :P_TO
   group  by papf2.person_id
)  papf3
on     papf3.person_id = fu.employee_id
left   outer join per_all_people_f      papf
on     papf.person_id = fu.employee_id
and    papf.effective_start_date = papf3.max_effective_start_date

想想看,Oracle 肯定在主键索引中的 effective_start_dateeffective_end_date 上浪费了大量磁盘空间,因为它们唯一会被使用的时候是如果您知道 effective_start_date 的日期.

【问题讨论】:

  • 看不懂,为什么需要LEFT OUTER JOIN,正常的join应该没问题
  • 还有,为什么不能从papf2 获取employee_number,为什么要从papf 获取它
  • 并非所有用户都分配了员工,因此左外连接。选择employee_number作为例子,一个更好的例子是full_name,当某人结婚时它会改变。
  • 左外连接和 from 和 to 参数的效率进一步低下,因为我必须将整个查询包装在子查询中以使 from 和 to 参数工作(不适用于左外连接的可选性)
  • 加入business_group_id,它应该会略微提高性能。

标签: sql oracle indexing oracle-ebs


【解决方案1】:

无需加入per_all_people_f 两次,改为尝试ROW_NUMBER。

select fu.user_name, papf3.employee_number
from   fnd_user fu
left   outer join
(
   select papf2.person_id, papf2.employee_number,
          row_number() -- latest date first
          over (partition by papf2.person_id
                order by effective_start_date desc ) as rn
   from   per_all_people_f papf2
   where  papf2.effective_start_date between :P_FROM and :P_TO
)  papf3
on     papf3.person_id = fu.employee_id
and    papf3.rn = 1

【讨论】:

  • 肯定有帮助,但仍然有全表扫描,并没有真正解决我的日期有效问题。
  • @Superdooperhero:假设大部分工作是查找最新行,effective_start_date 上的索引(或分区)可能会有所帮助(如果它有足够的选择性)。显然,连接列是两个表中的另一个候选。
  • 有人会认为,如果它可以使用 Effective_start_date 索引进行排序并找到最大值,那么它已经使用 person_id、effective_start_date、effective_end_date 的主键索引这样做了。
  • @Superdooperhero:你现有的PK中的第1列是person_id,如何有效地找到第2列的最大值?索引按person_id, effective_start_date, effective_end_date 排序,您认为这对ORDER BY effective_start_date 有用吗?顺便说一句,这不是一个真正的 PK,因为该表看起来像一个慢慢 Changing Dimension 并且 PK 允许重叠的日期范围。
  • 如果我将其更改为 row_number() over (partition by papf2.person_id order by person_id, effective_start_date desc) as rn;但它可能只是缓存。
【解决方案2】:

这是另一种选择:

select
fu.user_name,
papf.employee_number
from
fnd_user fu,
(
select distinct
papf.person_id,
min(papf.employee_number) keep (dense_rank last order by papf.effective_start_date) over (partition by papf.person_id) employee_number 
from
per_all_people_f papf
where
papf.effective_start_date between :p_from and :p_to
) papf
where
fu.employee_id=papf.person_id(+)

性能注意事项:如果您想列出特定日期范围内的所有用户及其相应的可能人员记录更改,则对两个完整数据集进行哈希联接可能是最佳选择。 如果您有大量员工,但其中没有多少员工有应用程序用户,那么/*+ push_pred(papf)*/ 建议的索引访问可能会更好。如果日期范围很小且具有选择性,请在 Effective_start_date 上创建一个索引,让优化器对 fnd_user 和该自定义索引获取的 per_all_people_f 记录进行哈希连接。

判断哪个选项最好,不要看执行时间,开启autotrace,看看哪个选项IO最低。

【讨论】:

    猜你喜欢
    • 2019-06-19
    • 2019-12-23
    • 2018-07-25
    • 1970-01-01
    • 1970-01-01
    • 2020-07-19
    • 2015-08-15
    • 1970-01-01
    相关资源
    最近更新 更多