来自 Pavan 的回答:请注意,这种方法会根据您查询的次数向您收取查询的源表的扫描成本。
来自 Pentium10 cmets:所以假设我有几年的数据,我需要为每天准备不同的查询并运行所有数据,假设我有 1000 天的历史记录,我需要支付 1000 倍的全部查询价格来自源表?
正如我们所见 - 这里的主要问题是每天都进行全面扫描。其余的问题不大,可以在任何client of the choice 中轻松编写脚本
那么,下面是 - 如何在避免每天进行全表扫描的同时对表进行分区?
下面一步一步显示方法
它足够通用,可以扩展/适用于任何实际用例 - 同时我正在使用 bigquery-public-data.noaa_gsod.gsod2017,并且我将“练习”限制为仅 10 天以保持其可读性
第 1 步 - 创建数据透视表
在这一步中,我们
a) 将每一行的内容压缩到记录/数组中
和
b)将它们全部放入各自的“每日”列
#standardSQL
SELECT
ARRAY_CONCAT_AGG(CASE WHEN d = 'day20170101' THEN r END) AS day20170101,
ARRAY_CONCAT_AGG(CASE WHEN d = 'day20170102' THEN r END) AS day20170102,
ARRAY_CONCAT_AGG(CASE WHEN d = 'day20170103' THEN r END) AS day20170103,
ARRAY_CONCAT_AGG(CASE WHEN d = 'day20170104' THEN r END) AS day20170104,
ARRAY_CONCAT_AGG(CASE WHEN d = 'day20170105' THEN r END) AS day20170105,
ARRAY_CONCAT_AGG(CASE WHEN d = 'day20170106' THEN r END) AS day20170106,
ARRAY_CONCAT_AGG(CASE WHEN d = 'day20170107' THEN r END) AS day20170107,
ARRAY_CONCAT_AGG(CASE WHEN d = 'day20170108' THEN r END) AS day20170108,
ARRAY_CONCAT_AGG(CASE WHEN d = 'day20170109' THEN r END) AS day20170109,
ARRAY_CONCAT_AGG(CASE WHEN d = 'day20170110' THEN r END) AS day20170110
FROM (
SELECT d, r, ROW_NUMBER() OVER(PARTITION BY d) AS line
FROM (
SELECT
stn, CONCAT('day', year, mo, da) AS d, ARRAY_AGG(t) AS r
FROM `bigquery-public-data.noaa_gsod.gsod2017` AS t
GROUP BY stn, d
)
)
GROUP BY line
在 Web UI 中以 pivot_table(或任何首选名称)作为目标运行上述查询
如我们所见 - 在这里我们将获得 10 列的表 - 一天一列,每列的架构是原始表架构的副本:
第 2 步 – 一个接一个地处理分区,只扫描相应的列(没有全表扫描) – 插入到相应的分区中
#standardSQL
SELECT r.*
FROM pivot_table, UNNEST(day20170101) AS r
使用名为 mytable$20160101 的目标表从 Web UI 运行上述查询
你可以在第二天继续运行
#standardSQL
SELECT r.*
FROM pivot_table, UNNEST(day20170102) AS r
现在你的目标表应该是 mytable$20160102 等等
您应该能够使用您选择的任何客户端自动执行此步骤/编写脚本
您可以如何使用上述方法有很多变体 - 这取决于您的创造力
注意:BigQuery 最多允许表中包含 10000 列,因此一年中的相应天数为 365 列绝对不是问题 :o)
除非对新分区的回溯有限制——我听说(但还没有机会检查)现在回溯不超过 90 天
更新
请注意:
上面的版本有一点额外的逻辑,将所有聚合单元格打包成尽可能少的最终行数。
ROW_NUMBER() OVER(PARTITION BY d) AS line
然后
GROUP BY line
还有
ARRAY_CONCAT_AGG(…)
这样做
当原始表中的行大小没有那么大时,这很有效,因此最终组合的行大小仍将在 BigQuery 的行大小限制内(我认为目前为 10 MB)
如果您的源表的行大小已经接近该限制 - 请使用以下调整后的版本
在这个版本中 - 分组被删除,使得每一行只有一列的值
#standardSQL
SELECT
CASE WHEN d = 'day20170101' THEN r END AS day20170101,
CASE WHEN d = 'day20170102' THEN r END AS day20170102,
CASE WHEN d = 'day20170103' THEN r END AS day20170103,
CASE WHEN d = 'day20170104' THEN r END AS day20170104,
CASE WHEN d = 'day20170105' THEN r END AS day20170105,
CASE WHEN d = 'day20170106' THEN r END AS day20170106,
CASE WHEN d = 'day20170107' THEN r END AS day20170107,
CASE WHEN d = 'day20170108' THEN r END AS day20170108,
CASE WHEN d = 'day20170109' THEN r END AS day20170109,
CASE WHEN d = 'day20170110' THEN r END AS day20170110
FROM (
SELECT
stn, CONCAT('day', year, mo, da) AS d, ARRAY_AGG(t) AS r
FROM `bigquery-public-data.noaa_gsod.gsod2017` AS t
GROUP BY stn, d
)
WHERE d BETWEEN 'day20170101' AND 'day20170110'
正如您现在所看到的 - 数据透视表 (sparce_pivot_table) 足够稀疏(相同的 21.5 MB,但现在 114,089 行与 pivot_table 中的 11,584 行相比),因此它的平均行大小为 190B,而初始版本为 1.9KB。这显然比示例中的列数少 10 倍。
所以在使用这种方法之前,需要做一些数学运算来预测/估计可以做什么以及如何做!
仍然:数据透视表中的每个单元格都是原始表中整行的 JSON 表示。它不仅包含原始表中行的值,而且还包含模式
因此它非常冗长 - 因此单元格的大小可能比原始大小大几倍[这限制了这种方法的使用......除非你更有创意:o)......这仍然很多这里申请的领域:o)]