【问题标题】:BigQuery - Delete with two column in where and in clauseBigQuery - 在 where 和 in 子句中删除两列
【发布时间】:2021-09-13 21:53:44
【问题描述】:

我有一个从 OLTP 数据库流向 BQ 的数据。因此,如果任何行被更新,BQ 会在 2 个不同的行中同时包含新旧记录。所以我有一个查询去重复数据,但我想每天至少一次从 BQ 中删除旧值。

这是我的选择查询,它将为我提供最新记录。

SELECT DISTINCT o._sdc_sequence
      FROM `my-production.bqtest.mytbl` o
INNER JOIN (
     SELECT id,
            MAX(_sdc_sequence) AS seq,
            MAX(_sdc_batched_at) AS batch
    FROM `my-production.bqtest.mytbl`
    GROUP BY id) oo
ON o.id = oo.id
AND o._sdc_sequence = oo.seq
AND o._sdc_batched_at = oo.batch
  • id - 整数(主键)
  • sdc_sequence- unix时间戳(数据插入BQ时)
  • _sdc_batched_at timestamp - (它是一个批处理,所以是批处理开始时间的时间戳)

以上列的样本数据:

select id, 
_sdc_sequence, 
_sdc_batched_at 
FROM `my-production.bqtest.mytbl`

ID: 2741332
_sdc_sequence: 1565726907840002084
_sdc_batched_at: 2019-08-13 21:01:07.687 UTC

我想删除旧记录,我可以用最新的行进行每日表轮换,但是如果我更改表结构上的某些内容,我使用的 ETL 工具将不起作用。

我在下面的查询中尝试了这个,但它也删除了一些有效的行。

delete from  `my-production.bqtest.mytbl` where _sdc_sequence not in( 
SELECT DISTINCT o._sdc_sequence
      FROM `my-production.bqtest.mytbl` o
INNER JOIN (
     SELECT id,
            MAX(_sdc_sequence) AS seq,
            MAX(_sdc_batched_at) AS batch
    FROM `my-production.bqtest.mytbl` 
    GROUP BY id) oo
ON o.id = oo.id
AND o._sdc_sequence = oo.seq
AND o._sdc_batched_at = oo.batch

因为我有2行具有相同的序列ID,所以我需要用where _sdc_sequence not in + max(_sdc_batched_at)过滤掉它

或任何其他更好的查询来执行此操作。

【问题讨论】:

  • Google Bigquery 等平台在硬删除方面并不是很好的性能解决方案。您是否考虑过软删除解决方案,即在大查询中汇总所有版本的增量提要并创建使用分区函数(例如行号)来抑制所有非最新版本的行的特定视图?
  • 是的,即使我不乐意​​这样做,但是,它的要求,我需要实现。这就是为什么我每天都这样做一次。然后它将减少我的查询执行时间。
  • 使用删除来改变目标 BQ 表的要求也意味着每次运行硬删除时都会丢失所有非最近记录的历史记录,使用物化视图,您可以获取在目标 BQ 表上完成的所有流插入的完整数据沿袭。

标签: sql google-cloud-platform google-bigquery


【解决方案1】:
select t.*
from `my-production.bqtest.mytbl` t
where (id, _sdc_sequence) in
          (select (t2.id, MAX(t2._sdc_sequence))
           from `my-production.bqtest.mytbl` t2
           group by t2.id
          );

在选择查询的列列表中使用(),以避免出现Subquery of type IN must have only one output column 错误

【讨论】:

    【解决方案2】:

    如果您只想为每个id 保留一行,那么元组语法可能是最简单的。保存记录:

    select t.*
    from `my-production.bqtest.mytbl` t
    where (id, _sdc_sequence) in
              (select t2.id, MAX(t2._sdc_sequence)
               from `my-production.bqtest.mytbl` t2
               group by t2.id
              );
    

    根据您的描述,我不确定批次与问题有什么关系,所以我把它省略了。

    您可以使用not in 或类似的逻辑将其转换为delete

    delete from `my-production.bqtest.mytbl` t
    where (id, _sdc_sequence) not in
              (select t2.id, MAX(t2._sdc_sequence)
               from `my-production.bqtest.mytbl` t2
               group by t2.id
              );
    

    你也可以这样表述:

    delete from `my-production.bqtest.mytbl` t
    where _sdc_sequence <
              (selectd max(t2._sdc_sequence)
               from `my-production.bqtest.mytbl` t2
               where t2.id = t.id
              );
    

    【讨论】:

    • 感谢您的回答,我收到此错误Subquery of type IN must have only one output column at [4:12]
    • @Bhuvanesh。 . .我实际上使用带有元组的in 测试了代码,可以肯定的是,它确实有效。无论如何,这也可以使用相关子查询来表达。
    【解决方案3】:

    @Mr.Llama

    你可以通过使用分隔符来避免它

    例如CONCAT(ABCD, EF) ---&gt; CONCAT(ABCD,'-', EF)

    【讨论】:

      【解决方案4】:

      只需在 where 检查和子查询中连接两个字段,将它们变成具有唯一值的单个字段(在运行删除之前确保它们是唯一的):

      select t.*
      from `my-production.bqtest.mytbl` t
      where concat(id, _sdc_sequence) in
                (select concat(t2.id, MAX(t2._sdc_sequence))
                 from `my-production.bqtest.mytbl` t2
                 group by t2.id
                )
      

      【讨论】:

      • 我不能推荐这种方法。它要求所有数据类型都是字符串,但还假设连接字符串后的结果没有歧义。例如,CONCAT(ABC, DEF)CONCAT(ABCD, EF) 的结果相同。
      猜你喜欢
      • 1970-01-01
      • 2017-01-25
      • 1970-01-01
      • 1970-01-01
      • 2019-06-24
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多