Oracle SQL - 分析功能超过一个组?

时间:2012-04-27 23:02:47

标签: sql oracle greatest-n-per-group

我的表:

ID   NUM   VAL
1    1     Hello
1    2     Goodbye
2    2     Hey
2    4     What's up?
3    5     See you

如果我想返回每个ID的最大数量,那就非常干净了:

SELECT MAX(NUM) FROM table GROUP BY (ID)

但是如果我想获取与每个ID的每个数字的最大值相关联的值呢?

为什么我不能这样做:

SELECT MAX(NUM) OVER (ORDER BY NUM) FROM table GROUP BY (ID) 

为什么会出错?我想让这个选择按ID分组,而不是为每个窗口单独分区......

编辑:错误是“不是GROUP BY表达式”。

3 个答案:

答案 0 :(得分:14)

您可以使用MAX() KEEP(DENSE_RANK LAST...)功能:

with sample_data as (
  select 1 id, 1 num, 'Hello' val from dual union all
  select 1 id, 2 num, 'Goodbye' val from dual union all
  select 2 id, 2 num, 'Hey' val from dual union all
  select 2 id, 4 num, 'What''s up?' val from dual union all
  select 3 id, 5 num, 'See you' val from dual)
select id, max(num), max(val) keep (dense_rank last order by num)
from sample_data
group by id;

答案 1 :(得分:4)

使用窗口函数时,不再需要使用GROUP BY,这就足够了:

select id, 
     max(num) over(partition by id) 
from x 

实际上你可以在不使用窗口函数的情况下得到结果:

select * 
from x
where (id,num) in
  (
     select id, max(num) 
     from x 
     group by id
  )

输出:

ID  NUM VAL
1   2   Goodbye
2   4   What's up
3   5   SEE YOU

http://www.sqlfiddle.com/#!4/a9a07/7


如果您想使用窗口功能,可以这样做:

select id, val, 
     case when num =  max(num) over(partition by id) then
        1
     else
        0
     end as to_select
from x 
where to_select = 1

或者这个:

select id, val 
from x 
where num =  max(num) over(partition by id) 

但由于不允许这样做,你必须这样做:

with list as
(
  select id, val, 
     case when num =  max(num) over(partition by id) then
        1
     else
        0
     end as to_select
  from x
)
select * 
from list 
where to_select = 1

http://www.sqlfiddle.com/#!4/a9a07/19

答案 2 :(得分:3)

如果您希望获得包含MAX(num) GROUP BY id值的 ,这往往是一种常见模式......

WITH
  sequenced_data
AS
(
  SELECT
    ROW_NUMBER() OVER (PARTITION BY id ORDER BY num DESC) AS sequence_id,
    *
  FROM
    yourTable
)
SELECT
  *
FROM
  sequenced_data
WHERE
  sequence_id = 1


修改

我不知道TeraData是否允许这样做,但逻辑似乎有意义......

SELECT
  *
FROM
  yourTable
WHERE
  num = MAX(num) OVER (PARTITION BY id)

或者也许......

SELECT
  *
FROM
(
  SELECT
    *,
    MAX(num) OVER (PARTITION BY id) AS max_num_by_id
  FROM
    yourTable
)
  AS sub_query
WHERE
  num = max_num_by_id 

这与我之前的回答略有不同;如果多个记录与同一个MAX(num)绑定,则会返回所有记录,另一个答案将只返回一个。


修改

在您提议的SQL中,错误与OVER()子句包含不在GROUP BY中的字段这一事实有关。这就像试图这样做......

SELECT id, num FROM yourTable GROUP BY id

num无效,因为对于返回的每一行,该字段中可能有多个值(返回的行由GROUP BY id定义)

以同样的方式,您不能将num放在OVER()子句中。

SELECT

  id,

  MAX(num),                <-- Valid as it is an aggregate

  MAX(num)                 <-- still valid
  OVER(PARTITION BY id),   <-- Also valid, as id is in the GROUP BY

  MAX(num)                 <-- still valid
  OVER(PARTITION BY num)   <-- Not valid, as num is not in the GROUP BY

FROM
  yourTable
GROUP BY
  id


如果您无法在OVER()子句中指定内容,并且(我认为)何时可以显示答案,请参阅此问题:over-partition-by-question