从具有 NULL 列的大表中删除重复项,这也需要考虑
Posted
技术标签:
【中文标题】从具有 NULL 列的大表中删除重复项,这也需要考虑【英文标题】:Removing duplicates from a large table with NULL columns that also need to be taken into account 【发布时间】: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);
我有几个大表我必须运行它。
【参考方案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;
【讨论】:
以上是关于从具有 NULL 列的大表中删除重复项,这也需要考虑的主要内容,如果未能解决你的问题,请参考以下文章