如何在BigQuery表中选择最新的分区?

时间:2016-09-27 20:34:51

标签: google-bigquery

我正在尝试从日期分区的BigQuery表中的最新分区中选择数据,但查询仍然从整个表中读取数据。

我已经尝试过(据我所知,BigQuery不支持QUALIFY):

SELECT col FROM table WHERE _PARTITIONTIME = (
  SELECT pt FROM (
    SELECT pt, RANK() OVER(ORDER by pt DESC) as rnk FROM (
      SELECT _PARTITIONTIME AS pt FROM table GROUP BY 1)
    )
  )
  WHERE rnk = 1
);

但是这不起作用并且读取所有行。

SELECT col from table WHERE _PARTITIONTIME = TIMESTAMP('YYYY-MM-DD')

其中'YYYY-MM-DD'是特定日期确实有效。

但是,我需要在将来运行此脚本,但表更新(和_PARTITIONTIME)是不规则的。有没有办法只从BigQuery的最新分区中提取数据?

7 个答案:

答案 0 :(得分:4)

很抱歉,您想找到这个老问题,但它在Google搜索中出现,我认为公认的答案具有误导性。

据我从documentation进行的测试中得出的结论,接受的答案将修剪分区,因为使用子查询来确定最新的分区:

  

需要解析查询的多个阶段才能解析谓词的复杂查询(例如内部查询或子查询),不会从查询中删除分区。

因此,尽管建议的答案将提供您期望的结果,但仍将查询所有分区。它将忽略所有旧分区,仅查询最新分区。

诀窍是使用或多或少的常量进行比较,而不是使用子查询。例如,如果_PARTITIONTIME并非每天都有规律,请尝试通过获取昨天的分区来修剪分区,如下所示:

SELECT * FROM [dataset.partitioned_table]
    WHERE _PARTITIONDATE = DATE_SUB(CURRENT_DATE(), INTERVAL 1 DAY)

当然,这并不总是最新的数据,但就我而言,这恰好足够接近。如果您需要今天的数据,请使用INTERVAL 0 DAY,并且不在乎该查询在一天中尚未创建分区的那一天的一部分将返回0结果。

很高兴得知是否有更好的解决方法来获取最新的分区!

答案 1 :(得分:1)

列出所有分区:

#standardSQL
SELECT
  _PARTITIONTIME as pt
FROM
  `[DATASET].[TABLE]`
GROUP BY 1

然后选择最新的时间戳。

祝你好运:)

https://cloud.google.com/bigquery/docs/querying-partitioned-tables

答案 2 :(得分:1)

一种折衷方案,可以仅查询几个分区,而不必求助于脚本编写或在固定日期丢失分区而失败。

WITH latest_partitions AS (
  SELECT *, _PARTITIONDATE AS date
  FROM `myproject.mydataset.mytable`
  WHERE _PARTITIONDATE > DATE_SUB(CURRENT_DATE(), INTERVAL 7 DAY)
)
SELECT
  *
FROM
  latest_partitions
WHERE
  date = (SELECT MAX(date) FROM latest_partitions)

答案 3 :(得分:0)

  

更新那些喜欢downvoting而不检查上下文等的人。

我认为,这个答案之所以被接受是因为它解决了OP的主要问题Is there a way I can pull data only from the latest partition in BigQuery?,并且在评论中提到很明显BQ引擎仍然会扫描所有行,但会根据最近的分区返回结果。正如问题评论中已经提到的那样 - Still something that easily to be addressed by having that logic scripted - first getting result of subquery and then use it in final query

尝试

SELECT * FROM [dataset.partitioned_table]
WHERE _PARTITIONTIME IN (
  SELECT MAX(TIMESTAMP(partition_id))
  FROM [dataset.partitioned_table$__PARTITIONS_SUMMARY__]
)  

SELECT * FROM [dataset.partitioned_table]
WHERE _PARTITIONTIME IN (
  SELECT MAX(_PARTITIONTIME) 
  FROM [dataset.partitioned_table]
)

答案 4 :(得分:0)

我找到了解决此问题的方法。您可以使用with语句,选择最后几个分区并过滤出结果。我认为这是更好的方法,因为:

  1. 您不受固定分区日期(例如今天-1天)的限制。它将始终采用给定范围内的最新分区。
  2. 它只会扫描最后几个分区,而不是整个表。

最近3个分区扫描的示例:

WITH last_three_partitions as (select *, _PARTITIONTIME as PARTITIONTIME 
    FROM dataset.partitioned_table 
    WHERE  _PARTITIONTIME > TIMESTAMP_SUB(CURRENT_TIMESTAMP(), INTERVAL 3 DAY))
SELECT col1, PARTITIONTIME from last_three_partitions 
WHERE PARTITIONTIME = (SELECT max(PARTITIONTIME) from last_three_partitions)

答案 5 :(得分:0)

您可以利用表的__TABLES__列表来避免重新扫描所有内容,或者不必希望最新分区在3天之前。我做了splitordinal的工作,以防我的表前缀由于某种原因在表名中多次出现。

这应该适用于_PARTITIONTIME_TABLE_SUFFIX

select * from `project.dataset.tablePrefix*` 
where _PARTITIONTIME = (
    SELECT split(table_id,'tablePrefix')[ordinal(2)] FROM `project.dataset.__TABLES__` 
    where table_id like 'tablePrefix%'
    order by table_id desc limit 1)

答案 6 :(得分:0)

我在一个不太受欢迎的问题中得到了这个答案,因此将其复制到相关的位置(此问题将获得更多的浏览量):

Mikhail的答案看起来像这样(处理公共数据):

SELECT MAX(views)
FROM `fh-bigquery.wikipedia_v3.pageviews_2019` 
WHERE DATE(datehour) = DATE_SUB(CURRENT_DATE(), INTERVAL 7 DAY)     
AND wiki='es' 
# 122.2 MB processed

但是问题似乎想要这样:

SELECT MAX(views)
FROM `fh-bigquery.wikipedia_v3.pageviews_2019` 
WHERE DATE(datehour) = (SELECT DATE(MAX(datehour)) FROM `fh-bigquery.wikipedia_v3.pageviews_2019` WHERE wiki='es')     
AND wiki='es'
# 50.6 GB processed

...但小于50.6GB

现在您需要某种脚本,以2个步骤执行此操作:

max_date = (SELECT DATE(MAX(datehour)) FROM `fh-bigquery.wikipedia_v3.pageviews_2019` WHERE wiki='es')   

;

SELECT MAX(views)
FROM `fh-bigquery.wikipedia_v3.pageviews_2019` 
WHERE DATE(datehour) = {{max_date}}
AND wiki='es'
# 115.2 MB processed

您将不得不在BigQuery之外编写脚本-或等待https://issuetracker.google.com/issues/36955074上的新闻。