【问题标题】:how to group data based on its sequence and group by other columns如何根据数据的顺序对数据进行分组并按其他列分组
【发布时间】:2014-11-08 15:48:00
【问题描述】:

我在 Oracle 中有一个包含 3 列 c1、c2、c3 的表,如下所示:

c1  c2  c3
 1  34   2
 2  34   2
 3  34   2
 4  24   2
 5  24   2
 6  34   2
 7  34   2
 8  34   1

我需要对col1 进行分组,并根据其序列col2col3 获取最小和最大数量(col1)。

即,我需要如下结果:

c1_min  c1_max  c2  c3
     1       3  34   2
     4       5  24   2
     6       7  34   2
     8       8  34   1

【问题讨论】:

    标签: sql oracle gaps-and-islands


    【解决方案1】:

    有多种方法可以联系gaps-and-islands problem。作为 Sylvain 的 lag 版本的替代方案 - 不是更好,只是不同 - 您可以使用基于分组字段分析计算的行号的技巧。这会在表值中添加一个“链”伪列,这对于 c2/c3 对的每个连续组都是唯一的:

    select c1, c2, c3,
      dense_rank() over (partition by c2, c3 order by c1)
        - dense_rank() over (partition by null order by c1) as chain
    from t42
    order by c1, c2, c3;
    

    (我不能为此归功于 - 我第一次看到它here)。然后,您可以将其用作内联视图来计算总和:

    select min(c1) as c1_min, max(c1) as c1_max, c2, c3
    from (
      select c1, c2, c3,
        dense_rank() over (partition by c2, c3 order by c1)
          - dense_rank() over (partition by null order by c1) as chain
      from t42
    )
    group by c2, c3, chain
    order by c1_min;
    
        C1_MIN     C1_MAX         C2         C3
    ---------- ---------- ---------- ----------
             1          3         34          2 
             4          5         24          2 
             6          7         34          2 
             8          8         34          1 
    

    SQL Fiddle也显示了中间阶段。

    您可以使用其他分析函数,例如row_number(),而不是dense_rank();对于某些数据,它们可能会给出略有不同的结果,但您会得到 same result with this sample

    【讨论】:

    • 这个答案解决了我对 SQL Server 2008 R2 的类似问题,它不支持 LAG 函数。
    【解决方案2】:

    如果我理解得很好,您希望将 连续 行组合在一起。这远非微不足道。或者至少,我现在找不到简单 的方法。为了便于理解,我将把查询分成几个步骤:

    第 1 步:

    首先要确定您的“群体”界限。使用LAG 分析函数可能会对您有所帮助:

    CASE WHEN LAG("c2", 1) OVER(ORDER BY "c1") = "c2" 
          AND LAG("c3", 1) OVER(ORDER BY "c1") = "c3" 
         THEN 0 
         ELSE 1
    END CLK,
    T.* FROM T
    ORDER BY "c1"
    

    第 2 步:

    第二步是给每个组编号。一个简单的SUM over partition 就可以解决问题。这导致:

    SELECT SUM(CLK) OVER (ORDER BY "c1"
                          ROWS BETWEEN UNBOUNDED PRECEDING 
                          AND CURRENT ROW) GRP,
           V.* 
    FROM (
      SELECT
        CASE WHEN LAG("c2", 1) OVER(ORDER BY "c1") = "c2" 
              AND LAG("c3", 1) OVER(ORDER BY "c1") = "c3" 
             THEN 0 
             ELSE 1
        END CLK,
        T.* FROM T
    ) V
    ORDER BY "c1";
    

    最后一步:

    最后,您可以将其包装在一个简单的GROUP BY 查询中以获得所需的输出:

    SELECT MIN("c1"), MAX("c1"), "c2", "c3" FROM
    (
        SELECT SUM(CLK) OVER (ORDER BY "c1"
                              ROWS BETWEEN UNBOUNDED PRECEDING 
                              AND CURRENT ROW) GRP,
               V.* 
        FROM (
          SELECT
            CASE WHEN LAG("c2", 1) OVER(ORDER BY "c1") = "c2"
                  AND LAG("c3", 1) OVER(ORDER BY "c1") = "c3"
                 THEN 0 
                 ELSE 1
            END CLK,
            T.* FROM T
        ) V
    )
    GROUP BY GRP, "c2", "c3"
    ORDER BY GRP
    

    http://sqlfiddle.com/#!4/7d57c/10

    【讨论】:

    • 当我运行上述查询时,我正在使用 toad 运行查询。它给了我一个错误,说 ORA-00904: "c3": invalid identifier'
    • @Mars - Sylvain 引用了您的标识符,我猜是因为您在查询结果中将它们显示为小写。如果它们没有真正被引用,那么只需从查询中删除所有双引号。
    猜你喜欢
    • 1970-01-01
    • 2021-11-25
    • 1970-01-01
    • 1970-01-01
    • 2020-08-05
    • 1970-01-01
    • 2015-05-15
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多