【问题标题】:Remove duplicate rows based on specific columns根据特定列删除重复行
【发布时间】:2022-01-23 15:00:29
【问题描述】:

我有一个包含这些列的表:

ID (varchar)
SETUP_ID (varchar)
MENU (varchar)
LABEL (varchar)

我想要实现的是基于两列(SETUP_ID, MENU) 从表中删除所有重复项。

我的桌子:

id  |  setup_id  |  menu  |  label  |
-------------------------------------
1   |    10      |  main  |  txt    |
2   |    10      |  main  |  txt    |
3   |    11      | second |  txt    |
4   |    11      | second |  txt    |
5   |    12      | third  |  txt    |

我想要的表:

id  |  setup_id  |  menu  |  label  |
-------------------------------------
1   |    10      |  main  |  txt    |
3   |    11      | second |  txt    |
5   |    12      | third  |  txt    |

【问题讨论】:

  • 这没有回答问题。它提供了一种创建新表的解决方案。
  • @VynlJunkie 这就是我发表评论而不是答案的原因
  • 任务不完全清楚,不知道setup_idmenu是否可以为NULL。以及如果可能的话如何处理 NULL 值。另外,请始终声明您的 Postgres 版本,并告诉我们性能是否重要。如果是这样,表中大概有多少行,大概有多少骗子?最后,您是只想选择不同的行,还是主动从表中删除重复项?

标签: sql postgresql duplicates


【解决方案1】:

您可以使用公用表表达式 (cte) 实现此目的

with cte as ( 
           select id, setup_id, menu, 
                  row_number () over (partition by setup_id, menu, label) rownum
           from atable )
delete from atable a
where id in (select id from cte where rownum >= 2) 

这将为您提供所需的输出。

Common Table Expression docs

【讨论】:

  • 如果我正确理解 OP 的要求,我认为 label 不应包含在窗口定义中。此外,rownum = 2 假设给定的(setup_id, menu) 元组最多可以有一个重复行。要在重复时只保留一行(无论多少次),您应该使用where rownum >= 2
  • 好地方,已更新删除标签。
  • 从性能的角度来看,编写子查询是个坏主意。
【解决方案2】:

假设一个名为tbl 的表同时定义了setup_idmenuNOT NULLidPRIMARY KEY
EXISTS 会很好:

DELETE FROM tbl t0
WHERE  EXISTS (
   SELECT FROM tbl t1
   WHERE  t1.setup_id = t0.setup_id
   AND    t1.menu = t0.menu
   AND    t1.id < t0.id
   );

这将删除找到具有较低id 的欺骗的每一行,有效地只保留每组欺骗中具有最小id 的行。 (setup_id, menu) 甚至 (setup_id, menu, id) 上的索引将有助于大表的性能很多

如果没有 PK 并且没有可靠的 UNIQUE(组合)列,您可以回退到使用 ctid。如果可能涉及 NULL 值,则需要指定如何处理这些值。
考虑:

清理重复后,添加UNIQUE 约束以防止新的重复:

ALTER TABLE tbl ADD CONSTRAINT tbl_setup_id_menu_uni UNIQUE (setup_id, menu);

如果您在 (setup_id, menu) 上有索引,请立即删除它。它已被 UNIQUE 约束所取代。

【讨论】:

    【解决方案3】:

    您可以尝试按照这些方式删除除第一行之外的所有内容,以防出现重复(请注意,这未经任何测试!):

    DELETE FROM your_table WHERE id IN (
        SELECT unnest(duplicate_ids[2:]) FROM (
            SELECT array_agg(id) AS duplicate_ids FROM your_table
                GROUP BY SETUP_ID, MENU
                HAVING COUNT(*) > 1
            )
        )
    )
    

    上面将重复行的 id (COUNT(*) &gt; 1) 收集到一个数组 (array_agg) 中,然后获取该数组 ([2:]) 中除第一个元素之外的所有元素,并将 id 值“分解”成行(unnest)。 外部查询只是删除结果中的每个 id。

    【讨论】:

      【解决方案4】:

      对于 mysql,类似的问题已经在这里得到解答 Find and remove duplicate rows by two columns

      尝试任何方法是否有助于解决此问题。

      我喜欢下面的MySql

      ALTER IGNORE TABLE your_table ADD UNIQUE (SETUP_ID, MENU);
      

      【讨论】:

      • 对不起...我不知道我在写 Mysql 时在想什么...我的意思是 postgresql 无法解决这个问题。
      【解决方案5】:
      DELETE t1 
      FROM table_name t1
          join table_name t2 on
          (t2.setup_id = t1.setup_id or t2.menu = t1.menu) and t2.id < t1.id
      

      【讨论】:

      • 我认为第一个条件应该是 (t2.setup_id = t1.setup_id AND t2.menu = t1.menu) -- 即 AND 而不是 OR。
      • 嗯好吧,因为我的英语不太流利,所以我不明白这个问题
      • 这不是合法的 Postgres 语法。
      【解决方案6】:

      我找到了最适合我的解决方案。 如果有人需要,就在这里:

      DELETE FROM table_name
      WHERE id IN
        (SELECT id
         FROM
             (SELECT id,
                     ROW_NUMBER() OVER( PARTITION BY setup_id,
           menu
          ORDER BY  id ) AS row_num
              FROM table_name ) t
         WHERE t.row_num > 1 );
      

      【讨论】:

      • 有效。但是(就像这里提出的一些其他解决方案一样)比必要的要贵得多。
      【解决方案7】:

      有很多方法可以根据条件查找和删除所有重复行。但我喜欢内连接方法,即使在大量数据中也能快速工作。请检查以下内容:

      DELETE T1 FROM <TableName> T1
      INNER JOIN <TableName> T2 
      WHERE
          T1.id > T2.id AND 
          T1.<ColumnName1> = T2.<ColumnName1> AND T1.<ColumnName2> = T2.<ColumnName2>;
        
      

      在你的情况下,你可以写如下:

      DELETE T1 FROM <TableName> T1
          INNER JOIN <TableName> T2 
          WHERE
              T1.id > T2.id AND 
              T1.setup_id = T2. setup_id;
      

      如果您遇到任何问题或需要更多帮助,请告诉我。

      【讨论】:

      • 这不是合法的 Postgres 语法。
      【解决方案8】:

      链接:https://www.postgresql.org/docs/current/queries-union.html
      https://www.postgresql.org/docs/current/sql-select.html#SQL-DISTINCT
      let's sat 表名是 a

      select distinct on (setup_id,menu ) a.* from a;
      

      关键点:DISTINCT ON 表达式必须匹配最左边的 ORDER BY 表达式。 ORDER BY 子句通常包含附加表达式,这些表达式确定每个 DISTINCT ON 组中所需的行优先级。

      这意味着你只能在这个distinct on query范围内按setup_id,menu排序

      想要相反:
      EXCEPT 返回所有在 query1 结果中但不在 query2 结果中的行。 (这有时称为两个查询之间的差异。)同样,除非使用 EXCEPT ALL,否则会消除重复项。

      SELECT * FROM a
      EXCEPT 
      select distinct on (setup_id,menu ) a.* from a;
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2017-11-26
        • 1970-01-01
        • 1970-01-01
        • 2022-11-17
        • 2021-07-04
        • 2017-08-08
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多