BigQuery中的行级原子MERGE REPLACE

时间:2018-07-04 10:29:48

标签: mongodb google-bigquery etl acid

对于我的用例,我正在使用源处的唯一键可识别的数据分解为n个(不确定的)数量的目标条目,这些条目已加载到BigQuery表中以进行分析。

构建此ETL以使用Mongo最近的变更流功能我想删除BigQuery中的所有条目,然后自动加载新条目。

探索BigQuery DML,我看到支持MERGE操作,但只能使用WHEN MATCHED THEN DELETEWHEN MATCHED THEN UPDATE

我对何时匹配然后删除并按照INSERT 操作感兴趣。

我如何在BigQuery中实现这样的ETL,同时在数据可用性和正确性方面保持原子性或最终一致性。


编辑1:我想提供一个具体的例子来详细说明。

我在此数据集上具有的最小唯一性粒度为user_id。行不是唯一可识别的。

示例

1。

从mongo更改流收到的更新的用户对象:

user={_id: "3", name="max", registered="2018-07-05" q=["a", "b", "c"]}

2。

当前BigQuery.user_q保持

| user_id | q |
...
|    3    | a |
|    3    | b |
...

3。

转换代码将修改后的用户对象加载到BigQuery.user_q_incoming

| user_id | q |
|    3    | a |
|    3    | b |
|    3    | c |

4。

user_quser_q_incoming之间合并:

  1. user_q中属于user_id 3的2行被删除
  2. user_q_incoming中,属于user_id 3
  3. 3行已插入。
  4. ...中的其余数据(user_q)保留不变,未修改。

5。

BigQuery.user_q保存

| user_id | q |
...
|    3    | a |
|    3    | b |
|    3    | c |
...

例如,用户可以从个人资料中删除问题。将其余的行保留为q=["a", "c"]。我还需要将其转换为BigQuery世界视图。

2 个答案:

答案 0 :(得分:2)

BigQuery DML支持INSERT

  

MERGE语句是DML语句,可以将INSERT,UPDATE和DELETE操作组合为单个语句,并自动执行操作。

例如

MERGE dataset.Inventory T
USING dataset.NewArrivals S
ON FALSE
WHEN NOT MATCHED AND product LIKE '%washer%' THEN
  INSERT (product, quantity) VALUES(product, quantity)
WHEN NOT MATCHED BY SOURCE AND product LIKE '%washer%' THEN
  DELETE   

因此,您最好使用ETL

  

根据添加到问题的更多具体细节进行编辑

好的,我知道-我认为在这种情况下,MERGE将不适用,因为只能对NOT MATCH子句执行INSERT。有人可能会想出在这种情况下如何使MERGE起作用的方法,但与此同时,下面的解决方案可以实现您想要实现的目标-我认为是:o)

CREATE OR REPLACE TABLE `project.dataset.user_q` (user_id INT64, q STRING) AS
SELECT * FROM `project.dataset.user_q`
WHERE NOT user_id IN (SELECT DISTINCT user_id FROM `project.dataset.user_q_incoming`)
UNION ALL
SELECT * FROM `project.dataset.user_q_incoming`
WHERE user_id IN (SELECT DISTINCT user_id FROM `project.dataset.user_q`)

答案 1 :(得分:1)

有一个类似的问题和一个解决方法,可以使MERGE(https://issuetracker.google.com/issues/35905927#comment9)工作。

基本上,以下操作应该有效,

MERGE `project.dataset.user_q` T
USING (
  SELECT *, false AS is_insert FROM `project.dataset.user_q_incoming`
UNION ALL
  SELECT *, true AS is_insert FROM `project.dataset.user_q_incoming`
) S
ON T.user_id = S.user_id and NOT is_insert
WHEN MATCHED THEN
  DELETE
WHEN NOT MATCHED AND is_insert THEN
  INSERT(user_id, q) VALUES(user_id, q)

理想情况下,以下是您所需要的,但尚不支持。

MERGE `project.dataset.user_q`
USING `project.dataset.user_q_incoming`
ON FALSE
WHEN NOT MATCHED BY TARGET THEN
  INSERT(user_id, q) VALUES(user_id, q)
WHEN NOT MATCHED BY SOURCE AND user_id in (SELECT user_id FROM `project.dataset.user_q_incoming`) THEN
  DELETE