1

SQL(postgresql 8.4.x)で、可能性のある包含ネットブロックの最小COUNTネットブロック内にあるIPレコードの数を効率的にするにはどうすればよいですか?たとえば、との両方にカウントしたくありません。10.0.0.110/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
4

3 に答える 3

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;
于 2010-12-07T16:04:50.780 に答える
1

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を同じ形式に変換する必要があります)これが機能するために)。

于 2010-12-07T15:15:53.180 に答える
0

@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)
于 2010-12-10T13:46:41.987 に答える