【问题标题】:Removing duplicates from a large table with NULL columns that also need to be taken into account从具有 NULL 列的大表中删除重复项,这也需要考虑
【发布时间】:2017-12-08 02:26:49
【问题描述】:

我有兴趣从一个非常大的表(40+ 百万条记录,3+ GB 大小)中删除重复项,在确定重复项时还必须计算 NULL 列。 5.5.56-MariaDB,表格为InnoDB格式。

我遇到的问题是一些推荐的方法在 NULL 字段比较时不起作用。例如,我有一个表,它有大约十几个列,其中许多列可以包含 NULL 值,并且我希望 NULL 被视为任何其他值,因为如果一行在相同列中具有相同的 NULL 值,则它被认为是骗人的。

以下技术不起作用:

create table tmp like mytable;
alter table tmp ADD UNIQUE INDEX(field1,field2, field3,...);
insert IGNORE into tmp (select * from mytable);

此时,tmp 应该已删除所有重复项,但唯一索引中忽略了 NULL 列,因此它不起作用。如果它确实有效,那么我可以重命名这些表,或者截断原始表并复制过来(假设我想在原始表中保留一个主键)。

在处理 NULL 列时,删除大文件中的重复数据最有效的方法是什么?

附加说明:我有一个名为“recno”的字段,它是需要忽略的自动增量。

【问题讨论】:

  • 您最终会得到少于一半的行数吗?或者更多?到目前为止,重点是前者。后者导致DELETEing dups。
  • 如果只有几列可以NULL(或只有几列组合),那我可能有一个设计。有多少?
  • 我估计最多可能有 10-20% 的数据库被复制。不过可能更多。表中大约有 23 个不同类型的列,其中大多数可以有一个 NULL 值,包括一堆 VARCHAR(2) 保存“Y”“N”或 NULL,这会导致问题。 NULL 是有意义的,需要在检测欺骗时加以考虑。
  • 以下查询已被证明有效,但它已经运行了超过 48 小时.. 必须有更快的方法:DELETE a FROM t1_test as a, t1_test as b WHERE (a.provnum =b.provnum) AND (a.field1=b.field1 OR (a.field1 IS NULL AND b.field1 IS NULL)) [20 其他列] AND (a.recno>b.recno);
  • 我有几个大表我必须运行它。

标签: mysql sql database mariadb


【解决方案1】:

您可以使用如下查询来选择它们:

  SELECT field1, field2, field3, count(1)
    FROM t
GROUP BY field1, field2, field3
  HAVING count(1) > 1

然后用代码删除它们或使用某种控制逻辑,无论 mysql 中可用什么。我相信还有其他方法可以做到这一点。

【讨论】:

  • 我需要一个 mysql/mariadb 特定的解决方案。我不确定你的建议是什么。
  • @TrentThree,该语法适用于 MySql 和 MariaDb。
  • 可以添加额外的控制逻辑吗?我不确定这将如何工作......上面的查询只是显示了欺骗记录?假设我有一个名为“recno”的rowid列,我可以在recno =(select rowid ....)的某种表中使用它吗?
  • 另外,对于有 40+ 百万条记录的表,GROUP BY 不是一个非常耗时的查询吗?
  • 不知道时间,你得测试一下。 Group By 有效,我不知道理论,但我确信 mysql 已经弄清楚了。至于控制逻辑,我正在考虑使用游标或 T-SQL 或 C 或 Java 来删除查询的结果。我不知道mysql中有什么可用的。很高兴你明白了。
【解决方案2】:

哦,那是一个挑战。

CREATE TABLE _t LIKE real;
INSERT INTO _t
    SELECT MIN(recno),   -- or MAX, if you prefer
           a,b,c,...,w   -- all the other columns
        FROM real
        GROUP BY
           a,b,c,...,w,
           ((a IS NULL) << 0) |
           ((b IS NULL) << 1) |
           ...
           ((w IS NULL) << 22) ;
-- Note: This will not work for more than 64 columns.

-- Put it in place (with no downtime):
RENAME TABLE real TO old, _t TO real;
DROP TABLE old;

【讨论】:

    【解决方案3】:

    为什么不这样做呢?

    create table tmp as
        select distinct t.*
        from mytable t;
    

    编辑:

    如果你想要第一个最小值的所有字段:

    create table tmp as
        select min(id), field1, field2, . . .
        from mytable t
        group by field1, field2, . . .
    

    【讨论】:

    • 我还有一个需要忽略的自动增量字段。
    • @TrentThree 。 . .那就别用*了,把你关心的列都列出来。
    • 有什么办法可以保存recno A.I.列?
    • @TrentThree 。 . .你可以使用min()
    • min() 在 select 查询中似乎不起作用 - 不确定如何在查询中混合不同的列和非不同的列..
    【解决方案4】:

    这是我使用的方法。我最终不得不重新创建表并重命名它,但是对于一个 3.1GB 和 4200 万条记录的数据库(大约 977,000 次重复),处理时间约为 90 分钟):

    drop table if exists tmp;
    create table tmp AS
    select distinct field1, field2, field3, ...
     from mytable;
    -- add back my rowid
    ALTER TABLE `tmp` ADD `recno` INT UNSIGNED NOT NULL AUTO_INCREMENT 
    -- add any indexes you were using
    
    -- rename tables
    rename table mytable to table_hold;
    rename table tmp to mytable;
    

    【讨论】:

      猜你喜欢
      • 2011-05-04
      • 2021-06-29
      • 1970-01-01
      • 2019-12-03
      • 1970-01-01
      • 1970-01-01
      • 2015-01-17
      • 2018-05-29
      • 1970-01-01
      相关资源
      最近更新 更多