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