snippetsqlMinor
How can I match the items in an array column, with a different table in Postgres?
Viewed 0 times
canthepostgrescolumnarraywithmatchdifferentitemshow
Problem
I have a table in a Postgres 12 database with that has an array column with a bunch of dog breed guids. I want to lookup these values and basically return the lookup values instead of the guids.
I would like to be able to return the following:
Essentially, look them up in my 'breeds' table before returning the values. Order of elements is not relevant.
CREATE TABLE dogs(
name text,
breeds text[]
);
INSERT INTO dogs (name, breeds) VALUES ('Barkley', '{"abc", "xyz"}');
INSERT INTO dogs (name, breeds) VALUES ('Ponyo', '{"zzz", "xyz"}');
CREATE TABLE breeds(
guid text,
breed text
);
INSERT INTO breeds (guid, breed) VALUES ('abc', 'Maltipoo');
INSERT INTO breeds (guid, breed) VALUES ('xyz', 'Jack Russel');
INSERT INTO breeds (guid, breed) VALUES ('zzz', 'Dalmatian');I would like to be able to return the following:
Barkley, ['Maltipoo', 'Jack Russel']
Ponyo, ['Jack Russel', 'Dalmatian']Essentially, look them up in my 'breeds' table before returning the values. Order of elements is not relevant.
Solution
SELECT d.name, b.breeds_text
FROM dogs d
CROSS JOIN LATERAL (
SELECT ARRAY(SELECT b.breed
FROM unnest(breeds) a(guid)
JOIN breeds b USING (guid)) AS breeds_text
) b;Because we
JOIN after unnnest(), the order of elements is not necessarily preserved.I had
LEFT JOIN LATERAL (...) ON true at first. But since the ARRAY constructor makes the subquery always return exactly one row, that's equivalent to a simpler CROSS JOIN. See:- How to apply ORDER BY and LIMIT in combination with an aggregate function?
To guarantee order of elements (if you need that?), use
WITH ORDINALITY and ORDER BY in the LATERAL subquery:SELECT d.name, b.breeds_text
FROM dogs d
CROSS JOIN LATERAL (
SELECT ARRAY(SELECT b.breed
FROM unnest(breeds) WITH ORDINALITY AS a(guid, ord)
JOIN breeds b USING (guid)
ORDER BY a.ord) AS breeds_text
) b;Or a lowly correlated subquery, probably a bit faster:
SELECT d.name
, ARRAY(SELECT b.breed
FROM unnest(d.breeds) WITH ORDINALITY AS a(guid, ord)
JOIN breeds b USING (guid)
ORDER BY a.ord) AS breeds_text
FROM dogs d;db<>fiddle here
NULL values and empty arrays produce an empty array in the result. To preserve NULL, you'd need to do more. Like: use CASE ...Duplicates in the array are preserved as given with either query.
See:
- How to preserve the original order of elements in an unnested array?
- PostgreSQL unnest() with element number
- LEFT OUTER JOIN on array column with multiple values
Or consider a normalized many-to-many relational design instead of the array to begin with:
- How to implement a many-to-many relationship in PostgreSQL?
Code Snippets
SELECT d.name, b.breeds_text
FROM dogs d
CROSS JOIN LATERAL (
SELECT ARRAY(SELECT b.breed
FROM unnest(breeds) a(guid)
JOIN breeds b USING (guid)) AS breeds_text
) b;SELECT d.name, b.breeds_text
FROM dogs d
CROSS JOIN LATERAL (
SELECT ARRAY(SELECT b.breed
FROM unnest(breeds) WITH ORDINALITY AS a(guid, ord)
JOIN breeds b USING (guid)
ORDER BY a.ord) AS breeds_text
) b;SELECT d.name
, ARRAY(SELECT b.breed
FROM unnest(d.breeds) WITH ORDINALITY AS a(guid, ord)
JOIN breeds b USING (guid)
ORDER BY a.ord) AS breeds_text
FROM dogs d;Context
StackExchange Database Administrators Q#295284, answer score: 4
Revisions (0)
No revisions yet.