Mybatis 批量插入报ORA-00933: SQL 命令未正确结束

Mybatis 批量插入报ORA-00933: SQL 命令未正确结束

错误出现的场景是这样的,将多个月份的1000条数据分别插入到各月份表中。

MySQL中,批量插入数据到表,会在一定程度提高效率。

insert into table (column1, column2)values(value1, value2), (value1, value2)

对于List<Map<String, Object>>类型的参数,使用mybatis实现上面语句需要用到动态SQL–foreach。

<insert id="batchInsert"">
	insert into ${table_name} (
	id, name)
	values 
	<foreach collection="LIST" item="item" index="index" open="(" separator="," close=")">
	  #{item.id, jdbcType=VARCHAR},
	  #{item.name, jdbcType=VARCHAR}
	</foreach>
</insert>

上面的语句对于MySQL执行起来没有问题,但对Oracle数据库则会报如题错误:SQL命令未正确结束。

经过一系列的测试实验,找到了用Oracle虚拟表来解决这个问题,首先将数据foreach作为一张虚拟表,然后全量插入到新表中。

INSERT INTO TEMP_20200210 (DMC)
    SELECT CD.* FROM
    (
        SELECT 1234567 FROM DUAL UNION
        SELECT 1234568 FROM DUAL UNION
        SELECT 1234568 FROM DUAL
    ) CD

上面语句,有博客中测试150万条数据182秒插入完成。本文的场景每次最多插入1000条,秒级肯定能完成,因为笔者场景是异步插入数据,对性能要求不高。

由此,改造mybatis语句如下

<insert id="batchInsert">
	insert into ${table_name} (
	id, name)
	SELECT A.* FROM (
	<foreach collection="LIST" item="item" index="index" separator="UNION ALL">
	  select
	  #{item.id, jdbcType=VARCHAR} id,
	  #{item.name, jdbcType=VARCHAR} name
	  FROM DUAL
	</foreach>
	) A
</insert>

肯定有同学对语句中的$有疑问,有SQL注入风险。因此业务场景完全没有对外接口,可以放心使用。

我将1000条数据按照月份分成了多个Map作为插入参数,大致格式如下。

{
	"table_name": "user_4",
	"LIST": [
		{
			"id": "1",
			"name": "bee"
		},
		{
			"id": "2",
			"name": "stack"
		}
	]
}
### ORA-00933 错误的解决方案 ORA-00933 错误通常表示 SQL 语句存在语法问题,导致 Oracle 数据库无法正确解析该命令。以下是可能的原因及对应的解决方法: 1. **SQL 关键字前后缺少空格** 如果 SQL 语句中的关键字(如 `FROM`、`WHERE` 等)前后缺少必要的空格,可能会导致此错误。例如: ```sql SELECT T.codeas CODE from info_table T ``` 在上述语句中,`codeas` 缺少空格,应改为: ```sql SELECT T.code as CODE from info_table T[^1] ``` 2. **Oracle 表别名规则** Oracle 数据库中为表指定别名时,不需要使用 `AS` 关键字。如果使用了 `AS`,会导致语法错误。例如: ```sql SELECT T.code AS CODE FROM info_table AS T ``` 正确的写法是: ```sql SELECT T.code AS CODE FROM info_table T[^1] ``` 3. **SQL 语句中多余的标点符号或关键字** 如果 SQL 语句中包含多余的标点符号(如逗号 `,`)或关键字(如 `AND`),也会引发此错误。例如: ```sql SELECT T.code, FROM info_table T ``` 上述语句中多了一个逗号,应改为: ```sql SELECT T.code FROM info_table T ``` 4. **数据库版本或类型差异** 不同数据库(如 PostgreSQLOracle)支持的 SQL 语法可能存在差异。某些在 PostgreSQL 中合法的语法,在 Oracle 中可能无效。例如: ```sql UPDATE A SET ID = B.ID FROM A, B WHERE A.NAME = B.NAME; ``` 上述语句在 Oracle 中不被支持,应改为: ```sql UPDATE A SET ID = (SELECT B.ID FROM B WHERE A.NAME = B.NAME)[^2] ``` 5. **MyBatis 批量插入问题** 在使用 MyBatis 进行批量插入时,若正确配置 `useGeneratedKeys` 属性,也可能引发 ORA-00933 错误。例如: ```xml <insert id="insertUserStoriesFiles" parameterType="com.piao.domain.TbFile"> insert into TB_FILE ( "ID", "FILE_NAME", "NAS_FILE_NAME", "DESCRIPTION", "WHEN", "SIZE", "USER_STORIES_ID", "ADD_USER", "ADD_TIME", "UPDATE_USER", "UPDATE_TIME" ) <foreach collection="list" item="file" separator="union all"> ( select #{file.id}, #{file.fileName}, #{file.nasFileName}, #{file.description}, #{file.when}, #{file.size}, #{file.userStoriesId}, #{file.createBy}, #{file.createTime}, #{file.updateBy}, #{file.updateTime} from dual ) </foreach> </insert> ``` 解决方案是在 `<insert>` 标签中添加 `useGeneratedKeys="false"` 属性: ```xml <insert id="insertUserStoriesFiles" parameterType="com.piao.domain.TbFile" useGeneratedKeys="false"> ... </insert>[^4] ``` ### 示例代码 以下是一个修正后的 SQL 查询示例: ```sql SELECT T.code AS CODE FROM info_table T; ``` 如果需要更新操作,可以参考以下修正后的语句: ```sql UPDATE A SET ID = (SELECT B.ID FROM B WHERE A.NAME = B.NAME); ``` ### 注意事项 确保 SQL 语句符合 Oracle 的语法规范,并根据具体场景调整语句结构。不同数据库之间的语法差异可能导致相同的 SQL 语句在不同环境中表现不一致。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值