如何密集排名数据集

时间:2015-01-20 22:02:51

标签: sql oracle rank dense-rank

我正在尝试获得密集排名以将数据集合在一起。在我的表中,我有ID,GRP_SET,SUB_SET和INTERVAL,它们只代表一个日期字段。当使用ID插入记录时,它们将被插入为3行的GRP_SET,显示为SUB_SET。正如您所看到的,当插入发生时,间隔可以在完成插入集之前稍微改变。

以下是一些示例数据,DRANK列代表了我想要获得的排名。

with q as (
select 1 id, 'a' GRP_SET, 1 as SUB_SET, 123 as interval, 1 as DRANK from dual union all
select 1, 'a', 2, 123, 1 from dual union all
select 1, 'a', 3, 124, 1 from dual union all
select 1, 'b', 1, 234, 2 from dual union all
select 1, 'b', 2, 235, 2 from dual union all
select 1, 'b', 3, 235, 2 from dual union all
select 1, 'a', 1, 331, 3 from dual union all
select 1, 'a', 2, 331, 3 from dual union all
select 1, 'a', 3, 331, 3 from dual)

select * from q

示例数据

ID GRP_SET SUBSET INTERVAL DRANK
1  a       1      123      1
1  a       2      123      1
1  a       3      124      1
1  b       1      234      2
1  b       3      235      2
1  b       2      235      2
1  a       1      331      3
1  a       2      331      3
1  a       3      331      3

这是我有的查询,但我似乎需要像:

  • 分区依据: ID
  • 在分区内按顺序排序: ID,间隔
  • 在以下时间更改排名: ID,GRP_SET(更改)

select
   id, GRP_SET, SUB_SET, interval,
   DENSE_RANK() over (partition by ID order by id, GRP_SET) as DRANK_TEST
from q
Order by
   id, interval

2 个答案:

答案 0 :(得分:2)

这可能适合你。复杂的因素是,对于区间123124以及区间234235,您需要相同的“DENSE RANK”。因此,为了排序DENSE_RANK()函数,我们会将它们截断到最接近的10:

SELECT id, grp_set, sub_set, interval, drank
     , DENSE_RANK() OVER ( PARTITION BY id ORDER BY TRUNC(interval, -1), grp_set ) AS drank_test
  FROM q

Please see SQL Fiddle demo here.

如果您希望区间更加靠近以便组合在一起,则可以在截断之前将该值乘以。这会将它们分组3s(但也许你不需要它们那么精细):

SELECT id, grp_set, sub_set, interval, drank
     , DENSE_RANK() OVER ( PARTITION BY id ORDER BY TRUNC(interval*10/3, -1), grp_set ) AS drank_test
  FROM q

答案 1 :(得分:2)

使用MODEL子句

看哪,你的要求超出了“普通”SQL中易于表达的限制。但幸运的是,你正在使用具有MODEL子句的Oracle,这个设备的神秘功能仅超过其功能(excellent whitepaper here)。你应该写:

SELECT
   id, grp_set, sub_set, interval, drank
FROM (
  SELECT id, grp_set, sub_set, interval, 1 drank
  FROM q
)
MODEL PARTITION BY (id)
      DIMENSION BY (row_number() OVER (ORDER BY interval, sub_set) rn)
      MEASURES (grp_set, sub_set, interval, drank)
      RULES (
        drank[any] = NVL(drank[cv(rn) - 1] + 
                         DECODE(grp_set[cv(rn) - 1], grp_set[cv(rn)], 0, 1), 1)
      )

Proof on SQLFiddle

说明:

SELECT
   id, grp_set, sub_set, interval, drank
FROM (
  -- Here, we initialise your "dense rank" to 1
  SELECT id, grp_set, sub_set, interval, 1 drank
  FROM q
)

-- Then we partition the data set by ID (that's your requirement)
MODEL PARTITION BY (id)

-- We generate row numbers for all columns ordered by interval and sub_set,
-- such that we can then access row numbers in that particular order
      DIMENSION BY (row_number() OVER (ORDER BY interval, sub_set) rn)

-- These are the columns that we want to generate from the MODEL clause
      MEASURES (grp_set, sub_set, interval, drank)

-- And the rules are simple: Each "dense rank" value is equal to the
-- previous "dense rank" value + 1, if the grp_set value has changed
      RULES (
        drank[any] = NVL(drank[cv(rn) - 1] + 
                         DECODE(grp_set[cv(rn) - 1], grp_set[cv(rn)], 0, 1), 1)
      )

当然,这仅在没有交错事件时有效,即在123和124之间没有其他grp_set而不是a