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

3次阅读

MySQL 存储过程循环插入慢因每行默认单事务、未批量提交、索引实时更新;SQL Server 应改用 INSERT SELECT 分批 + 禁用索引;PostgreSQL DO 块需显式 BEGIN/COMMIT 或用 generate_series();Oracle FORALL 需合理设 LIMIT 并注意触发器兼容性。

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

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 INTOINSERT 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 LIMITFORALL 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(对大表有效)
  • 如果目标表有 TRIGGERFORALL 不会自动批量触发——得手动改写成 FORALL i IN …… INSERT …… VALUES (arr(i)) 并确认触发器逻辑兼容

真正卡住的从来不是语法,是事务边界、索引维护时机、还有你忘了 COMMIT 在哪一层——循环体里漏写,或者误以为存储过程自动帮你收尾了。

星耀云
版权声明:本站原创文章,由 星耀云 2026-03-21发表,共计1525字。
转载说明:转载本网站任何内容,请按照转载方式正确书写本站原文地址。本站提供的一切软件、教程和内容信息仅限用于学习和研究目的;不得将上述内容用于商业或者非法用途,否则,一切后果请用户自负。本站信息来自网络,版权争议与本站无关。
text=ZqhQzanResources