【问题标题】:SQL Merge/Group lines into a single oneSQL将行合并/组合成一个
【发布时间】:2019-03-15 10:42:58
【问题描述】:

我想知道针对这个问题最优化的 SQL 是什么。 我必须对包含无效数据的表应用 SQL 查询,以更正所有这些无效数据。 该表的结构如下:

TABLE(customer_id, start_date, end_date, type)

目前,该表可以包含给定元组 (customer_id,type) 的多行。 我的查询需要将属于一个组的所有行“合并”为一个行,并保留开始日期的最新日期和结束日期的最旧日期:

Cust1;01/01/2012;01/01/2020;1
Cust1;01/01/2010;01/01/2024;1

应该变成单行

Cust1;01/01/2012;01/01/2024;1

我需要更正数据,不仅要选择它:如果多于 1 行,则删除行并从每个行中检索数据! 我希望我的解释足够清楚! 我使用 Oracle DBMS

谢谢,

【问题讨论】:

    标签: sql oracle merge


    【解决方案1】:

    使用max()函数

        select customer_id,type, max(start_date),max(end_date)
        from t1
        group by customer_id,type
    

    我认为您想使用这些数据创建另一个表

     create table test_t as
     select customer_id,type, max(start_date),max(end_date)
            from t1
            group by customer_id,type 
    

    【讨论】:

    • 感谢您的快速回答,但我需要更正表格中的数据。不仅得到正确的结果!
    • 所以我无法避免复制?还是谢谢
    • @LostReality 你肯定可以避免复制 - 查看替代答案
    【解决方案2】:

    进行聚合:

    select customer_id, max(start_date), max(end_date), type
    from table t
    group by customer_id, type;
    

    【讨论】:

    • 感谢您的快速回答,但我需要更正表格中的数据。不仅得到正确的结果!
    • @LostReality。 . .然后使用此查询将数据复制到其他表中并对旧表执行截断操作。
    • 然后简单地应用“插入选择”?好简单的想法。谢谢
    【解决方案3】:

    如果您只有少量重复行,则使用就地更新/删除的替代方法将是首选。

    所以先检查重复行数

    with clean as (
    select CUSTOMER_ID, TYPE, max(start_date) start_date_clean, max(end_date)  end_date_clean
    from tab group by CUSTOMER_ID, TYPE)
    select tab.*, start_date_clean, end_date_clean
    from tab join clean on tab.CUSTOMER_ID = clean.CUSTOMER_ID and tab.TYPE = clean.TYPE
    where  start_date != start_date_clean or  end_date != end_date_clean
    ;
    

    此查询将返回将要处理的所有行,即开始日期或结束日期不正确。

    如果这个数字很大 - 按照其他答案建议的方式 - 复制表格并用副本替换原始表格。

    如果**数字很小*,走update/delete方式:

    update tab a
    set a.START_DATE = (select max(b.START_DATE) from tab b where a.customer_id = b.customer_id and a.type = b.type),
    a.END_DATE = (select max(b.END_DATE) from tab b where a.customer_id = b.customer_id and a.type = b.type)
    where (a.customer_id, a.type) in 
    ( 
    select tab.CUSTOMER_ID, tab.TYPE 
    from tab join 
    (select CUSTOMER_ID, TYPE, max(start_date) start_date_clean, max(end_date)  end_date_clean
    from tab group by CUSTOMER_ID, TYPE) clean 
    on tab.CUSTOMER_ID = clean.CUSTOMER_ID and tab.TYPE = clean.TYPE
    where  start_date != start_date_clean or  end_date != end_date_clean);
    

    这会将所有受影响行中的 start 和 end 日期更新为正确的值。

    例子

    CUSTOMER_ID START_DATE          END_DATE                  TYPE
    ----------- ------------------- ------------------- ----------
              1 01-01-2013 00:00:00 01-01-2016 00:00:00          1 
              1 01-01-2012 00:00:00 01-01-2018 00:00:00          1 
              1 01-01-2010 00:00:00 01-01-2017 00:00:00          1 
              2 01-01-2010 00:00:00 01-01-2018 00:00:00          1 
              3 01-01-2010 00:00:00 01-01-2018 00:00:00          1
    

    更新为

    CUSTOMER_ID START_DATE          END_DATE                  TYPE
    ----------- ------------------- ------------------- ----------
              1 01-01-2013 00:00:00 01-01-2018 00:00:00          1 
              1 01-01-2013 00:00:00 01-01-2018 00:00:00          1 
              1 01-01-2013 00:00:00 01-01-2018 00:00:00          1 
              2 01-01-2010 00:00:00 01-01-2018 00:00:00          1 
              3 01-01-2010 00:00:00 01-01-2018 00:00:00          1 
    

    在下一步中,必须删除重复的行。这使得下一次删除哪个用户ROW_NUMBER 来识别重复项:

    delete from tab where rowid in 
    (select RID from (
      select rowid rid,
      row_number() over (partition by CUSTOMER_ID, TYPE order by null) rn
      from tab) 
    where rn > 1)
    ;
    

    您所看到的 - 蛮力 复制方法在查询中很简单,但会使表离线一段时间。您需要两倍的空间来执行它,并且需要一些时间。

    update 方法更复杂,但没有维护窗口并且很快就完成了。

    【讨论】:

    • 非常感谢您的详细回答!这对我帮助很大。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-05-27
    • 1970-01-01
    相关资源
    最近更新 更多