BigQuery 在选择不同行时按一个字段中的最大值分组

Posted

技术标签:

【中文标题】BigQuery 在选择不同行时按一个字段中的最大值分组【英文标题】:BigQuery group by maximum value in one field while selecting distinct rows 【发布时间】:2019-02-04 15:09:11 【问题描述】:

对于每 20 分钟的时间间隔,我试图找到带宽的最大值(下表中的 mbps 列),每个唯一的 IP 地址生成,以及相应的端口号。

每个 IP 地址在每 20 分钟内可能会或可能不会出现超过一次。每次在 20 分钟的时间间隔内记录一个 IP 地址时,它可能会列出相同的端口号,也可能不会列出。

例如,在下表中,IP 地址 192.168.10.1 在列为 12:20 的时间段内出现了 3 次,端口号分别为 443、80 和 80。在另一种情况下,IP 地址 192.168.10.2 在 12:40 期间出现了两次,使用相同的端口号 443,列出了两次,但 mbps(带宽)列的值不同。

目标是在每 20 分钟的时间段内,仅选择并列出每个唯一的 IP 地址一次,并按 DESC 顺序按 mbps 排序。

根据数据注入的时间对表进行分区。

我打算编写一个 cron 作业,以自动执行此查询。 cron 作业将每周运行 7 天,每小时运行一次。查询应使用标准 SQL。

原表:

Row time                ip_address          port        mbps

1   01/01/2019 12:20    192.168.10.1        443         100
2   01/01/2019 12:20    192.168.10.1        80          120
3   01/01/2019 12:20    192.168.10.2        80          200
4   01/01/2019 12:20    192.168.10.1        80          110
5   01/01/2019 12:40    192.168.10.2        443         200
6   01/01/2019 12:40    192.168.10.3        443         300
7   01/01/2019 12:40    192.168.10.2        443         200
8   01/01/2019 12:40    192.168.10.1        443         300
9   01/01/2019 13:00    192.168.10.3        443         90
10  01/01/2019 13:00    192.168.10.2        80          100
11  01/01/2019 13:00    192.168.10.1        443         500

执行下面的代码,

#standardSQL
SELECT
  FORMAT_TIMESTAMP("%d/%m/%Y %H:%M", TIMESTAMP_SECONDS, 'Europe/London') AS time,
  ip_address,
  port,
  SUM(bandwidth) AS mbps,
FROM
  dataset1.table1
WHERE
  _PARTITIONDATE = DATE_SUB(CURRENT_DATE(),INTERVAL 0 DAY)  
  AND timestamp > TIMESTAMP_ADD(CURRENT_TIMESTAMP(),INTERVAL -40 MINUTE)
GROUP BY
  time,
  ip_address,
  port
ORDER BY
  time,
  mbps DESC

我得到了这张桌子,

Row time                ip_address          port        mbps

1   01/01/2019 12:20    192.168.10.2        80          200
2   01/01/2019 12:20    192.168.10.1        80          120
3   01/01/2019 12:20    192.168.10.1        80          110
4   01/01/2019 12:20    192.168.10.1        443         100
5   01/01/2019 12:40    192.168.10.1        443         300
6   01/01/2019 12:40    192.168.10.3        443         300
7   01/01/2019 12:40    192.168.10.2        25          200
8   01/01/2019 12:40    192.168.10.2        443         160
9   01/01/2019 13:00    192.168.10.1        443         500
10  01/01/2019 13:00    192.168.10.2        80          100
11  01/01/2019 13:00    192.168.10.3        443         90

这不是我想要的。相反,我想要这个:

Row time                ip_address          port        mbps

1   01/01/2019 12:20    192.168.10.2        80          200
2   01/01/2019 12:20    192.168.10.1        80          120
3   01/01/2019 12:40    192.168.10.1        443         300
4   01/01/2019 12:40    192.168.10.3        443         300
5   01/01/2019 12:40    192.168.10.2        25          200
6   01/01/2019 13:00    192.168.10.1        443         500
7   01/01/2019 13:00    192.168.10.2        80          100
8   01/01/2019 13:00    192.168.10.3        443         90

我做错了什么?

【问题讨论】:

【参考方案1】:

以下是 BigQuery 标准 SQL

#standardSQL
SELECT time, ip_address, 
  ARRAY_AGG(STRUCT(port, mbps) ORDER BY mbps DESC LIMIT 1)[OFFSET(0)].*
FROM `project.dataset.table`
GROUP BY time, ip_address

您可以使用您问题中的示例数据进行测试,如以下示例所示

#standardSQL
WITH `project.dataset.table` AS (
  SELECT '01/01/2019 12:20' time, '192.168.10.1' ip_address, 443 port, 100 mbps UNION ALL
  SELECT '01/01/2019 12:20', '192.168.10.1', 80, 120 UNION ALL
  SELECT '01/01/2019 12:20', '192.168.10.2', 80, 200 UNION ALL
  SELECT '01/01/2019 12:20', '192.168.10.1', 80, 110 UNION ALL
  SELECT '01/01/2019 12:40', '192.168.10.2', 443, 200 UNION ALL
  SELECT '01/01/2019 12:40', '192.168.10.3', 443, 300 UNION ALL
  SELECT '01/01/2019 12:40', '192.168.10.2', 443, 200 UNION ALL
  SELECT '01/01/2019 12:40', '192.168.10.1', 443, 300 UNION ALL
  SELECT '01/01/2019 13:00', '192.168.10.3', 443, 90 UNION ALL
  SELECT '01/01/2019 13:00', '192.168.10.2', 80, 100 UNION ALL
  SELECT '01/01/2019 13:00', '192.168.10.1', 443, 500 
)
SELECT time, ip_address, 
  ARRAY_AGG(STRUCT(port, mbps) ORDER BY mbps DESC LIMIT 1)[OFFSET(0)].*
FROM `project.dataset.table`
GROUP BY time, ip_address
-- ORDER BY time, ip_address

结果

Row time                ip_address      port    mbps     
1   01/01/2019 12:20    192.168.10.1    80      120  
2   01/01/2019 12:20    192.168.10.2    80      200  
3   01/01/2019 12:40    192.168.10.1    443     300  
4   01/01/2019 12:40    192.168.10.2    443     200  
5   01/01/2019 12:40    192.168.10.3    443     300  
6   01/01/2019 13:00    192.168.10.1    443     500  
7   01/01/2019 13:00    192.168.10.2    80      100  
8   01/01/2019 13:00    192.168.10.3    443     90   

【讨论】:

非常感谢@mikhail-berlyant。在我的情况下,我将如何选择数百万行?我还需要使用UNION ALL吗? 否 - 提供给您的第二个查询用于测试。您应该使用第一个查询,将 `project.dataset.table` 替换为对真实表的引用。有意义吗? 感谢您的及时答复@mikhail-berlyant!就我而言,我有数百万行,因此我不能使用UNION ALL 什么是达到相同结果的最佳方法,但对于数百万行?我尝试了很多方法,没有具体的结果。 嗨@michail-berlyant,我已经设法让它工作了,除了一小部分。关于 mbps 列,我必须首先将我得到的原始数据(以字节为单位)转换为 mbps,即,incoming_bytes * 8 * 10000 / 1000000 AS mbps。这意味着我必须具备以下条件:incoming_bytes 131,072 AS mbps, ARRAY_AGG(STRUCT(port, incoming_bytes AS mbps) ORDER BY mbps DESC LIMIT 1)[OFFSET(0)]。 问题是,当我执行上述操作时,它会引发错误 Error: Unrecognized name: mbps 知道如何让 ORDER BY mbps 在这种情况下工作吗?一如既往的感谢! 谢谢米哈伊尔。我很高兴接受上面的答案。我将为新问题打开一个新帖子。谢谢!

以上是关于BigQuery 在选择不同行时按一个字段中的最大值分组的主要内容,如果未能解决你的问题,请参考以下文章

仅选择几行时 bigquery 会多收费

BigQuery:按日期将子选择合并为一行

根据 BigQuery 中嵌套字段的不同值选择行

当从多个分表查询超过 4 亿行时,BigQuery 的预期查询响应性能是多少?

按时间范围谷歌选择不同的用户组 - bigquery SQL

选择 UITableView 中的行时如何清除文本字段?