【问题标题】:Oracle Analytic functions - resetting a windowing clauseOracle 分析函数 - 重置窗口子句
【发布时间】:2013-07-23 08:43:31
【问题描述】:

我有以下数据集。

create table t1 (
  dept number,
  date1 date
);

Table created.

insert into t1 values (100, '01-jan-2013');
insert into t1 values (100, '02-jan-2013');
insert into t1 values (200, '03-jan-2013');
insert into t1 values (100, '04-jan-2013');
commit;

我的目标是创建一个在每次部门更改时都会重置的排名列。我可以用于“partition by”子句的最接近的列是 dept,但这不会给我想要的结果。

SQL> select * from t1;

      DEPT DATE1
---------- ---------
       100 01-JAN-13
       100 02-JAN-13
       200 03-JAN-13
       100 04-JAN-13

select dept,  
       date1,
       rank () Over (partition by dept order by date1) rnk
from t1
order by date1;

      DEPT DATE1            RNK
---------- --------- ----------
       100 01-JAN-13          1
       100 02-JAN-13          2
       200 03-JAN-13          1
       100 04-JAN-13          3

所需的输出如下。最后一个 rnk=1 是因为 Jan-04 记录是更改后的第一个记录。

      DEPT DATE1            RNK
---------- --------- ----------
       100 01-JAN-13          1
       100 02-JAN-13          2
       200 03-JAN-13          1
       100 04-JAN-13          1  <<<----------

任何指针?

【问题讨论】:

  • +1 。 . .您包含代码以实际测试您想知道的内容。和样本结果。我可以投票两次吗?
  • 谢谢戈登。这是我通常对其他问题的抱怨,我最好按照我的说法:)

标签: sql oracle analytic-functions


【解决方案1】:

这有点复杂。而不是使用rank() 或类似的,使用lag() 来查看什么时候发生了变化。然后做flag的累计和。

select dept, date1,
       CASE WHEN StartFlag = 0 THEN 1
            ELSE 1+StartFlag+NVL(lag(StartFlag) over (order by date1),0)
       END as rnk
from (select t1.*,
             (case when dept = lag(dept) over (order by date1)
                   then 1
                   else 0
              end) as StartFlag
      from t1
     ) t1
order by date1;

Here 是 SQLFiddle。

编辑:

这是戈登在编辑我自己的答案。哎呀。最初的查询已经完成了 90%。它确定了数字应该增加的,但没有分配组内的数字。我会用另一个级别的row_number() 来做到这一点,如:

select dept, date1,
       row_number() over (partition by dept, grp order by date1) as rnk
from (select dept, date1, startflag,
             sum(StartFlag) over (partition by dept order by date1) as grp
      from (select t1.*,
                   (case when dept = lag(dept) over (order by date1)
                         then 0
                         else 1
                    end) as StartFlag
            from t1
           ) t1
     ) t1
order by date1;

所以,总体思路如下。首先使用lag() 来确定组的开始位置(即从一个日期到下一个日期发生部门变更的位置)。然后,通过累积和为这些分配一个“组 ID”。这些是要枚举的记录。最后一步是使用row_number() 枚举它们。

【讨论】:

  • 为了使延迟起作用,我在内部查询中按部门删除了分区,我看到了预期的 0 和 1,但 sum(Flag) 没有给出最终结果。如果有帮助,我在这里创建了一个 sql fiddle。 sqlfiddle.com/#!4/0a9fe/7
  • 如果您在结果中添加按日期排序,您会看到排名未按预期计算。
  • 在正确的轨道上,我认为这是需要的:sqlfiddle.com/#!4/fc339/14
  • Jeff - 我们需要 Gordon 发布的最终分区。您发布的小提琴将获得组,但当我们有更多数据时会失败。检查这个。 sqlfiddle.com/#!4/93e5b/1。最后改动的排名不太对。
【解决方案2】:

这可能是 model 子句的情况,但不幸的是,与 Gordon 的查询相比,它在大量行上的表现明显不佳。

select dept, date1, rank from t1
model 
  dimension by ( row_number() over(order by date1) as rn )
  measures( 1 as rank, dept, date1 ) 
  rules ( 
    rank[1] = 1,
    rank[rn > 1] = 
    case dept[cv()] 
      when dept[cv()-1] then rank[cv()-1] + 1 
      else 1
     end
  )

http://www.sqlfiddle.com/#!4/fc339/132

【讨论】:

  • 谢谢你提醒我,我真的需要理解示范条款,它的用法很好。 :)
【解决方案3】:

方法是:

  1. 用“行号”和“已更改”标志标记每一行
  2. 将最终的“rnk”计算为“行号”与对应于“已更改”行的最大先前“行号”之间的差。

这类似于 Gordon 的答案,但使用 CTE 编写,我觉得更容易阅读。

with cte as (
    select dept, date1,
        row_number() over (order by date1) as row,
        case when dept = (lag(dept) over (order by date1)) then 0 else 1 end as changed
    from t1
)
select dept, date1, 
    row - max(case when changed = 1 then row else 1 end) over (order by date1) + 1 as rnk
from cte
order by date1

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2011-07-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-12-16
    • 1970-01-01
    • 2022-11-15
    相关资源
    最近更新 更多