【问题标题】:Set date to last day of previous month in Oracle if it's not the last row如果不是最后一行,则在 Oracle 中将日期设置为上个月的最后一天
【发布时间】:2014-04-29 15:05:28
【问题描述】:

SQL Fiddle 上的完整设置:http://sqlfiddle.com/#!4/1fd0e/5

我有一些包含人员 ID、级别和级别日期范围的数据,如下所示:

   PID        LVL START_DATE END_DATE

     1          1 01.01.14   19.03.14 
     1          2 20.03.14   15.08.14 
     1          3 16.08.14   09.10.14 
     1          4 10.10.14   31.12.14 
     2          1 01.01.14   31.12.14 
     3          1 01.01.14   16.01.14 

我需要将开始日期设置为每月的第一天,将结束日期设置为每月的最后一天。最后一天规则仅在不是该人的最后一行数据时适用

到目前为止我所做的:

select
pid, lvl, 
trunc(start_date, 'month') as start_date,
case when lead(pid, 1) over (PARTITION BY pid order by end_date) is not null 
     then last_day(add_months(end_date, -1)) 
     else last_day(end_date) 
     end as end_date
from date_tbl t;

给我想要的输出:

   PID        LVL START_DATE END_DATE

     1          1 01.01.14   28.02.14 
     1          2 01.03.14   31.07.14 
     1          3 01.08.14   30.09.14 
     1          4 01.10.14   31.12.14 
     2          1 01.01.14   31.12.14 
     3          1 01.01.14   31.01.14 

但是:它只适用于我的测试数据。在包含 25k+ 行数据的表上的生产数据(我想说,女巫不是太多数据)它的执行速度非常慢。

谁能给我一个提示,我可以如何提高查询的性能?例如,要在哪些列上设置什么索引...?到目前为止唯一的索引列是 PID 列。

【问题讨论】:

  • “真的很慢”有多慢?什么是完整的查询?由于有 25K 行,我假设它们并非全部都显示在屏幕上。是否有UPDATE 语句,或者是否有谓词来限制特定人的结果?

标签: sql performance oracle lead


【解决方案1】:

实际上,据我了解,如果人员只有一条记录(pid = 3 的情况),您的脚本会产生错误的结果

拜托,你能试试这个吗?

select
  pid, 
  lvl, 
  trunc(start_date, 'month') as start_date,
  last_day(add_months(end_date, case when lvl = max(lvl) over (partition by pid) then 0 else -1 end)) end_date
from date_tbl t;

我猜你需要为列建立索引(pid,lvl desc)

【讨论】:

  • 谢谢回答。 pid 3 记录是正确的:它的有效期只有一个月。记录必须每月有效。
【解决方案2】:

好的,抱歉耽误了您的时间。简而言之:这是我的错。在我的过程中,上面的查询在某些子查询中对另一个表进行了 LEFT JOIN:

with dates as (
  select
  pid, lvl, 
  trunc(start_date, 'month') as start_date,
  case when lead(pid, 1) over (PARTITION BY pid order by end_date) is not null 
       then last_day(add_months(end_date, -1)) 
       else last_day(end_date) 
       end as end_date
  from date_tbl t
),
  some_other_table as (
  select pid, (...some more columns)
  from other_table
)
select * from (
  select 
    b.pid,  -- <== this has to be a.pid. b is much bigger than a!
    a.start_date, 
    a.end_date
  from dates a left join some_other_table b on a.pid = b.pid
)

整个查询要大得多。

@jonearles 谢谢你的评论。 “完整的查询是什么?”帮助我重回正轨:将查询分成几部分,然后再次检查真正减慢它的速度。

【讨论】:

    猜你喜欢
    • 2013-10-18
    • 1970-01-01
    • 2018-03-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-01-31
    • 1970-01-01
    相关资源
    最近更新 更多