如果在其他地方已经回答道歉,我找不到类似的东西。
有没有办法将表拆分成多个分片而不必使用多个查询?以下是两个例子:
1)加载带有带时间戳的数据的表(unix时间戳)。我希望白天将数据保存到一个表中。天真的方法是:a)加载数据; b)运行查询以获取每天的所有数据并将其附加到适当的分片中。这种方法将导致查询触及N x [整个表格的大小]数据,其中N是表格中的天数。再加上一个查询来查找最小和最大时间戳,以便我可以建立我需要创建的分片范围。
2)使用字段中的数据将表拆分为分片。例如,一个包含10亿行的表,其中包含一个具有1,000个不同值的字段X.如果我想将表拆分为1000个不同的表,每个X的值为一个表,那么天真的方法是运行SELECT * FROM表WHERE X = [value],并将结果插入到shard table_value中。但是,这将导致1000个查询,每个查询都触及整个表的数据!
当然我错过了一些东西,必须有更有效的方法来做到这一点。
答案 0 :(得分: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
2 我们需要将数据拆分为单独的"较小的"由提交的" a" 分割的表格 表A1
Row b c
1 11 12
2 13 14
3 15 16
4 17 18
TableA2
Row b c
1 21 22
2 23 24
3 25 26
4 27 28
表A3
Row b c
1 31 32
2 33 34
3 35 36
4 37 38
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;
优点:速度与激情!
缺点:我们需要尽可能多的整个表的表扫描(全部成本)和许多不同的值#34; 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)
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"}
逻辑步骤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"}
注意:以上数据的大小与原始表的大小(没有列a)的顺序相同 它仍然比原始数据更大,因为现在数据采用冗长的json格式与原生数据类型+列名称相比 这可以通过消除空格,不需要引号,规范化/最小化原始列名称以仅在名称中使用一个字符等来优化。 N认为这个差异在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
)
第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
//对于TableA2:
SELECT
JSON_EXTRACT_SCALAR(a2, '$.b') AS b,
JSON_EXTRACT_SCALAR(a2, '$.c') AS c
FROM TableAllPivot
WHERE NOT a2 IS NULL
//对于TableA3:
SELECT
JSON_EXTRACT_SCALAR(a3, '$.b') AS b,
JSON_EXTRACT_SCALAR(a3, '$.c') AS c
FROM TableAllPivot
WHERE NOT a3 IS NULL
第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
)
2 如果您想探索并深入了解此选项,请参阅以下相关&amp;在这里可能有用的代码
Pivot Repeated fields in BigQuery
How to scale Pivoting in BigQuery?
How to extract all the keys in a JSON object with BigQuery
答案 1 :(得分:4)
CREATE TABLE ... AS SELECT * FROM old-table
请参阅以下帖子,以便从群集中受益:
我真的很喜欢米哈伊尔的答案,但是让我给你一个不同的答案:分而治之:
假设您的表有8个数字(将每个数字视为一个分区):12345678。要将其分为8个表,您需要在表大小为8的情况下运行8次查询(成本:8 * 8 = 64)。
如果您首先将此表格分为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。通过做一半,我们将将表中的表从64分区为48的成本。
从数学角度讲,我们从O(n ** 2)到O(n(log n)) - 这总是一件好事。
成本方面米哈伊尔的答案更好,因为它从O(n ** 2)到O(n),但编写中间辅助函数将为任务带来额外的复杂性。