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