通过联接合并到 BigQuery 分区表中,无需扫描整个表

Posted

技术标签:

【中文标题】通过联接合并到 BigQuery 分区表中,无需扫描整个表【英文标题】:Merge into a BigQuery Partitioned Table via a Join Without Scanning Entire Table 【发布时间】:2019-09-26 05:12:14 【问题描述】:

示例场景.. 我有数百万行的“BigTable”和只有几行的“TinyTable”。我需要将 TinyTable 中的一些信息合并到 BigTable 中。 BigTable 由列“date_time”分区。我的合并将在 date_time 和 ID 上加入。 我真的只需要 ID 列来执行连接,但我认为在那里也有 date_time 列将允许 BQ 修剪分区并只查看必要的日期。没有。它对 BigTable 进行全面扫描(向我收取千兆字节的数据费用)......即使 TinyTable 只有一个值(即从一个日期开始)。

BigTable
+---------------------------+---------+-------+
|         date_time         |      ID | value |
+---------------------------+---------+-------+
| '2019-03-13 00:00:00 UTC' |     100 | .2345 |
| '2019-03-13 00:00:00 UTC' |     101 |   .65 |
| '2019-03-14 00:00:00 UTC' |     102 |  .648 |
|  [+50 millions rows...]   |         |       |
+---------------------------+---------+-------+


TinyTable
+---------------------------+---------+-------+
|         date_time         |      ID | value |
+---------------------------+---------+-------+
| '2019-03-13 00:00:00 UTC' |     100 |  .555 |
| '2019-03-14 00:00:00 UTC' |     102 |  .666 |
|                           |         |       |
+---------------------------+---------+-------+

...

使用 8 GB...

 MERGE BigTable
    USING TinyTable
    ON BigTable.date_time = TinyTable.date_time and BigTable.id = TinyTable.id 
    WHEN MATCHED THEN
      UPDATE SET date_time = TinyTable.date_time, value = TinyTable.value
    WHEN NOT MATCHED THEN
      INSERT  (date_time, id , value) values (date_time, id , value);

使用 8 GB...

update BigTable 
set value = TinyTable.value 
from 
TinyTable where 
BigTable.date_time = TinyTable.date_time 
and 
BigTable.id = TinyTable.id 

如果我在时间戳文字中硬编码而不是使用连接中的值(但不是我所追求的),则可以按预期工作(仅 12 MB)...

update BigTable 
set value = TinyTable.value 
from 
TinyTable where 
BigTable.date_time = '2019-03-13 00:00:00 UTC' 
and 
BigTable.id = TinyTable.id 

我需要每天运行数百次这样的事情。照原样,这在成本方面是不可持续的。我错过了什么?

谢谢!

【问题讨论】:

【参考方案1】:

有了 BigQuery scripting(现在是测试版),有一种方法可以降低成本。

基本上,脚本变量被定义为捕获子查询的动态部分。然后在后续查询中,使用脚本变量作为过滤器来修剪要扫描的分区。

DECLARE date_filter ARRAY<DATETIME> 
  DEFAULT (SELECT ARRAY_AGG(d) FROM TinyTable);

update BigTable 
set value = TinyTable.value 
from 
TinyTable where 
BigTable.date_time in UNNEST(date_filter) --This prunes the partition to be scanned
AND
BigTable.date_time = TinyTable.date_time 
and 
BigTable.id = TinyTable.id; 

【讨论】:

【参考方案2】:

可能的解决方案 1:

    从某个分区获取所有数据并保存到临时表中 对临时表执行更新/合并语句 用临时表内容重写分区

对于第 3 步 - 您可以使用 $ 装饰器访问某些分区:Dataset.BigTable$20190926

可能的解决方案 2:

您可以安排 python 脚本运行 SQL 查询,如上一个。谷歌提供nice library。您甚至可以使用来自concurrent.futuresThreadPoolExecutor 或任何其他线程库并行运行它们。

【讨论】:

感谢您的提示。我目前正在使用类似解决方案 2 的 Java 客户端库。我维护一个需要更新的日期表,对其进行查询,然后动态生成一个包含日期文字的查询。只是看起来很傻。我以为我只是错过了一些东西。没想到解决方案 1。这很有趣……给了我一些新的思考。

以上是关于通过联接合并到 BigQuery 分区表中,无需扫描整个表的主要内容,如果未能解决你的问题,请参考以下文章

合并 2 个分区表 BigQuery

BigQuery 无法识别联接中子选择的字段

流插入,然后定期合并到 Dataflow 管道中的 BigQuery [关闭]

联接 (SQL Server)

通过 CLI 将存储桶中的 AVRO 加载到具有日期分区的 BigQuery 中

我可以通过单个查询将多个 BigQuery 列合并到一个重复字段中吗?