如何删除 SQL 中的重复行(Clickhouse)?

How to delete duplicate rows in SQL ( Clickhouse)?

所以我使用 clickhouse 创建了一个 table,但它有重复项。

以下查询为我提供了 table

中的重复项
select *, count() AS cnt from my_table   GROUP BY *
HAVING cnt > 1 

在 clickhouse 中显然你需要通过改变 table 来做到这一点:https://clickhouse.com/docs/en/sql-reference/statements/alter/delete/

所以,我尝试了以下方法:

ALTER TABLE my_table DELETE WHERE (select *, count() AS cnt from my_table  GROUP BY *
HAVING cnt > 1 ); 

但我收到以下错误:

Exception: The argument of function isZeroOrNull must have simple numeric type, possibly Nullable:

有人在使用 clickhouse 之前遇到过这个问题吗?

在此视频中,他们明确提到 clickhouse 不是此类操作的最佳选择:https://www.youtube.com/watch?v=FsVrFbcyb84&t=1865s

但我想知道是否有人想出解决方案

首先,答案取决于您使用的 table 引擎。 ClickHouse 上最常见的是 MergeTree 家族。

如果您使用任何 MergeTree 系列 tables、MaterializedView 或 Buffer 引擎,您可以使用 OPTIMIZE 查询:

OPTIMIZE TABLE table DEDUPLICATE BY name -- you can put any expression here

https://clickhouse.com/docs/en/sql-reference/statements/optimize/

在将上述查询视为答案之前,您必须了解为什么以及为什么这样做不是正确的方法。

在Clickhouse中,同一个主键有多行是正常的,un-like大多数数据库引擎,插入一行时根本没有检查。这允许在 tables 中非常快速地插入。

名称“MergeTree”并非无用,事实上,如果 Clickhouse 认为有必要 or/and 如果有时间,table 会自动“优化”。

ClickHouse 中的优化是什么意思? 此操作只是强制 table 合并它的数据。取决于您构建 table 的方式。 ClickHouse 将根据您的设置查找重复的行并应用您要求的功能。

两个例子 :

  • ReplacingMergeTree,这里可选参数设置为datetime,提示ClickHouse哪一行是最新的。然后在重复项上,最新的保留在其他项之上。
create table radios
(
    id                UInt64,
    datetime          DateTime,
    name              Nullable(String) default NULL
)
    engine = ReplicatedReplacingMergeTree(datetime)
    ORDER BY id -- it's the primary key
-- example
INSERT INTO radios VALUES (1, now(), 'Some name'), (1, now(), 'New name')
-- after merging:
id,              datetime,       name
 1, '2022-04-04 15:15:00', 'New name'
  • AggregatingMergeTree,这里应用了一个函数来计算最后一行。这是您会发现最接近 UPDATE 语句的语句。
create table radio_data
(
    datetime                        DateTime,
    id                              UInt64,
    power                           SimpleAggregateFunction(anyLast, Nullable(Float64)) default NULL,
    access                          SimpleAggregateFunction(sum, Nullable(UInt64))    default NULL
)
    engine = ReplicatedAggregatingMergeTree()
        ORDER BY (id, datetime) -- the primary key

-- example
INSERT INTO radio_data VALUES ('2022-04-04 15:15:00', 1, NULL, 1), ('2022-04-04 15:15:00', 1, 12, 2)
-- will give after merging :
datetime           , id, power, access
2022-04-04 15:15:00,  1,    12,      3

您选择的 table,您选择的功能,必须非常接近您最终想要对数据执行的操作。您是否替换了更新时的所有行?那么 ReplacingMergeTree 是最好的,你是否部分更新一行并在其上应用一些功能?那么 AggregatingMergeTree 是最好的...等等

这就是说,在某些情况下,您需要让数据“新鲜”而不是重复。 当你的table配置好了,一个简单的OPTIMIZE TABLE ...就够了。 但是 这很昂贵,如果您不想破坏服务器性能,就必须聪明地完成。 您也可以即时合并数据,但同样,这很昂贵并且必须对一小部分数据进行合并,否则最好进行优化。

SELECT * FROM radio_data FINAL WHERE id = 1

例如,我们对“过去”的所有 un-merged 分区进行优化,例如前一天。目标是做尽可能少的OPTOIMIZE操作。

我最后要说的是 ALTER TABLE 语句的用法。它允许删除和更新。但它们是突变 (https://clickhouse.com/docs/en/sql-reference/statements/alter/#mutations) 并且不是同步的!如果您需要新数据,请不要依赖它们。

您可以在此处找到更多 material:

https://clickhouse.com/docs/en/engines/table-engines/mergetree-family/mergetree/#mergetree https://clickhouse.com/docs/en/sql-reference/statements/optimize/ https://clickhouse.com/docs/en/sql-reference/statements/alter/