2010-12-07 4 views
1

In SQL (postgresql 8.4.x), wie kann ich effizient COUNT die Anzahl des IP-Datensätze, die in dem kleinsten netblock möglicherweise netblocks umfasst? Ich möchte 10.0.0.1 unter 10/8 und 0/0 zum Beispiel nicht zählen.Counting Datensätze pro kleinstem netblock (Intervall)

Konkreter gegeben:

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

Wie kann ich die Ergebnismenge effizient produzieren:

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

denke ich, Ihre Frage und meine Antwort wäre besser passen auf [dba.se] (http: // dba .stackexchange.com /) - Wenn Sie einverstanden sind, sind Sie bereit, sich für die Migration selbst zu melden? Ich sehe, Sie haben bereits ein Konto ... –

Antwort

3

Dies funktioniert für mich auf 8.3 - es sollte auch auf 8.4 in Ordnung sein. Wir brauchen eine individuelle Aggregat, weil max(cidr) nicht eingebaut ist (obwohl > ist)

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 

Wenn Sie das benutzerdefinierte Aggregat nicht möchten, können Sie tun:

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; 

oder ähnliches mit einem with Klausel und keine Aussicht auf 8.4, aber die Frage, sagte effizient :-)

mit diesen Ansichten getestet:

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; 
+0

+1 für mehrere Lösungen, die Aggregatfunktion, und im Allgemeinen nähert sich dies in einer Weise, an die ich nicht gedacht hatte! – pilcrow

1

Da IPv4-Adressen im Wesentlichen 4 Bytes sind, they can be represented as an integer. Sie können eine Tabelle mit netblock_start und netblock_end erstellen (also zB 192.168.1.0/24 wäre 192.168.1.0 bis 192.168.1.255 bzw. 3232235776 bis 3232235776), dann zählen Sie ip >= netblock_start && ip <= netblock_end (die IP aus Ihrem Protokoll muss in dieselbe konvertiert werden) Format für das zu arbeiten).

+0

Das ist der Weg, es zu tun, aber wenn die ganzen Zahlen nicht physisch in den Tabellen gespeichert werden, wird es nicht effektiv sein. Otoh, es gibt keine Lösung einschließlich der Analyse der IPs, die effektiv sein werden. –

+0

Danke, Piskvor, aber nicht notwendig. Die Typen "CIDR" und "INET" verstehen bereits, dass sie intervallisch sind: '1.2.3.4' :: inet << '0.0.0.0/0'::cidr. Die Frage bezieht sich auf die Gruppierung nur im * kleinsten * umfassenden Intervall. – pilcrow

+0

@pilcrow: Ah, ich würde annehmen, dass es intern zu Integers konvertiert, nett. – Piskvor

0

@JackPDouglas' answer ist überlegen. Der Vollständigkeit halber ist dies die naive Ansatz kam ich mit aus der Spitze von meinem Kopf:

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) 
Verwandte Themen