基于月份的 Oracle SQL 数据迁移行到列因类型而失败

Posted

技术标签:

【中文标题】基于月份的 Oracle SQL 数据迁移行到列因类型而失败【英文标题】:Oracle SQL data migration row to column based in month fails due to type 【发布时间】:2020-10-08 19:40:40 【问题描述】:
TABLE1

CODE    RATE1   type    MONTH
A       0       Acc1    201906
A       0       Acc1    201907
A       0       Acc1    201908
A       1       Acc1    201909
A       1       Acc1    201910
A       1       Acc1    201911
A       1       Acc1    201912
A       1       Acc1    202001
A       1       Acc1    202002
A       1       Acc1    202003
A       1       Acc1    202004
A       1       Acc1    202005
A       1       Acc1    202006
A       1       Acc1    202007
A       1       Acc1    202008
A       1       Acc1    202009

A       0       Acc2    201906
A       0       Acc2    201907
A       0       Acc2    201908
A       1       Acc2    201909
A       1       Acc2    201910
A       1       Acc2    201911
A       1       Acc2    201912
A       1       Acc2    202001
A       1       Acc2    202002
A       1       Acc2    202003
A       1       Acc2    202004
A       1       Acc2    202005
A       1       Acc2    202006
A       1       Acc2    202007
A       1       Acc2    202008
A       1       Acc2    202009

表2

CODE   RATE2    MONTH
A       10       202001
A       10       202002
A       10       202003
A       10       202004

我正在将数据从旧系统迁移到新系统。作为每月维护的旧系统数据的一部分,如果数据更新并且表包含一个月的一行,则将更新同一行,我正在迁移到新闻系统,它包含创建活动记录的开始日期和结束日期。所以在更新新数据时需要插入和更新旧行结束日期

我有几个表需要加入并根据 rate1 和 rate2 找到开始日期和结束日期。 我的第一个表包含所有月份的数据,第二个表数据可用,直到它在 decativate 上的活动数据将不可用。如果利率可用,我们认为是 0。

我的预期输出

CODE    RATE1   RATE2   Type    START_DT    END_DT
A       0       0       Acc1    201906      201908
A       1       0       Acc1    201909      201912
A       1       10      Acc1    202001      202004
A       1       0       Acc1    202005      202009
A       0       0       Acc2    201906      201908
A       1       0       Acc2    201909      201912
A       1       10      Acc2    202001      202004
A       1       0       Acc2    202005      202009

但我的结果低于 23 行。

    select code1, rate1, rate2, type, min(month) start_dt, 
    case when row_number() over(partition by code1 order by max(month) desc) = 1 then 999912 else max(month) end end_dt
from (
    select t1.month, code1, rate1, rate2, type,
        row_number() over(partition by code1 order by t1.month) rn1,
        row_number() over(partition by code1, type, rate1, rate2, type order by t1.month) rn2
    from table1 t1 left join table2 t2 on t1.code1 = t2.code2 and t1.month = t2.month
) t
group by code1, rate1, rate2, type, rn1 - rn2
order by start_dt

请在此 URL https://dbfiddle.uk/?rdbms=oracle_18&fiddle=b4f77cd13967c1c5a74efcacfb3d3a22 中找到我的查询

提前致谢。

如果您需要更多信息,请发表评论

【问题讨论】:

【参考方案1】:

你已经接近解决这个差距和岛屿的任务了。问题在于列type。需要走两个窗口函数的partition by子句,所以和外层的group by一致。

这会给你想要的结果:

select code1, rate1, coalesce(rate2, 0), type, min(month) start_dt, max(month) end_dt
from (
    select t1.month, t1.code1, t1.rate1, t2.rate2, type,
        row_number() over(partition by t1.code1, t1.type order by t1.month) rn1,
        row_number() over(partition by t1.code1, t1.type, t1.rate1, t2.rate2 order by t1.month) rn2
    from table1 t1 
    left join table2 t2 on t1.code1 = t2.code2 and t1.month = t2.month
) t
group by code1, rate1, rate2, type, rn1 - rn2
order by type, start_dt

注意事项:

我不确定您要在外部查询中使用日期转换实现的逻辑,并且它似乎与预期的结果不一致,因此我将其删除

用它们所属的表来限定子查询中的所有列;这样可以避免歧义

我还修复了外部order by 子句,以便按所需顺序返回结果,并使用coalesce() 默认缺少rate2s 到0

Demo on DB Fiddlde

代码1 |速率1 |合并(RATE2,0) |类型 | START_DT | END_DT :---- | ----: | ----------------: | :--- | --------: | -----: 一个 | 0 | 0 | ACC1 | 201906 | 201908 一个 | 1 | 0 | ACC1 | 201909 | 201912 一个 | 1 | 10 | ACC1 | 202001 | 202004 一个 | 1 | 0 | ACC1 | 202005 | 202009 一个 | 0 | 0 | ACC2 | 201906 | 201908 一个 | 1 | 0 | ACC2 | 201909 | 201912 一个 | 1 | 10 | ACC2 | 202001 | 202004 一个 | 1 | 0 | ACC2 | 202005 | 202009

【讨论】:

【参考方案2】:

您可以将两个表连接起来,然后使用MATCH_RECOGNIZE

SELECT code,
       rate1,
       rate2,
       type,
       first_month,
       last_month
FROM   (
  SELECT t1.code,
         t1.rate1,
         COALESCE( t2.rate2, 0 ) AS rate2,
         t1.type,
         t1.month
  FROM   table1 t1
         LEFT OUTER JOIN table2 t2
         ON ( t1.code = t2.code AND t1.month = t2.month )
)
MATCH_RECOGNIZE (
   PARTITION BY type
   ORDER BY month
   MEASURES
      FIRST( code ) AS code,
      FIRST( rate1 ) AS rate1,
      FIRST( rate2 ) AS rate2,
      FIRST( month ) AS first_month,
      LAST( month ) AS last_month
   ONE ROW PER MATCH
   PATTERN (FIRST_ROW SAME_RATES*)
   DEFINE
      SAME_RATES AS (    SAME_RATES.rate1 = PREV(SAME_RATES.rate1)
                     AND SAME_RATES.rate2 = PREV(SAME_RATES.rate2) )
)
ORDER BY type, first_month;

其中,对于您的示例数据:

CREATE TABLE table1 ( CODE, RATE1, type, MONTH ) AS
SELECT 'A', 0, 'Acc1', 201906 FROM DUAL UNION ALL
SELECT 'A', 0, 'Acc1', 201907 FROM DUAL UNION ALL
SELECT 'A', 0, 'Acc1', 201908 FROM DUAL UNION ALL
SELECT 'A', 1, 'Acc1', 201909 FROM DUAL UNION ALL
SELECT 'A', 1, 'Acc1', 201910 FROM DUAL UNION ALL
SELECT 'A', 1, 'Acc1', 201911 FROM DUAL UNION ALL
SELECT 'A', 1, 'Acc1', 201912 FROM DUAL UNION ALL
SELECT 'A', 1, 'Acc1', 202001 FROM DUAL UNION ALL
SELECT 'A', 1, 'Acc1', 202002 FROM DUAL UNION ALL
SELECT 'A', 1, 'Acc1', 202003 FROM DUAL UNION ALL
SELECT 'A', 1, 'Acc1', 202004 FROM DUAL UNION ALL
SELECT 'A', 1, 'Acc1', 202005 FROM DUAL UNION ALL
SELECT 'A', 1, 'Acc1', 202006 FROM DUAL UNION ALL
SELECT 'A', 1, 'Acc1', 202007 FROM DUAL UNION ALL
SELECT 'A', 1, 'Acc1', 202008 FROM DUAL UNION ALL
SELECT 'A', 1, 'Acc1', 202009 FROM DUAL UNION ALL

SELECT 'A', 0, 'Acc2', 201906 FROM DUAL UNION ALL
SELECT 'A', 0, 'Acc2', 201907 FROM DUAL UNION ALL
SELECT 'A', 0, 'Acc2', 201908 FROM DUAL UNION ALL
SELECT 'A', 1, 'Acc2', 201909 FROM DUAL UNION ALL
SELECT 'A', 1, 'Acc2', 201910 FROM DUAL UNION ALL
SELECT 'A', 1, 'Acc2', 201911 FROM DUAL UNION ALL
SELECT 'A', 1, 'Acc2', 201912 FROM DUAL UNION ALL
SELECT 'A', 1, 'Acc2', 202001 FROM DUAL UNION ALL
SELECT 'A', 1, 'Acc2', 202002 FROM DUAL UNION ALL
SELECT 'A', 1, 'Acc2', 202003 FROM DUAL UNION ALL
SELECT 'A', 1, 'Acc2', 202004 FROM DUAL UNION ALL
SELECT 'A', 1, 'Acc2', 202005 FROM DUAL UNION ALL
SELECT 'A', 1, 'Acc2', 202006 FROM DUAL UNION ALL
SELECT 'A', 1, 'Acc2', 202007 FROM DUAL UNION ALL
SELECT 'A', 1, 'Acc2', 202008 FROM DUAL UNION ALL
SELECT 'A', 1, 'Acc2', 202009 FROM DUAL;

CREATE TABLE table2 ( CODE, RATE2, MONTH ) AS
SELECT 'A', 10, 202001 FROM DUAL UNION ALL
SELECT 'A', 10, 202002 FROM DUAL UNION ALL
SELECT 'A', 10, 202003 FROM DUAL UNION ALL
SELECT 'A', 10, 202004 FROM DUAL;

输出:

代码 |速率1 |速率2 |类型 |第一个月 |上个月 :--- | ----: | ----: | :--- | ----------: | ---------: 一个 | 0 | 0 | ACC1 | 201906 | 201908 一个 | 1 | 0 | ACC1 | 201909 | 201912 一个 | 1 | 10 | ACC1 | 202001 | 202004 一个 | 1 | 0 | ACC1 | 202005 | 202009 一个 | 0 | 0 | ACC2 | 201906 | 201908 一个 | 1 | 0 | ACC2 | 201909 | 201912 一个 | 1 | 10 | ACC2 | 202001 | 202004 一个 | 1 | 0 | ACC2 | 202005 | 202009

db小提琴here

【讨论】:

以上是关于基于月份的 Oracle SQL 数据迁移行到列因类型而失败的主要内容,如果未能解决你的问题,请参考以下文章

Oracle 行到列的转换

SQL查询到行到列[重复]

sql server中的表行到列

使用 PIVOT 函数的行到列 (Oracle)

在Oracle中按组不使用聚合函数或行到列

SQL 查询中的行到列