跳转到主内容
趣航编程网 - 趣学编程,启航技术之路!

SQL插入大量测试数据_编写循环存储过程进行快速填充

MySQL存储过程循环插入慢因每行默认单事务、未批量提交、索引实时更新;SQL Server应改用INSERT SELECT分批+禁用索引;PostgreSQL DO块需显式BEGIN/COMMIT或用generate_series();Oracle FORALL需合理设LIMIT并注意触发器兼容性。 MySQL里用存储过程循环插入10万行测试数据,为什么特别慢? 因为默认每条
INSERT
都走完整事务流程,没批量、没禁用索引、没关自动提交——结果不是“快填”,而是“卡死”。 每次循环都触发一次磁盘写入(除非显式
START TRANSACTION
+
COMMIT
包住)
AUTO_INCREMENT
字段在高并发模拟下可能隐式加锁,拖慢自增步进 如果表上有二级索引,每插一行都要更新所有索引页,IO翻倍 没关
autocommit
时,等于每行都在开事务、写redo log、刷盘 SQL Server中用WHILE循环+INSERT SELECT填充测试数据,怎么避免日志爆炸? 直接
INSERT INTO ... SELECT TOP 1000 ... FROM sys.objects
递归拼接,比单行
INSERT
快十倍,且日志量可控。 用
SELECT INTO
或
INSERT SELECT
替代逐行
VALUES
,减少解析和计划重编译开销 把循环粒度从“每行”提到“每千行”,配合
GO 100
分批提交(注意:不是T-SQL的
GO
,是客户端批处理命令) 临时关闭非聚集索引:
ALTER INDEX [idx_name] ON [table] DISABLE
,填完再
REBUILD
确保数据库恢复模式为
BULK_LOGGED
(仅限大容量操作),否则日志照常满写 PostgreSQL用DO块写匿名存储过程插入测试数据,为什么报错“cannot execute INSERT in a read-only transaction”? 因为
DO
块默认运行在只读事务上下文中,而
INSERT
需要写权限。 必须显式加
BEGIN
/
COMMIT
,不能只靠
DO $$ ... $$
包裹 正确写法是:
DO $$ BEGIN FOR i IN 1..10000 LOOP INSERT INTO t(x) VALUES (i); END LOOP; COMMIT; END $$;
更推荐用
generate_series()
一次性生成:
INSERT INTO t(x) SELECT g FROM generate_series(1,10000) AS g;
——无循环、无PL/pgSQL开销、不锁表 如果真要用循环,记得在
DO
前设
SET LOCAL statement_timeout = '30s'
防卡死 Oracle用PL/SQL写BULK COLLECT + FORALL插入百万级数据,哪些参数影响实际速度?
BULK COLLECT LIMIT
和
FORALL SAVE EXCEPTIONS
的组合方式,直接决定内存占用和错误定位成本。
LIMIT
值太小(如50)→ 上下文切换频繁;太大(如10000)→ PGA内存溢出风险;建议200–500之间实测 没加
SAVE EXCEPTIONS
时,一条失败整批回滚;加了之后需遍历
SQL%BULK_EXCEPTIONS
,但能继续执行 务必在
FORALL
前加
EXECUTE IMMEDIATE 'ALTER SESSION SET "_serial_direct_read"=true'
绕过buffer cache(对大表有效) 如果目标表有
TRIGGER
,
FORALL
不会自动批量触发——得手动改写成
FORALL i IN ... INSERT ... VALUES (arr(i))
并确认触发器逻辑兼容 真正卡住的从来不是语法,是事务边界、索引维护时机、还有你忘了
COMMIT
在哪一层——循环体里漏写,或者误以为存储过程自动帮你收尾了。

相关文章