【问题标题】:Oracle SQL -- Analytic functions OVER a group?Oracle SQL -- 对一个组的分析函数?
【发布时间】:2012-05-08 16:40:36
【问题描述】:

我的桌子:

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 表达式”。

【问题讨论】:

  • 我们无法从这里看到您的错误消息。
  • 你能显示你期望的输出吗,描述对我来说没有意义......I want to grab the value associated with the max of each number for each ID。我也不确定 ORDER BY 会对 MAX() 函数产生什么影响...
  • 不是这个问题的确切答案,但可以在orafaq 找到有关 oracle 分析函数的良好介绍。这篇文章通过简单的例子帮助理解这些概念。

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


【解决方案1】:

您或许可以使用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 哇,这个答案很好。不知道窗口功能上有 KEEP 功能,或者它可能是 Oracle 专有的,但这是一个优雅的解决方案。这个答案适用于大多数数据库虽然stackoverflow.com/a/10359164Oracle:sqlfiddle.com/#!4/a9a07/19Sql Server:sqlfiddle.com/#!3/a9a07/1Postgresql:sqlfiddle.com/#!1/a9a07/6
  • 我喜欢我不断发现有关 Oracle 的问题,只需要一个查询,而没有解释它在做什么以及它是如何工作的。对于将解决方案应用于其他情况非常有帮助。
【解决方案2】:

当你使用开窗功能时,你不需要再使用 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

【讨论】:

  • ORDER BY num 实现了什么? MAX()1,2,3MAX()2,3,1 相同...对于 MAX(),肯定只有 PARTITION BY 有意义吗?
  • 没错,只有 PARTITION BY 在 MAX 上有意义。我的答案中的第一个代码(MAX OVER ORDER BY)代码只是从 OP 的问题中快速复制粘贴的。窗口函数上的 ORDER BY 仅对运行总计有意义,例如总和
  • 好吧,ROW_NUMBER()、RANK() 等,(因为它们根据位置/顺序返回不同的值) 但不是 MIN()、MAX()、SUM ()?
  • 这就是为什么我说 e.g.,而不是 i.e. :-) 所以顺序对 row_number、rank 等也很重要。 SUM 上的订单也很重要,请参阅此示例:ienablemuch.com/2010/06/running-total.html
  • @Dems 检查这个查询,我刚刚发现了一个在MAX OVER 子句上使用 ORDER BY 的用例,这可能是在ORDER BY 子句中使用ORDER BY 子句有意义的罕见情况之一stackoverflow.com/a/10401572 CTE 禁止在查询末尾放置 ORDER BY sqlfiddle.com/#!6/db290/8 解决方法是将 ORDER BY 放在窗口函数上而不是 sqlfiddle.com/#!6/db290/9
【解决方案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

【讨论】:

  • 快速访问http://sqlfiddle.com 可以验证这不起作用:select id, val from yourTable where num = max(num) over(partition by id)。如果它可以工作就好了,非常简洁
猜你喜欢
  • 1970-01-01
  • 2020-02-25
  • 1970-01-01
  • 2010-12-04
  • 1970-01-01
  • 1970-01-01
  • 2017-10-21
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多