【问题标题】:UPSERT inserts duplicate null entry into table (ORACLE)UPSERT 将重复的空条目插入表 (ORACLE)
【发布时间】:2018-08-22 12:37:28
【问题描述】:

我正在尝试通过检查一些示例通过 PL/SQL 在 ORACLE 上创建一个 upsert 触发器,我做得很好,我认为这是我应该只配置的最后一步。我的要求是:

将插入该字段的系统将始终保持一列为空,因此我将从另一个表中读取列值,然后将其插入并包含该值。

d2c_region_locale_config 持有 d2c_is_active 值,所以我首先读取有关区域设置条件的值,然后触发插入或通过在 active_for_d2c 列上添加此值来更新表。(对于更新,我正在使用区域设置和国家/地区列,因为它显示在 where 子句中,它们不是PK但没有空条件)

所以我创建了这个触发器:

CREATE OR REPLACE TRIGGER BL_PIM_LOCALE_COUNTRY 
BEFORE INSERT OR UPDATE ON PIM_LOCALE_COUNTRY REFERENCING NEW AS NEW OLD AS OLD 
FOR EACH ROW
DECLARE
   l_active_for_d2c INTEGER;
BEGIN
  if :NEW.active_for_d2c is null then
    DELETE from pim_locale_country where active_for_d2c is null;
   select distinct(d2c_isactive) into l_active_for_d2c from d2c_region_locale_config where d2c_locale= :NEW.locale;
   UPDATE pim_locale_country 
    SET locale = :NEW.locale, locale_name = :NEW.locale_name, 
    country = :NEW.country, country_name = :NEW.country_name, isdummy = :NEW.isdummy,
    active_for_d2c = l_active_for_d2c, itextpos = :NEW.itextpos, locale_charset = :NEW.locale_charset,
    fallback_locale = :NEW.fallback_locale, default_for_lang = :NEW.default_for_lang, opeclang = :NEW.opeclang
    where locale = :NEW.locale and country = :NEW.country;
   IF ( sql%notfound ) THEN
      INSERT INTO PIM_LOCALE_COUNTRY (locale,locale_name,country,country_name,isdummy,active_for_d2c,itextpos,locale_charset,fallback_locale,default_for_lang,opeclang)
      VALUES (:NEW.locale, :NEW.locale_name,:NEW.country,:NEW.country_name,:NEW.isdummy,l_active_for_d2c,:NEW.itextpos,:NEW.locale_charset,:NEW.fallback_locale,:NEW.default_for_lang,:NEW.opeclang);
   END IF;
  end if;
END; 

它目前完成这项工作,读取值并插入或更新现有的语言环境-国家/地区对以获取其他值。但关键是,表总是有一个“空”值(请检查屏幕截图),即使我在触发器的开头运行 delete 语句。所以我的问题是如何删除,或者如何在触发端使用这种方法?

非常感谢您的回答!

【问题讨论】:

    标签: sql oracle database-trigger


    【解决方案1】:

    触发器 before 插入不会阻止插入本身,因此您插入该记录两次。也就是说,一旦您的触发器完成其工作(插入或更新记录),oracle 将使用位于触发器的新记录中的值继续插入(或更新)。如果trigger修改了NEW.,它会在你修改的时候被存储,但是如果trigger自己插入一些东西,你可以获得更多的记录。

    您可以使用代替插入或更新触发器,然后oracle 将不会在触发器完成后运行自己的插入/更新。

    但对于 1 记录触发器,更常见的方法是修改 NEW 中的字段,对于这种情况,字段 NEW.d2c_is_active。

    看起来像这样(可能有错别字,请检查)

    CREATE OR REPLACE TRIGGER BL_PIM_LOCALE_COUNTRY 
    BEFORE INSERT OR UPDATE ON PIM_LOCALE_COUNTRY REFERENCING NEW AS NEW OLD AS OLD 
    FOR EACH ROW
    BEGIN
      if :NEW.active_for_d2c is null then
        select d2c_isactive 
        into :NEW.active_for_d2c 
        from d2c_region_locale_config 
        where d2c_locale= :NEW.locale and rownum<=1;
      end if;
    END; 
    

    【讨论】:

    • 其实,我没明白你说的插入触发器没有阻塞,但例子是在插入触发器之前,所以不幸的是它帮不了我......
    • 好吧,然后更改第二行,您的触发器将是“而不是插入或更新 PIM_LOCALE_COUNTRY REFERENCING NEW AS NEW AS OLD AS OLD”
    • 不工作...但问题不在于我两次从其他表中选择值,因为我在 select 语句中编写了 distinct(),问题是第一个语句总是以某种方式执行,它将空值插入表中...如果我添加条件不为空,则无法执行触发器...
    • 当我尝试过这种方式时,它仍然在表中添加“null”值..
    • 无论如何我都赞成这个答案。原始触发器通过插入具有预期值的第二行来响应插入行中的空值。此外,令人困惑的是,由于它是一个 INSERT OR UPDATE 触发器,它自己执行插入和更新,它会自行触发,因此它的一部分运行两次。 @Hayra 建议的代码的重要部分是它为 :NEW.active_for_d2c 分配一个值,它更正当前插入的行,而不是尝试更新以前插入的行或插入新行,这两者都不是你真正想要的去做。
    猜你喜欢
    • 2010-09-19
    • 2011-02-12
    • 1970-01-01
    • 1970-01-01
    • 2015-03-01
    • 1970-01-01
    • 2012-01-29
    • 1970-01-01
    相关资源
    最近更新 更多