Question

I have the following SQL query

UPDATE container  SET member_containers = members
FROM 
(
SELECT inter_container_membership.owning_container_id AS owner_cont
      ,array_to_string(array_agg(inter_container_membership.member_container_id),',') AS members
FROM inter_container_membership 
GROUP BY inter_container_membership.owning_container_id) v
WHERE container.id = owner_cont; 

The problem is, if the sub-query does not return anything then member_containers will not be updated, I need to set to null if it's the case, I've tried using EXISTS clause but it didn't work.

UPDATE container  SET member_containers = 
     CASE WHEN EXISTS (
          SELECT * FROM inter_container_membership
          WHERE container.id  = inter_container_membership.owning_container_id
          ) THEN members
     ELSE NULL END
FROM  (
SELECT inter_container_membership.owning_container_id AS owner_cont
      ,array_to_string(array_agg(inter_container_membership.member_container_id),',') AS members
FROM inter_container_membership 
GROUP BY inter_container_membership.owning_container_id) v
WHERE container.id = owner_cont;

So now I am thinking of nullifying out member_containers before updating it, but I could not find a way for doing that.

Was it helpful?

Solution

To set the column NULL in case the subquery does not find anything, you could use a correlated subquery instead of the join:

UPDATE container c
SET    member_containers = (
   SELECT string_agg(member_container_id, ',')
   FROM   inter_container_membership i
   WHERE  i.owning_container_id = c.id
   GROUP  BY owning_container_id
   );

Often, this kind of query is wrong, because the column shouldn't be nullified if nothing is found. But you seem to want exactly that.
Also, correlated subqueries tend to be slow. Consider to add this left join instead (doing the same):

UPDATE container c
SET    member_containers = i.members
FROM   container c1
LEFT   JOIN (
   SELECT owning_container_id
        , string_agg(member_container_id, ',') AS members
   FROM   inter_container_membership 
   GROUP  BY 1
   ) i ON i.owning_container_id = c1.id
WHERE  c.id = c1.id;

Also using string_agg() instead of array_to_string(array_agg(...)). Requires Postgres 9.0+.

OTHER TIPS

Use a CTE:

WITH m AS (
  SELECT icm.owning_container_id AS owner_cont,
         array_to_string(array_agg(icm.member_container_id),',') AS members
  FROM inter_container_membership icm
  GROUP BY icm.owning_container_id)
UPDATE container SET member_containers = m.members
WHERE id = m.owner_cont;
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top