slow inet within cidr query

From: "Edwin Grubbs" <edwin(dot)grubbs(at)gmail(dot)com>
To: pgsql-general(at)postgresql(dot)org
Subject: slow inet within cidr query
Date: 2006-11-28 20:07:22
Message-ID: d6b3d4ab0611281207o450e4e4ct15f2527a47f93b69@mail.gmail.com
Views: Raw Message | Whole Thread | Download mbox | Resend email
Thread:
Lists: pgsql-general

Under postgres 8.1, the "<<=" comparison yields very slow queries with large
tables. I can rewrite the query without the "<<=" operator by generating all
33 possible netmasks (0 through 32) for a given IP. This ugly rewrite runs
about 12 times faster (6 seconds versus 0.5 seconds). Be aware that EXPLAIN
ANALYZE seems to be run a different query plan since the first query runs
even slower with EXPLAIN ANALYZE. Setting enable_seqscan did not improve the
query speed.

-Edwin

Here is the original query:
------------------------------------------------------------------------
SELECT ip_address, ip_block
FROM ip_inventory
JOIN route ON ip_address <<= ip_block
WHERE ip_address < '1.5.0.0'
ORDER BY ip_address;
------------------------------------------------------------------------

Here is the rewritten query:
------------------------------------------------------------------------
SELECT ip_address, ip_block
from ip_inventory
JOIN route
ON ip_block IN (
network(ip_address),
network((host(ip_address) || '/31')::inet),
network((host(ip_address) || '/30')::inet),
network((host(ip_address) || '/29')::inet),
network((host(ip_address) || '/28')::inet),
network((host(ip_address) || '/27')::inet),
network((host(ip_address) || '/26')::inet),
network((host(ip_address) || '/25')::inet),
network((host(ip_address) || '/24')::inet),
network((host(ip_address) || '/23')::inet),
network((host(ip_address) || '/22')::inet),
network((host(ip_address) || '/21')::inet),
network((host(ip_address) || '/20')::inet),
network((host(ip_address) || '/19')::inet),
network((host(ip_address) || '/18')::inet),
network((host(ip_address) || '/17')::inet),
network((host(ip_address) || '/16')::inet),
network((host(ip_address) || '/15')::inet),
network((host(ip_address) || '/14')::inet),
network((host(ip_address) || '/13')::inet),
network((host(ip_address) || '/12')::inet),
network((host(ip_address) || '/11')::inet),
network((host(ip_address) || '/10')::inet),
network((host(ip_address) || '/9')::inet),
network((host(ip_address) || '/8')::inet),
network((host(ip_address) || '/7')::inet),
network((host(ip_address) || '/6')::inet),
network((host(ip_address) || '/5')::inet),
network((host(ip_address) || '/4')::inet),
network((host(ip_address) || '/3')::inet),
network((host(ip_address) || '/2')::inet),
network((host(ip_address) || '/1')::inet),
'0.0.0.0'::cidr
)
WHERE ip_address < '1.5.0.0'
ORDER BY ip_address;
------------------------------------------------------------------------

Here is the SQL for creating and populating the test tables:
------------------------------------------------------------------------
BEGIN;

CREATE TABLE range (value integer);
COPY range FROM STDIN;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
\.

CREATE TABLE ip_inventory (ip_address inet unique);

-- add 279841 ips
INSERT INTO ip_inventory
SELECT (a || '.' || b || '.' || c || '.' || d)::inet
FROM range AS w(a)
JOIN range AS x(b) ON TRUE
JOIN range AS y(c) ON TRUE
JOIN range AS z(d) ON TRUE;

CREATE TABLE route (ip_block cidr unique);

-- add 12167 routes
INSERT INTO route
SELECT (a || '.' || b || '.' || c || '.0/24')::cidr
FROM range AS w(a)
JOIN range AS x(b) ON TRUE
JOIN range AS y(c) ON TRUE;

COMMIT;
------------------------------------------------------------------------

Responses

Browse pgsql-general by date

  From Date Subject
Next Message Tony Caduto 2006-11-28 20:10:17 Re: Only MONO/WinForms is a way to go
Previous Message Tony Caduto 2006-11-28 20:01:03 Re: Only MONO/WinForms is a way to go