【问题标题】:How to get the complete row from a maximum calculation?如何从最大计算中获得完整的行?
【发布时间】:2012-11-02 21:08:35
【问题描述】:

我再次与GROUP BY 作斗争。我可以处理的基础知识,但就是这样:如何在不破坏我的分组的情况下访问我在group by 中命名的不同列?请注意group by 只是我自己的想法,可能还有其他更好的方法。不过,它必须在 Oracle 中工作。

这是我的例子:

create table xxgroups (
  groupid int not null primary key,
  groupname varchar2(10)
);
insert into xxgroups values(100, 'Group 100');
insert into xxgroups values(200, 'Group 200');

drop table xxdata;
create table xxdata (
  num1 int,
  num2 int,
  state_a int,
  state_b int,
  groupid int,
    foreign key (groupid) references xxgroups(groupid)
);
-- "ranks" are 90, 40, null, 70:
insert into xxdata values(10, 10, 1, 4, 100);
insert into xxdata values(10, 10, 0, 4, 200);
insert into xxdata values(11, 11, 0, 3, 100);
insert into xxdata values(20, 22, 5, 7, 200);

任务是为每个distinct (num1, num2) 创建一个结果行,并打印groupnamestate_astate_b 中计算出的最高“排名”。

请注意,前两行具有相同的nums,因此只应选择较高的排名——groupname 为“Group 200”。

我认为基本的group by 已经很远了。

SELECT xd.num1||xd.num2 nummer, max(ranking.goodness)
FROM xxdata xd
    , xxgroups xg
    ,( select state_a, state_b, r as goodness
       from dual
       model return updated rows
       dimension by (0 state_a, 0 state_b) measures (0 r)
       rules (r[1,4]=90, r[3,7]=80,r[5,7]=70, r[4,7]=60, r[0,7]=50, r[0,4]=40)
       order by goodness desc
      ) ranking
WHERE xd.groupid=xg.groupid
  and ranking.state_a (+) = xd.state_a
  and ranking.state_b (+) = xd.state_b
GROUP BY xd.num1||xd.num2
ORDER BY nummer
;

结果是我需要的 90%:

nummer   ranking
----------------
1010     90
1111      
2022     70

100% 完美

nummer   groupname
-------------------
1010     Group 100
1111     Group 100
2022     Group 200
  • 棘手的部分是,我想要结果中的groupname。而且我不能将它包含在select 中,因为那样我必须 也将它放入group by 中——我确实想要(然后我不会从所有组中选择排名最高的条目)
  • 在我的解决方案中,使用model 表来计算“排名”。我确定还有其他解决方案。关键是,这是一个重要的计算,我不想做两次。
  • 我从其他示例中知道,可以使用第二个查询返回原始行以获取 groupname,但我看不出我怎么能在这里做到这一点, 无需重复我的排名计算。
  • 一个不错的建议是将group by 替换为LIMIT 1/ORDER BY goodness,并将此计算选择用作过滤子选择。但是a)Oracle 中没有LIMIT,我怀疑rownum<=1 会在子选择中执行,并且b)无论如何我都无法绕开它。也许有办法?

【问题讨论】:

    标签: sql oracle join group-by


    【解决方案1】:

    您可以使用FIRST 聚合修饰符选择性地将您的函数应用于组的一部分行——这里是单行 (SQLFiddle demo):

    SELECT xd.num1||xd.num2 nummer, 
           MAX(xg.groupname) KEEP (DENSE_RANK FIRST 
                                   ORDER BY ranking.goodness DESC) grp,
           max(ranking.goodness)
    FROM xxdata xd
        , xxgroups xg
        ,( select state_a, state_b, r as goodness
           from dual
           model return updated rows
           dimension by (0 state_a, 0 state_b) measures (0 r)
           rules (r[1,4]=90, r[3,7]=80,r[5,7]=70, r[4,7]=60, r[0,7]=50, r[0,4]=40)
           order by goodness desc
          ) ranking
    WHERE xd.groupid=xg.groupid
      and ranking.state_a (+) = xd.state_a
      and ranking.state_b (+) = xd.state_b
    GROUP BY xd.num1||xd.num2
    ORDER BY nummer;
    

    您的分析方法也适用,但由于我们已经在此处使用聚合,我们不妨使用FIRST 修饰符一次性获取所有列。

    【讨论】:

    • 有趣。我将查找KEEPFIRST 的文档。我的猜测是否正确,本质上您应用 max() 是在不同列上完成计算操作的地方? IE。 max(xg.groupname) 本身没有意义,因此您告诉 Oracle 计算该最大值“请参考dense_ranking,当您按降序排序时使用第一个结果”?这听起来像是“我想要获得其他列的分组”问题的通用解决方案,对吧?
    • 是的,它允许 Oracle 仅在具有最大 goodness 的行上应用 MAX(groupname) 函数。本质上,如果goodness 是唯一的,它可以让您选择行中具有另一列最大值的任何列。
    • 太棒了。有了this docu,我很容易理解FIRST。这不是说我也可以用over (partition by ...) 替换group by 吗?
    • ...哦,我必须在最外面的选择中添加一个distinct ...,因为dense_rank 可以返回多行且rank=1?还是dense_rank first保证只返回一行?
    • 所有aggregate functions 属于一个家庭。它们与GROUP BY OVER 一起使用(在这种情况下,它们变为analytic functions)。您可以通过添加KEEP FIRST 来进一步修改聚合函数。很简单,不是吗? :) 现在在你的情况下,我不建议使用 BOTH OVERKEEP FIRST
    【解决方案2】:

    哇,我之前搜索过,但现在我找到了this answer,我可以采用它来回答我的问题。这里的 Oracle 解决方案是 overpartition byorder byrow_number()

    select *
    from ( select data.*, row_number()
             over (partition by nummer order by goodness desc) as seqnum
    from (
    SELECT xd.num1, xd.num2 nummer, xg.groupname, ranking.goodness
    FROM xxdata xd
        , xxgroups xg
        ,( select state_a, state_b, r as goodness
           from dual
           model return updated rows
           dimension by (0 state_a, 0 state_b) measures (0 r)
           rules (r[1,4]=90, r[3,7]=80,r[5,7]=70, r[4,7]=60, r[0,7]=50, r[0,4]=40)
          ) ranking
    WHERE xd.groupid=xg.groupid
      and ranking.state_a (+) = xd.state_a
      and ranking.state_b (+) = xd.state_b
    ORDER BY nummer
    ) data )
    where seqnum = 1
    ;
    

    结果是

        10         10 Group 100          90          1
        11         11 Group 100                      1
        20         22 Group 200          70          1
    

    太美了。

    现在我必须尝试了解over 中的select 究竟做了什么......

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-07-07
      • 2011-02-10
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多