【问题标题】:What is the most efficient query to get Latest rows with Group By clause on Joined tables in BigQuery?在 BigQuery 中的联接表上使用 Group By 子句获取最新行的最有效查询是什么?
【发布时间】:2018-10-13 12:35:49
【问题描述】:

当前设计

Table: 1_notes
------------------------------------------
| id  | text           | created_at          |
------------------------------------------
| 1_1 | u1 first note  | 2018-01-01 10:00:00 |
| 1_2 | u1 second note | 2018-01-03 10:00:00 |


Table: 1_note_timeline
---------------------------------------------------------------------
| note_id  | note_created_at     | likes_count | created_at          |
---------------------------------------------------------------------
| 1_1      | 2018-01-01 10:00:00 | 10          | 2018-01-01 10:00:00 |
| 1_1      | 2018-01-01 10:00:00 | 20          | 2018-01-02 10:00:00 |
| 1_2      | 2018-01-03 10:00:00 | 10          | 2018-01-03 10:00:00 |
| 1_1      | 2018-01-01 10:00:00 | 15          | 2018-01-03 10:00:00 |


Table: 2_notes
--------------------------------------------
| id  | text           | created_at            |
--------------------------------------------
| 2_1 | u2 first note  | 2018-01-01 10:00:00 |
| 2_2 | u2 second note | 2018-01-03 10:00:00 |


Table: 2_note_timeline
---------------------------------------------------------------------
| note_id | note_created_at     | likes_count | created_at          |
---------------------------------------------------------------------
| 2_1     | 2018-01-01 10:00:00 | 10          | 2018-01-01 10:00:00 |
| 2_1     | 2018-01-01 10:00:00 | 20          | 2018-01-02 10:00:00 |
| 2_2     | 2018-01-03 10:00:00 | 10          | 2018-01-03 10:00:00 |
| 2_1     | 2018-01-01 10:00:00 | 15          | 2018-01-03 10:00:00 |

对于每个用户,他们的笔记数据有 2 个表。

  • {{userId}}_notes 表包含 ID 为 userId 的用户的注释
  • {{userId}}_note_timeline 表每天跟踪笔记 likes_count 数据

要求的结果应该有:

  • 点赞最多的两个用户的前 2 个注释
  • 点赞数必须是最新的(而不是 MAX_VALUE,因为点赞数会随时间减少)

最终输出

Output
---------------------------------------------------------------
| note_id | note_created_at     | likes_count | text          |
---------------------------------------------------------------
| 1_1     | 2018-01-01 10:00:00 | 15          | u1 first note |
| 2_1     | 2018-01-01 10:00:00 | 15          | u2 first note |

【问题讨论】:

  • 将不同的用户数据存储在不同的表中绝对不是结构化数据的最佳方式。您应该为 所有 个用户提供一组表。
  • @GordonLinoff 这样做会大大增加成本。因为每个用户都有数百万条笔记和数百万条note_timeline。而且我一次只需要查询最多 4 个用户。
  • @GordonLinoff BigQuery 允许按摄取时间或 TIMESTAMP/DATE 列 (cloud.google.com/bigquery/docs/partitioned-tables) 进行分区。因此,根据您的建议,我将无法按用户 ID 对其进行分区。我当前设计的好处是我可以按月对每个用户笔记进行分区。
  • @GordonLinoff 为什么你删除了你的评论?请不要这样做,未来的用户将无法理解上下文。除此之外,BigQuery 还提供了一种自动构建这些相同架构表的方法 (cloud.google.com/bigquery/…)
  • @user2579651 。 . .我认为 BigQuery 已经实现了该功能。在改进的分区发布之前,我的评论只是误导。

标签: sql google-cloud-platform google-bigquery


【解决方案1】:

以下是 BigQuery 标准 SQL

#standardSQL
WITH `project.dataset.user_notes` AS (
  SELECT * FROM `project.dataset.user1_notes` UNION ALL
  SELECT * FROM `project.dataset.user2_notes`
), `project.dataset.user_note_timeline` AS (
  SELECT * FROM `project.dataset.user1_note_timeline` UNION ALL
  SELECT * FROM `project.dataset.user2_note_timeline`
)
SELECT note_id, note_created_at, likes_count, text 
FROM (
  SELECT note_id, ARRAY_AGG(STRUCT(note_created_at, likes_count, created_at) ORDER BY created_at DESC LIMIT 1)[OFFSET(0)].*
  FROM `project.dataset.user_note_timeline`
  GROUP BY note_id
  ORDER BY likes_count DESC, note_created_at
  LIMIT 2
) t
JOIN `project.dataset.user_notes` n
ON note_id  = id

【讨论】:

  • 谢谢,先生,它工作得很好。您能否指导我,当前设计中的缺陷。我真的很害怕我应该采用这种设计吗?现在我可以看到,通过这种设计,我将无法从所有用户那里获取趋势注释(BigQuery 在单个查询中限制 MAX 1000 个表,所以我最多一次可以查询多达 500_user_notes,然后必须以编程方式调用另一个查询,最后必须加入结果但是我怎么能实现分页)
  • 这是一个完全不同的问题——我会考虑的。显然,每个用户有两个表是一个糟糕的主意。你为什么往这个方向走?解释您的思维方式 - 这可能有助于为您提供好的推荐
  • 虽然,你提到了at a time I only need to query at most 4 users at a time - 那你为什么担心MAX 1000?你只需要 8 个 - 所以更好地解释自己
  • 没错,现在我最多需要查询 4 ​​个用户。但我试图预见将来是否必须查询所有用户,而不是采用这种设计。我为每个用户拆分表以降低查询成本。这是唯一的原因。我同意你的看法,这是一个糟糕的主意,但我担心现在节省查询成本可能会在未来花费更多(重新设计/移动数据)
  • 先生,我能想到的将来查询所有用户笔记的最便宜的方法是为每个用户创建一个单独的表,同时创建一个包含 allUsers_note_timeline 的新表(每个 noteId 都有前缀通过 userId 所以不用担心 noteId 重复和按 TIMESTAMP 对该表进行分区)。因此,如果我必须查询多达 500 个用户,我可以使用单独的表,如果我必须查询 > 500 个用户而不是使用 allUsers_note_timeline 表。先生,请建议是否有其他更好更便宜的方法。
【解决方案2】:

希望这能解决您的问题

SELECT TOP 1 t1.note_id, t1.note_created_at, t1.likes_count, t2.[text]
  FROM 1_note_timeline t1 INNER JOIN 1_notes t2 ON t1.note_id = t2.Id
 ORDER BY t1.created_at DESC

UNION

SELECT TOP 1 t1.note_id, t1.note_created_at, t1.likes_count, t2.[text]
  FROM 2_note_timeline t1 INNER JOIN 2_notes t2 ON t1.note_id = t2.Id
 ORDER BY t1.created_at DESC

最好为表引入索引以提高性能。

【讨论】:

    猜你喜欢
    • 2014-05-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-09-19
    • 1970-01-01
    • 2017-09-03
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多