【问题标题】:BigQuery: Deleting Duplicates in Partitioned TableBigQuery:删除分区表中的重复项
【发布时间】:2019-05-08 02:27:27
【问题描述】:

我有按插入时间分区的 BQ 表。 我正在尝试从表中删除重复项。这些是真正的重复:对于 2 个重复的行,所有列都是相等的 - 当然,拥有唯一键可能会有所帮助:-(

起初我尝试了一个 SELECT 查询来枚举重复项并删除它们:

SELECT
    * EXCEPT(row_number)
FROM (
    SELECT
    *,
    ROW_NUMBER() OVER (PARTITION BY id_column) row_number
    FROM
    `mytable`)
WHERE
    row_number = 1

这会产生唯一的行,但会创建一个不包含分区数据的新表 - 所以不好。

我见过answer here,它指出保留分区的唯一方法是使用上述查询逐个检查它们并保存到特定的目标表分区。

我真正想做的是使用 DML DELETE 删除重复的行。我尝试了类似于this answer suggested:

DELETE
FROM `mytable` AS d
WHERE (SELECT ROW_NUMBER() OVER (PARTITION BY id_column)
   FROM `mytable ` AS d2
   WHERE d.id = d2.id) > 1;

但接受的答案不起作用并导致 BQ 错误:

Error: Correlated subqueries that reference other tables are not supported unless they can be de-correlated, such as by transforming them into an efficient JOIN

如果有人能提供一种更简单(DML 或其他)的方法来处理这个问题,那就太好了,这样我就不需要单独遍历所有分区了。

【问题讨论】:

  • 嗨@MikhailBerlyant,不确定这与我有什么关系,因为答案假定每行都有一个唯一的附加列(loadTime)。我怎样才能适应我的情况?

标签: google-bigquery bigquery-standard-sql


【解决方案1】:

有点骇人听闻,但您可以使用MERGE 语句删除表的所有内容,并以原子方式仅重新插入不同的行。这是一个例子:

-- Create a table with some duplicate rows
CREATE TABLE dataset.PartitionedTable
PARTITION BY date AS
SELECT x, CONCAT('foo', CAST(x AS STRING)) AS y, DATE_SUB(CURRENT_DATE(), INTERVAL x DAY) AS date
FROM UNNEST(GENERATE_ARRAY(1, 10)) AS x, UNNEST(GENERATE_ARRAY(1, 10));

现在是MERGE 部分:

-- Execute a MERGE statement where all original rows are deleted,
-- then replaced with new, deduplicated rows:
MERGE dataset.PartitionedTable AS t1
USING (SELECT DISTINCT * FROM dataset.PartitionedTable) AS t2
ON FALSE
WHEN NOT MATCHED BY TARGET THEN INSERT ROW
WHEN NOT MATCHED BY SOURCE THEN DELETE

【讨论】:

  • 有没有办法让not matched by target 子句更加健壮?表有很多列,其中一些列是可重复的数组?
  • 例如,您可以将 SELECT 与 ANY_VALUE 和 GROUP BY 一起使用,而不是 DISTINCT。
【解决方案2】:

您可以在一个 SQL MERGE 语句中执行此操作,而无需创建额外的表。

-- 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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-07-22
    • 1970-01-01
    • 1970-01-01
    • 2020-01-13
    • 2017-02-18
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多