【问题标题】:An extreme multidimension Oracle Group By Select一个极端的多维 Oracle Group By Select
【发布时间】:2016-03-18 18:28:26
【问题描述】:

一个我想不通的难题

在 Oracle 表(名为 tbs_test)中提供这些数据。第一行是列名

A  |B| C
100|6|1000
100|6|1001
100|6|1002
100|7|1003 **
200|6|2000
200|6|2001
300|7|3000
300|7|3001
400|6|4000 **
400|7|4001
400|7|4002

通过 Oracle SQL 选择,我想检索标有 ** 的两条记录。

规则是:

  1. 我对 A=100 和 A=400 感兴趣,因为同一个 A 中有两个不同的 B

    A=100 有 B=6 和 B=7

    A=400 有 B=6 和 B=7

  2. 我对 C=1003 感兴趣,因为在 A=100 中 B=7(一)比 B=6(三)少

    而且我对 C0 4000 感兴趣,因为 A=100 中 B=6(一)比 B=7(二)少

伙计,我很困扰......谁能看到解决方案?

干杯 托斯滕

【问题讨论】:

  • 如果您解释总体目标更好..

标签: sql oracle group-by


【解决方案1】:

您可以使用聚合和分析函数来做到这一点:

select a, b, c
from (select a, b, max(c) as c,
             row_number() over (partition by a order by count(*)) as seqnum,
             count(*) over (partition by a) as numBs
      from tbs_test  t
      group by a, b
     ) ab
where seqnum = 1 and numBs > 1;

一个问题是每个“A”只返回一行。当B 上有多个匹配项最少的匹配项时,不清楚该怎么办。

如果您想要所有C 值,最简单的方法是使用listagg(C, ',') within group (order by C) 而不是max(C)

【讨论】:

  • 非常优雅。对于分析师新手来说,您对 select-construction 的建议似乎是最直观的。感谢 Gordon Linoff、FyzzyTree 和 MT0
【解决方案2】:
select * from (
    select *, 
        rank() over (partition by a order by cnt) rn
    from (
        select * , 
            count(*) over (partition by a,b) cnt
        from mytable t1
        where exists (
            select 1 from mytable t2
            where t2.a = t1.a
            and t2.b <> t1.b
        )
    ) t
) t where rn = 1
  • where exists ... 检索具有多个不同 B 值的行 每个A

  • count(*) over ... 计算每个 (a,b) 对的行数

  • rank() ... 按 count() 对行进行排名

  • where rn = 1 选择计数最少的行

【讨论】:

  • 它几乎成功了 :-) 我不得不做一个小调整:在两个“”前面加上表格别名(“t”和“t1”)。如果没有这些别名,Oracle 声称该语句缺少“来自”。奇怪 :-)
    ´select * from (select t.
    , rank() over (partition by a order by cnt) rn from (select t1.* , count(*) over (partition by a, b) cnt from tbs_test t1 where exists (select 1 from tbs_test t2 where t2.a = t1.a and t2.b t1.b) t ) t where rn = 1;´
【解决方案3】:

Oracle 设置

CREATE TABLE tbs_test (A, B, C )AS
SELECT 100,6,1000 FROM DUAL UNION ALL
SELECT 100,6,1001 FROM DUAL UNION ALL
SELECT 100,6,1002 FROM DUAL UNION ALL
SELECT 100,7,1003 FROM DUAL UNION ALL
SELECT 200,6,2000 FROM DUAL UNION ALL
SELECT 200,6,2001 FROM DUAL UNION ALL
SELECT 300,7,3000 FROM DUAL UNION ALL
SELECT 300,7,3001 FROM DUAL UNION ALL
SELECT 400,6,4000 FROM DUAL UNION ALL
SELECT 400,7,4001 FROM DUAL UNION ALL
SELECT 400,7,4002 FROM DUAL;

查询

SELECT A,B,C
FROM   (
  SELECT t.*,
         COUNT( CASE B WHEN 6 THEN 1 END ) OVER ( PARTITION BY A ) AS count6,
         COUNT( CASE B WHEN 7 THEN 1 END ) OVER ( PARTITION BY A ) AS count7
  FROM   tbs_test t
)
WHERE ( B = 6 AND count6 < count7 )
OR    ( B = 7 AND count7 < count6 );

输出

         A          B          C
---------- ---------- ----------
       100          7       1003 
       400          6       4000 

【讨论】:

  • 完美。谢谢。要了解 count-over-partition,您可以点击此链接。帮了我很多:orafaq.com/node/55
【解决方案4】:

我扩展了任务。除了定位“错误”B,我还想为 A 组检索正确的 B 值 select a, b, c, n+p as the_other from (select a, b, max(c) as c, row_number() over (partition by a order by count(*)) as seqnum, count(*) over (partition by a) as numbs ,lead(b, 1, 0) over (partition by a order by b desc nulls last) as n ,lag(b, 1, 0) over (partition by a order by b desc nulls last) as p from tbs_test t group by a, b ) ab where seqnum = 1 and numbs > 1;

领先和落后可以解决问题。伙计,很高兴您向我介绍了 Oracle 分析

【讨论】:

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