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 在选择不同行时按一个字段中的最大值分组的主要内容,如果未能解决你的问题,请参考以下文章
当从多个分表查询超过 4 亿行时,BigQuery 的预期查询响应性能是多少?