使用间接架构更改更新 BigQuery 视图

Posted

技术标签:

【中文标题】使用间接架构更改更新 BigQuery 视图【英文标题】:Update BigQuery view with indirect schema changes 【发布时间】:2019-01-04 11:22:42 【问题描述】:

更新视图时,似乎没有发现间接架构更改。

重现步骤

使用field1 字段创建view1(例如SELECT 1 AS field1) 创建view2view1 中选择所有字段 更新view1,使其也包括field2(例如SELECT 1 AS field1, 2 AS field2) 使用与以前相同的查询更新view2(由于documented limitation)

期望的结果

view1view2 的架构,包括 field1field2 视图更新应该是原子的

实际结果

view1 的架构已正确更新(包括 field1field2view2 的架构仅包括 field1view2 中选择实际上会返回field1field2

我可以删除 view2 并重新创建它,但这不是原子操作,而且有时视图不可用,这是不希望的。

我还尝试更新 view2 的架构属性,但被 Cannot add fields (field: field2) 拒绝:

google.api_core.exceptions.BadRequest: 400 PATCH https://www.googleapis.com/bigquery/v2/projects/<project-id>/datasets/dataset1/tables/view2: 提供的架构与表 :dataset1.view2 不匹配。无法添加字段(字段:field2)

问题

有什么方法可以自动更新视图,同时更新间接更改的架构(视图从中选择的表/视图)。

注意:当然我的 view2 会添加额外的字段,我目前可以通过创建一个新的临时视图来确定它的架构。

注意:架构很重要,因为 Data Studio 的 BigQuery 连接器等工具正在检查架构。

重现步骤的代码

# Python 3.6+
import google.api_core.exceptions
from google.cloud import bigquery


def delete_table_if_exists(client: bigquery.Client, table: bigquery.Table):
    try:
        client.delete_table(table)
    except google.api_core.exceptions.NotFound:
        pass


def full_table_id(table: bigquery.Table) -> str:
    # Note: the documentation says it should be separated by a dot but uses a colon
    return table.full_table_id.replace(':', '.')


def view_test():
    client = bigquery.Client()

    dataset_ref = client.dataset('dataset1')
    try:
        client.create_dataset(dataset_ref)
    except google.api_core.exceptions.Conflict:
        pass

    view1 = bigquery.Table(dataset_ref.table('view1'))
    view2 = bigquery.Table(dataset_ref.table('view2'))
    delete_table_if_exists(client, view1)
    delete_table_if_exists(client, view2)

    view1.view_query = 'SELECT 1 AS field1'
    view1 = client.create_table(view1)

    view2.view_query = f'SELECT * FROM `full_table_id(view1)`'
    client.create_table(view2)

    view1.view_query = 'SELECT 1 AS field1, 2 AS field2'
    client.update_table(view1, ['view_query'])

    client.update_table(view2, ['view_query'])
    print('view2 schema:', client.get_table(view2).schema)

    # trying to update the schema fails with 'Cannot add fields (field: field2)'
    view2.schema = client.get_table(view1).schema
    client.update_table(view2, ['schema'])


if __name__ == '__main__':
    view_test()

Bash 示例做同样的事情

#!/bin/bash

set -e

project_id=$(gcloud config list --format 'value(core.project)' 2>/dev/null)

bq mk -f dataset1

bq rm -f dataset1.view1
bq rm -f dataset1.view2

bq mk --use_legacy_sql=false --view 'SELECT 1 AS field1' dataset1.view1
bq mk --use_legacy_sql=false --view 'SELECT * FROM `'$project_id'.dataset1.view1`' dataset1.view2

bq update --use_legacy_sql=false --view 'SELECT 1 AS field1, 2 AS field2' dataset1.view1
bq update --use_legacy_sql=false --view 'SELECT * FROM `'$project_id'.dataset1.view1`' dataset1.view2

bq show dataset1.view2

更新:接受答案的代码

Python 代码

def get_create_or_replace_view_query(view: bigquery.Table) -> str:
    return f'CREATE OR REPLACE VIEW view.dataset_id.view.table_id AS view.view_query'


def view_test():
    # ...
    query_job = client.query(get_create_or_replace_view_query(view2))
    query_job.result()
    print('view2 schema:', client.get_table(view2).schema)

重击魔法

bq query --use_legacy_sql=false 'CREATE OR REPLACE VIEW dataset1.view2 AS SELECT * FROM `'$project_id'.dataset1.view1`'

【问题讨论】:

我什么都不懂,如果你说 view2 从 view1 中选择所有字段,那么架构实际上是一个 * 星号运算符,所以如果你修改视图 1,这些字段也可以在视图 2 中使用.该文档清楚地说明了此限制:创建视图时,基础表的模式与视图一起存储。如果在创建视图之后添加、删除列等,则在更新视图之前,报告的架构将不准确。即使报告的架构可能不准确,所有提交的查询都会产生准确的结果cloud.google.com/bigquery/docs/views-intro @Pentium10 我知道 view2 的架构不会自动更新。我很高兴手动更新它,但我不确定如何自动更新。 @Pentium10 我添加了之前只提到过的步骤,我也尝试更新 view2。 您需要使用 PATCH 方法而不是 update 方法。另一方面,为 information_schema 列出了一个 alpha,也加入其中:issuetracker.google.com/issues/35906063 @Pentium10 你有更多的信息吗? update_table 方法似乎正在使用PATCH HTTP 方法。我添加了一个更详细的异常,它表明(当我尝试更新架构时)。 【参考方案1】:

您应该使用CREATE OR REPLACE VIEW 声明;见related documentation。 BigQuery 为所有执行表修改的查询提供 ACID 语义,CREATE OR REPLACE VIEW 也不例外,因此它原子地替换了视图的定义和架构。

【讨论】:

以上是关于使用间接架构更改更新 BigQuery 视图的主要内容,如果未能解决你的问题,请参考以下文章

如何在 Big Query 视图中计算短语

使用应用程序脚本在 Big Query 中创建视图 |错误:对 bigquery.tables.insert 的 API 调用失败并出现错误:缺少必需的参数(第 21 行

如何在每次上传桶时更新Big Query后端数据

更新 BigQuery 架构/在 Java 中添加新列

python中的Web服务,用于获取Big Query中表的架构信息

Bigquery:我们应该在模式更新后等待多长时间才能流式传输数据?