Reputation: 99
Not entirely sure how to phrase this, but hope someone can help. Here's my problem.
I have a table T which has the following fields
ID CF Value ------------------------ 976 13 Severity 1 978 36 branch a 978 13 severity 1
I want to pull out items which have the attributes branch a and severity 1.
The problem is, the value field is used for both branch and severity, only differentiated by a different CF value of 13 or 36. If an item has both attributes, it is given a duplicate entry (for example ID 978 above)
So I can't select from D where value = branch a AND value = severity1.
What I want to do is a Select - As - From with the following fields
ID Branch Severity
And pull the value field into Branch if cf = 36 and pull value into Severity if cf = 13.
But I'm not sure how i'd do this syntactically. I've tried a few Cases and If statements but keep getting errors.
Any help much appreciated.
Thanks.
Upvotes: 0
Views: 248
Reputation:
The standard approach for doing a pivot when having to cope with such a model is this:
select id,
max(branch) as branch,
max(severity) as severity
from (
select id,
case
when cf = 36 then value
else null
end as branch,
case
when cf = 13 then value
else null
end as severity
from t
) p
group by id
This type of "design" is called "entity-attribute-value" pattern and - as Catcall has already stated - is considered an anti-pattern. Precisely for the complicated way to retrieve things.
If you search for that term in the internet you'll get plenty of hits for this.
In PostgreSQL you could also use the crosstab function from the tablefunc module, which essentially creates the above statement dynamically inside a function.
http://www.postgresql.org/docs/current/static/tablefunc.html
Using the crosstab function, the query would look like this:
SELECT *
FROM crosstab(
'select id, cf, value
from t
where cf in (13,36)
order by 1,2'::text)
AS ct(id integer, severity text, branch text);
Upvotes: 1
Reputation: 133492
You can do simple operations with this sort of schema:
select id from t where cf = 36 and value = 'branch a'
intersect
select id from t where cf = 13 and value = 'severity1'
As Catcall already pointed out, this system can get pretty unwieldy after a while. If the id value in T refers so some entity table E, then you can create a view a bit like this:
create view e_attrs as
select e.id, t_36.value as branch, t_13.value as severity
from e
left join t t_36 on t_36.id = e.id and t_36.cf = 36
left join t t_13 on t_13.id = e.id and t_13.cf = 36
and then hope that join elimination (from postgresql 9.1, I think) will remove extraneous joins against T if you query on the view.
Upvotes: 0
Reputation: 95572
You can get the ID numbers this way.
select id
from your_table
where value = 'branch a'
or value = 'Severity 1'
group by id
having count(id) = 2
But in general you'll find that anything more complex will tie you in knots. This kind of structure is a well-known anti-pattern, and that's not an accident.
Upvotes: 0