在SQL(postgresql 8.4.x)中,如何有效COUNT
属于可能包含网络块的 最小 网络块中的IP记录数量?例如,我不想在10.0.0.1
和10/8
下计算0/0
。
更具体地说,给定:
-- CREATE TABLE iplog (ip INET NOT NULL, ...)
--
ip | ...
==============+=====
192.168.1.100 | ...
192.168.1.101 | ...
192.168.55.5 | ...
10.1.2.3 | ...
-- CREATE TABLE netblocks (nb CIDR UNIQUE NOT NULL, ...)
--
nb | ...
===============+======
192.168.1.0/24 | ...
192.168.0.0/16 | ...
10.0.0.0/8 | ...
0.0.0.0/0 | ...
如何有效地生成结果集:
nb | ips_logged
===============+============
192.168.1.0/24 | 2
192.168.0.0/16 | 1
10.0.0.0/8 | 1
答案 0 :(得分:3)
这对我来说在8.3上也适用 - 它在8.4上也应该没问题。我们需要自定义聚合,因为max(cidr)
不是内置的(即使>
是)
create or replace function greatest_pair(cidr, cidr)
returns cidr
language 'sql' immutable as
$$select greatest($1, $2);$$;
create aggregate max( basetype = cidr,
sfunc = greatest_pair,
stype = cidr );
select max_nb, count(*)
from ( select ip, max(nb) as max_nb
from netblocks n join iplog i on(i.ip << n.nb)
group by ip ) z
group by max_nb;
max_nb | count
----------------+-------
192.168.1.0/24 | 2
10.0.0.0/8 | 1
192.168.0.0/16 | 1
如果您不想要自定义聚合,可以执行以下操作:
create or replace view v as
select ip, nb from netblocks n join iplog i on(i.ip << n.nb);
select nb, count(*)
from ( select *
from v o
where not exists ( select *
from v i
where i.ip=o.ip and i.nb>o.nb ) ) z
group by nb;
或类似使用with
子句而不是8.4的视图,但问题是有效: - )
使用这些观点测试:
create or replace view iplog as
select '192.168.1.100'::inet as ip union all
select '192.168.1.101'::inet union all
select '192.168.55.5'::inet union all
select '10.1.2.3'::inet;
create or replace view netblocks as
select '192.168.1.0/24'::cidr as nb union all
select '192.168.0.0/16'::cidr union all
select '10.0.0.0/8'::cidr union all
select '0.0.0.0/0'::cidr;
答案 1 :(得分:1)
由于IPv4地址基本上是4个字节,they can be represented as an integer。您可以创建一个包含netblock_start和netblock_end的表(例如192.168.1.0/24将是192.168.1.0到192.168.1.255,分别为3232235776到3232235776),然后计数ip >= netblock_start && ip <= netblock_end
(您的日志中的IP需要是转换成相同的格式,以便工作)。
答案 2 :(得分:0)
@JackPDouglas' answer优越。为了完整起见,这是我想到的天真的方法:
SELECT nb, COUNT('X')
FROM netblocks
JOIN iplog
ON ip << nb
AND
nb = ( SELECT nb
FROM netblocks
WHERE ip << nb
ORDER BY nb DESC
LIMIT 1)
GROUP BY 1;
nb | count
----------------+-------
192.168.1.0/24 | 3
192.168.0.0/16 | 1
10.0.0.0/8 | 1
(3 rows)