Re: Poor performance on 9.4.4 when matching multiple columns from left side of LATERAL join

From: Thomas Kellerer <spam_eater(at)gmx(dot)net>
To: pgsql-general(at)postgresql(dot)org
Subject: Re: Poor performance on 9.4.4 when matching multiple columns from left side of LATERAL join
Date: 2015-11-14 09:30:16
Message-ID: n26uvc$7f3$1@ger.gmane.org
Views: Raw Message | Whole Thread | Download mbox | Resend email
Thread:
Lists: pgsql-general

Steven Grimm schrieb am 14.11.2015 um 07:25:
> We have a table, call it "multi_id", that contains columns with IDs of various kinds of objects in my system,
>and another table that's a generic owner/key/value store for object attributes (think configuration settings,
>and I'll refer to this table as "settings"). To wit:
>
> ---------------------------------------------
> CREATE TABLE multi_id (
> id1 INTEGER PRIMARY KEY,
> id2 INTEGER,
> id3 INTEGER
> );
> CREATE TABLE settings (
> owner_id INTEGER,
> setting_id INTEGER,
> setting_value TEXT,
> PRIMARY KEY (owner_id, setting_id)
> );
> CREATE UNIQUE INDEX multi_id_idx_id1 ON multi_id (id1, id2);
> CREATE UNIQUE INDEX multi_id_idx_id2 ON multi_id (id2, id1);
> CREATE INDEX settings_idx_setting_id ON settings (setting_id, setting_value);
> ---------------------------------------------
>
> We want to find all the rows from multi_id where any of the IDs (including its primary key) have a certain setting with a certain value.
>
> LATERAL seemed like the tool for the job, so we tried the following:
>
> ---------------------------------------------
> SELECT mid.id1
> FROM multi_id AS mid,
> LATERAL (
> SELECT 1
> FROM settings
> WHERE setting_id = 1
> AND setting_value = 'common_1'
> AND owner_id IN (mid.id1, mid.id2, mid.id3)
> ) AS setting_matcher;
> ---------------------------------------------
>

The above is actualy a CROSS JOIN between multi_id and settings which generates duplicate values for id1 and is probably not what you want

I _think_ what you are after is something like this:

with sett as (
SELECT owner_id
FROM settings
WHERE setting_id = 1
AND setting_value = 'common_1'
)
select mid.id1
from multi_id as mid
where exists (SELECT 1
FROM sett
WHERE owner_id = mid.id1)
or exists (SELECT 1
FROM sett
where owner_id = mid.id2)
or exists (SELECT 1
FROM sett
where owner_id = mid.id3);

This returns the same result as your original query (when I apply a DISTINCT on it to remove the duplicate ids).
It runs in 23ms on my computer, your cross join takes roughly 4 seconds.

This is the plan from your statement: http://explain.depesz.com/s/EyjJ
This is the plan for my statement: http://explain.depesz.com/s/Dt7x

In response to

Browse pgsql-general by date

  From Date Subject
Next Message Kevin Grittner 2015-11-14 16:22:36 Re: fast refresh materialized view
Previous Message David Rowley 2015-11-14 08:32:19 Re: Poor performance on 9.4.4 when matching multiple columns from left side of LATERAL join