在 redshift 中,按 IP 网络分组

In redshift, group by IP network

我想在红移中找到最常被禁止的网络 table。我试过这个:

select network(set_masklen(ip::inet,8)), count(1)
from banlist where status='BLOCKED' 
group by 1 order by 2 desc limit 10;

并出现以下错误:

INFO:  Function ""network"(inet)" not supported.
INFO:  Function "set_masklen(inet,integer)" not supported.
INFO:  Function "inet(text)" not supported.
INFO:  Function ""network"(inet)" not supported.
INFO:  Function "set_masklen(inet,integer)" not supported.
INFO:  Function "inet(text)" not supported.
ERROR:  Specified types or functions (one per INFO message) not supported on Redshift tables.

OTOH,这个有效:

# select network(set_masklen('10.0.0.1'::inet, 24));                                                          network
-------------
 10.0.0.0/24
(1 row)

根据 Redshift 文档,不支持来自 PostgreSQL 的网络地址函数和运算符。

参考文献-

PostgreSQL functions not supported in Redshift

PostgreSQL documentation - Network Address Functions and Operators

虽然其他人关于创建 UDF 的回应可能是一个不错的选择,但如果您愿意放弃一些灵活性并只获得 class A、class B 或class C 子网,您可以使用 SPLIT_PART 和连接运算符来获得(不是超级)快速而肮脏的解决方案。

select SPLIT_PART(ip_address, '.', 1) || '.' || SPLIT_PART(ip_address, '.', 2) || '.' || SPLIT_PART(ip_address,'.', 3) as network, count(1) as mc from banlist group by network order by mc desc limit 10;

现在这是一个老问题,但我想我会添加我刚刚发现的内容。

@earino 的方法有效,但我还想添加其他两种有效的方法,因为我还需要解决这个聚合问题。

select SPLIT_PART(ip, '.', 1) || '.' || SPLIT_PART(ip, '.', 2) || '.' || SPLIT_PART(ip,'.', 3) || '.0/24' as cidr from banlist;
select REGEXP_REPLACE(ip, '\.[0-9]{1,3}$', '.0/24') as cidr from banlist;
select REGEXP_SUBSTR(ip, '^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.') || '0/24' as cidr from banlist;

我不确定哪个表现更好,但我认为正则表达式更容易阅读。