Lakehouse 数据去重处理指南

概述

在数据仓库建设中,数据去重是最常见的需求之一。云器 Lakehouse 提供了多种去重方案,从简单的

DISTINCT
DISTINCT
到基于窗口函数的复杂去重,满足不同业务场景的需求。本指南按场景分类,帮助你快速选择并实现最优的去重方案。

涉及的 SQL 命令

命令/函数用途适用场景
SELECT DISTINCT
SELECT DISTINCT
去除完全重复的行所有字段都相同的重复数据
ROW_NUMBER() OVER (...)
ROW_NUMBER() OVER (...)
为每组数据生成唯一序号需要按时间/优先级保留特定行
QUALIFY
QUALIFY
直接过滤窗口函数结果替代子查询,简化 SQL
GROUP BY
GROUP BY
+
MAX/MIN/ANY_VALUE
MAX/MIN/ANY_VALUE
分组聚合去重只需保留部分字段或任意一条
INSERT OVERWRITE
INSERT OVERWRITE
覆盖写入去重后的数据物理清理重复数据

前置准备

以下示例使用模拟的订单表

orders_with_dupes
orders_with_dupes
,包含重复数据:

-- 创建测试表 CREATE TABLE IF NOT EXISTS orders_with_dupes ( order_id STRING, customer_id INT, amount DOUBLE, order_time TIMESTAMP ); -- 插入测试数据(包含重复) INSERT INTO orders_with_dupes VALUES ('O001', 101, 100.0, '2024-06-01 10:00:00'), ('O001', 101, 100.0, '2024-06-01 10:00:00'), -- 完全重复 ('O002', 102, 200.0, '2024-06-01 11:00:00'), ('O002', 102, 200.0, '2024-06-01 11:05:00'), -- 订单号相同,时间不同 ('O003', 103, 300.0, '2024-06-01 12:00:00');


完全重复数据去重

当一行数据的所有字段都完全相同时,使用

DISTINCT
DISTINCT
是最简单高效的方式。

-- 查询去重结果 SELECT DISTINCT * FROM orders_with_dupes;

结果说明

order_idcustomer_idamountorder_time
O0011011002024-06-01 10:00:00
O0021022002024-06-01 11:00:00
O0021022002024-06-01 11:05:00
O0031033002024-06-01 12:00:00

保留最新一条记录

业务场景中,往往需要根据时间戳保留最新的一条记录(例如:重复上报的日志、多次更新的订单状态)。Lakehouse 推荐使用

ROW_NUMBER()
ROW_NUMBER()
配合
QUALIFY
QUALIFY
子句,语法更简洁。

-- 保留每个 order_id 中 order_time 最新的一条 SELECT order_id, customer_id, amount, order_time FROM orders_with_dupes WINDOW w AS (PARTITION BY order_id ORDER BY order_time DESC) QUALIFY ROW_NUMBER() OVER w = 1;

结果说明

order_idcustomer_idamountorder_time
O0011011002024-06-01 10:00:00
O0021022002024-06-01 11:05:00
O0031033002024-06-01 12:00:00

语法优势

使用

QUALIFY
QUALIFY
可以避免嵌套子查询,SQL 更易读:

-- ❌ 传统写法(子查询嵌套) SELECT order_id, customer_id, amount, order_time FROM ( SELECT *, ROW_NUMBER() OVER (PARTITION BY order_id ORDER BY order_time DESC) as rn FROM orders_with_dupes ) t WHERE rn = 1; -- ✅ Lakehouse 推荐写法(QUALIFY) SELECT order_id, customer_id, amount, order_time FROM orders_with_dupes QUALIFY ROW_NUMBER() OVER (PARTITION BY order_id ORDER BY order_time DESC) = 1;


基于多字段组合去重

当重复数据仅部分字段不同(如时间戳不同),且你只需要保留关键字段时,可以使用

GROUP BY
GROUP BY
配合聚合函数。

-- 按 order_id 分组,保留最大金额和最新时间 SELECT order_id, customer_id, MAX(amount) as max_amount, MAX(order_time) as latest_time FROM orders_with_dupes GROUP BY order_id, customer_id;

适用场景

  • 只需要统计维度数据,不关心具体是哪一条记录。
  • 数据量极大,窗口函数性能不如聚合函数时。

物理去重(重写表)

查询去重只是逻辑上去重,如果需要物理清理重复数据,可以将去重结果写回原表或新表。

方案 1:写入新表(推荐)

-- 创建去重后的新表 CREATE TABLE orders_deduped AS SELECT * FROM orders_with_dupes QUALIFY ROW_NUMBER() OVER (PARTITION BY order_id ORDER BY order_time DESC) = 1;

方案 2:覆盖原表

-- 覆盖原表数据(谨慎操作) INSERT OVERWRITE TABLE orders_with_dupes SELECT * FROM orders_with_dupes QUALIFY ROW_NUMBER() OVER (PARTITION BY order_id ORDER BY order_time DESC) = 1;


清理测试数据

完成去重验证后,建议清理测试表,避免占用存储空间:

-- 删除测试表 DROP TABLE IF EXISTS orders_with_dupes; DROP TABLE IF EXISTS orders_deduped;


注意事项

  1. 性能优化

    • 对于大表去重,建议先使用
      WHERE
      WHERE
      过滤掉明显无效的数据。
    • 如果表已分区,按分区进行去重可以显著减少计算量。
  2. NULL 值处理

    • DISTINCT
      DISTINCT
      会将多个
      NULL
      NULL
      视为相同值,只保留一行。
    • ROW_NUMBER()
      ROW_NUMBER()
      排序时,
      NULL
      NULL
      值默认排在最后(
      DESC
      DESC
      时)或最前(
      ASC
      ASC
      时),可通过
      NULLS FIRST/LAST
      NULLS FIRST/LAST
      控制。
  3. 动态表去重

    • 如果去重逻辑用于 Dynamic Table,建议使用
      GROUP BY
      GROUP BY
      方案,增量计算支持更完善。
    • QUALIFY
      QUALIFY
      在 Dynamic Table 中完全支持,但复杂窗口函数可能导致刷新模式变为全量。

相关文档

联系我们
预约咨询
微信咨询
电话咨询