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