BigQuery:如何将字段添加到 REPEATED 记录?

Posted

技术标签:

【中文标题】BigQuery:如何将字段添加到 REPEATED 记录?【英文标题】:BigQuery: How do I add a field to a REPEATED record? 【发布时间】:2020-04-03 15:16:39 【问题描述】:

我在 Google BigQuery 中有一个表,其中包含几个字段,然后是一个 REPEATED 记录,其中可能包含一个或多个对象。我想在 REPEATED 数据中创建一个带有额外字段的新表,并将我的原始数据复制到新表中,用 GENERATE_UUID() 的输出填充新字段,以便每个 REPEATED 数据行有一个唯一标识符。

我在How do I copy from one BigQuery Table to another when the target contains REPEATED fields? 有一个类似的问题,但我不知道如何调整它以适应我当前的用例。

这是我的“新”架构 1(即上述链接中的架构 2)

[
    "name": "id", "type": "NUMERIC", "mode": "REQUIRED",
    "name": "name", "type": "STRING", "mode": "REQUIRED",
    "name": "created", "type": "TIMESTAMP", "mode": "REQUIRED",
    "name": "valid", "type": "BOOLEAN", "mode": "REQUIRED",
    "name": "parameters", "type": "RECORD", "mode": "REPEATED", "fields":
        [
            "name": "parameter1", "type": "STRING", "mode": "REQUIRED",
            "name": "parameter2", "type": "FLOAT", "mode": "REQUIRED",
            "name": "parameter3", "type": "BOOLEAN", "mode": "REQUIRED"
        ]
    
]

我希望它以这样的方式结束,架构 2:

[
    "name": "id", "type": "NUMERIC", "mode": "REQUIRED",
    "name": "name", "type": "STRING", "mode": "REQUIRED",
    "name": "created", "type": "TIMESTAMP", "mode": "REQUIRED",
    "name": "valid", "type": "BOOLEAN", "mode": "REQUIRED",
    "name": "parameters", "type": "RECORD", "mode": "REPEATED", "fields":
        [
            "name": "uuid", "type": "STRING", "mode": "REQUIRED",
            "name": "parameter1", "type": "STRING", "mode": "REQUIRED",
            "name": "parameter2", "type": "FLOAT", "mode": "REQUIRED",
            "name": "parameter3", "type": "BOOLEAN", "mode": "REQUIRED"
        ]
    
]

所以我已经用这个模式创建了我的新表(表 2)。我想从表 1 中复制,我正在尝试这样的事情:

insert into table2_with_uuid(id, name, created, valid, parameters)
select id, name, created, valid,
[(
GENERATE_UUID(), parameters.parameter1, parameters.parameter2, parameters.parameter3
)]
from table1_no_guid;

这给了我一个错误说: Cannot access field ceId on a value with type ARRAY<STRUCT<parameter1(等)

有人对如何进行有任何建议吗?谢谢!

【问题讨论】:

【参考方案1】:

我已按照官方文档中Data Manipulation Language syntax 中的程序进行操作。

那么,基本上你想要的就是update repeated records。我遵循了所有示例,从插入到更新,直到将第二条评论添加到重复记录的那一刻。

然后我应用了 UNNEST 查询:

insert into `testing.followingDMLmod`  (product, quantity, supply_constrained, comments)
select product, quantity, supply_constrained,
[(
GENERATE_UUID(), com.created, com.comment
)]
from `testing.followingDML` , UNNEST(comments) com;

这当然有效,但不能提供所需的结果。

根据official documentation,“BigQuery 本身支持多种架构更改,例如向记录添加新的嵌套字段或放宽嵌套字段的模式。”然后,也许路径是复制表,然后添加额外的字段。

这可以通过管理表模式documentation 来完成。也就是说,要么使用 API 并调用 tables.patch,这在 this 其他堆栈溢出帖子中进行了更详细的讨论,要么使用带有命令行模式的 JSON 文件。

我个人遵循了第二种方法(JSON 模式文件)并且对我来说非常有效。更详细地说,我遵循的步骤是(在here 中找到):

使用 BigQuery UI 中的复制表来获取不带“id”的表的副本。我的起始表是followDML,副本是followDMLmod。

通过在 Cloud Shell 中运行以下命令,将表中的架构复制到 JSON 文件(此处称为 myschema.json)中

bq show \
--schema \
--format=prettyjson \
testing.followingDMLmod > myschema.json
在文本编辑器中打开架构。例如运行
vim myschema.json
现在修改架构以将新的嵌套列添加到字段数组的末尾。 (如果您从未使用过 vim,一个非常简单的解释是“esc”将您返回到正常模式,而在正常模式下单击“i”允许您写入打开的文件,“:w”保存文件并“ :q" 退出文件) 我包含了“id”字段:

  "mode": "NULLABLE",
  "name": "id",
  "type": "STRING"

现在您需要通过运行更新架构
bq update testing.followingDMLmod myschema.json

最后,回到我使用查询的 BigQuery UI

UPDATE `testing.followingDMLmod` 
SET comments = ARRAY(
    SELECT AS STRUCT * REPLACE(GENERATE_UUID() AS id)
    FROM UNNEST(comments)
  )
WHERE true

在创建后填充 id 字段。遵循this 堆栈溢出帖子中的建议。现在最终的结果确实是预期的!

【讨论】:

感谢您的大量详细和深思熟虑的回复,非常感谢。我遵循你的推理,但我已经无法知道如何处理 tables.get 和 tables.patch API Explorers - 我之前没有使用过这些,也没有使用任何 API Explorer,只有 BigQuery Web UI。另外,当您说使用 JSON 文件的“命令行”时,您可以对此进行扩展吗?您是指 BigQuery UI 右上角的 Cloud Shell 链接吗?对不起,这也是新手。 JSON 文件中还有什么内容? 没问题,我将在我的回复中明确包含我从 Cloud Shell 中的命令行执行此操作所遵循的步骤。 我已经包含了这些步骤,如果其中任何一个不清楚,请告诉我。确实可以使用 UI 右上角的链接打开 Cloud Shell。 感谢扩展答案!非常熟悉 vi 顺便说一句:D 我可能会在明天进行测试,但再次感谢您的帮助并扩展原始答案。 嗨 - 再次感谢您提供详细的步骤。如果我理解正确,这就是我们正在做的事情: 1. 通过 BigQuery UI 创建原始表的相同副本(无 uuid) 2. 在命令行中,将新表的架构提取到一些 json 3. 将 json 编辑为添加新字段“重复”部分 4. 在命令行中使用 bq update 使用新的 json 更改表的架构 5. 在 UI 中运行 UPDATE 以将 uuid 移动到新字段中。 (继续)【参考方案2】:

每个人都是正确的。而且不正确。 unnest 将原始数据替换为每个重复记录一行。试试这个查询:

insert into dummydata_withuuid (id, name, created, valid, parameters)
select id, name, created, valid,
[(
GENERATE_UUID(), parameters.parameter1, parameters.parameter2, parameters.parameter3
)]
from dummydata_nouuid;

在第一个 parameters.parameter1 上显示错误,“Cannot access field parameter1 on a value with type ARRAY> at [5:29]”

但是,删除insert into...并修改如下行,查询有效。

-- insert into dummydata_withuuid (id, name, created, valid, parameters)
select id, name, created, valid,
[(
GENERATE_UUID(), parameters
)]
from dummydata_nouuid;

我可以将结果另存为另一个表格,这对于获得我需要的答案还有很长的路要走。我需要在我的insert into... 行中修改某些内容以使查询有效吗?

【讨论】:

如果我使用这个新查询,我得到的结果与上一个选项完全相同,但缺点是现在必须创建一个新表。此外,底层架构不一样(不会是 Schema2)。我仍然不明白为什么前面的查询对您不起作用。 UNNEST 用每个重复记录一行替换原始数据,但在选择它们后,我们将它们重新组合成一种记录类型,最终得到 Schema2。你能分享一个过去的语法(使用 UNNEST)失败的虚拟例子吗?你的预期结果是什么?谢谢。 是的,我同意您关于 UNNEST 为每条重复记录创建一行的说法——我不确定的是“在选择它们之后,我们将它们重新组合成一个记录类型”。假设我们有一个约定好的平面表 - 什么是 select 让我们回到一个重复记录的表?这也不需要中间表吗? 您好托尼,我一直在调查并找到一种方法来实现您想要的。为了清楚起见,我决定发布一个包含所有步骤的答案。 谢谢!我将对其进行测试并在下面发布 cmets/问题。【参考方案3】:

在我发布之前,我已经设法找到了这个问题的答案 - 但我认为分享该方法对其他人很有用。这是有效的查询:

insert into table2_with_uuid(id, name, created, valid, parameters)
select id, name, created, valid,
[(
GENERATE_UUID(), params.parameter1, params.parameter2, params.parameter3
)]
from table1_no_guid, UNNEST(parameters) params;

希望这是有用的!请随时添加到我的结果或评论以继续对话。

【讨论】:

这显然是完全错误的!你实际测试过吗? 为什么这个解决方案无效?我现在已经对其进行了测试,尽管它对行进行了重新排序,但 id、name 等与正确的参数对齐,并且每个参数都生成了一个 UUID。您需要保留订单吗? 感谢您的回复。我将使用另一个数据集重新测试以确保,但要回答您的问题,不,顺序并不重要 - 尽管我假设分区和集群将被保留。 请参阅我在上面发布的第二个“答案”以继续讨论。谢谢!

以上是关于BigQuery:如何将字段添加到 REPEATED 记录?的主要内容,如果未能解决你的问题,请参考以下文章

使用现有 JSON 文件,如何将此数据上传到 BigQuery 并使用 JSON 文件中的数据计算新字段?

如何将查询参数添加到 BigQuery 并将其连接到 Data Studio?

将 CSV 导入 BigQuery 中的表时无法添加字段

向 BigQuery 中的历史表添加新字段

将列动态添加到现有 BigQuery 表

如何将日期时间字符串上传到“TIME”字段 bigquery