在 BigQuery 中使用所有字符串列的限制
Posted
技术标签:
【中文标题】在 BigQuery 中使用所有字符串列的限制【英文标题】:Limitations in using all string columns in BigQuery 【发布时间】:2019-06-07 05:35:12 【问题描述】:我在 BigQuery 中有一个输入表,其中所有字段都存储为字符串。例如,表格如下所示:
name dob age info
"tom" "11/27/2000" "45" "['one', 'two']"
在查询中,我目前正在执行以下操作
WITH
table AS (
SELECT
"tom" AS name,
"11/27/2000" AS dob,
"45" AS age,
"['one', 'two']" AS info )
SELECT
EXTRACT( year from PARSE_DATE('%m/%d/%Y', dob)) birth_year,
ANY_value(PARSE_DATE('%m/%d/%Y', dob)) bod,
ANY_VALUE(name) example_name,
ANY_VALUE(SAFE_CAST(age AS INT64)) AS age
FROM
table
GROUP BY
EXTRACT( year from PARSE_DATE('%m/%d/%Y', dob))
此外,我尝试执行一个非常基本的group by
操作,将项目转换为字符串与否,并且我没有看到大约 1M 行的数据集有任何性能下降(实际上,在这种特殊情况下,转换到一个字符串更快):
除了“保留”这个全字符串表而不将其转换为正确的类型是不好的做法之外,我在保留一个表时会遇到哪些限制(无论是功能还是性能方面) -string 而不是将其存储为正确的类型。我知道由于存储字符串而不是数字/日期/布尔/等,大小会略有增加,但是如果我保持这种方式,我会遇到哪些主要限制或性能损失?
在我的脑海中,我看到的唯一限制是:
查询会变得更加复杂(但如果使用查询构建器则无关紧要)。 从数组字段中提取非字符串项有点困难。 插入数据变得有点棘手(例如,需要跟踪日期格式是什么)。但这些似乎都是可以解决的非常小的项目。是否还有其他“更大”的原因导致使用所有字符串字段会成为一个巨大的限制,无论是限制查询能力还是在各种情况下都会对性能造成巨大影响?
【问题讨论】:
它可能与问题的实质无关 - 但您的查询将不会运行,因为GROUP 2
它可能是其他东西遗留下来的?
@MikhailBerlyant 感谢您指出这一点。我用两个实际例子更新了这个问题。
这个问题主要是基于意见的。您肯定会因反复转换字段的开销而导致性能下降。
@MạnhQuyếtNguyễn 尽管如此,我正在评估它是否是一个可行的选择。
【参考方案1】:
首先 - 我真的没有看到比你已经认识和入伍的人更能吸引眼球的人了
同时,
虽然如果使用查询构建器并不重要......
基于上述摘录 - 我想谈谈这种方法的某些方面(将所有内容存储为字符串)
虽然我们通常关心从字符串转换为原生类型以应用相关函数等等,但我意识到在某些情况下使用某种查询构建器构建复杂和通用的查询需要相反 - 将原生类型转换为字符串以应用函数就像STRING_AGG
[只是] 作为一个简单的例子
所以,我的想法是:
当表是为直接用户访问而设计的,带有琐碎甚至复杂的查询 - 拥有原生类型是有益的,性能明智,并且更易于用户理解等。
同时,如果您正在开发自己的查询构建器并且您设计的表可以供用户通过该查询构建器进行查询,并实现了一些通用逻辑 - 将所有字段都放在字符串中有助于构建查询构建器本身。
所以这是一个平衡 - 您可能会在性能上有所损失,但您可以通过更好地实现通用查询构建器来获胜。这种平衡取决于您的业务性质——既来自数据预期,也包括您设想支持的查询类型
注意:您的问题非常广泛且基于意见(顺便说一句,在 SO 上不太受尊重)所以,显然我的回答 - 完全是我的意见,但基于 BigQuery 的丰富经验
【讨论】:
您认为哪些查询在性能方面受到的影响最大? @David542 - 看起来每个人都同意 CAST 是这里的主要受害者。因此,例如,您需要使用数字类型进行操作的那些查询将受到最大影响。显然,如果您按年龄分组 - 您不需要 CAST。但是,如果您比较年龄 - 需要 CAST(除非您将年龄特别格式化为“015”与“15”。如果您需要 SUM 或 AVG 或类似的 - 除了 CAST,您别无选择 - 这些查询在我的看法【参考方案2】:您可以将字符串"33/02/2000"
作为日期存储在一行中,"21st of December 2012"
在另一行中,"22ое октября 2013"
在另一行中吗?
您可以将字符串"45"
存储为年龄并将"young"
存储在另一行吗?
当年龄"10"
小于年龄"9"
时你还好吗?
数据类型在数据库级别提供了一些基本的数据验证机制。
BigQuery 数据库有索引的概念吗?
如果是,那么一旦您开始将字符串转换为适当的类型,这些索引很可能会变得无用,例如
SELECT
...
WHERE
age > 10 and age < 30
对
SELECT
...
WHERE
ANY_VALUE(SAFE_CAST(age AS INT64)) > 10
and ANY_VALUE(SAFE_CAST(age AS INT64)) < 30
【讨论】:
仅供参考:否 - BigQuery 没有索引的概念Are you OK to store string "45" as age in one row and "young" in another row?
-- 这很好,因为当对 int 执行 safe_cast
时会产生“年轻”null
,如果我们在列级别 (int)。【参考方案3】:
使用较少的列/行,您感觉不到问题是正常的。当您的数据变得庞大时,您就会开始感受到问题。
主要关注点:
代码维护:考虑您可能收到的未来需求。数据操作的每次转换都会给您的代码增加额外的复杂性。例如,如果您的客户将来要求检索青少年,您需要将字符串转换为日期以获取年龄,然后才能进行处理。
数据大小:数据大小有更广泛的影响,一开始是看不到的。例如,如果您有 N 个需要自己的测试系统的并行测试团队,您将需要分配更多的磁盘空间。
读取性能:当您在大表中读取更多字节时,将花费大量时间。例如,通常电信运营商每月拥有数十亿行数据。
如果您的代码复杂度增加,您将需要在多个位置复制转换。
即使是上面的一项也应该推动一项远离使用字符串的所有事情。
【讨论】:
【参考方案4】:我认为最大的问题是是否有其他用户使用此表/数据,例如,如果有人试图用它编写报告并进行计算或图表或日期范围,这可能会让人头疼始终使用他们使用的任何工具转换或转换数据。您或某人可能会收到很多关于它的投诉。
如果有人决定在此数据和转换所有数据的报告工具之间建立一个层,那么您最好只对表格/数据执行一次并完成它。
【讨论】:
【参考方案5】:从下面的解决方案中,您可能会遇到一些存储和性能问题,您可以在official documentation 中找到一些指导:
主要的性能问题来自 CAST 操作,请记住,BigQuery 引擎必须为每行的每个值处理 CAST 操作。 为了测试此操作的计算成本,我使用了以下查询:
SELECT
street_number
FROM
`bigquery-public-data.austin_311.311_service_requests`
LIMIT
5000
检查执行细节中执行的阶段,我们可以看到以下内容:
READ
$1:street_number
FROM bigquery-public-data.austin_311.311_service_requests
LIMIT
5000
WRITE
$1
TO __stage00_output
只需要Read
、Limit
和Write
操作。但是,如果我们执行相同的查询,添加 CAST
运算符。
SELECT
CAST(street_number AS int64)
FROM
`bigquery-public-data.austin_311.311_service_requests`
LIMIT
5000
我们看到,为了执行强制转换操作,还需要计算操作:
READ
$1:street_number
FROM bigquery-public-data.austin_311.311_service_requests
LIMIT
5000
COMPUTE
$10 := CAST($1 AS INT64)
WRITE
$10
TO __stage00_output
这些计算操作会消耗一些时间,这可能会在扩大操作规模时引起问题。
另外,请记住,每次您想使用每种数据类型的 data type properties 时,您都必须转换您的值,并处理所需的计算操作时间。
最后,提到存储性能,正如你所说,字符串没有固定大小,这可能会导致大小增加。
【讨论】:
感谢您的回答。我在问题和示例中添加了更多细节。以上是关于在 BigQuery 中使用所有字符串列的限制的主要内容,如果未能解决你的问题,请参考以下文章