1

Is there an easy way to update composite types inside an array?

Currently I have the following table (I truncated other fields):

CREATE TYPE order_item AS (delivery_date DATE, status INT);
CREATE TABLE demo (id SERIAL PRIMARY KEY, data order_item[]);

I want to update the status of all order_items. When it's greater than 1, all status should be updated + 1.

For a table without array field it would be easy:

UPDATE mytab SET complex_col.r = (complex_col).r + 1;

However, I want to do the same inside an array.

Erwin Brandstetter
  • 605,456
  • 145
  • 1,078
  • 1,228
Christian Schmitt
  • 837
  • 16
  • 47

1 Answers1

2

The root of your problem is the relational design. A normalized schema (separate table in 1:n relationship) would be much cleaner than the array column, and easier to index or update etc. Would hardly occupy more space on disk either, array overhead is similar to row overhead.

While stuck with your unfortunate design, you have to unnest the array, update and aggregate back, taking care not to break things at each step:

I want to update the status of all order_items. When it's greater than 1, all status should be updated + 1.

UPDATE demo d
SET    data = x.data
FROM (
   SELECT d.id, array_agg((o.delivery_date
                         , CASE WHEN o.status > 1 THEN o.status + 1 ELSE o.status END
                           )::order_item) AS data
   FROM   demo d
   LEFT   JOIN  LATERAL unnest(data) o ON true
   GROUP  BY d.id
   HAVING count(*) FILTER (WHERE o.status > 1) > 0
   ) x
WHERE  d.id = x.id;

Order of elements is likely not to change, but there are no guarantees without ORDER BY.
To guarantee original order of elements:

Community
  • 1
  • 1
Erwin Brandstetter
  • 605,456
  • 145
  • 1,078
  • 1,228