如何针对具有记录字段的表创建视图?

Posted

技术标签:

【中文标题】如何针对具有记录字段的表创建视图?【英文标题】:How to create a view against a table that has record fields? 【发布时间】:2014-05-24 00:19:55 【问题描述】:

我们有一个每周备份流程,将我们的生产 Google Appengine Datastore 导出到 Google Cloud Storage,然后到 Google BigQuery。每周,我们都会创建一个名为 YYYY_MM_DD 的新数据集,其中包含当天生产表的副本。随着时间的推移,我们收集了许多数据集,例如2014_05_102014_05_17 等。我想创建一个数据集Latest_Production_Data,其中包含最新YYYY_MM_DD 数据集中每个表的视图。这将使下游报告更容易编写一次查询并始终检索最新数据。

为此,我的代码从 BigQuery API 获取最新数据集和数据集包含的所有表的名称。然后,对于这些表中的每一个,我都会调用 tables.insert 来创建一个视图,该视图是我要创建引用的表中的 SELECT *

对于包含 RECORD 字段的表,这将失败,这看起来是一个非常良性的列命名规则。

例如,我有这张桌子:

为此我发出此 API 调用:


  'tableReference': 
    'projectId': 'redacted',
    'tableId': u'AccountDeletionRequest',
    'datasetId': 'Latest_Production_Data'
  
  'view': 
    'query': u'SELECT * FROM [2014_05_17.AccountDeletionRequest]'
  ,

这会导致以下错误:

HttpError: https://www.googleapis.com/bigquery/v2/projects//datasets/Latest_Production_Data/tables?alt=json 返回“无效的字段名称”__key__.namespace。字段只能包含字母、数字、和下划线,以字母或下划线开头,长度最多为 128 个字符。">

当我在 BigQuery 网络控制台中执行此查询时,列被重命名以将 . 转换为 _。当我发出创建视图 API 调用时,我有点预料到会发生同样的事情。

是否有一种简单的方法可以以编程方式为我的数据集中的每个表创建一个视图,而不管它们的底层架构如何?我现在遇到的问题是记录列,但我预计的另一个问题是具有重复字段的表。 SELECT * 是否有一些神奇的替代方案可以为我处理所有这些错综复杂的问题?

我的另一个想法是做一个table copy,但如果可以避免的话,我宁愿不复制数据。

【问题讨论】:

我记得 BQ 开发人员说过一些关于视图的一些错误,SELECT *。你可以尝试一下 SELECT 语句中明确提到所有字段名称吗? 我想这会起作用,但它需要读取每个表的模式来生成定义每个视图的 SQL。相反,我可能会退回到表格复制方法。 【参考方案1】:

这是我为每个表动态生成SELECT 语句而编写的workaround code:

def get_leaf_column_selectors(dataset, table):
    schema = table_service.get(
            projectId=BQ_PROJECT_ID,
            datasetId=dataset,
            tableId=table
        ).execute()['schema']

    return ",\n".join([
        _get_leaf_selectors("", top_field)
        for top_field in schema["fields"]
    ])


def _get_leaf_selectors(prefix, field):
    if prefix:
        format = prefix + ".%s"
    else:
        format = "%s"

    if 'fields' not in field:
        # Base case
        actual_name = format % field["name"]
        safe_name = actual_name.replace(".", "_")
        return "%s as %s" % (actual_name, safe_name)
    else:
        # Recursive case
        return ",\n".join([
            _get_leaf_selectors(format % field["name"], sub_field)
            for sub_field in field["fields"]
        ])

【讨论】:

我修改了更大的要点来处理重命名的sub_field 与同名的***字段发生冲突的情况。在我的例子中,一个表在顶层同时有一个 user.email 和一个 user_email 字段,所以前者在重命名时会与后者发生冲突。 今天,我注意到这段代码导致了这个错误:"Cannot create valid output schema for field _next_tasks_path. Try renaming _next_tasks_path to next_tasks._next_tasks_path in the outermost SELECT." 我的测试表明SELECT * 现在可以定义一个视图,所以我将从我的项目中删除这段代码。【参考方案2】:

我们遇到了一个错误,您需要在视图中选择单个字段并使用“as”将字段重命名为合法的名称(即名称中没有“.”)。

该错误现已修复,因此您不应再看到此问题。如果您再次看到它,请 ping 此线程或开始一个新问题。

【讨论】:

嗯,我想我应该写我的评论作为答案。 @Jordan:根据最近的行为,我相信这个错误已经得到解决。您能否确认和/或指出任何可能感兴趣的细节?

以上是关于如何针对具有记录字段的表创建视图?的主要内容,如果未能解决你的问题,请参考以下文章

如何创建包含时间戳列的表视图?

基于三个不同的表创建视图

更新脚本以针对具有递增 ID 的新创建的表行运行

如何创建一个存储视图数据的表 - MySQL

基于 XML 字段创建视图

mysql---视图