每个最小网块计数记录(间隔)

时间:2010-12-07 15:07:29

标签: sql postgresql count intervals

在SQL(postgresql 8.4.x)中,如何有效COUNT属于可能包含网络块的 最小 网络块中的IP记录数量?例如,我不想在10.0.0.110/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

3 个答案:

答案 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)