SQL update multiple rows in destination table with same id but different values from other table
-
17-01-2021 - |
Question
creditNote table
id invoice_id generated_at(timestamp) status
10101 111 2018-02-28 14:42:39.247 generated
23982 111 2018-03-30 11:11:11.247 generated
paymentModeAmount table
id invoice_id paid_on(timestamp) payment_mode
98236 111 creditNote
63725 111 creditNote
After execution of below query OUTPUT is
paymentModeAmount table
id invoice_id paid_on(timestamp) payment_mode
98236 111 2018-02-28 14:42:39.247 creditNote
63725 111 2018-02-28 14:42:39.247 creditNote
Here PaidOn Column has same data, but we need to get two different timestamps
Expected
id invoice_id paid_on(timestamp) payment_mode
98236 111 2018-02-28 14:42:39.247 creditNote
63725 111 2018-03-30 11:11:11.247 creditNote
paidOn timestamp's should be different as expected
PL/SQL Function
CREATE OR REPLACE FUNCTION updatePaidOn()
RETURNS TABLE(
invoiceid BIGINT,
generatedat TIMESTAMP)
AS $$
BEGIN
RETURN QUERY SELECT
generated_invoice_id as invoiceid, generated_at as generatedat
FROM
credit_note
WHERE
status='generated';
END; $$
LANGUAGE 'plpgsql';
Execute the below function
UPDATE payment_mode_amount pm
SET paid_on = sq.generatedat
from updatePaidOn() as sq
where pm.invoice_id = sq.invoiceid
and pm.paid_on is null
and pm.payment_mode='creditNote';
Update accordingly timestamps..
Solution
If you don't care which timestamp is used for which target row, you can match the two tables by joining them on a generated "row number" using e.g. this:
with cn as (
select id, invoice_id, generated_at, row_number() over (order by id) as rn
from creditnote
where status = 'generated'
), pnr as (
select id, invoice_id, generated_at, row_number() over (order by id) as rn
from paymentmodeamount
where payment_mode = 'creditNote'
)
select *
from pnr
join cn on cn.rn = pnr.rn and cn.invoice_id = pnr.invoice_id;
This joins the rows based on the ordering of the ID values in the tables creditnote
and paymentmodeamount
. If you want you can also order the creditnote
table by generated_at
.
Given your sample data this would return something like this (this is only to demonstrate what the above join does):
id | invoice_id | generated_at | rn | id | invoice_id | generated_at | rn
------+------------+--------------+----+-------+------------+---------------------+---
63725 | 111 | | 1 | 10101 | 111 | 2018-02-28 14:42:39 | 1
98236 | 111 | | 2 | 23982 | 111 | 2018-03-30 11:11:11 | 2
Now the above query can be used as part of an UPDATE statement to bring the rows together.
with cn as (
select id, invoice_id, generated_at, row_number() over (order by id) as rn
from creditnote
where status = 'generated'
), pnr as (
select id, invoice_id, generated_at, row_number() over (order by id) as rn
from paymentmodeamount
where payment_mode = 'creditNote'
)
update paymentmodeamount p
set generated_at = cn.generated_at
from pnr
join cn on cn.rn = pnr.rn and cn.invoice_id = pnr.invoice_id
where p.id = pnr.id
and p.generated_at is null;
This assumes that paymentmodeamount.id
is the primary key of the table.
Note that this won't be very fast for large tables.
Online example: https://rextester.com/YXAK25669