【问题标题】:SQL merge statement with multiple conditions多个条件的 SQL 合并语句
【发布时间】:2017-04-26 17:14:50
【问题描述】:

我有一些业务规则需要在 SQL 上实现(在 PL/SQL 块内):我需要评估这些规则并根据结果执行相应的更新、删除或插入到目标表中。

我的数据库模型包含一个“暂存”表和一个“真实”表。真实表存储过去插入的记录,暂存表包含来自某个需要合并到真实表中的“新鲜”数据。

基本上这些是我的业务规则:

  1. staging MINUS real --> Insert 行之间的增量
  2. real MINUS staging--> 删除行之间的差异
  3. PK 相同但任何其他字段不同的行:更新

(那些“MINUS”将比较所有字段以获得相等并区分第三种情况)

我还没有想出通过使用合并语句在不重叠规则的情况下完成此类任务的方法:对合并结构有什么建议吗?是否可以在同一个合并中一起完成所有这些操作?

谢谢!

【问题讨论】:

  • 我知道,我的意思是 PL/SQL 块内的 SQL 合并。谢谢
  • 提供desc的暂存表和真实表,至少是主键。

标签: sql plsql merge conditional-statements


【解决方案1】:

如果我理解你的任务正确,下面的代码应该可以完成这项工作:

--drop table real;
--drop table stag;

create table real (
  id NUMBER,
  col1 NUMBER,
  col2 VARCHAR(10)
);

create table stag (
  id NUMBER,
  col1 NUMBER,
  col2 VARCHAR(10)
);

insert into real values (1, 1, 'a');
insert into real values (2, 2, 'b');
insert into real values (3, 3, 'c');
insert into real values (4, 4, 'd');
insert into real values (5, 5, 'e');
insert into real values (6, 6, 'f');
insert into real values (7, 6, 'g'); -- PK the same but at least one column different
insert into real values (8, 7, 'h'); -- PK the same but at least one column different
insert into real values (9, 9, 'i');
insert into real values (10, 10, 'j'); -- in real but not in stag

insert into stag values (1, 1, 'a');
insert into stag values (2, 2, 'b');
insert into stag values (3, 3, 'c');
insert into stag values (4, 4, 'd');
insert into stag values (5, 5, 'e');
insert into stag values (6, 6, 'f');
insert into stag values (7, 7, 'g'); -- PK the same but at least one column different
insert into stag values (8, 8, 'g'); -- PK the same but at least one column different
insert into stag values (9, 9, 'i');
insert into stag values (11, 11, 'k'); -- in stag but not in real

merge into real
     using (WITH w_to_change AS (
              select *
                from (select stag.*, 'I' as action from stag
                       minus
                      select real.*, 'I' as action from real
                     )
               union (select real.*, 'D' as action from real
                       minus 
                      select stag.*, 'D' as action from stag
                     )
            )
            , w_group AS (
              select id, max(action) as max_action
                from w_to_change
               group by id
            )
            select w_to_change.*
              from w_to_change
              join w_group
                on w_to_change.id = w_group.id
               and w_to_change.action = w_group.max_action
           ) tmp
   on (real.id = tmp.id)
 when matched then
   update set real.col1 = tmp.col1, real.col2 = tmp.col2
   delete where tmp.action = 'D'
 when not matched then
   insert (id, col1, col2) values (tmp.id, tmp.col1, tmp.col2);

【讨论】:

  • 这很像魅力,但我有一个问题:如果要删除标记为“D”的行,我可以在“匹配时”块而不先更新它?我问它是因为我有一个触发器可以将所有更新或删除的内容复制到“跟踪”表中,并且由于之前的更新+删除,删除的记录会两次进入该表,我想避免这种无用的重复跨度>
  • 不可能。请检查这个问题:stackoverflow.com/questions/17709602/….
猜你喜欢
  • 2018-08-28
  • 2016-11-29
  • 1970-01-01
  • 2020-07-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多