muffin
muffin

Reputation: 2104

Postgresql : Alternative to joining the same table multiple times

If i have two tables entry and entry_metadata, with the entry_metadata as a description table for the entry referenced by entry_id and a variable.

If i have this :

entry

id | name   |
-------------
1  | entry1 |
2  | entry2 |
3  | entry3 |

entry_metadata

id | entry_id | variable | value
1  |    1     | width    | 10
2  |    1     | height   | 5
3  |    2     | width    | 8
4  |    2     | height   | 7
5  |   ...    |  ....    | ..

and i'm getting the table :

id | name   | width | height| ...  | ...
-----------------------------------------
1  | entry1 |  10   |  5    |
2  | entry2 |  8    |  7    |
3  | entry3 |  ..   |  ..   |

by the sql :

select e.name, em.width, emr.height
from
  public.entry e
left join
  public.entry_metadata em
on
  em.entry_id = e.id and em.variable = 'width'
left join
  public.entry_metadata emr
on
  emr.entry_id = e.id and emr.variable = 'height'

The query above works. But as I add more variables to get the values (the entry_metadata table includes a large variety of variables) from the entry metadata. The query gets really really slow. every join I do slows down the execution greatly. Is there a way to get around this?

Upvotes: 0

Views: 1731

Answers (3)

Radek Postołowicz
Radek Postołowicz

Reputation: 4774

Is there a way to get around this?

Yes, replace entry_metadata table with addtional column in entry (possible solutions are hstore or jsonb) with key - value storage of entry metadata.

Btw. your tables represents well known controversial database desing pattern known as "Entity Attribute Value".

Upvotes: -1

sfrutig
sfrutig

Reputation: 287

Just use subselects for this:

SELECT
  e.id,
  e.name,
  (SELECT em.value FROM public.entry_metadata em WHERE em.entry_id = e.id AND em.variable = 'width') AS width,
  (SELECT em.value FROM public.entry_metadata em WHERE em.entry_id = e.id AND em.variable = 'height') AS height
FROM
  public.entry e

So for each new variable you just need to add one more subselect.

Upvotes: 1

Gordon Linoff
Gordon Linoff

Reputation: 1269483

You can also do this with conditional aggregation:

select id, name,
       max(case when variable = 'width' then value end) as width,
       max(case when variable = 'height' then value end) as height
from public.entry_metadata em
group by id, name;

Adding additional columns is just adding more aggregation functions.

Upvotes: 2

Related Questions