当使用与重复字段同名的字段时,BigQuery会变平

时间:2015-11-09 07:11:22

标签: google-bigquery

已编辑使用公共数据集

我有一个包含以下架构的表,您可以在此处访问: https://bigquery.cloud.google.com/table/realself-main:rs_public.test_count enter image description here

如果我运行以下查询,我会得到 cnt1 cnt2 的不同结果。

SELECT 
  COUNT(*) AS cnt1,
  COUNT(dr_id) as cnt2,
FROM (SELECT * FROM rs_public.test_count) AS tc
WHERE
  tc.is_published

enter image description here

如果我从where子句中删除 tc 别名,我会得到两个计数相同的结果:

SELECT 
  COUNT(*) AS cnt1,
  COUNT(dr_id) as cnt2,
FROM (SELECT * FROM rs_public.test_count) AS tc
WHERE
  is_published

enter image description here

但是,如果我重复第一个查询但使用where子句中的 is_claimed 字段,我会再次获得相同的计数。

SELECT 
  COUNT(*) AS cnt1,
  COUNT(dr_id) as cnt2,
FROM (SELECT * FROM rs_public.test_count) AS tc
WHERE
  tc.is_claimed

enter image description here

我认为这是一个错误并且BigQuery变得混乱,因为 is_published 是外部字段,也是 cover_photos 记录的叶字段。在评估结果是否应该展平时,错误地使用 cover_photos.is_published 字段 - 但在根据where子句过滤结果时使用外部 is_published 字段。

以下是没有使用select *的反例,我在下面对Felipe的回答的评论中提到了这一点:

SELECT
  COUNT(*)
FROM (
  SELECT
    dr_id,
    cover_photos.is_published
  FROM
    [realself-main:rs_public.test_count] )

返回3.

SELECT
  COUNT(*), COUNT(0)
FROM (
  SELECT
    dr_id,
    cover_photos.is_published
  FROM
    [realself-main:rs_public.test_count] )

返回7和3!根据我的评论,似乎唯一安全的选择是永远不要使用count(*)

3 个答案:

答案 0 :(得分:1)

如果可以通过多种方式解释查询,BigQuery将尽最大努力猜测您的意图是什么 - 有时产生非一致的结果。这对每个数据库都是如此,因为SQL有这些含糊不清的空间。

解决方案:消除查询中的歧义 - 可能两种结果都是正确的,具体取决于您要计算的内容。

(通过不使用*来消除歧义,并使前缀显式化,同时您还可以明确请求表格平铺的方式)

我真的想评论您的具体数据和结果,但鉴于您没有提供公开样本,我不能。

答案 1 :(得分:1)

感谢分享@alan的数据集!让我们看看它的外观:

enter image description here

这是一个有趣的表:它有3列3行(很小,但是普通的SQL表)。有趣的是,第3列可以托管嵌套记录。在第一行它没有(null),第二行只有1个值,第三行有5个不同的嵌套值。

当您按列开始计算时,事情会变得很有趣:

SELECT COUNT(*) 
FROM [realself-main:rs_public.test_count]
3

这是有道理的,数据集有3行。

SELECT COUNT(dr_id) 
FROM [realself-main:rs_public.test_count]
3

这也是有道理的,有3个dr_id。

SELECT COUNT(cover_photos.is_published) 
FROM [realself-main:rs_public.test_count]
6

现在事情变得更有趣了。它是6,因为cover_photos.is_published有6个值(null值不计算)。

SELECT COUNT(cover_photos.is_published), COUNT(dr_id)
FROM [realself-main:rs_public.test_count]
6   3

这仍然有意义:6 cover_photos.is_published,3 dr_id。

SELECT COUNT(*) 
FROM (
  SELECT cover_photos.is_published, dr_id
  FROM [realself-main:rs_public.test_count]
)
3

这也很有趣:如果我们进行子查询,COUNT(*)会查看返回的行数。返回了3行。这仍然有意义。

但是:

SELECT COUNT(*), COUNT(cover_photos.is_published)
FROM (
  SELECT cover_photos.is_published, dr_id
  FROM [realself-main:rs_public.test_count]
)
7   6   

7和6.七?为什么7?

嗯,BigQuery必须为子查询选择一个展平策略。看看我粘贴在那里的表 - 你能看到它有7行吗?这是计算的七行。

让我们明确地看一下它们:

SELECT dr_id, cover_photos.is_published
FROM (
  SELECT cover_photos.is_published, dr_id
  FROM [realself-main:rs_public.test_count]
)

enter image description here

请参阅?那是七排。选择具有嵌套记录的行(BigQuery的一个很好的功能)时,BigQuery有时需要展平数据以处理某些查询。前两行被扁平化为2行(一行有一个cover_photos.is_published为null),第三行被展平为5行,每行为cover_photos.is_published。

故事的寓意是在使用嵌套数据时要小心:有些查询会以对用户来说意外的方式将其弄平,但这在计算机尝试决定时会非常有意义。

根据要求,让我们更进一步:

看看这两个查询之间的区别:

SELECT COUNT(*)
FROM (
  SELECT *
  FROM (
    SELECT * FROM [realself-main:rs_public.test_count]  
    WHERE is_published
  ) 
)

SELECT COUNT(*)
FROM (
  SELECT *
  FROM (
    SELECT * FROM [realself-main:rs_public.test_count]  
  ) 
)
WHERE is_published

在查看结果之前,您能猜出每个查询会给您带来什么结果吗?不,你不能。这两个查询都不明确,因此为了得到答案,BigQuery需要做出一些猜测和优化。

第一个查询的结果是7,第二个查询的结果是3.去试试。

有什么区别?好吧,通过查看这些查询的结果,我可以看出,在第二个中,BigQuery看到您感兴趣的唯一列是'is_published',因此它优化了树,因此只读取该列。但是BigQuery更难以优化第一个查询 - 所以它猜测“也许他们真的想要*,而*意味着我需要在将表传递到下一层之前将其展平”。它使表变平,所以后来最上面的查询看到7行。

这些结果是否有保证?不 - 查询含糊不清。如何减少歧义?不要使用“SELECT *”,而是告诉BigQuery你要查找哪些列 - 所以它不需要为你猜测。

答案 2 :(得分:1)

我正在添加一个新的答案,因为你不断添加问题的元素 - 他们都应该得到不同的答案。

你说这个问题令你感到惊讶:

SELECT COUNT(*), COUNT(0)
FROM (
  SELECT dr_id, cover_photos.is_published
  FROM [realself-main:rs_public.test_count] )

你很惊讶,因为结果是7和3。

如果我尝试这个可能会有意义:

SELECT COUNT(*), COUNT(0), 
       GROUP_CONCAT(STRING(cover_photos.is_published)),
       GROUP_CONCAT(STRING(dr_id)), 
       GROUP_CONCAT(IFNULL(STRING(cover_photos.is_published),'null')),
       GROUP_CONCAT("0")
FROM (
  SELECT dr_id, cover_photos.is_published
  FROM [realself-main:rs_public.test_count] 
)

请参阅?它是相同的查询,加上4个相同子列的不同聚合,其中一个由嵌套的重复数据组成,并且在一行中也有一个空值。

查询结果如下:

7   3   1,1,1,0,0,0 1234,4321,9999  null,1,1,1,0,0,0    0,0,0

7来自嵌套数据的完全扩展为7行,如第5列提示。 3来自于仅评估“0”三次,如第6列所示。

这些细微之处都与使用嵌套重复数据有关。我建议你不要使用嵌套的重复数据,直到你准备好接受在处理嵌套的重复数据时会发生这些细微之处。