Oracle数据库-批量插入+序列的两种方式

定义序列名称为 oracle_table_seq

方式1

insert into oracle_table  (id,code)
    select id,code from ( 
       select oracle_table_seq.nextval as id, '1' as code  from dual 
       union all   
       select oracle_table_seq.nextval as id, '1' as code  from dual     
       union all   
       select oracle_table_seq.nextval as id, '1' as code  from dual        
       union all   
       select oracle_table_seq.nextval as id, '1' as code  from dual         
    ) ;

 直接把序列名称.nextval替换对应值即可

方式2

begin
  insert into oracle_table ( id, code ) values( oracle_table_seq.nextval  , '1' );  
  insert into oracle_table ( id, code ) values( oracle_table_seq.nextval  , '2' );  
  insert into oracle_table ( id, code ) values( oracle_table_seq.nextval  , '3' );   
  insert into oracle_table ( id, code ) values( oracle_table_seq.nextval  , '4' );
end;

同样, 序列名称.nextval替换对应值即可

额外说明

以上是两种使用序列进行批量插入的方式。先前文章提到过批量插入的三种方法,还有以下写法

 insert all 
 into oracle_table ( id, code ) values( 1 , '1' )  
 into oracle_table ( id, code ) values( 2 , '2' )   
 into oracle_table ( id, code ) values( 3 , '3' )   
 into oracle_table ( id, code ) values( 4 , '4' )   
 select 1 from dual ;

 但是实际使用,会发现此种方法的序列值始终保持不变,因此不能使用。

<think>我们正在讨论的是在Oracle数据库中如何使用类似于MyBatis中foreach标签的批量插入SQL写法。根据引用[3]和引用[4]的内容,Oracle数据库不支持像MySQL那样使用VALUES后接多个元组的语法,而是需要采用其他方式。 在Oracle中,常见的批量插入方式两种: 1. 使用`UNION ALL`方式:将多个SELECT语句用UNION ALL连接,然后插入到表中。 2. 使用`INSERT ALL`语句:可以一次性插入多行到多个表(或同一个表),但每行都需要一个INTO子句。 结合MyBatis的foreach标签,我们可以这样实现: 方法一:使用UNION ALL(适用于需要序列生成主键的情况) 引用[3]中给出了一个例子,使用序列生成主键,然后通过SELECT FROM dual UNION ALL的方式构建多行数据。 示例SQL(在MyBatis的mapper文件中): ```xml <insert id="insertForeach" parameterType="java.util.List" useGeneratedKeys="false"> INSERT INTO EXCELIMPORTFIELDCFG (EXCELIMPORTFIELDCFGID, DESCRIPTION, LENGTH, ROWNUMBER, TABLEID, TYPE, FIELDNAME) SELECT EXCELIMPORTFIELDCFGSEQ.NEXTVAL, t.* FROM ( <foreach collection="fields" item="item" separator="union all"> SELECT #{item.description,jdbcType=OTHER}, #{item.length,jdbcType=DECIMAL}, #{item.rownumber,jdbcType=DECIMAL}, #{item.tableid,jdbcType=DECIMAL}, #{item.type,jdbcType=OTHER}, #{item.fieldname,jdbcType=OTHER} FROM dual </foreach> ) t </insert> ``` 方法二:使用INSERT ALL(适用于不需要返回主键或者主键由其他方式生成的情况) 引用[4]中给出了一个例子,使用INSERT ALL配合多个INTO子句,最后用SELECT 1 FROM dual结束。 示例SQL(在MyBatis的mapper文件中): ```xml <insert id="batchInsert" parameterType="java.util.List"> INSERT ALL <foreach collection="list" item="item" separator=" "> INTO table_name (column1, column2, ...) VALUES (#{item.value1}, #{item.value2}, ...) </foreach> SELECT 1 FROM dual </insert> ``` 注意:在INSERT ALL方法中,foreach标签的separator属性这里设置为空格,因为每个INTO子句之间不需要逗号,只需要空格分隔。最后必须加上SELECT 1 FROM dual,因为INSERT ALL语句要求后面必须有一个子查询。 但是,INSERT ALL有一个限制:它一次最多只能插入1000行。这是因为Oracle中每个INSERT ALL语句的INTO子句数量不能超过1000。如果需要插入超过1000条,需要分批执行。 另外,如果我们需要在插入时获取自增主键(Oracle中通常使用序列),在方法一中我们可以在SELECT语句中直接使用序列的NEXTVAL,这样每行都会生成一个新的序列值。而在方法二中,我们可以在每个INTO子句的VALUES中使用序列,例如: ```sql INSERT ALL INTO table_name (id, col1) VALUES (seq_name.NEXTVAL, 'value1') INTO table_name (id, col1) VALUES (seq_name.NEXTVAL, 'value2') ... SELECT 1 FROM dual ``` 在MyBatis中,如果使用INSERT ALL并配合序列,可以这样写: ```xml <insert id="batchInsert" parameterType="java.util.List"> INSERT ALL <foreach collection="list" item="item" separator=" "> INTO table_name (id, col1, col2) VALUES (seq_name.NEXTVAL, #{item.col1}, #{item.col2}) </foreach> SELECT 1 FROM dual </insert> ``` 但是,这种方法无法返回生成的主键值到Java对象中,因为每次插入多条,而MyBatis的useGeneratedKeys通常只支持单条记录返回主键。如果需要返回主键,可能需要采用其他方式,比如使用<selectKey>标签,但批量插入返回多个主键比较复杂。 总结一下,在Oracle中,使用MyBatis的foreach标签进行批量插入,推荐两种写法: 1. 使用SELECT ... FROM dual UNION ALL方式(可以配合序列生成主键,但无法直接返回主键到Java对象,除非在插入前预先分配序列值) 2. 使用INSERT ALL方式(简单直观,但有1000条限制,且同样难以返回主键) 根据具体需求选择合适的方式。 下面我们给出一个完整的示例,假设我们有一个表T_USER,包含字段ID(主键,使用序列SEQ_USER)、NAME、AGE。 方法一(UNION ALL)的MyBatis Mapper写法: ```xml <insert id="batchInsertUsers" parameterType="java.util.List"> INSERT INTO T_USER (ID, NAME, AGE) SELECT SEQ_USER.NEXTVAL, t.* FROM ( <foreach collection="list" item="user" separator="union all"> SELECT #{user.name,jdbcType=VARCHAR} AS NAME, #{user.age,jdbcType=INTEGER} AS AGE FROM dual </foreach> ) t </insert> ``` 方法二(INSERT ALL)的MyBatis Mapper写法: ```xml <insert id="batchInsertUsers" parameterType="java.util.List"> INSERT ALL <foreach collection="list" item="user" separator=" "> INTO T_USER (ID, NAME, AGE) VALUES (SEQ_USER.NEXTVAL, #{user.name}, #{user.age}) </foreach> SELECT 1 FROM dual </insert> ``` 注意:在方法二中,由于每个INTO子句都会调用一次SEQ_USER.NEXTVAL,所以每个插入都会得到一个新的序列值。 另外,如果插入的数据量非常大,建议在程序中进行分批插入(比如每1000条执行一次批量插入),以避免超出数据库的限制或影响性能。 最后,关于返回主键的问题,在Oracle批量插入中,MyBatis没有直接支持返回多个主键。如果必须返回主键,可以考虑在插入前先通过查询序列获取一批主键值(例如:先获取1000个序列值,然后分配给这1000个对象),然后再插入。但这样就需要额外的数据库交互。或者,插入后通过其他方式(如按唯一字段查询)获取生成的主键。 希望以上内容能帮助你解决Oracle批量插入的问题。</think>### Oracle 批量插入 SQL 的 `foreach` 写法详解 在 Oracle 中使用 MyBatis 的 `foreach` 标签实现批量插入,主要有两种高效写法: --- #### 方法一:`INSERT ALL` 写法(推荐) ```xml <insert id="batchInsert" parameterType="java.util.List"> INSERT ALL <foreach collection="list" item="item" separator=" "> INTO your_table (id, name, age) VALUES ( #{item.id}, #{item.name}, #{item.age} ) </foreach> SELECT 1 FROM dual </insert> ``` **关键点**: 1. `INSERT ALL` + `INTO` 子句组合语法 2. `SELECT 1 FROM dual` 是必需的结束符 3. `<foreach>` 的 `separator` 设为空格(无需逗号) 4. 支持单次最多插入 **1000 行**(Oracle限制)[^4] --- #### 方法二:`UNION ALL` 写法(带序列主键) ```xml <insert id="batchInsertWithSeq" parameterType="java.util.List"> INSERT INTO your_table (id, name, age) SELECT your_seq.NEXTVAL, t.* FROM ( <foreach collection="list" item="item" separator="UNION ALL"> SELECT #{item.name} AS name, #{item.age} AS age FROM dual </foreach> ) t </insert> ``` **关键点**: 1. 通过 `SELECT ... FROM dual UNION ALL` 构建数据集 2. 在外部查询中调用序列 `your_seq.NEXTVAL` 生成主键 3. 字段别名必须显式声明(如 `AS name`) 4. 适合需要自增主键的场景[^3] --- ### 性能优化建议 1. **分批处理**:每批插入 500-1000 条,避免超限 ```java // Java 调用示例 int batchSize = 500; for (int i = 0; i < list.size(); i += batchSize) { List<User> subList = list.subList(i, Math.min(i + batchSize, list.size())); userMapper.batchInsert(subList); } ``` 2. **事务控制**:在 Service 层开启事务 ```java @Transactional public void batchInsertUsers(List<User> users) { userMapper.batchInsert(users); } ``` 3. **禁用约束检查**(超大数据量) ```sql ALTER TABLE your_table DISABLE CONSTRAINT ALL; -- 执行批量插入 -- ALTER TABLE your_table ENABLE CONSTRAINT ALL; ``` --- ### 两种写法对比 | **特性** | `INSERT ALL` 写法 | `UNION ALL` 写法 | |------------------|--------------------------------|--------------------------------| | 语法简洁度 | ★★★★★ | ★★★☆☆ | | 主键处理 | 需预生成主键 | 支持序列自增 | | 单次最大行数 | 1000 行 | 无硬限制 | | 执行效率 | 更高(单语句执行) | 中等 | | 适用场景 | 常规批量插入 | 需要自增主键的插入 | --- ### 常见问题解决 1. **主键冲突** - 预先生成主键值(如 UUID) - 使用序列(如 `SELECT your_seq.NEXTVAL FROM dual`) 2. **超 1000 行限制** - 必须分批处理 - 动态调整批次大小: ```xml <foreach collection="list" item="item" index="index" open="INSERT ALL" close="SELECT 1 FROM dual"> <if test="index % 1000 != 0">INTO ...</if> </foreach> ``` 3. **特殊类型处理** - 指定 JDBC 类型避免转换错误: ```xml #{item.createTime, jdbcType=TIMESTAMP} ``` --- ### 相关问题 1. 如何在 MyBatis 中返回 Oracle 批量插入的自增主键? 2. 如何优化超百万级数据的批量插入性能? 3. Oracle 的 `INSERT ALL` 和 MySQL 的 `VALUES` 批量插入有何本质区别? 4. 批量插入时如何避免触发过多触发器执行? > 示例表结构参考: > ```sql > CREATE TABLE your_table ( > id NUMBER PRIMARY KEY, > name VARCHAR2(50), > age NUMBER > ); > CREATE SEQUENCE your_seq START WITH 1 INCREMENT BY 1; > ```
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值