如何转换 SQL 服务器与已删除和已插入输出的合并
How to convert SQL Server merge with deleted and inserted output
我正在执行 SQL 服务器到 PostgreSQL 的迁移,有很多过程如下所示,我们需要访问插入/更新和删除的数据:
CREATE PROCEDURE dbo.CGTACCUMULATED_INSUPD
@AccID int
, @Input tvpCGTACCUMULATED_INSUPD readonly
as
merge CGTAccumulated t
using (
select @AccID [AccID]
, F_YEAR [FinancialYear]
, F_ACCUMCOST [CarryForwardLoss]
, F_B1 [OffBook]
, F_E1 [ExternalISAContribution]
from @Input
) s
on t.AccID = @AccID and t.FinancialYear = s.FinancialYear
when matched and isnull(s.CarryForwardLoss, t.CarryForwardLoss) = 0 and isnull(s.OffBook, t.OffBook) = 0
and isnull(s.ExternalISAContribution, t.ExternalISAContribution) = 0 then
delete
when matched and (t.CarryForwardLoss <> isnull(s.CarryForwardLoss, t.CarryForwardLoss)
or t.OffBook <> isnull(s.OffBook, t.OffBook)
or t.ExternalISAContribution <> isnull(s.ExternalISAContribution, t.ExternalISAContribution)) then
update
set CarryForwardLoss = isnull(s.CarryForwardLoss, t.CarryForwardLoss)
, OffBook = isnull(s.OffBook, t.OffBook)
, ExternalISAContribution = isnull(s.ExternalISAContribution, t.ExternalISAContribution)
when not matched and (s.CarryForwardLoss <> 0 or s.OffBook <> 0 or s.ExternalISAContribution <> 0) then
insert (AccID, FinancialYear, CarryForwardLoss, OffBook, ExternalISAContribution)
values (@AccID, s.FinancialYear, isnull(s.CarryForwardLoss, 0), isnull(s.OffBook, 0), isnull(s.ExternalISAContribution, 0))
output
isnull(deleted.FinancialYear, inserted.FinancialYear) [FinancialYear]
, deleted.CarryForwardLoss [OldCarryForwardLoss]
, deleted.OffBook [OldOffBook]
, deleted.ExternalISAContribution [OldExternalISAContribution]
, inserted.CarryForwardLoss [NewCarryForwardLoss]
, inserted.OffBook [NewOffBook]
, inserted.ExternalISAContribution [NewExternalISAContribution];
将其转换为 Postgresql 的最佳方法是什么?我正在按照下面的思路进行思考(但这不起作用,因为它有两个 into 子句)。此外,为每个 return 值创建参数似乎有点笨拙,有没有更好的方法(也许是临时的 table 或其他东西)?
CREATE PROCEDURE cgtaccumulated_insupd(IN par_accid INTEGER, IN par_input tvpcgtaccumulated_insupd, INOUT p_refcur refcursor)
AS
$BODY$
DECLARE
l_old_CarryForwardLoss double precision;
l_old_OffBook double precision;
l_old_ExternalISAContribution double precision;
l_old_FinancialYear double precision;
l_new_CarryForwardLoss double precision;
l_new_OffBook double precision;
l_new_ExternalISAContribution double precision;
l_new_FinancialYear double precision;
BEGIN
-- This creates a temporary table to hold the composite type data
PERFORM tvpaccid$aws$f('par_input$aws$tmp');
INSERT INTO "par_input$aws$tmp"
SELECT *
FROM UNNEST(par_input);
with src as (
select par_accid accid
,f_year FinancialYear
,f_accumcost CarryForwardLoss
,f_b1 OffBook
,f_e1 ExternalISAContribution
from "par_input$aws$tmp"
), deleted as (
delete from CGTAccumulated tgt
where tgt.accid in
(select accid
from src
where tgt.FinancialYear = src.FinancialYear
and coalesce(src.CarryForwardLoss, tgt.CarryForwardLoss) = 0
and coalesce(src.OffBook, tgt.OffBook) = 0
and coalesce(src.ExternalISAContribution, tgt.ExternalISAContribution) = 0)
returning CarryForwardLoss, OffBook, ExternalISAContribution, FinancialYear
into l_old_CarryForwardLoss, l_old_OffBook, l_old_ExternalISAContribution, l_old_FinancialYear
)
insert into CGTAccumulated (AccID, FinancialYear, CarryForwardLoss, OffBook, ExternalISAContribution)
select accid, FinancialYear, CarryForwardLoss, OffBook, ExternalISAContribution
from src
on conflict(accid) do update set
CarryForwardLoss = coalesce(src.CarryForwardLoss, CarryForwardLoss)
,OffBook = coalesce(src.OffBook, OffBook)
,ExternalISAContribution = coalesce(src.ExternalISAContribution, ExternalISAContribution)
returning CarryForwardLoss, OffBook, ExternalISAContribution, FinancialYear
into l_new_CarryForwardLoss, l_new_OffBook, l_new_ExternalISAContribution, l_new_FinancialYear;
open p_refcur for
select coalesce(l_old_FinancialYear, l_new_FinancialYear) FinancialYear
,l_old_CarryForwardLoss OldCarryForwardLoss
,l_old_OffBook OldOffBook
,l_old_ExternalISAContribution OldExternalISAContribution
,l_new_CarryForwardLoss NewCarryForwardLoss
,l_new_OffBook NewOffBook
,l_new_ExternalISAContribution NewExternalISAContribution;
END;
$BODY$
LANGUAGE plpgsql;
更新
我在 SO 上找到了类似的线程,但没有发现任何与在一个过程中同时输出插入/更新和删除有关的线程。
这是一个例子。我使用了简化的 table 结构。在 postgresql 13 上测试。
create table test (
id int primary key
,name varchar(100)
,amount int
)
-- initial data
with src as (
select *
from (values
(1, 'a', 100)
,(2, 'b', 200)
,(3, 'c', 300)
,(4, 'd', 400)
) as t(id, name, amount)
)
insert into test
select *
from src
-- simulate merge and return deleted and inserted
with src as (
select *
from (values
(1, 'a', 101)
,(4, 'd', 0)
) as t(id, name, amount)
)
,deleted as (
delete
from test
where id in (select id from src where amount = 0)
returning *
)
,inserted as (
insert into test
select *
from src
where src.amount != 0
on conflict(id) do update set
amount = excluded.amount
returning *
)
select 'deleted' rec_type, d.* from deleted d
union all
select 'inserted / updated', i.* from inserted i
我把插入和更新的记录放在一个桶里。您可以根据需要轻松地将它们分开。
我正在执行 SQL 服务器到 PostgreSQL 的迁移,有很多过程如下所示,我们需要访问插入/更新和删除的数据:
CREATE PROCEDURE dbo.CGTACCUMULATED_INSUPD
@AccID int
, @Input tvpCGTACCUMULATED_INSUPD readonly
as
merge CGTAccumulated t
using (
select @AccID [AccID]
, F_YEAR [FinancialYear]
, F_ACCUMCOST [CarryForwardLoss]
, F_B1 [OffBook]
, F_E1 [ExternalISAContribution]
from @Input
) s
on t.AccID = @AccID and t.FinancialYear = s.FinancialYear
when matched and isnull(s.CarryForwardLoss, t.CarryForwardLoss) = 0 and isnull(s.OffBook, t.OffBook) = 0
and isnull(s.ExternalISAContribution, t.ExternalISAContribution) = 0 then
delete
when matched and (t.CarryForwardLoss <> isnull(s.CarryForwardLoss, t.CarryForwardLoss)
or t.OffBook <> isnull(s.OffBook, t.OffBook)
or t.ExternalISAContribution <> isnull(s.ExternalISAContribution, t.ExternalISAContribution)) then
update
set CarryForwardLoss = isnull(s.CarryForwardLoss, t.CarryForwardLoss)
, OffBook = isnull(s.OffBook, t.OffBook)
, ExternalISAContribution = isnull(s.ExternalISAContribution, t.ExternalISAContribution)
when not matched and (s.CarryForwardLoss <> 0 or s.OffBook <> 0 or s.ExternalISAContribution <> 0) then
insert (AccID, FinancialYear, CarryForwardLoss, OffBook, ExternalISAContribution)
values (@AccID, s.FinancialYear, isnull(s.CarryForwardLoss, 0), isnull(s.OffBook, 0), isnull(s.ExternalISAContribution, 0))
output
isnull(deleted.FinancialYear, inserted.FinancialYear) [FinancialYear]
, deleted.CarryForwardLoss [OldCarryForwardLoss]
, deleted.OffBook [OldOffBook]
, deleted.ExternalISAContribution [OldExternalISAContribution]
, inserted.CarryForwardLoss [NewCarryForwardLoss]
, inserted.OffBook [NewOffBook]
, inserted.ExternalISAContribution [NewExternalISAContribution];
将其转换为 Postgresql 的最佳方法是什么?我正在按照下面的思路进行思考(但这不起作用,因为它有两个 into 子句)。此外,为每个 return 值创建参数似乎有点笨拙,有没有更好的方法(也许是临时的 table 或其他东西)?
CREATE PROCEDURE cgtaccumulated_insupd(IN par_accid INTEGER, IN par_input tvpcgtaccumulated_insupd, INOUT p_refcur refcursor)
AS
$BODY$
DECLARE
l_old_CarryForwardLoss double precision;
l_old_OffBook double precision;
l_old_ExternalISAContribution double precision;
l_old_FinancialYear double precision;
l_new_CarryForwardLoss double precision;
l_new_OffBook double precision;
l_new_ExternalISAContribution double precision;
l_new_FinancialYear double precision;
BEGIN
-- This creates a temporary table to hold the composite type data
PERFORM tvpaccid$aws$f('par_input$aws$tmp');
INSERT INTO "par_input$aws$tmp"
SELECT *
FROM UNNEST(par_input);
with src as (
select par_accid accid
,f_year FinancialYear
,f_accumcost CarryForwardLoss
,f_b1 OffBook
,f_e1 ExternalISAContribution
from "par_input$aws$tmp"
), deleted as (
delete from CGTAccumulated tgt
where tgt.accid in
(select accid
from src
where tgt.FinancialYear = src.FinancialYear
and coalesce(src.CarryForwardLoss, tgt.CarryForwardLoss) = 0
and coalesce(src.OffBook, tgt.OffBook) = 0
and coalesce(src.ExternalISAContribution, tgt.ExternalISAContribution) = 0)
returning CarryForwardLoss, OffBook, ExternalISAContribution, FinancialYear
into l_old_CarryForwardLoss, l_old_OffBook, l_old_ExternalISAContribution, l_old_FinancialYear
)
insert into CGTAccumulated (AccID, FinancialYear, CarryForwardLoss, OffBook, ExternalISAContribution)
select accid, FinancialYear, CarryForwardLoss, OffBook, ExternalISAContribution
from src
on conflict(accid) do update set
CarryForwardLoss = coalesce(src.CarryForwardLoss, CarryForwardLoss)
,OffBook = coalesce(src.OffBook, OffBook)
,ExternalISAContribution = coalesce(src.ExternalISAContribution, ExternalISAContribution)
returning CarryForwardLoss, OffBook, ExternalISAContribution, FinancialYear
into l_new_CarryForwardLoss, l_new_OffBook, l_new_ExternalISAContribution, l_new_FinancialYear;
open p_refcur for
select coalesce(l_old_FinancialYear, l_new_FinancialYear) FinancialYear
,l_old_CarryForwardLoss OldCarryForwardLoss
,l_old_OffBook OldOffBook
,l_old_ExternalISAContribution OldExternalISAContribution
,l_new_CarryForwardLoss NewCarryForwardLoss
,l_new_OffBook NewOffBook
,l_new_ExternalISAContribution NewExternalISAContribution;
END;
$BODY$
LANGUAGE plpgsql;
更新
我在 SO 上找到了类似的线程,但没有发现任何与在一个过程中同时输出插入/更新和删除有关的线程。
这是一个例子。我使用了简化的 table 结构。在 postgresql 13 上测试。
create table test (
id int primary key
,name varchar(100)
,amount int
)
-- initial data
with src as (
select *
from (values
(1, 'a', 100)
,(2, 'b', 200)
,(3, 'c', 300)
,(4, 'd', 400)
) as t(id, name, amount)
)
insert into test
select *
from src
-- simulate merge and return deleted and inserted
with src as (
select *
from (values
(1, 'a', 101)
,(4, 'd', 0)
) as t(id, name, amount)
)
,deleted as (
delete
from test
where id in (select id from src where amount = 0)
returning *
)
,inserted as (
insert into test
select *
from src
where src.amount != 0
on conflict(id) do update set
amount = excluded.amount
returning *
)
select 'deleted' rec_type, d.* from deleted d
union all
select 'inserted / updated', i.* from inserted i
我把插入和更新的记录放在一个桶里。您可以根据需要轻松地将它们分开。