Re: Subselect query enhancement

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Michael Artz wrote:
Here are some numbers for 3 different queries using a very selective
query (port = 2222).  I'm thinking that, since the row estimates are
different from the actuals (2 vs 2000), that this particular port
didn't make it into the statistics ... is that true?  Does this
matter?  If this isn't enough data, I can type up some more.

SELECT * FROM pg_stats WHERE tablename='foo';
This will show you frequency-stats on each column (as generated by analyse). You're interested in n_distinct, most_common_vals, most_common_freqs.

One thing that I forgot to mention is that the network_events and
host_events tables are partitioned by first octet of the IP, so when I
say "various scans of ..." that means that there is a scan of each of
the partitions, the type determined by the planner and the statistics,
I assume.

So you've got xxx_events tables partitioned by ip, but ip_info is one table? Do you do a lot of scans across the bottom 3 bytes of the IP? If not, I'm not clear what we're gaining from the partitioning.

** Dual subselects:
SELECT * FROM ip_info
WHERE ip IN (SELECT ip FROM network_events WHERE port = 2222)
      OR ip IN (SELECT ip FROM host_events WHERE port = 2222);

Seq scan on ip_info (cost=2776..354575 rows=9312338 width=72) (actual
time=34..8238 rows=234 loops=1)
Filter:  ... AND ((hashed_subplan) OR (hashed_subplan))

Well, the estimate here is rubbish - 9.3 million rows whereas we actually get 234. Now we know you're likely to get a lot of overlap, and the planner might not realise that. Still - that looks very bad. Of course, because it's expecting so many rows a seq-scan of ip_info looks like a good choice to it.

** Single subselect:

SELECT * FROM ip_info
WHERE ip IN (SELECT DISTINCT ip FROM network_events WHERE port = 2222
            UNION
            SELECT DISTINCT ip FROM host_events WHERE port = 2222);

Nested Loop (cost=2841..2856 rows=2 width=72) (actual time=55..106
rows=2349 loops=1)

This is clearly a lot better, Not sure whether the DISTINCT in each subquery works or not.

** The join:

SELECT distinct ip_info.*
 FROM ip_info RIGHT JOIN network_events USING (ip)
      RIGHT JOIN  host_events USING (ip)
 WHERE (network_events.port=2222 OR host_events.port=2222)

Unique (cost=9238..9367 rows=1965 width=72) (actual time=61..61 rows=52 loops=1)
 -> Sort (cost=9238..9288 rows=1965 width=72) (actual time=61..61
rows=63 loops=1)

OK, so what do the plans look like for port=80 or something larger like that?

Then try adding an index to the various host/network_events tables
CREATE INDEX ... ON ... (ip) WHERE port=80;

--
  Richard Huxton
  Archonet Ltd


[Postgresql General]     [Postgresql PHP]     [PHP Users]     [PHP Home]     [PHP on Windows]     [Kernel Newbies]     [PHP Classes]     [PHP Books]     [PHP Databases]     [Yosemite]

  Powered by Linux