We have an EAV (Entity-Attribute-Value) data model that can be simplified with those 3 tables:
We want to sort entities on certain field:
SELECT entities.* FROM entities JOIN values ON values.entity_id = entities.entity_id JOIN fields ON fields.field_id = values.field_id WHERE fields.name = 'my-field' ORDER BY values.value DESC
However, this will not include entities that do not have values for this field.
The wanted behavior would be to have those NULL
entries at the beginning of the result, like usual ORDER BY
.
Do you have suggestion of how to include those values ? Thanks
EDIT: Here is a concrete example:
Imagine we want to display and sort a list of 3 users, which are 3 entities having 2 fields, name
and age
:
The Entity
table:
entity_id --------- 1 2 3
The Field
table:
field_id | name ---------|------------- 1 | "firstname" 2 | "age"
The Value
table:
entity_id | field_id | value ----------|----------|----------- 1 | 1 | "Alice" 2 | 1 | "Bob" 3 | 1 | "Charlie" 1 | 2 | "24" 2 | 2 | "27"
Here, Charlie have no value for age
. How to sort users by the age
field without removing Charlie ?
The above query will remove it from the result-set.
Advertisement
Answer
Note: I’m still somewhat confused about what you want your resultset to look like.
select * from value v1 left join value v2 on (v1.entity_id = v2.entity_id and v2.field_id = 2) where v1.field_id = 1 -- assuming all users have a name order by v2.value;
Result:
ENTITY_ID | FIELD_ID | VALUE | ENTITY_ID | FIELD_ID | VALUE ----------|----------|-----------|------------------------------ 1 | 1 | "Alice" | 1 | 2 | "24" 2 | 1 | "Bob" | 2 | 2 | "27" 3 | 1 | "Charlie" | null | null | null
Or if you prefer the names of the fields:
select v1.entity_id, f1.name, v1.value, f2.name, v2.value from value v1 join field f1 on v1.field_id = f1.field_id left join value v2 on (v1.entity_id = v2.entity_id and v2.field_id = 2) left join field f2 on v2.field_id = f2.field_id where f1.name = 'firstname' -- assuming all users have a name order by v2.value;
Result:
ENTITY_ID | NAME | VALUE | NAME | VALUE ----------|-------------|-----------|-------------- 1 | "firstname" | "Alice" | "age" | "24" 2 | "firstname" | "Bob" | "age" | "27" 3 | "firstname" | "Charlie" | null | null
For the case where there’s an entity, that doesn’t have a firstname, I added two rows to entity and a row to value:
insert into entity values (4); insert into entity values (5); insert into value values (4, 2, '102');
If a record exists in the entity table, it will always be in this result:
select * from entity e left join value v1 on (e.entity_id = v1.entity_id and v1.field_id = 1) -- firstname left join value v2 on (e.entity_id = v2.entity_id and v2.field_id = 2) -- age -- order by v2.value IS NULL; -- age nulls last
Result:
ENTITY_ID | ENTITY_ID | FIELD_ID | VALUE | ENTITY_ID | FIELD_ID | VALUE ----------|-----------|------------|-----------|------------|----------|------ 1 | 1 | 1 | "Alice" | 1 | 2 | "24" 2 | 2 | 1 | "Bob" | 2 | 2 | "27" 4 | null | null | null | 4 | 2 | "102" 3 | 3 | 1 | "Charlie" | null | null | null 5 | null | null | null | null | null | null