Warning: file_get_contents(/data/phpspider/zhask/data//catemap/5/sql/71.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Sql Oracle合并-如果不匹配,则在条件通过时更新_Sql_Oracle_Conditional Statements_Sql Merge - Fatal编程技术网

Sql Oracle合并-如果不匹配,则在条件通过时更新

Sql Oracle合并-如果不匹配,则在条件通过时更新,sql,oracle,conditional-statements,sql-merge,Sql,Oracle,Conditional Statements,Sql Merge,我需要将一些值合并到一个表中,当具有指定键的行已经存在时更新字段,或者在不存在时插入新行 这是我的桌子: profiles(name, surname, active); 其中: name VARCHAR2(30) surname VARCHAR2(30) active NUMBER(1) name and surname -> composite primary key 我正在使用此查询: 使用合并到配置文件中 选择 “马克”我的名字, “紫碧”我的名字, “1”myAct

我需要将一些值合并到一个表中,当具有指定键的行已经存在时更新字段,或者在不存在时插入新行

这是我的桌子:

profiles(name, surname, active);
其中:

name    VARCHAR2(30)
surname VARCHAR2(30)
active  NUMBER(1)

name and surname -> composite primary key
我正在使用此查询:

使用合并到配置文件中 选择 “马克”我的名字, “紫碧”我的名字, “1”myActive 来自双重 在…上 name=myName 姓氏=mySurname 当匹配时 更新集 活动=我的活动 当不匹配时 插入 名称 姓 忙碌的 价值观 我的名字, 我的名字, myActive ; 它可以工作,但即使active已设置为1,它也会更新记录

我想做的是这样的:

WHEN MATCHED THEN
    IF(active != myActive)
        UPDATE SET
            active = myActive
    ELSE
        RAISE CUSTOM EXCEPTION
WHEN NOT MATCHED THEN
    INSERT [...]

可能吗?好吧,我不能将这样的if放入MERGE语句中,那么怎么做呢?

好吧,我想这不是一个好的做法,但由于活动列的类型为NUMBER1,您可以通过简单地尝试将其值更新为更大的值来轻松生成ORA-01438异常。例如,如果active的新值和旧值相等,类似这样的内容将引发异常:

MERGE INTO profiles USING (
    SELECT
        'Mark' myName,
        'Zibi' mySurname,
        1    myActive
   FROM DUAL
) ON (
   name = myName
   AND surname = mySurname
)
WHEN MATCHED THEN
    UPDATE SET
        active =  CASE WHEN active = myActive THEN 11 ELSE myActive END
WHEN NOT MATCHED THEN
    INSERT (
        name,
        surname,
        active
    ) VALUES (
        myName,
        mySurname,
        myActive
    );
使用PL/SQL运行条件合并操作 编辑:原始帖子询问如何通过SQL或PL/SQL可以解决的方法将一组现有数据处理到一个名为PROFILES的已建立表中

再次编辑:OP的最后一条评论非常微妙。如果您没有直接的SQL访问权限,那么您将需要一个游标、一个驱动查询或其他构造来处理您输入的每个记录。许多基于JDBC的中间件组件也接受游标作为输入。您可以在一个过程调用中输入所有数据。。。看看PL/SQL中的REF-CURSOR数据类型。如果是这样,这个解决方案仍然有帮助

使用复合联接键,根据多个条件更新目标表中的数据:

如果源数据不存在,请插入源数据。 如果存在人员标识符名称+姓氏,则切换或更新状态值。 如果目标表中已存在人员且其状态为“活动”,则跳过该人员。 样本数据 我给我的表命名略有不同,并修改了列名,这是一个保留的sql/plsql关键字。。。防止将来可能发生的任何冲突

示例数据插入语句DML:

*为清楚起见:测试模式中的名称与OP不完全匹配。STACK_PROFILES=PROFILES和STACK_PROFILE_MERGE_SOURCE表示某些源。。。这可能是xml提要、csv文本文件等

创建表堆栈配置文件 配置文件名称VARCHAR240, 姓VARCHAR240, 活动编号1,0, 约束堆栈\配置文件\主键配置文件\名称、姓氏启用

在堆栈配置文件中插入名称、姓氏、活动值“LOIS”、“LAINE”、0; 在堆栈配置文件中插入配置文件名称、姓氏、活动值“MARTIN”、“SHORT”1; 在堆栈配置文件中插入配置文件名称、姓氏、活动值“ROBIN”、“WILLIAMS”,0; 在堆栈配置文件中插入配置文件名称、姓氏、活动值“GRACE”、“HOPPER”、0; 在堆栈配置文件中插入配置文件名称、姓氏、活动值“LOIS”、“LAINE-KENT”,0

承诺。。。

创建表堆栈\u配置文件\u合并\u源 配置文件名称VARCHAR240, 姓VARCHAR240, 约束堆栈\配置文件\合并\源\主键配置文件\名称、姓氏 使可能 /

插入堆栈_配置文件_合并_源配置文件_名称,姓氏值“BRUCE”,“WAYNE”; 插入堆栈_配置文件_合并_源配置文件_名称,姓氏值“海绵”,“罗伯特”; 插入堆栈_PROFILE_MERGE_SOURCE PROFILE_name,姓氏值'CLARK','KENT'; 插入堆栈_配置文件_合并_源配置文件_名称,姓氏值'LOIS','LAINE'; 插入堆栈_PROFILE_MERGE_SOURCE PROFILE_name,姓氏值'MARTIN','SHORT'; 插入堆栈_配置文件_合并_源配置文件_名称,姓氏值'DAMON','WAYANS'; 插入堆栈_配置文件_合并_源配置文件_名称,姓氏值‘ROBIN’、‘WILLIAMS’; 插入堆栈_PROFILE_MERGE_SOURCE PROFILE_name,姓氏值'BRUCE','WILLIS'; 插入堆栈_配置文件_合并_源配置文件_名称,姓氏值‘DENNIS’、‘HOPPER’; 插入堆栈_配置文件_合并_源配置文件_名称,姓氏值'WHOOPI','GOLDBERG'; 插入堆栈_PROFILE_MERGE_SOURCE PROFILE_name,姓氏值'GRACE','HOPPER'; 插入堆栈_PROFILE_MERGE_SOURCE PROFILE_name,姓氏值'JERI','RYAN';

测试用例 理解所提出的要求是很有帮助的。写几个测试用例可以让我们更接近

对于测试用例1和2

对于测试用例3和4

PL/SQL源代码 有一种更简单的方法可以通过类似SQL合并的函数应用附加的条件逻辑。下面的PL/SQL匿名块使用外部连接语法来标识要插入的记录和要更新的记录。第三个c 当光标处理循环跳过该定义的记录时,还可以观察到目标表中已存在且处于活动状态的类别

处理循环和游标

我们在dml操作中使用FOR UPDATE和WHERE CURRENT语法,因为此查询中引用的数据的状态在其使用寿命期间发生变化

 declare
    c_default_status_active   constant number:= 1;
    c_status_inactive         constant number:= 0;

    cursor profile_cur is
       select sp.profile_name as target_name, 
              sp.surname as target_surname, sp.active as original_status,
              spm.profile_name as source_name, spm.surname as source_surname

         from stack_profiles sp, stack_profile_merge_source spm
        where spm.profile_name = sp.profile_name(+)
          and spm.surname = sp.surname(+)
        order by spm.profile_name asc nulls last, 
          spm.surname asc
          for update of sp.profile_name, sp.surname, sp.active;

        v_rec_profile  profile_cur%ROWTYPE;

     begin

      open profile_cur;
     fetch profile_cur into v_rec_profile;

     while profile_cur%found loop
       -- insert condition (no match in outer join...)
       if v_rec_profile.original_status is null
       then
       insert into stack_profiles (profile_name, surname, active)
       values (v_rec_profile.source_name, v_rec_profile.source_surname, 
           c_default_status_active);

       elsif
       -- flip status from inactive to active for existing but 
       -- inactive records.
       v_rec_profile.original_status = c_status_inactive then
       update stack_profiles
          set active = c_default_status_active
        where current of profile_cur;
          end if;

      fetch profile_cur into v_rec_profile;
      end loop;
      close profile_cur;

commit;

end;
讨论
我注意到对这类问题有许多不同的处理方法。这里使用的具体方法是演示所涉及的概念。结果可能因数据库配置、使用和设置而异

在这种情况下,最好通过存储过程使用PL/SQL,或者只从客户端执行匿名SQL块,而不是执行单个MERGE SQL语句

匿名PL/SQL块可能如下所示:

declare
  -- Parameters of query, initialization values  
  pName    profiles.name%type    := 'Mark';
  pSurname profiles.surname%type := 'Zibi';
  pActive  profiles.active%type  := 0;

  -- variable used for test against table
  vIsActiveInDb profiles.active%type;
begin

  select 
    max(profs.active) into vIsActiveInDb
  from 
    profiles profs
  where 
    profs.name = pName and profs.surname = pSurname
  ;

  if(vIsActiveInDb is null) then
    -- profile not found, create new one 
    insert into profiles(name, surname, active)
    values(pName, pSurname, pActive);

  elsif(vIsActiveInDb != pActive) then
    -- profile found, activity flag differs 
    update profiles set active = pActive 
    where name = pName and surname = pSurname;

  else
    -- profile found with same activity flag
    raise_application_error(
      -20001, -- custom error code from -20000 to -20999
      'Profile "'||pName||' '||pSurname||'" already exists with same activity flag'
    );  
  end if;

end;

以上代码中有两个建议: 1.姓名、姓氏对是一个主键,所以总是选择单行或不选; 2.活动字段不能为空,例如,使用not null约束创建。 如果这个建议失败了,代码会稍微复杂一点。该变体可在中找到

我从未使用过MyBatis,但基于XML描述,此类查询可能如下所示:

<update id="UpdateProfileActivity" parameterType="map" statementType="CALLABLE">   
  declare
    -- Parameters of query, initialization values
    pName    profiles.name%type    := #{piName,    mode=IN, jdbcType=VARCHAR};
    pSurname profiles.surname%type := #{piSurname, mode=IN, jdbcType=VARCHAR};
    pActive  profiles.active%type  := #{piActivity,mode=IN, jdbcType=NUMERIC};

    -- variable used for test against table
    vIsActiveInDb profiles.active%type;   begin

    select
      max(profs.active) into vIsActiveInDb
    from
      profiles profs
    where
      profs.name = pName and profs.surname = pSurname
    ;

    if(vIsActiveInDb is null) then
      -- profile not found, create new one
      insert into profiles(name, surname, active)
      values(pName, pSurname, pActive);

    elsif(vIsActiveInDb != pActive) then
      -- profile found, activity flag differs
      update profiles set active = pActive
      where name = pName and surname = pSurname;

    else
      -- profile found with same activity flag
      raise_application_error(
        -20001, -- custom error code from -20000 to -20999
        'Profile "'||pName||' '||pSurname||'" already exists with same activity flag'
      );
    end if;

  end; 
</update>
您可以通过在源代码上添加where条件(使用--subquery--)来完成此操作,以便在匹配命令时进行筛选,或者在不匹配命令后添加where条件

在下面的示例中,我将合并从ID520到530的记录, 同时,我不会在id=525的位置插入记录

--------
merge into merchant_tmp2 dest
using (select * from merchant where id between 520 and 530) src
on(dest.id=src.id)
when matched then 
update set address=address ||' - updated'
when not matched then 
insert (ID,....)
values (src.ID,....)
where src.id <> 525;

ref:

您可以在此处添加WHERE子句,但不能添加异常。你真的需要一个例外,还是仅仅更新记录不够?您可以添加一个触发器来捕获更改活动标志的代码实例,如果是,则引发一个异常。@Ben如果可能的话,我希望有一个异常。.您是仅限于SQL还是可以在此处使用PL/SQL?例如,执行开始。。。终止block?@ThinkJet我想我可以使用PL/SQL。我从一个java应用程序调用数据库,在这个应用程序中我使用MyBatis处理数据库,它应该支持PL/SQL。在MyBatis映射XML中使用PL/SQL,因此它应该是完美的。对于此任务,不需要外部连接、显式游标和游标循环。将commit合并到SQL代码中是一种糟糕的做法。另外,请举例说明。我将编辑您的建议+ThinkJet,但其他内容似乎不正确。。。我想我记得您和OP也认为PL/SQL,即外部连接、显式游标和游标循环可能是一种可能的方法…>您是仅限于SQL还是可以在这里使用PL/SQL?例如,执行开始。。。终止区块?–ThinkJet 10小时前我也喜欢SQLfiddle,但我也失去了使用他们系统的工作。。。如果他们真的出缺了,我在这里的职位还有意义吗?称我为老派,但我将永远把我的工作签入scm,或者发布完整的SQLDDL和DML,以便其他人可以跟随。我们不仅仅是在解决一个人的问题。。。我们正在为未来数不清的搜索提供有意义的指南。。。让你的工作尽可能长。无论如何,开发人员应该可以自由地试验和使用那些似乎适合表达他们观点的工具。你的方法是可能的,但对于这个简单的查询来说太复杂了。关于PL/SQL没有异议,在我自己的回答中,我也使用PL/SQL。我所有的建议都是关于PL/SQL代码的风格。我的建议:一,。关于显式游标,请查看。2.如果只存在一条记录,则不需要循环。3.不需要任何连接,查询中的堆栈\配置文件\合并\源是什么?4.不需要在中输入个人资料\姓名和姓氏来更新。。。记录锁定可能有用,但选择取决于应用程序逻辑。我们现在没有足够的信息来做出这样的决定。我只是假设将用户状态更新到所需状态并不重要。即使在您的变体中,如果未找到所需的配置文件,在插入重复主键的情况下也可能引发本机Oracle错误。所以从我的观点来看,这个解决方案过于复杂了。
--------
merge into merchant_tmp2 dest
using (select * from merchant where id between 520 and 530) src
on(dest.id=src.id)
when matched then 
update set address=address ||' - updated'
when not matched then 
insert (ID,....)
values (src.ID,....)
where src.id <> 525;