【问题标题】:How to delete Duplicate records in snowflake database table如何删除雪花数据库表中的重复记录
【发布时间】:2019-10-06 17:16:02
【问题描述】:

如何从雪花表中删除重复记录。谢谢

ID Name
1  Apple
1  Apple
2  Apple
3  Orange
3  Orange

结果应该是:

ID Name
1  Apple
2  Apple
3  Orange

【问题讨论】:

    标签: snowflake-cloud-data-platform


    【解决方案1】:

    在此处添加一个不重新创建表的解决方案。这是因为重新创建表可能会破坏许多现有配置和历史记录。

    相反,我们将只删除重复的行并在事务中插入每行的一个副本:

    
    -- find all duplicates
    create or replace transient table duplicate_holder as (
        select $1, $2, $3
        from some_table
        group by 1,2,3
        having count(*)>1
    );
    
    -- time to use a transaction to insert and delete
    begin transaction;
    
    -- delete duplicates
    delete from some_table a
    using duplicate_holder b
    where (a.$1,a.$2,a.$3)=(b.$1,b.$2,b.$3);
    
    -- insert single copy
    insert into some_table
    select * 
    from duplicate_holder;
    
    -- we are done
    commit;
    

    优点:

    • 不重新创建表
    • 不修改原始表
    • 仅删除和插入重复的行(有利于时间旅行存储成本,避免不必要的重新集群)
    • 全部在一个事务中

    【讨论】:

    • 谢谢你 - 我正在使用一个变体,create duplicate_holder as ( select * ... qualify (row_number() over (partition by some_key)) = 2 。优点是可以编写一个 select * 这对于宽表很方便。
    • 我喜欢这个解决方案,但它缺少使用特定 PK 字段作为参考来查找重复项的能力。我已经修改了解决方案来处理它。 Check it out!
    【解决方案2】:

    Snowflake 没有有效的主键,它们主要用于 ERD 工具。 Snowflake 也没有类似 ROWID 的东西,因此无法识别要删除的重复项。

    可以临时添加“is_duplicate”列,例如。使用 ROW_NUMBER() 函数对所有重复项进行编号,然后删除“is_duplicate”> 1 的所有记录,最后删除实用程序列。

    另一种方法是创建一个重复的表并交换,正如其他人所建议的那样。 但是,必须保留约束和授权。一种方法是:

    CREATE TABLE new_table LIKE old_table COPY GRANTS;
    INSERT INTO new_table SELECT DISTINCT * FROM old_table;
    ALTER TABLE old_table SWAP WITH new_table;
    

    上面的代码删除了 exact 个重复项。如果您想为每个“PK”结束一行,您需要包含逻辑以选择要保留的哪个副本。

    这说明了在雪花数据仓库中添加更新时间戳列的重要性。

    【讨论】:

    • 根据我的经验,重复删除大多是手动完成的,因此交换表然后设置权限是最简单的。
    • 如果您想防止重复,请使用 merge() 而不是 insert(),这将强制更新现有键而不是添加重复记录。
    【解决方案3】:

    如果你有这样的主键:

    CREATE TABLE fruit (key number, id number, name text);
    
    insert into fruit values (1,1, 'Apple'), (2,1,'Apple'),
          (3,2, 'Apple'), (4,3, 'Orange'), (5,3, 'Orange');
    

    那时

    DELETE FROM fruit
    WHERE key in (
      SELECT key 
      FROM (
          SELECT key
              ,ROW_NUMBER() OVER (PARTITION BY id, name ORDER BY key) AS rn
          FROM fruit
      )
      WHERE rn > 1
    );
    

    但是,如果您没有唯一键,则无法以这种方式删除。在哪一点

    CREATE TABLE new_table_name AS
    SELECT id, name FROM (
        SELECT id
            ,name
            ,ROW_NUMBER() OVER (PARTITION BY id, name) AS rn
        FROM table_name
    )
    WHERE rn > 1
    

    然后swap他们

    ALTER TABLE table_name SWAP WITH new_table_name
    

    【讨论】:

    • 结合 WITH ... AS 和 DELETE 对我来说会抛出错误,SQL compilation error: syntax error line 10 at position 0 unexpected 'DELETE'.。我认为你只能使用SELECT,见docs.snowflake.net/manuals/sql-reference/constructs/…
    • 很公平,我没有测试过它,但鉴于 CTE 并不常见(多次使用),它可以通过 WHERE 键 IN (SELECT. ..) 形式
    • 非常正确,替换为子选择。
    • 为什么ALLOW_DUPLICATE 仅适用于 JSON 文件格式而不适用于所有其他文件格式? @SimeonPilgrim
    • @Vishrant 看来评论是一个不相关的问题,也许最适合新问题??
    【解决方案4】:

    这也困扰了我一段时间。由于雪花增加了对限定的支持,您现在可以使用没有子选择的单个语句创建去重表:

    CREATE TABLE fruit (id number, nam text);
    insert into fruit values (1, 'Apple'), (1,'Apple'),
          (2, 'Apple'), (3, 'Orange'), (3, 'Orange');
    
    
    CREATE OR REPLACE TABLE fruit AS 
    SELECT * FROM 
    fruit 
    qualify row_number() OVER (PARTITION BY id, nam ORDER BY id, nam) = 1;
    SELECT * FROM fruit;
    

    当然,您会留下一个新表和松散的表历史记录、主键、外键等。

    【讨论】:

      【解决方案5】:

      基于上述想法.....以下查询在我的情况下完美运行。

      CREATE OR REPLACE TABLE SCHEMA.table
       AS
      SELECT
          DISTINCT * 
      FROM
          SCHEMA.table
        ;
      

      【讨论】:

        【解决方案6】:

        这是一个非常简单的方法,不需要任何临时表。它非常适合小表,但可能不是大表的最佳方法。

        insert overwrite into some_table
        select distinct * from some_table
        ;
        

        OVERWRITE 关键字表示在插入发生之前该表将被截断。

        【讨论】:

          【解决方案7】:

          您的问题归结为:How can I delete one of two perfectly identical rows?。你不能。你只能做一个DELETE FROM fruit where ID = 1 and Name = 'Apple';,然后两行都会消失。或者你不这样做,并保留两者。

          对于某些数据库,有使用内部行的解决方法,但雪花中没有,请参阅https://support.snowflake.net/s/question/0D50Z00008FQyGqSAL/is-there-an-internalmetadata-unique-rowid-in-snowflake-that-i-can-reference。你也不能限制删除,所以你唯一的选择是创建一个新表并交换。


          关于 Hans Henrik Eriksen 关于更新时间戳重要性的评论的附加说明:这对于稍后添加重复项时提供了真正的帮助。例如,如果您想保留较新的值,则可以这样做:

          -- setup
          create table fruit (ID Integer, Name VARCHAR(16777216), "UPDATED_AT" TIMESTAMP_NTZ);
          insert into fruit values (1, 'Apple', CURRENT_TIMESTAMP::timestamp_ntz)
          , (2, 'Apple', CURRENT_TIMESTAMP::timestamp_ntz)
          , (3, 'Orange', CURRENT_TIMESTAMP::timestamp_ntz);
          -- wait > 1 nanosecond
          insert into fruit values (1, 'Apple', CURRENT_TIMESTAMP::timestamp_ntz)
          , (3, 'Orange', CURRENT_TIMESTAMP::timestamp_ntz);
          
          -- delete older duplicates (DESC)
          DELETE FROM fruit
            WHERE (ID
            , UPDATED_AT) IN (
               SELECT ID
               , UPDATED_AT
               FROM (
                   SELECT ID
                   , UPDATED_AT
                   , ROW_NUMBER() OVER (PARTITION BY ID ORDER BY UPDATED_AT DESC) AS rn
                   FROM fruit
               )
               WHERE rn > 1
            );
          

          【讨论】:

          • 如果行相同,那么为什么不使用相同的技巧,而是仅在 ID 字段上进行分区和排序。确切删除哪一行可能是不确定的,但它们是相同的,所以没关系
          • 另一个为什么要考虑DELETEDELETE 只是重写表,因此CREATE OR REPLACE TABLE AS SELECT DISTINCT 在雪花中同样有效,因为它读取所有行并写入所有行,除了只有一小部分数据受到影响的边缘情况外,删除会被修剪(假设有一些约束)
          【解决方案8】:

          如果您将一列或几列作为表的主键引用,则以下解决方案非常有效。

          -- Create a temp table to hold our duplicates (only second occurrence)
          CREATE OR REPLACE TRANSIENT TABLE temp_table AS (
            SELECT [col1], [col2], .. [coln]
            FROM (
              SELECT *, ROW_NUMBER () OVER(
                PARTITION BY [pk]1, [pk]2, .. [pk]m
                ORDER BY [pk]1, [pk]2, .. [pk]m) AS duplicate_count
                FROM [schema].[table]
            ) WHERE duplicate_count = 2
          );
          
          
          -- Delete all the duplicate records from the table
          DELETE FROM [schema].[table] t1
          USING temp_table t2
          WHERE 
            t1.[pk]1 = t2.[pk]1 AND 
            t1.[pk]2 = t2.[pk]2 AND
            ..
            t1.[pk]n = t2.[pk]m;
          
          -- Insert single copy using the temp_table in the original table
          INSERT INTO [schema].[table]
          SELECT * 
          FROM temp_table;
          

          【讨论】:

            【解决方案9】:

            不确定人们是否仍然对此感兴趣,但我使用了以下更优雅且似乎有效的查询

            create or replace table {{your_table}} as 
            select * from {{your_table}}
            qualify row_number() over (partition by {{criteria_columns}} order by 1) = 1
            

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 2020-04-08
              • 2010-10-14
              • 1970-01-01
              • 2021-09-06
              • 1970-01-01
              • 2018-01-02
              • 2018-09-19
              • 1970-01-01
              相关资源
              最近更新 更多