My users
table contains Alice, Bob and Charles. Alice and Bob have a 3 and 2 fruits
respectively. Charles has none. A relationship is established using a foreign key constraint foreign key (user_id) references users (id)
and a unique (user_id, name)
constraint, allowing zero or one fruit per user.
create table users ( id integer primary key, firstname varchar(64) ); create table fruits ( id integer primary key not null, user_id integer not null, name varchar(64) not null, foreign key (user_id) references users (id), unique (user_id, name) ); insert into users (id, firstname) values (1, 'Alice'); insert into users (id, firstname) values (2, 'Bob'); insert into users (id, firstname) values (3, 'Charles'); insert into fruits (id, user_id, name) values (1, 1, 'grape'); insert into fruits (id, user_id, name) values (2, 1, 'apple'); insert into fruits (id, user_id, name) values (3, 1, 'pear'); insert into fruits (id, user_id, name) values (4, 2, 'orange'); insert into fruits (id, user_id, name) values (5, 2, 'cherry');
Charles does not have an orange, so there is no resulting row (first query below). However, running the same query for a user that does not exist (second query below) also returns no result.
test=# select * from fruits where user_id = 3 and name = 'orange'; id | user_id | name ----+---------+------ (0 rows) test=# select * from fruits where user_id = 99 and name = 'orange'; id | user_id | name ----+---------+------ (0 rows)
Is it possible to perform a single query whilst simultaneously differentiating between the user not existing vs the user existing and not having a fruit?
If so, can this also be done to find all the fruits belonging to a particular user (i.e. select * from fruits where user_id = 3
vs select * from fruits where user_id = 99
.
Advertisement
Answer
Use a LEFT [OUTER] JOIN
:
SELECT u.id, u.firstname, f.name AS fruit -- , COALESCE(f.name, '') AS fruit -- alternatively an empty string FROM users u LEFT JOIN fruits f ON f.user_id = u.id AND f.name = 'orange' WHERE u.id = 3;
If the user exists, you always get a row.
If the user has no fruit (of that name), fruit
defaults to NULL
– which is otherwise impossible since fruit.name
is defined NOT NULL
.
To get an empty string instead, throw in COALESCE()
. But no fruit can be named ''
then or it’ll be ambiguous again. Add a CHECK
constraint to be sure.
Related:
Aside 1: “name” is not a good name.
Aside 2: Typically, this would be a many-to-many design with three tables: “users”, “fruits” and “user_fruits”. See: