Oracle批量添加主键冲突,实现数据不存在则插入,数据存在则更新(insert or update)

本文介绍了一种在Oracle中通过创建函数实现数据不存在则插入,数据存在则更新(insert-or-update)的方法,包括使用`fn_merge_index`函数的示例和mergeinto语句的讨论,同时提到了不同版本Oracle中的注意事项。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

此文章为转载文章,仅供学习

转载自:Oracle实现数据不存在则插入,数据存在则更新(insert or update) - 走看看

      思路是写一个函数,先按条件查询数据,假设查询到数据则更新。假设没有查询到数据则插入:

create or replace function fn_merge_index(statdate      in date,
                                          cpid          in varchar2,
                                          indextypecode in number,
                                          indexitemcode in number,
                                          indexdata     in varchar2)
  return number is
  numb number;
begin
  select count(*)
    into numb
    from cp_index_statistics_rec
   where stat_date = to_date(to_char(statdate, 'yyyy/mm/dd'), 'yyyy/mm/dd')
     and cp_id = cpid
     and index_type_code = indextypecode
     and index_item_code = indexitemcode;
  if numb = 0 then
    --数据不存在,insert
    begin
      insert into cp_index_statistics_rec
        (stat_id,
         stat_date,
         diagnosis,
         cp_id,
         is_validate,
         index_type_code,
         index_item_code,
         stat_data,
         stat_create_date,
         cp_name)
      values
        (cp_index_statistics_rec_seq.nextval,
         to_date(to_char(statdate, 'yyyy/mm/dd'), 'yyyy/mm/dd'),
         '',
         cpid,
         1,
         indextypecode,
         indexitemcode,
         indexdata,
         (select sysdate from dual),
         (select cp_name from cp_templet_master where cp_id = cpid));
      commit;
    end;
  else
    --数据存在,update
    begin
      update cp_index_statistics_rec
         set is_validate      = 1,
             stat_data        = indexdata,
             stat_create_date =
             (select sysdate from dual)
       where stat_date = to_date(to_char(statdate, 'yyyy/mm/dd'), 'yyyy/mm/dd')
         and cp_id = cpid
         and index_type_code = indextypecode
         and index_item_code = indexitemcode;
      commit;
    end;
  end if;
  return numb;
end fn_merge_index;

注意to_date(to_char(statdate, 'yyyy/mm/dd'), 'yyyy/mm/dd')这个写法,假设写成to_date(statdate, 'yyyy/mm/dd'),依据NLS不同。可能导致数据出错。详细请看这里

另外oracle提供了merge into能够实现此功能。理论上讲比上面的效率会高。可是没做试验。merge into有个缺点就是在10g下面版本号的oracle中会出现故障,导致比較严重的后果(据说会把全部的数据都更新,而9i又不支持在update后加条件),所以我没有採用这种方法。

merge into的使用方法:

merge into bonuses d 
using (select employee_id, salary, department_id from employees 
where department_id = 80) s 
on (d.employee_id = s.employee_id) 
when matched then update set d.bonus = d.bonus + s.salary*.01 
when not matched then insert (d.employee_id, d.bonus) 
values (s.employee_id, s.salary*0.01); 


另外还有个思路。直接update,运行后会返回受影响的行数。假设行数为0,表示没有符合条件的数据。后面运行insert;假设行数大于0。表示有符合条件的行数且update运行成功。

<think>我们在SQL和XML环境下操作,通常是在MyBatis框架中编写映射文件(XML)来实现。 需求:根据主键判断数据是否存在,循环插入更新数据到表中(即批量操作:存在更新存在插入) 在MySQL中,我们可以使用ON DUPLICATE KEY UPDATE语法来实现单条数据的“存在更新存在插入”。对于批量操作,同样可以使用此语法,但需要将多条数据用VALUES()函数分别指定更新值。 步骤: 1. 在Mapper接口中定义方法,参数为List(要操作的数据列表) 2. 在XML映射文件中,使用<insert>标签,并编写SQL语句 3. SQL语句使用INSERT INTO ... VALUES ... ON DUPLICATE KEY UPDATE ... 的格式 4. 对于批量操作,使用MyBatis的<foreach>标签遍历列表,生成多条VALUES记录 注意:该操作依赖于主键或唯一索引。当插入数据导致主键或唯一索引冲突时,就会执行更新操作。 假设我们有一个表`product`,主键为`id`(或者有唯一索引的字段,比如`code`),我们以`code`作为判断依据(唯一索引)。 示例XML配置(参考引用[3]并修改为批量操作): 注意:在批量操作中,ON DUPLICATE KEY UPDATE后面跟的更新字段,如果要使用插入时的值,可以使用VALUES(字段名)来获取(这样即使批量插入,每个记录更新时也会使用对应插入记录的值)。 具体实现如下:</think>### 在 SQL 和 MyBatis XML 中实现循环插入/更新操作 根据主键判断数据是否存在并执行插入更新操作,在 MySQL 中可通过 `ON DUPLICATE KEY UPDATE` 语法实现。以下是具体实现方案: #### 1. SQL 核心语法 ```sql INSERT INTO 表名 (字段1, 字段2, 主键字段) VALUES (值1, 值2, 主键), (值1, 值2, 主键) ON DUPLICATE KEY UPDATE 字段1 = VALUES(字段1), 字段2 = VALUES(字段2); ``` - **执行逻辑**:当主键冲突时自动转为更新操作 - **VALUES() 函数**:动态引用 INSERT 语句中的值 - **必要条件**:必须存在主键或唯一索引约束[^1][^3] #### 2. MyBatis XML 实现 ```xml <insert id="insertOrUpdateBatch"> INSERT INTO your_table (id, name, status, update_time) VALUES <foreach item="item" collection="list" separator=","> ( #{item.id}, #{item.name}, #{item.status}, NOW() ) </foreach> ON DUPLICATE KEY UPDATE name = VALUES(name), status = VALUES(status), update_time = NOW() </insert> ``` #### 3. 关键配置说明 | 组件 | 作用 | |----------------|----------------------------------------------------------------------| | `<foreach>` | 遍历 List 参数生成批量 VALUES 子句 | | `VALUES(字段)` | 引用 INSERT 时传入的值,避免硬编码更新值 | | `NOW()` | 自动设置更新时间戳 | | 主键约束 | 表中必须存在 PRIMARY KEY 或 UNIQUE 索引 | #### 4. 性能优化建议 1. **批量分段**:单次处理 500-1000 条数据,避免超大 SQL ```java // Java 中分批处理示例 List<List<Item>> batches = Lists.partition(fullList, 500); batches.forEach(batch -> mapper.insertOrUpdateBatch(batch)); ``` 2. **索引优化**:确保判断字段有索引 3. **事务控制**:在 Service 层添加 `@Transactional` 注解 #### 5. 使用场景示例 ```java // 实体类 public class Product { private Long id; // 主键 private String name; private Integer status; } // Mapper 接口 void insertOrUpdateBatch(List<Product> products); ``` > **注意**:SQL Server 可使用 `MERGE INTO` 语法,Oracle 用 `MERGE` 语句实现相同功能,语法结构有所同[^1]。 --- ### 相关问题 1. 如何优化 MyBatis 批量操作的性能? 2. `ON DUPLICATE KEY UPDATE` 和 `REPLACE INTO` 有什么区别? 3. 在多数据库兼容的场景下如何实现存在更新? 4. 批量操作时如何避免死锁问题? 5. 如何通过 MyBatis 注解方式实现存在更新逻辑? [^1]: MySQL 官方文档:ON DUPLICATE KEY UPDATE 语法 [^2]: MyBatis 批量操作最佳实践 [^3]: 数据库唯一约束与索引设计原则
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值