【问题标题】:Select only one row for each "ID" in a nested repeated schema in BigQueryBigQuery 中嵌套重复架构中的每个“ID”仅选择一行
【发布时间】:2017-01-01 11:08:37
【问题描述】:

我在 BigQuery 中有一个表,其中包含一个 ID 字段和一个重复记录字段以及其他一些字段,例如数据收集时间。

每个 ID 在此表中有多行,我想以某种方式为每个 ID 选择/合并一行。在我的用例中,几乎所有解决方案(例如选择最后一个、选择第一个和在一行中聚合具有重复 ID 的行)都是可以接受的,但到目前为止我无法让它们中的任何一个工作。

更准确地说,我的表有一个 ID 字段,在 BigQuery 术语中是: {name: ID, type: STRING, mode: NULLABLE} 和重复字段:{name: data, type: RECORD, mode: REPEATED} 以及其他一些(普通)字段。 在我的表中,每个 ID 都有多行,每个 ID 都有一个重复字段 data 。在我的查询结果中,我想要一个具有完全相同架构的表,其中每个 ID 只出现一次,并且其对应的 data 字段是来自原始表中出现的 ID 之一的 data 字段。 (或理想情况下的所有事件的联合)

这里列出了在这里不起作用的解决方案:

首先:使用

row_number() OVER (PARTITION BY ID ORDER BY collection_time) as rn ... where  rn=1

原因:BigQuery 在使用 partition by 时会展平结果,即使使用了 Unflatten Results 选项也是如此。

第二:选择具有最大/最小收集时间值的行:

因为:由于系统其他部分的一些重复,对于我的表中的每个 id,列的值不是唯一的。

第三:在其他字段上使用group by ID 和nest/first

原因: 在重复记录上使用nest 会破坏记录字段中的关系。例如SELECT ID, nest(data.a), nest(data.b)来自:

ID     data.a      data.b
--------------------------
1      1a1          null
       1a2          1b2
--------------------------
1      2a1          2b1
       null         2b2

结果

ID      data.a       data.b
----------------------------
1        1a1         1b2
         1a2         2b1
         2a1         2b2

【问题讨论】:

  • 所以你有不同的行有相同的ID,每一行都有重复的字段,你想把重复的结构合并成一个结构吗?
  • @Pentium10 是的,但我在“合并”方面很灵活,因此每个 ID 只选择一行也是可以接受的。
  • 让我们定义基数,因为它令人困惑,所以行意味着插入时的整行,例如定义ID 的行。然后repeated record 表示重复,所以它大于 1。合并后您希望纯记录仅 1 个条目,还是希望在合并后保留所有条目?
  • @Pentium10 我在问题中添加了一段,希望我能够清楚地描述问题。
  • 在您想要的结果中 - 您不匹配 data.a 和 data.b 以致不再保留初始的 a/b 对。有预期的逻辑吗?所以 a 和 b 之间没有硬性关系?

标签: sql google-bigquery


【解决方案1】:

使用standard SQL 可以更轻松地解决此问题(取消选中“显示选项”下的“使用旧版 SQL”)。您可以将GROUP BYARRAY_CONCAT_AGG 一起使用,例如:

SELECT id, ARRAY_CONCAT_AGG(data) AS data
FROM MyTable
GROUP BY id;

【讨论】:

  • 难道没有办法使用旧版 SQL 做到这一点吗?使用标准 SQL 时,我的表中出现了一些关于无效时间戳值的奇怪错误(而我可以使用旧版 SQL 毫无问题地查询表)
  • 标准 SQL 对不允许无效时间戳更加严格。我们正在研究一种解决方案,以便更轻松地避免一开始就将这些内容提取到表格中,尽管我现在没有可以指出的未解决问题。
  • 为了跟进这一点,我们确实实现了一些功能来启用可能无效的时间戳。更多信息在这里:code.google.com/p/google-bigquery/issues/detail?id=730.
【解决方案2】:

在标准 SQL 模式下尝试以下操作

SELECT id, ARRAY_AGG(STRUCT(a, b)) AS data
FROM (
  SELECT id, a, ROW_NUMBER() OVER() AS num 
  FROM YourTable, UNNEST(data) WHERE NOT a IS NULL 
) FULL OUTER JOIN (
  SELECT id, b, ROW_NUMBER() OVER() AS num 
  FROM YourTable, UNNEST(data) WHERE NOT b IS NULL 
)  
USING(id, num) 
GROUP BY id  

它为您提供exactly 您在问题中所期望的结果(消除了 NULL):

ID      data.a       data.b
----------------------------
1        1a1         1b2
         1a2         2b1
         2a1         2b2

如果(另一方面)您想要保留原始 a/b 对 - 您应该在下面使用(仍处于标准 SQL 模式)

SELECT id, ARRAY_CONCAT_AGG(data) AS data
FROM YourTable
GROUP BY id

这会给你下面的结果

ID      data.a       data.b
----------------------------
1        1a1         null
         1a2         1b2
         2a1         2b1
         null        2b2

您可以通过针对您的实际表运行它们来测试这两个查询(将YourTable 更改为您的实际表 -> `project.dataset.table`),或者通过在相应的查询前面添加以下代码并按原样运行

WITH YourTable AS (
  SELECT 1 AS id, ARRAY<STRUCT<a STRING, b STRING>>[('1a1', NULL),('1a2','1b2')] AS data UNION ALL
  SELECT 1 AS id, ARRAY<STRUCT<a STRING, b STRING>>[('2a1', '2b1'),(NULL,'2b2')] AS data 
)

【讨论】:

  • 我在问题中给出的示例不是预期的是第三个解决方案不起作用的原因。
  • 不确定这是否意味着您的问题已得到解答。如果不是 - 你能澄清一下吗?
  • 我再次阅读了您的问题,我现在看到了 - 第三个示例是未预期的示例。很抱歉造成混乱。所以这意味着 Elliott 的答案就是你所需要的
  • 是的,感谢您的回答。我只是想指出您的第一个解决方案在这里没有做我想要的,但第二个解决方案就像@Elliott Brossard 所建议的那样完成了工作。
猜你喜欢
  • 2018-11-28
  • 2019-02-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-03-02
  • 2019-05-17
  • 2016-06-30
  • 1970-01-01
相关资源
最近更新 更多