I want to update table prod_replay_out
based on subquery results in Postgres. However, subquery returns multiple rows but I want to skip those rows and update table based on single rows return by subquery.
I have referred link Subquery returns more than 1 row error but max()
function will not apply for my expected results. Could you please provide me some suggestion to modify query? Thank you.
prod_replay_out
has following columns:
seller, buyer, sender_tag, seller_tag, buyer_tag, isin, quantity, in_msg_time, msg_type, cdsx_time
prod_replay_in
has following columns:
seller, buyer, sender_tag, seller_tag, buyer_tag, isin, quantity, msg_type, cdsx_time
What I have tried?
Please find below update sql:
Update sql:
update prod_replay_out O
set in_msg_id =
(Select id
From prod_replay_in I
Where I.msg_type = 'CDST010'
and I.seller = O.seller
and I.buyer = O.buyer
and I.sender_tag = O.sender_tag
and I.seller_tag = O.seller_tag
and I.buyer_tag = O.buyer_tag
and I.isin = O.isin
and I.quantity = O.quantity
and I.cdsx_time = O.in_msg_time
and I.cdsx_time::text like '2020-05-12%'
)
where O.msg_type = 'CDST01C'
and O.cdsx_time::text like '2020-05-12%';
I have tried below solution. Is it the correct approach or is there any loophole?
update prod_replay_out O
set in_msg_id =
(Select id
From prod_replay_in I
Where I.msg_type = 'CDST010'
and I.seller = O.seller
and I.buyer = O.buyer
and I.sender_tag = O.sender_tag
and I.seller_tag = O.seller_tag
and I.buyer_tag = O.buyer_tag
and I.isin = O.isin
and I.quantity = O.quantity
and I.cdsx_time = O.in_msg_time
and I.cdsx_time::text like '2020-05-12%'
and 1 = (Select count(id)
From prod_replay_in I
Where I.msg_type = 'CDST010'
and I.seller = O.seller
and I.buyer = O.buyer
and I.sender_tag = O.sender_tag
and I.seller_tag = O.seller_tag
and I.buyer_tag = O.buyer_tag
and I.isin = O.isin
and I.quantity = O.quantity
and I.cdsx_time = O.in_msg_time
and I.cdsx_time::text like '2020-05-12%'
)
)
where O.msg_type = 'CDST01C'
and O.cdsx_time::text like '2020-05-12%';
Advertisement
Answer
You want to update only when the subquery returns one row. One option uses aggreation and having
in the subquery:
update prod_replay_out o
set in_msg_id = (
select max(id)
from prod_replay_in i
where
i.msg_type = 'cdst010'
and i.seller = o.seller
and i.buyer = o.buyer
and i.sender_tag = o.sender_tag
and i.seller_tag = o.seller_tag
and i.buyer_tag = o.buyer_tag
and i.isin = o.isin
and i.quantity = o.quantity
and i.cdsx_time = o.in_msg_time
and i.cdsx_time >= '2020-05-12'::date
and i.cdsx_time < '2020-05-13'::date
having count(*) = 1
)
where
o.msg_type = 'cdst01c'
and o.cdsx_time >= '2020-05-12'::date
and o.cdsx_time < '2020-05-13'::date
Note that I rewrote the date filters to avoid the conversion to text (you can use an half-open interval with date literals instead, which is by far more efficient).
Note that this updates in_msg_id
to null
when the subquery would have returned multiple rows (or no rows at all). If you want to avoid that, you can filter in the where
clause:
update prod_replay_out o
set in_msg_id = (
select max(id)
from prod_replay_in i
where
i.msg_type = 'cdst010'
and i.seller = o.seller
and i.buyer = o.buyer
and i.sender_tag = o.sender_tag
and i.seller_tag = o.seller_tag
and i.buyer_tag = o.buyer_tag
and i.isin = o.isin
and i.quantity = o.quantity
and i.cdsx_time = o.in_msg_time
and i.cdsx_time >= '2020-05-12'::date
and i.cdsx_time < '2020-05-13'::date
having count(*) = 1
)
where
o.msg_type = 'cdst01c'
and o.cdsx_time >= '2020-05-12'::date
and o.cdsx_time < '2020-05-13'::date
and (
select count(*)
from prod_replay_in i
where
i.msg_type = 'cdst010'
and i.seller = o.seller
and i.buyer = o.buyer
and i.sender_tag = o.sender_tag
and i.seller_tag = o.seller_tag
and i.buyer_tag = o.buyer_tag
and i.isin = o.isin
and i.quantity = o.quantity
and i.cdsx_time = o.in_msg_time
and i.cdsx_time >= '2020-05-12'::date
and i.cdsx_time < '2020-05-13'::date
) = 1