如何在 Oracle 中分组

How to group-by in Oracle

我有一个table像下面的[Original]。
我想按 [result].
这样的分组字段求和 有没有人有想法进行此查询?
预先感谢您的帮助。

WITH t1 as (
      SELECT 1 AS ID, 'A' AS FIELD, 1 AS VAL FROM dual
UNION SELECT 2 AS ID, 'A' AS FIELD, 2 AS VAL FROM dual
UNION SELECT 3 AS ID, 'A' AS FIELD, 1 AS VAL FROM dual
UNION SELECT 4 AS ID, 'B' AS FIELD, 2 AS VAL FROM dual
UNION SELECT 5 AS ID, 'B' AS FIELD, 2 AS VAL FROM dual
UNION SELECT 6 AS ID, 'B' AS FIELD, 1 AS VAL FROM dual
UNION SELECT 7 AS ID, 'A' AS FIELD, 3 AS VAL FROM dual
UNION SELECT 8 AS ID, 'A' AS FIELD, 2 AS VAL FROM dual
UNION SELECT 9 AS ID, 'A' AS FIELD, 1 AS VAL FROM dual
)
SELECT *
FROM t1




[Original Data]
ID  FIELD   VAL
1   A   1
2   A   2
3   A   1
4   B   2
5   B   2
6   B   1
7   A   3
8   A   2
9   A   1


[Result]
ID  FIELD   VAL
1   A   4
4   B   5
7   A   6

这是岛屿和差距问题,您可以使用如下分析函数:

SQL> WITH t1 as (
  2        SELECT 1 AS ID, 'A' AS FIELD, 1 AS VAL FROM dual
  3  UNION SELECT 2 AS ID, 'A' AS FIELD, 2 AS VAL FROM dual
  4  UNION SELECT 3 AS ID, 'A' AS FIELD, 1 AS VAL FROM dual
  5  UNION SELECT 4 AS ID, 'B' AS FIELD, 2 AS VAL FROM dual
  6  UNION SELECT 5 AS ID, 'B' AS FIELD, 2 AS VAL FROM dual
  7  UNION SELECT 6 AS ID, 'B' AS FIELD, 1 AS VAL FROM dual
  8  UNION SELECT 7 AS ID, 'A' AS FIELD, 3 AS VAL FROM dual
  9  UNION SELECT 8 AS ID, 'A' AS FIELD, 2 AS VAL FROM dual
 10  UNION SELECT 9 AS ID, 'A' AS FIELD, 1 AS VAL FROM dual
 11  )
 12  SELECT MIN(ID) AS ID, FIELD, SUM(VAL)
 13    FROM (SELECT T1.*,
 14     SUM(CASE WHEN LAG_FIELD = FIELD THEN 0 ELSE 1 END)
 15  OVER (ORDER BY ID) AS SM
 16    FROM (SELECT T1.*,
 17     LAG(FIELD) OVER (ORDER BY ID) AS LAG_FIELD
 18    FROM t1
 19  ) T1
 20  )
 21  GROUP BY FIELD, SM
 22  ORDER BY 1;

        ID F   SUM(VAL)
---------- - ----------
         1 A          4
         4 B          5
         7 A          6

SQL>

这确实是一个孤岛问题。我认为这里最简单的方法是使用行号之间的差异来标识相邻行的组:

select min(id) as id, field, sum(val) as val
from (
    select t1.*,
        row_number() over(order by id) rn1,
        row_number() over(partition by field order by id) rn2
    from t1
) t
group by field, rn1 - rn2
order by min(id)

如果 id总是递增无间隙,这个就更简单了:

select min(id) as id, field, sum(val) as val
from (
    select t1.*,
        row_number() over(partition by field order by id) rn
    from t1
) t
group by field, id - rn
order by min(id)

从 Oracle 12 开始,您可以非常简单地使用 MATCH_RECOGNIZE:

WITH t1 as (
      SELECT 1 AS ID, 'A' AS FIELD, 1 AS VAL FROM dual
UNION SELECT 2 AS ID, 'A' AS FIELD, 2 AS VAL FROM dual
UNION SELECT 3 AS ID, 'A' AS FIELD, 1 AS VAL FROM dual
UNION SELECT 4 AS ID, 'B' AS FIELD, 2 AS VAL FROM dual
UNION SELECT 5 AS ID, 'B' AS FIELD, 2 AS VAL FROM dual
UNION SELECT 6 AS ID, 'B' AS FIELD, 1 AS VAL FROM dual
UNION SELECT 7 AS ID, 'A' AS FIELD, 3 AS VAL FROM dual
UNION SELECT 8 AS ID, 'A' AS FIELD, 2 AS VAL FROM dual
UNION SELECT 9 AS ID, 'A' AS FIELD, 1 AS VAL FROM dual
)
SELECT *
FROM   t1
MATCH_RECOGNIZE (
  ORDER BY id
  MEASURES
    FIRST( id ) AS id,
    FIRST( field ) AS field,
    SUM( val ) AS total
  ONE ROW PER MATCH
  PATTERN( same_field+ )
  DEFINE same_field AS FIRST(field) = field 
)

输出:

ID | FIELD | TOTAL
-: | :---- | ----:
 1 | A     |     4
 4 | B     |     5
 7 | A     |     6

db<>fiddle here