【问题标题】:Group rows into range while also showing gap将行分组到范围内,同时也显示间隙
【发布时间】:2020-04-21 03:55:12
【问题描述】:

我需要一个数据库选择查询解决方案,用于将一系列细分细节转换为它的摘要版本,可能在视图中。考虑下表。它有一个复合主键(PK_1PK_2PK_3SEQUENCE_NO)。

PK_1     PK_2 PK_3 SEQUENCE_NO STATUS_CODE
======== ==== ==== =========== ===========
20200421 A    1    1           Y
20200421 A    1    2           Y
20200421 A    1    3           Y
20200421 A    1    4           N
20200421 A    1    5           Y
20200421 A    1    6           Y
20200421 A    2    7           Y
20200421 A    2    8           Y
20200421 B    3    9           Y
20200421 B    3    10          Y
20200421 B    3    11          Y
20200422 B    3    11          Y

仅包括所有带有“Y”的STATUS_CODE 的记录,如何以连续记录的方式呈现记录,分别对应于它们的复合主键,形成值范围(由SEQUENCE_FROM 和@987654328 表示@,见下文)同时显示可能表示缺失行的间隙,或带有 STATUS_CODE 且值不是“Y”的行?

PK_1     PK_2 PK_3 SEQUENCE_FROM SEQUENCE_TO
======== ==== ==== ============= ===========
20200421 A    1    1             3
20200421 A    1    5             6
20200421 A    2    7             8
20200421 B    3    9             11
20200422 B    3    11            11

我使用了MINMAX,但显然它不适合显示范围之间的差距。

【问题讨论】:

  • PK_3 和 PK_2 如何在您的预期结果中得到交换?
  • @zealous 抱歉,这显然是一个错字。
  • 你能解释一下SEQUENCE_FROMSEQUENCE_TO的逻辑吗?
  • 考虑到记录 20200421-A-1,我希望所有带有 PK_1PK_2PK_3 的记录都将自己分组到一行中,在示例中为第 1 行到第 6 行. 排除STATUS_CODE 不等于“Y”的行,即第1-3 行和第5-6 行。我希望这两个范围在结果集上显示为单独的不同行。它并不总是两个范围。例如SEQUENCE_NO的最小值为1,最大值为100,而数字50、70缺失,则应拆分为1-49、51-69、71到100。

标签: sql oracle select


【解决方案1】:

这是一个空白和孤岛问题。这是使用行号差异法解决ROW_NUMBER 的一种方法:

WITH cte AS (
    SELECT t.*, SEQUENCE_NO -
        ROW_NUMBER() OVER (PARTITION BY PK_1, PK_2, PK_3 ORDER BY SEQUENCE_NO) AS diff
    FROM yourTable t
    WHERE STATUS_CODE = 'Y'
)


SELECT
    PK_1,
    PK_2,
    PK_3,
    MIN(SEQUENCE_NO) AS SEQUENCE_FROM,
    MAX(SEQUENCE_NO) AS SEQUENCE_TO
FROM cte
GROUP BY
    PK_1,
    PK_2,
    PK_3,
    (rn1 - rn2)
ORDER BY
    PK_1,
    PK_2,
    PK_3;

Demo

这里使用的逻辑的关键是,我们在每个PK_1PK_2PK_3 分区内为每个岛形成一个动态组号,通过获取序列号之间的差异和ROW_NUMBER。保证每个岛屿的这种差异始终是唯一的。

【讨论】:

    【解决方案2】:

    试试这个,应该会按预期工作。你可以看看fiddle

    select
        PK_1,
        PK_2,
        PK_3,
        min(sequence_no) as SEQUENCE_FROM,
        max(sequence_no) as SEQUENCE_TO
    from
    (
        select
            *,
            sequence_no - row_number() over (partition by STATUS_CODE, PK_3 order by SEQUENCE_NO) as rnk
        from myTable
        where STATUS_CODE = 'Y'
    ) t    
    group by
        PK_1,
        PK_2,
        PK_3,
        rnk
    

    输出:

    pk_1     pk_2 pk_3 sequence_from sequence_to
    ---------------------------------------------
    20200421  A     1       1             3
    20200421  A     1       5             6
    20200421  A     2       7             8
    20200421  B     3       9             11
    20200422  B     3       11            11
    

    【讨论】:

      【解决方案3】:

      这是使用match_recognize 子句(Oracle 12.1 及更高版本)的一种方法。如果我理解正确,当存在一个或多个状态代码为“N”的行时存在“间隙” - 并且仅当这些行之前和之后是 pk_1, pk_2, pk_3 的相同组合的“Y”行时。因此,在您的样本数据中,只有一个这样的差距。您没有准确解释要显示的内容(我假设一个数字,但您没有解释如何计算它)。我将其解释为“当前”序列的起始值与“前一个”序列的结束值之间的差异。

      with
        yourtable (pk_1, pk_2, pk_3, sequence_no, status_code) as (
          select 20200421, 'A', 1,  1, 'Y' from dual union all
          select 20200421, 'A', 1,  2, 'Y' from dual union all
          select 20200421, 'A', 1,  3, 'Y' from dual union all
          select 20200421, 'A', 1,  4, 'N' from dual union all
          select 20200421, 'A', 1,  5, 'Y' from dual union all
          select 20200421, 'A', 1,  6, 'Y' from dual union all
          select 20200421, 'A', 2,  7, 'Y' from dual union all
          select 20200421, 'A', 2,  8, 'Y' from dual union all
          select 20200421, 'B', 3,  9, 'Y' from dual union all
          select 20200421, 'B', 3, 10, 'Y' from dual union all
          select 20200421, 'B', 3, 11, 'Y' from dual union all
          select 20200422, 'B', 3, 11, 'Y' from dual
        )
      select mr.*
           , sequence_from - lag(sequence_to) over (partition by pk_1, pk_2, pk_3 
                                                    order by sequence_from) as gap
      from   yourtable
      match_recognize(
        partition by pk_1, pk_2, pk_3
        order     by sequence_no
        measures  first(sequence_no) as sequence_from
               ,  last (sequence_no) as sequence_to
        pattern   ( Y+ )
        define    Y as status_code = 'Y'
      ) mr
      ;
      

      输出:

            PK_1 PK_2       PK_3 SEQUENCE_FROM SEQUENCE_TO        GAP    
      ---------- ---- ---------- ------------- ----------- ----------
        20200421 A             1             1           3           
        20200421 A             1             5           6          2
        20200421 A             2             7           8           
        20200421 B             3             9          11           
        20200422 B             3            11          11           
      

      【讨论】:

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