从 BigQuery table 中删除重复行
Delete duplicate rows from a BigQuery table
我有一个 table,其中包含超过 100 万行数据和 20 多列数据。
在我的 table (tableX) 中,我在一个特定的列 (troubleColumn) 中发现了重复记录 (~80k)。
如果可能,我想保留原来的 table 名称并从有问题的列中删除重复记录,否则我可以创建一个新的 table (tableXfinal)相同的架构,但没有重复项。
我不精通SQL或任何其他编程语言所以请原谅我的无知。
delete from Accidents.CleanedFilledCombined
where Fixed_Accident_Index
in(select Fixed_Accident_Index from Accidents.CleanedFilledCombined
group by Fixed_Accident_Index
having count(Fixed_Accident_Index) >1);
您可以通过 运行 重写您的 table 的查询来删除重复项(您可以使用相同的 table 作为目标,或者您可以创建一个新的 table, 验证它是否有你想要的,然后将它复制到旧的 table).
这里有一个应该有效的查询:
SELECT *
FROM (
SELECT
*,
ROW_NUMBER()
OVER (PARTITION BY Fixed_Accident_Index)
row_number
FROM Accidents.CleanedFilledCombined
)
WHERE row_number = 1
如果您的架构没有任何记录 - 在 variation
下乔丹的回答将可以很好地覆盖相同的 table 或新的回答,等等
SELECT <list of original fields>
FROM (
SELECT *, ROW_NUMBER() OVER (PARTITION BY Fixed_Accident_Index) AS pos,
FROM Accidents.CleanedFilledCombined
)
WHERE pos = 1
在更一般的情况下 - 具有具有 records/netsed 字段等的复杂模式 - 上述方法可能是一个挑战。
我建议尝试将 Tabledata: insertAll API with rows[].insertId 设置为每一行的相应 Fixed_Accident_Index。
在这种情况下,BigQuery 将消除重复行
当然,这将涉及一些客户端编码 - 因此可能与这个特定问题无关。
我自己也没有尝试过这种方法,但觉得尝试一下可能会很有趣 :o)
2019 年更新:要使用 MERGE
删除单个分区上的重复行,请参阅:
Jordan 答案的替代方案 - 当重复项过多时,这个方案的扩展性更好:
#standardSQL
SELECT event.* FROM (
SELECT ARRAY_AGG(
t ORDER BY t.created_at DESC LIMIT 1
)[OFFSET(0)] event
FROM `githubarchive.month.201706` t
# GROUP BY the id you are de-duplicating by
GROUP BY actor.id
)
或更短的版本(取任意行,而不是最新的行):
SELECT k.*
FROM (
SELECT ARRAY_AGG(x LIMIT 1)[OFFSET(0)] k
FROM `fh-bigquery.reddit_comments.2017_01` x
GROUP BY id
)
删除现有 table 上的重复行:
CREATE OR REPLACE TABLE `deleting.deduplicating_table`
AS
# SELECT id FROM UNNEST([1,1,1,2,2]) id
SELECT k.*
FROM (
SELECT ARRAY_AGG(row LIMIT 1)[OFFSET(0)] k
FROM `deleting.deduplicating_table` row
GROUP BY id
)
不知道为什么没有人提到 DISTINCT 查询。
清除重复行的方法如下:
CREATE OR REPLACE TABLE project.dataset.table
AS
SELECT DISTINCT * FROM project.dataset.table
如果您有一个大尺寸分区table,并且只在某个分区范围内有重复项。您不想过度扫描,也不想处理整个 table。使用下面的 MERGE SQL 和分区范围上的谓词:
-- WARNING: back up the table before this operation
-- FOR large size timestamp partitioned table
-- -------------------------------------------
-- -- To de-duplicate rows of a given range of a partition table, using surrage_key as unique id
-- -------------------------------------------
DECLARE dt_start DEFAULT TIMESTAMP("2019-09-17T00:00:00", "America/Los_Angeles") ;
DECLARE dt_end DEFAULT TIMESTAMP("2019-09-22T00:00:00", "America/Los_Angeles");
MERGE INTO `gcp_project`.`data_set`.`the_table` AS INTERNAL_DEST
USING (
SELECT k.*
FROM (
SELECT ARRAY_AGG(original_data LIMIT 1)[OFFSET(0)] k
FROM `gcp_project`.`data_set`.`the_table` AS original_data
WHERE stamp BETWEEN dt_start AND dt_end
GROUP BY surrogate_key
)
) AS INTERNAL_SOURCE
ON FALSE
WHEN NOT MATCHED BY SOURCE
AND INTERNAL_DEST.stamp BETWEEN dt_start AND dt_end -- remove all data in partiion range
THEN DELETE
WHEN NOT MATCHED THEN INSERT ROW
信用:https://gist.github.com/hui-zheng/f7e972bcbe9cde0c6cb6318f7270b67a
是大多数情况下的最佳方法。这是完成相同操作的更优雅的方法:
CREATE OR REPLACE TABLE Accidents.CleanedFilledCombined
AS
SELECT
Fixed_Accident_Index,
ARRAY_AGG(x LIMIT 1)[SAFE_OFFSET(0)].* EXCEPT(Fixed_Accident_Index)
FROM Accidents.CleanedFilledCombined AS x
GROUP BY Fixed_Accident_Index;
为了安全起见,请确保在 运行 之前备份原始 table ^^
如果可能,我不建议使用 ROW NUMBER() OVER()
方法,因为您可能 运行 进入 BigQuery 内存限制并出现意外错误。
- 使用新的 table 列更新 BigQuery 架构作为 bq_uuid 使其为 NULLABLE 并键入 STRING
- 例如,通过 运行 相同命令 5 次创建重复行
插入 beginner-290513.917834811114.messages
(id, type, flow, updated_at) Values(19999,"hello", "inbound", '2021-06-08T12:09:03.693646')
检查是否存在重复条目
select * 来自 beginner-290513.917834811114.messages
其中 id = 19999
使用generate uuid函数生成每条消息对应的uuid
更新 beginner-290513.917834811114.messages
SET bq_uuid = GENERATE_UUID()
其中 id>0
清除重复条目
从 beginner-290513.917834811114.messages
中删除
其中 bq_uuid 在
(SELECT bq_uuid
从
(SELECT bq_uuid,
ROW_NUMBER() OVER( 分区 updated_at
按 bq_uuid ) 作为 row_num 排序
从 beginner-290513.917834811114.messages
) t
其中 t.row_num > 1 );
更简单的答案,没有子选择
SELECT
*,
ROW_NUMBER()
OVER (PARTITION BY Fixed_Accident_Index)
row_number
FROM Accidents.CleanedFilledCombined
WHERE TRUE
QUALIFY row_number = 1
Where True 是必需的,因为 qualify 需要 where、group by 或 having 子句
我有一个 table,其中包含超过 100 万行数据和 20 多列数据。
在我的 table (tableX) 中,我在一个特定的列 (troubleColumn) 中发现了重复记录 (~80k)。
如果可能,我想保留原来的 table 名称并从有问题的列中删除重复记录,否则我可以创建一个新的 table (tableXfinal)相同的架构,但没有重复项。
我不精通SQL或任何其他编程语言所以请原谅我的无知。
delete from Accidents.CleanedFilledCombined
where Fixed_Accident_Index
in(select Fixed_Accident_Index from Accidents.CleanedFilledCombined
group by Fixed_Accident_Index
having count(Fixed_Accident_Index) >1);
您可以通过 运行 重写您的 table 的查询来删除重复项(您可以使用相同的 table 作为目标,或者您可以创建一个新的 table, 验证它是否有你想要的,然后将它复制到旧的 table).
这里有一个应该有效的查询:
SELECT *
FROM (
SELECT
*,
ROW_NUMBER()
OVER (PARTITION BY Fixed_Accident_Index)
row_number
FROM Accidents.CleanedFilledCombined
)
WHERE row_number = 1
如果您的架构没有任何记录 - 在 variation
下乔丹的回答将可以很好地覆盖相同的 table 或新的回答,等等
SELECT <list of original fields>
FROM (
SELECT *, ROW_NUMBER() OVER (PARTITION BY Fixed_Accident_Index) AS pos,
FROM Accidents.CleanedFilledCombined
)
WHERE pos = 1
在更一般的情况下 - 具有具有 records/netsed 字段等的复杂模式 - 上述方法可能是一个挑战。
我建议尝试将 Tabledata: insertAll API with rows[].insertId 设置为每一行的相应 Fixed_Accident_Index。 在这种情况下,BigQuery 将消除重复行
当然,这将涉及一些客户端编码 - 因此可能与这个特定问题无关。 我自己也没有尝试过这种方法,但觉得尝试一下可能会很有趣 :o)
2019 年更新:要使用 MERGE
删除单个分区上的重复行,请参阅:
Jordan 答案的替代方案 - 当重复项过多时,这个方案的扩展性更好:
#standardSQL
SELECT event.* FROM (
SELECT ARRAY_AGG(
t ORDER BY t.created_at DESC LIMIT 1
)[OFFSET(0)] event
FROM `githubarchive.month.201706` t
# GROUP BY the id you are de-duplicating by
GROUP BY actor.id
)
或更短的版本(取任意行,而不是最新的行):
SELECT k.*
FROM (
SELECT ARRAY_AGG(x LIMIT 1)[OFFSET(0)] k
FROM `fh-bigquery.reddit_comments.2017_01` x
GROUP BY id
)
删除现有 table 上的重复行:
CREATE OR REPLACE TABLE `deleting.deduplicating_table`
AS
# SELECT id FROM UNNEST([1,1,1,2,2]) id
SELECT k.*
FROM (
SELECT ARRAY_AGG(row LIMIT 1)[OFFSET(0)] k
FROM `deleting.deduplicating_table` row
GROUP BY id
)
不知道为什么没有人提到 DISTINCT 查询。
清除重复行的方法如下:
CREATE OR REPLACE TABLE project.dataset.table
AS
SELECT DISTINCT * FROM project.dataset.table
如果您有一个大尺寸分区table,并且只在某个分区范围内有重复项。您不想过度扫描,也不想处理整个 table。使用下面的 MERGE SQL 和分区范围上的谓词:
-- WARNING: back up the table before this operation
-- FOR large size timestamp partitioned table
-- -------------------------------------------
-- -- To de-duplicate rows of a given range of a partition table, using surrage_key as unique id
-- -------------------------------------------
DECLARE dt_start DEFAULT TIMESTAMP("2019-09-17T00:00:00", "America/Los_Angeles") ;
DECLARE dt_end DEFAULT TIMESTAMP("2019-09-22T00:00:00", "America/Los_Angeles");
MERGE INTO `gcp_project`.`data_set`.`the_table` AS INTERNAL_DEST
USING (
SELECT k.*
FROM (
SELECT ARRAY_AGG(original_data LIMIT 1)[OFFSET(0)] k
FROM `gcp_project`.`data_set`.`the_table` AS original_data
WHERE stamp BETWEEN dt_start AND dt_end
GROUP BY surrogate_key
)
) AS INTERNAL_SOURCE
ON FALSE
WHEN NOT MATCHED BY SOURCE
AND INTERNAL_DEST.stamp BETWEEN dt_start AND dt_end -- remove all data in partiion range
THEN DELETE
WHEN NOT MATCHED THEN INSERT ROW
信用:https://gist.github.com/hui-zheng/f7e972bcbe9cde0c6cb6318f7270b67a
CREATE OR REPLACE TABLE Accidents.CleanedFilledCombined
AS
SELECT
Fixed_Accident_Index,
ARRAY_AGG(x LIMIT 1)[SAFE_OFFSET(0)].* EXCEPT(Fixed_Accident_Index)
FROM Accidents.CleanedFilledCombined AS x
GROUP BY Fixed_Accident_Index;
为了安全起见,请确保在 运行 之前备份原始 table ^^
如果可能,我不建议使用 ROW NUMBER() OVER()
方法,因为您可能 运行 进入 BigQuery 内存限制并出现意外错误。
- 使用新的 table 列更新 BigQuery 架构作为 bq_uuid 使其为 NULLABLE 并键入 STRING
- 例如,通过 运行 相同命令 5 次创建重复行
插入 beginner-290513.917834811114.messages
(id, type, flow, updated_at) Values(19999,"hello", "inbound", '2021-06-08T12:09:03.693646')
检查是否存在重复条目 select * 来自
beginner-290513.917834811114.messages
其中 id = 19999使用generate uuid函数生成每条消息对应的uuid 更新
beginner-290513.917834811114.messages
SET bq_uuid = GENERATE_UUID() 其中 id>0清除重复条目
从 beginner-290513.917834811114.messages
中删除
其中 bq_uuid 在
(SELECT bq_uuid
从
(SELECT bq_uuid,
ROW_NUMBER() OVER( 分区 updated_at
按 bq_uuid ) 作为 row_num 排序
从 beginner-290513.917834811114.messages
) t
其中 t.row_num > 1 );
更简单的答案,没有子选择
SELECT
*,
ROW_NUMBER()
OVER (PARTITION BY Fixed_Accident_Index)
row_number
FROM Accidents.CleanedFilledCombined
WHERE TRUE
QUALIFY row_number = 1
Where True 是必需的,因为 qualify 需要 where、group by 或 having 子句