如何分割BigQuery表?

use*_*176 5 sharding google-bigquery

如果在其他地方已经回答道歉,我找不到类似的东西.

有没有办法将表拆分成多个分片而不必使用多个查询?这是两个例子:

1)加载带有带时间戳的数据的表(unix时间戳).我希望白天将数据保存到一个表中.天真的方法是:a)加载数据; b)运行查询以获取每天的所有数据并将其附加到适当的分片中.这种方法将导致查询触及N x [整个表格的大小]数据,其中N是表格中的天数.再加上一个查询来查找最小和最大时间戳,以便我可以建立我需要创建的分片范围.

2)使用字段中的数据将表拆分为分片.例如,一个包含10亿行的表,其中包含一个具有1,000个不同值的字段X. 如果我想将表拆分为1000个不同的表,每个X的值为一个表,那么天真的方法是运行SELECT*FROM表WHERE X = [value],并将结果插入到shard table_value中.但是,这将导致1000个查询,每个查询都触及整个表的数据!

当然,我错过了一些东西,必须有更有效的方法来做到这一点.

Mik*_*ant 7

要求:

让我们假设下面的简化案例/场景

1 我们有一个"大"表:
TableAll

Row a   b   c
1   1   11  12
2   1   13  14
3   1   15  16
4   1   17  18
5   2   21  22
6   2   23  24
7   2   25  26
8   2   27  28
9   3   31  32
10  3   33  34
11  3   35  36
12  3   37  38
Run Code Online (Sandbox Code Playgroud)

2 我们需要将数据拆分为单独的"较小"表,这些表由提交的"a"
TableA1 分区

Row b   c
1   11  12
2   13  14
3   15  16
4   17  18
Run Code Online (Sandbox Code Playgroud)

TableA2

Row b   c
1   21  22
2   23  24
3   25  26
4   27  28
Run Code Online (Sandbox Code Playgroud)

TableA3

Row b   c
1   31  32
2   33  34
3   35  36
4   37  38
Run Code Online (Sandbox Code Playgroud)

3 要解决的问题
最简单的方法是发出三个单独的语句,分别写入TableA1,TableA2,TableA3

SELECT b, c FROM TableAll WHERE a = 1;
SELECT b, c FROM TableAll WHERE a = 2;
SELECT b, c FROM TableAll WHERE a = 3;
Run Code Online (Sandbox Code Playgroud)

优点:速度与激情!
缺点:我们需要尽可能多的整个表的表扫描(全部成本)和我们所拥有的"a"的许多不同值(在这种特殊情况下只有三个,但在现实生活中它可以说是N = 1K的不同值) .

所以最终成本为$ 5*N*SizeInTB(TableAll)

我们的目标目标

We want to minimize cost as much as possible 
ideally down to fixed price of $5 * SizeInTB(TableAll)  
Run Code Online (Sandbox Code Playgroud)

B可能的解决方案(理念和简单实施):

逻辑步骤1 - 转换数据如下所示(将列转换为JSON)

Row a   json
1   1   {"b":"11", "c":"12"}
2   1   {"b":"13", "c":"14"}
3   1   {"b":"15", "c":"16"}
4   1   {"b":"17", "c":"18"}
5   2   {"b":"21", "c":"22"}
6   2   {"b":"23", "c":"24"}
7   2   {"b":"25", "c":"26"}
8   2   {"b":"27", "c":"28"}
9   3   {"b":"31", "c":"32"}
10  3   {"b":"33", "c":"34"}
11  3   {"b":"35", "c":"36"}
12  3   {"b":"37", "c":"38"}
Run Code Online (Sandbox Code Playgroud)

逻辑步骤2 - 透视表,以便字段"a"的值成为字段名称(前缀为a以确保我们符合列名约定)

Row a1                    a2                    a3
1   {"b":"11", "c":"12"}  null                  null
2   {"b":"13", "c":"14"}  null                  null
3   {"b":"15", "c":"16"}  null                  null
4   {"b":"17", "c":"18"}  null                  null
5   null                  {"b":"21", "c":"22"}  null
6   null                  {"b":"23", "c":"24"}  null
7   null                  {"b":"25", "c":"26"}  null
8   null                  {"b":"27", "c":"28"}  null
9   null                  null                  {"b":"31", "c":"32"}
10  null                  null                  {"b":"33", "c":"34"}
11  null                  null                  {"b":"35", "c":"36"}
12  null                  null                  {"b":"37", "c":"38"}
Run Code Online (Sandbox Code Playgroud)

注意:上面数据的大小与原始表的大小相同(没有列a)
它仍然比原始数据大,因为数据现在是详细的json格式与本机数据类型+列名称.
这可以通过消除空格,不需要引号,标准化/最小化原始列名称以在名称中只有一个字符等来优化.
我认为这种差异在N上升时可以忽略不计!(虽然没有机会评估这个)

第3步 - 将结果数据保留到表TableAllPivot 实现示例:

SELECT 
  IF(a=1, json, NULL) as a1,
  IF(a=2, json, NULL) as a2,
  IF(a=3, json, NULL) as a3 
FROM (
  SELECT a, CONCAT("{\"b\":\"",STRING(b), "\","," \"c\":\"", STRING(c), "\"}") AS json 
  FROM TableAll
)
Run Code Online (Sandbox Code Playgroud)

第3步的成本:$5 * TableAllSizeInTB
根据步骤2中的注释,假设:Size(TableAllPivot) = 2 * Size(TableAll)

步骤4 - 通过每个分片仅查询一列来生成碎片
保留模式/数据类型 - 可以提前创建相应的碎片表

数据提取:
//对于TableA1:

SELECT 
  JSON_EXTRACT_SCALAR(a1, '$.b') AS b, 
  JSON_EXTRACT_SCALAR(a1, '$.c') AS c 
FROM TableAllPivot
WHERE NOT a1 IS NULL
Run Code Online (Sandbox Code Playgroud)

//对于TableA2:

SELECT 
  JSON_EXTRACT_SCALAR(a2, '$.b') AS b, 
  JSON_EXTRACT_SCALAR(a2, '$.c') AS c 
FROM TableAllPivot
WHERE NOT a2 IS NULL
Run Code Online (Sandbox Code Playgroud)

//对于TableA3:

SELECT 
  JSON_EXTRACT_SCALAR(a3, '$.b') AS b, 
  JSON_EXTRACT_SCALAR(a3, '$.c') AS c 
FROM TableAllPivot
WHERE NOT a3 IS NULL
Run Code Online (Sandbox Code Playgroud)

第4步的费用: $5 * TableAllPivot

总费用:Step 3 Cost + Step 4 Cost=
$5 * SizeInTB(TableAll) + $5 * SizeInTB(TableAllPivot)$5 * 3 * SizeInTB(TableAll)

摘要:
建议方法固定价格= $5 * 3 * SizeInTB(TableAll)

初始线性价格=$5 * N * SizeInTB(TableAll)

请注意:3$5 * 3 * SizeInTB(TableAll)我的简化示例中,公式不是由分片数量定义的,而是估计常量,主要反映了将数据转换为json的价格.碎片数量无关紧要.相同的公式将适用于100个分片和1K分片,依此类推.此解决方案中唯一的限制是10K分片,因为这是一个表中列数的硬限制

C一些帮助程序代码和引用:

1 生成透视查询(结果用于上面的步骤3)
对于初始表中的字段数量大于10-20,当手动输入查询很无聊时,可以使用,所以你可以使用下面的脚本/查询

SELECT 'SELECT ' + 
   GROUP_CONCAT_UNQUOTED(
      'IF(a=' + STRING(a) + ', json, NULL) as a' + STRING(a) 
   ) 
   + ' FROM (
 SELECT a, 
 CONCAT("{\\\"b\\\":\\\"\",STRING(b),"\\\","," \\\"c\\\":\\\"\", STRING(c),"\\\"}") AS json
 FROM TableAll
       )'
FROM (
  SELECT a FROM TableAll GROUP BY a 
)
Run Code Online (Sandbox Code Playgroud)

2 如果您想探索并深入了解此选项,请参阅下面对相关且可能有用的代码的参考

在BigQuery中旋转重复字段
如何在BigQuery 中扩展透视
如何使用BigQuery提取JSON对象中的所有键


Fel*_*ffa 5

2018更新

  • 与其创建多个表,不如创建一个分区的表。
  • 免费分区:创建分区表(按日期),导入其中。
  • 使用一个查询进行分区(一次扫描): CREATE TABLE ... AS SELECT * FROM old-table

请参阅以下帖子,以从群集中受益:


我非常喜欢Mikhail的回答,但让我给您一个不同的答案:分而治之:

假设您的表有8个数字(将每个数字视为一个分区):12345678。要将其分片为8个表,您正在考虑对8号表(成本:8 * 8 = 64)运行8次查询。

如果您首先将此表划分为2:1234、5678,该怎么办。成本为8 * 2(2次完整扫描),但是现在我们有2个表。如果要对这两个半表进行分区,那么现在只需要扫描一半2次(2 * 4 * 2)。然后我们剩下4张桌子:12,34,56,78。划分它们的成本将是4 * 2 * 2 ...因此总成本将是8 * 2 + 2 * 4 * 2 + 4 * 2 * 2 = 48。通过减半,我们将将8中的表从64分区到48的开销。

从数学上讲,我们要从O(n ** 2)变为O(n(log n))-这始终是一件好事。

从成本角度考虑,Mikhail的答案更好,因为它的范围从O(n ** 2)到O(n),但是编写中间帮助器函数会给任务带来更多的复杂性。