使用ARRAY_AGG()和SUM()汇总不同级别数据的最优雅方法?

时间:2019-04-24 14:54:26

标签: google-bigquery

我想在count级别总结id。对于每个id摘要行,我还希望有一个STRUCTs的ARRAY字段,它为a1a2这两个属性的每一个汇总数据。

这是我设法做到的方式,它具有4个查询和6条SELECT语句,但是我认为必须有一种更简化的方法来实现此目的。

1)对于每个属性摘要,在执行ARRAY_AGG()之后在外部查询中执行SUM()似乎可以一步完成,尽管不能将{{1} }和SUM()之内。

2)首先对ARRAY_AGG()进行每个摘要,然后对每个属性进行摘要,然后将所有这些摘要进行合并,似乎应该在一个步骤中完成。

请注意,我确实重视此查询的可读性。但我认为这里肯定缺少一些可读的“速记”。

id

2 个答案:

答案 0 :(得分:2)

  

但是我认为这里必须缺少一些可读的“速记”。

下面产生的结果与原始查询完全相同,但仍很可读,简单(我认为很性感)

#standardSQL
CREATE TEMP FUNCTION x(a ANY TYPE) AS (
  ARRAY(SELECT AS STRUCT val, SUM(`count`) `count` FROM UNNEST(a) GROUP BY val)
);
SELECT id, SUM(`count`) AS total,
  x(ARRAY_AGG(STRUCT(a1 AS val, `count`))) a1, 
  x(ARRAY_AGG(STRUCT(a2 AS val, `count`))) a2
FROM data
GROUP BY id   

您可以使用虚拟数据(如下面的示例n)进行测试,操作

#standardSQL
CREATE TEMP FUNCTION x(a ANY TYPE) AS (
  ARRAY(SELECT AS STRUCT val, SUM(`count`) `count` FROM UNNEST(a) GROUP BY val)
);
WITH data AS (
  SELECT "A" AS id, 1 AS `count`, "a" AS a1, "d" AS a2 UNION ALL 
  SELECT "A", 2, "a", "e" UNION ALL 
  SELECT "A", 3, "b", "d" UNION ALL 
  SELECT "A", 4, "a", "d" UNION ALL 
  SELECT "B", 2, "a", "e" UNION ALL 
  SELECT "B", 3, "b", "e" UNION ALL 
  SELECT "B", 4, "a", "d" 
)
SELECT id, SUM(`count`) AS total,
  x(ARRAY_AGG(STRUCT(a1 AS val, `count`))) a1, 
  x(ARRAY_AGG(STRUCT(a2 AS val, `count`))) a2
FROM data
GROUP BY id   

有结果

enter image description here

答案 1 :(得分:0)

这有点奇怪,因为您有意为a1和a2分组创建独立的重复。这意味着对a1的任何分析都不关心a2,反之亦然,因为一旦丢失了两个值之间的关系信息,就无法将它们重新组合。

如果确实需要使用a1和a2值的组合进行关联,请考虑将这些组合在一起放在结构中,而不是构建两个独立的数组。您还可以同时计算每个ID的总数:

SELECT
 id,
 SUM(subtotal) as total,
 ARRAY_AGG(STRUCT(a1, a2, subtotal)) as partial_sums
FROM
(
SELECT
  id,
  a1,
  a2,
  SUM(count) as subtotal
FROM data
GROUP BY id, a1, a2
)
GROUP BY id

假设您将部分汇总保留为表格,则可以稍后扩展部分摘要以计算仅a1或仅a2的细分。下面的示例使用ANY_VALUE来投影总数,因为所有行共享相同的值,并且避免了额外的GROUP BY。但是,对于用例而言,使用外部查询来构建数组可能是完全不必要的。

SELECT
   id,
   ANY_VALUE(total) as total,
   ARRAY_AGG(a1_summary) as a1_partial_sums
FROM
(
  SELECT
    id,
    total,
    STRUCT(p.a1, SUM(p.subtotal)) as a1_summary
  FROM
   `partial_summary_table`
  CROSS JOIN UNNEST(partial_sums) as p
  GROUP BY id, total, p.a1
)
GROUP BY id