有没有insert into的优化方法,我用多表关联select出来的数据有五十多万条,一次性插入到一张表中的效率太差,最好有代码举例
我的核心语句是insert into tab1 select Col1,col2,col3.…from tab2 left join tab3 left join
查询出来的表有五十多万行每天,每次单独查询的时候很快,一用insert into就卡死,拆分后也不管用
以下是执行计划

SELECT column1, column2, column3
FROM source_table1
JOIN source_table2 ON ...
JOIN source_table3 ON ...
LIMIT 10000;
然后,你可以通过循环或定时任务来重复执行此查询,直到所有数据都入。
事务处理:使用事务将多个插入操作组合在一起。这样可以减少提交事务时的开销。例如,使用MySQL:
START TRANSACTION;
-- 执行多个插入语句
INSERT INTO target_table (column1, column2, column3)
SELECT column1, column2, column3
FROM source_table1
JOIN source_table2 ON ...
JOIN source_table3 ON ...
LIMIT 10000;
-- 更多插入语句...
COMMIT;
关闭索引和约束:在进行大量数据插入之前,可以考虑关闭目标表的索引和约束。这样可以加速插入过程。但是,在插入完成后,别忘了重新启用它们,并检查数据的完整性。
使用加载数据文件代码
-- 导出查询结果到CSV文件
SELECT column1, column2, column3
FROM source_table1
JOIN source_table2 ON ...
JOIN source_table3 ON ...
INTO OUTFILE '/path/to/your/csvfile.csv'
FIELDS TERMINATED BY ','
ENCLOSED BY '"'
LINES TERMINATED BY '\n';
-- 使用LOAD DATA INFILE将数据导入到目标表中
LOAD DATA INFILE '/path/to/your/csvfile.csv'
INTO TABLE target_table
FIELDS TERMINATED BY ','
ENCLOSED BY '"'
LINES TERMINATED BY '\n'
(column1, column2, column3);
注意:这需要MySQL服务器和客户端之间的文件系统访问权限。并且,这种方法可能不适用于所有数据库系统。
发一下你的insert语句的执行计划和语句执行期间的等待事件看看。
insert执行慢通常是表有主键或唯一索引,关联查询部分慢只能看具体执行计划了。
INSERT ALL
INTO table1 (column1, column2) VALUES ('value1', 'value2')
INTO table1 (column1, column2) VALUES ('value3', 'value4')
INTO table1 (column1, column2) VALUES ('value5', 'value6')
...
SELECT 1 FROM DUAL;
INSERT INTO table1 (column1, column2)
SELECT column1, column2 FROM table2
WHERE ...
在mysql中,批量插入可以这么写:
insert into my_table(field_1,field_2)
values
(value_1,value_2),
(value_1,value_2),
(value_1,value_2);
oracle中不支持这么写,那在Oracle中,怎么通过一个insert语句批量插入数据呢?
```bash
INSERT ALL
INTO my_table(field_1,field_2) VALUES (value_1,value_2)
INTO my_table(field_1,field_2) VALUES (value_3,value_4)
INTO my_table(field_1,field_2) VALUES (value_5,value_6)
SELECT 1 FROM DUAL;
**insert all into并不表示一个表中插入多条记录,而是表示多表插入各一条记录,而这多表可以是同一个表,就成了单表插入多条记录。根据后面子查询的结果,前面每条into语句执行一次,博客正文中value都是“字面量”,所以用select 1 from dual返回一条记录即可。**
这里有一个多条记录插入的模板您可以试一试
insert all
into <tableName>[(<table_column1>,<table_column2>...)]
values([<column_value1>,<column_value2>...])
[into <tableName>[(<table_column1>,<table_column2>...)]
values([<column_value1>,<column_value2>...])]...
select <table_value1>[,<table_value2>...] from dual;
针对这种情况,可以考虑以下优化方法:
DECLARE @batch_size INT = 1000;
DECLARE @offset INT = 0;
WHILE @batch_size > 0
BEGIN
INSERT INTO tab1 (Col1, Col2, Col3)
SELECT Col1, Col2, Col3
FROM (
SELECT Col1, Col2, Col3, ROW_NUMBER() OVER (ORDER BY Col1) AS row_num
FROM tab2 LEFT JOIN tab3 ON tab2.id = tab3.tab2_id
) AS t
WHERE t.row_num > @offset AND t.row_num <= @offset + @batch_size;
SET @batch_size = @@ROWCOUNT;
SET @offset = @offset + @batch_size;
END
SELECT Col1, Col2, Col3
INTO #temp_table
FROM tab2 LEFT JOIN tab3 ON tab2.id = tab3.tab2_id;
INSERT INTO tab1 (Col1, Col2, Col3)
SELECT Col1, Col2, Col3
FROM #temp_table;
DROP TABLE #temp_table;
ALTER INDEX ALL ON tab1 DISABLE;
-- 插入数据的语句
ALTER INDEX ALL ON tab1 REBUILD;
这样可以减少插入数据时的索引维护开销,提高插入效率。
需要注意的是,对于大量数据的插入操作,可能会对数据库的性能产生较大影响,建议在非高峰期进行操作。同时,应该根据实际情况进行调整,选择最适合当前场景的优化方法。
看一下插入的表是否有索引,如果有索引可以先删除索引,再插入数据,最后再重建索引,有索引情况下插入大量数据效率会很低。
INSERT
INTO table1 (column1, column2) VALUES ('value1', 'value2')
INTO table1 (column1, column2) VALUES ('value3', 'value4')
INTO table1 (column1, column2) VALUES ('value5', 'value6')
...
SELECT 1 FROM DUAL;
建议分批插入 一次插入1000条 循环插入
建议通过代码的方式,先查询出来,然后采用insert all的方法加上线程池的方式来优化。批量插入数据太大会报错,代码中就可以在分批。比如分X批,每批300条,mapper.xml的写法如下(实际代码)
<insert id="batchSave" parameterType="java.util.List">
insert all
<foreach collection="list" item="item" separator=" ">
INTO cj_msg(
id,type,type_id,title,detail,user_id,login_name,
status,create_by,create_date,update_by,update_date,remark,del_flag)
values
(#{item.id},#{item.type},#{item.typeId},#{item.title},#{item.detail},#{item.user.id},#{item.loginName},
#{item.status},#{item.createBy.id},#{item.createDate},#{item.updateBy.id},#{item.updateDate},#{item.remark},#{item.delFlag})
</foreach>
select ${list.size} from dual
</insert>
针对大数据量的插入操作,可以采用以下一些优化方法:
1.使用分批插入:将插入操作分成多个批次,每个批次插入一部分数据,可以降低单次插入的数据量,减少锁冲突,提高插入效率。例如:
DECLARE @batch_size INT = 1000; -- 每次插入的数据量
DECLARE @offset INT = 0; -- 当前数据偏移量
WHILE 1=1
BEGIN
INSERT INTO tab1 (col1, col2, col3)
SELECT col1, col2, col3
FROM tab2
LEFT JOIN tab3 ON tab2.id = tab3.id
ORDER BY tab2.id
OFFSET @offset ROWS
FETCH NEXT @batch_size ROWS ONLY;
SET @offset = @offset + @batch_size;
IF @@ROWCOUNT < @batch_size
BREAK;
END
上述代码使用循环和OFFSET-FETCH语句来分批插入数据,每次插入@batch_size条数据,直到插入完毕。
2.关闭日志记录:插入操作会产生大量的日志记录,占用大量磁盘空间和系统资源,可以通过关闭日志记录来提高插入效率。例如:
-- 关闭日志记录
ALTER TABLE tab1 NOLOGGING;
-- 执行插入操作
INSERT INTO tab1 (col1, col2, col3)
SELECT col1, col2, col3
FROM tab2
LEFT JOIN tab3 ON tab2.id = tab3.id;
-- 开启日志记录
ALTER TABLE tab1 LOGGING;
上述代码使用ALTER TABLE语句关闭日志记录,执行插入操作后再重新开启日志记录。
3.使用临时表:将查询结果插入到临时表中,然后再将临时表中的数据插入到目标表中,可以减少对目标表的锁定和影响,提高插入效率。例如:
-- 创建临时表
CREATE TABLE #temp_tab1 (
col1 INT,
col2 VARCHAR(50),
col3 DATETIME
);
-- 插入数据到临时表
INSERT INTO #temp_tab1 (col1, col2, col3)
SELECT col1, col2, col3
FROM tab2
LEFT JOIN tab3 ON tab2.id = tab3.id;
-- 插入数据到目标表
INSERT INTO tab1 (col1, col2, col3)
SELECT col1, col2, col3
FROM #temp_tab1;
-- 删除临时表
DROP TABLE #temp_tab1;
上述代码将查询结果插入到临时表#temp_tab1中,然后再将临时表中的数据插入到目标表tab1中。
需要注意的是,以上优化方法需要根据实际情况和系统环境进行调整和测试,并在操作前备份数据以防数据丢失或损坏。同时,对于大数据量的插入操作,还需要注意一些系统参数的设置,如I/O、内存和CPU等方面的优化,以提高插入效率。
问题描述:你的插入没问题的,就是要多线程分批查入,每次插个一千条,多线程线程池处理,可以试试这个思路,