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

发布于 2024-10-06 03:50:53 字数 744 浏览 5 评论 0原文

在 SQL (postgresql 8.4.x) 中,如何有效地COUNT落入可能包含网络块的最小网络块内的 IP 记录数量?例如,我不想将 10.0.0.1 计入 10/80/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

In SQL (postgresql 8.4.x), how can I efficiently COUNT the number of IP records falling within the smallest netblock of possibly encompassing netblocks? I don't want to count 10.0.0.1 under both 10/8 and 0/0, for example.

More concretely, given:

-- 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      | ...

How can I efficiently produce the result set:

       nb      | ips_logged
===============+============
192.168.1.0/24 | 2
192.168.0.0/16 | 1
10.0.0.0/8     | 1

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

一刻暧昧 2024-10-13 03:50:53

这对我在 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;

This works for me on 8.3 - it should be fine on 8.4 as well. We need a custom aggregate because max(cidr) is not built-in (even though > is)

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

If you don't want the custom aggregate, you can do:

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;

or similar using a with clause and no view on 8.4, but the question said efficiently :-)

tested with these views:

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;
奶气 2024-10-13 03:50:53

由于 IPv4 地址本质上是 4 个字节,因此它们可以表示为整数。您可以制作一个包含 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 需要转换为相同的格式才能正常工作)。

Since IPv4 addresses are essentially 4 bytes, they can be represented as an integer. You can make a table containing netblock_start and netblock_end (so e.g. 192.168.1.0/24 would be 192.168.1.0 to 192.168.1.255, resp. 3232235776 to 3232235776), then count ip >= netblock_start && ip <= netblock_end (the IP from your log needs to be converted into the same format for this to work).

Spring初心 2024-10-13 03:50:53

@JackPDouglas的答案非常出色。为了完整起见,这是我突然想到的天真的方法:

  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)

@JackPDouglas' answer is superior. For completeness, this is the naive approach I came up with off the top of my head:

  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)
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文