【问题标题】:Scalable Solution to get latest row for each ID in BigQuery为 BigQuery 中的每个 ID 获取最新行的可扩展解决方案
【发布时间】:2017-01-04 10:16:48
【问题描述】:

我有一个很大的表,其中有一个字段ID,另一个字段为collection_time。我想为每个 ID 选择最新记录。不幸的是,(ID, collection_time) 时间的组合在我的数据中并不是唯一的。我只想要最大collection time 的记录之一。我尝试了两种解决方案,但没有一个对我有用:

首先:使用查询

SELECT *  FROM 
(SELECT *, ROW_NUMBER() OVER (PARTITION BY ID ORDER BY collection_time) as rn 
FROM mytable)  where rn=1

这导致Resources exceeded 错误,我猜是因为查询中的ORDER BY

第二 在表和最新时间​​之间使用连接:

(SELECT tab1.* 
FROM mytable AS tab1
INNER JOIN EACH 
(SELECT ID, MAX(collection_time) AS second_time 
FROM mytable GROUP EACH BY ID) AS tab2
ON tab1.ID=tab2.ID AND tab1.collection_time=tab2.second_time) 

这个解决方案对我不起作用,因为(ID, collection_time) 不是唯一的,所以在JOIN 结果中每个ID 会有多行。

我想知道是否有针对 resourcesExceeded 错误的解决方法,或者在我的情况下可以使用不同的查询?

【问题讨论】:

  • mytable 表上有索引吗
  • 你的意思不是我可以通过row_number() 做的吗?不,因为 BigQuery 不会创建索引,而且我在上传数据之前没有将此类字段附加到数据中。
  • 哦,我明白了.. 你还有其他有这些ID's 的表吗?或者这些 ID 是在哪里生成的?
  • @Prdp 我可以通过从该表中选择唯一 ID 来生成此类表。

标签: sql google-bigquery


【解决方案1】:

短且可扩展的版本:

select array_agg(t order by collection_time desc limit 1)[offset(0)].*
from mytable t
group by t.id;

【讨论】:

    【解决方案2】:
    SELECT
      agg.table.*
    FROM (
      SELECT
        id,
        ARRAY_AGG(STRUCT(table)
        ORDER BY
          collection_time DESC)[SAFE_OFFSET(0)] agg
      FROM
        `dataset.table` table
      GROUP BY
        id)
    

    这将为您完成这项工作,并且考虑到架构不断变化的事实,它是可扩展的,您不必更改它

    【讨论】:

      【解决方案3】:

      如果你不关心为每一列写一段代码:

      SELECT ID, 
        ARRAY_AGG(col1 ORDER BY collection_time DESC)[OFFSET(0)] AS col1,
        ARRAY_AGG(col2 ORDER BY collection_time DESC)[OFFSET(0)] AS col2
      FROM myTable
      GROUP BY ID
      

      【讨论】:

      • 如果列具有 NULL 值,此解决方案会导致问题。
      • 跳过 NULL 使用 IGNORE NULLS
      • 这个解决方案是我找了很久的解决方案,谢谢!
      【解决方案4】:

      另一种解决方案,它可能更具可扩展性,因为它避免了对同一个表的多次扫描(上述答案中的自联接和相关子查询都会发生这种情况)。此解决方案仅适用于标准 SQL(取消选中“使用旧版 SQL”选项):

      SELECT 
        ID, 
        (SELECT srow.* 
         FROM UNNEST(t.srows) srow 
         WHERE srow.collection_time = MAX(srow.collection_time))
      FROM
        (SELECT ID, ARRAY_AGG(STRUCT(col1, col2, col3, ...)) srows 
         FROM id_table 
         GROUP BY ID) t
      

      【讨论】:

      • 我无法测试查询,因为where 子句中不允许出现聚合函数错误,我无法用having 重写它。
      【解决方案5】:

      快速而肮脏的选项 - 将您的两个查询合并为一个 - 首先使用最新的 collection_time 获取所有记录(使用您的第二个查询),然后使用您的第一个查询对它们进行重复数据删除:

      SELECT * FROM (
        SELECT *, ROW_NUMBER() OVER (PARTITION BY tab1.ID) AS rn 
        FROM (
          SELECT tab1.* 
          FROM mytable AS tab1
          INNER JOIN (
            SELECT ID, MAX(collection_time) AS second_time 
            FROM mytable GROUP BY ID
          ) AS tab2
          ON tab1.ID=tab2.ID AND tab1.collection_time=tab2.second_time
        )
      )
      WHERE rn = 1  
      

      并且使用标准 SQL(由 S.Mohsen sh 提出)

      WITH myTable AS (
        SELECT 1 AS ID, 1 AS collection_time
      ),
      tab1 AS (
        SELECT ID,
        MAX(collection_time) AS second_time 
        FROM myTable GROUP BY ID
      ),
      tab2 AS (
        SELECT * FROM myTable
      ),
      joint AS (
        SELECT tab2.* 
        FROM tab2 INNER JOIN tab1
        ON tab2.ID=tab1.ID AND tab2.collection_time=tab1.second_time 
      )
      SELECT * EXCEPT(rn) 
      FROM (
        SELECT *, ROW_NUMBER() OVER (PARTITION BY ID) AS rn 
        FROM joint
      )
      WHERE rn=1
      

      【讨论】:

      • 这是一个好主意,但是当我尝试对其进行测试时,我得到了一个错误,tab1.ID 找不到。你知道如何解决这个问题吗?
      • 我将您的解决方案重写为标准 SQL,它运行良好,但我想知道 BigQuery Legacy SQL 中的问题是什么
      • 立即尝试 - 将 PARTITION BY ID 更改为 PARTITION BY tab1.ID 并使用标准 SQL 添加您的选项,只是修复了 WHERE 子句(它是 SELECT)并“规范化”它以使用与问题和初始相同的表名回答。至于原因:旧版 SQL 对别名非常敏感,因此当您遇到诸如 ID 未找到之类的错误时 - 您始终可以运行内部查询并查看它产生的字段名称,以便您可以调整外部查询。无论如何,我的建议似乎对您有所帮助-因此,如果确实有帮助,请考虑投票并接受:o)
      【解决方案6】:

      根据您的评论,考虑到您有一个具有唯一ID's 的表,您需要为其找到最新的collection_time。这是使用Correlated Sub-Query 的另一种方法。试试看吧。

      SELECT id, 
             (SELECT Max(collection_time) 
              FROM   mytable B 
              WHERE  A.id = B.id) AS Max_collection_time 
      FROM   id_table A 
      

      【讨论】:

      • 我认为这是我在SELECT ID, MAX(collection_time) AS second_time FROM mytable GROUP BY ID第二次查询中所做的部分
      • @S.Mohsensh 它不一样。它不会复制结果。它也是相关的。你试过我的解决方案了吗
      • 我评论中的查询也不会产生重复。重复发生在连接中。我没有尝试,因为 BigQuery 中不允许 subselect,但它会生成一组具有唯一 (id, collection_time) 对的行,不是吗?
      • 仅使用标准 SQL 支持相关查询,因此如果取消选中“使用旧版 SQL”选项,查询应该可以工作。
      • @Prdp 是的,我尝试了您的解决方案,我猜它会产生一对(android_id collection_time),就像我的子查询一样。我看到如果存在索引,这可能会有所帮助,但正如我所说的那样,表上没有索引。而且我不知道没有索引它怎么能提供帮助。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-04-27
      • 1970-01-01
      • 1970-01-01
      • 2011-04-08
      • 2016-06-04
      相关资源
      最近更新 更多