Db2 for i - RUNSQLSTM - 插入包含数百万条记录的脚本
Posted
技术标签:
【中文标题】Db2 for i - RUNSQLSTM - 插入包含数百万条记录的脚本【英文标题】:Db2 for i - RUNSQLSTM - Insert script with millions of records 【发布时间】:2015-03-10 16:06:23 【问题描述】:我正在尝试运行一个大型 SQL 脚本,其中包含对表的数百万个 INSERT(为了测试,我将其限制为 100,000 行)。
我尝试了许多不同的方法:CPYFRMSTMF、FTP、JDBC 客户端、客户端访问数据传输、IBM Data Studio、来自 System i Navigator 的 SQL 脚本...只是想知道其中哪一个更好。 其中一种方法是使用 RUNSQLSTM 运行脚本(位于 IFS 上)。
但是,开始插入记录需要很长时间。 很明显,在开始插入记录之前,该命令正在执行一些先前的任务。
我在 COMMIT 参数中选择了 *NONE,在 OPTION 中选择了 *NOLIST(我不需要假脱机文件)。
但我不知道为什么要花这么长时间。
一个令人难以置信的事情(在我的测试中,我已经尝试了 100,000 条记录)如果我从使用 JDBC 连接 iSeries 的软件中执行脚本,运行该脚本只需 1 分 40 秒。
有什么想法吗?
谢谢!
【问题讨论】:
你能给我们一个脚本的例子吗? Java代码怎么样?做得对,如果 java 代码只是一堆INSERT INTO mytbl values (1,2,3);
,java 代码会轻松击败一堆 INSERT INTO mytbl values (1,2,3);
OTHO,我希望它会更慢。
时间不同,因为查询优化器可能知道如何进行插入,因为您之前所做的测试在优化器缓存中。我运行了几组测试并使用最后一组测试的结果 99% 的时间最后一次测试是 SQL 在生产中的运行方式。
它们是常见的INSERT,像这样:INSERT INTO PRUMIG (ZONSDE, ZONCDE, PACSDE, PACCDE) VALUES (12345678, 12345.678, 12345678, 12345.678);这只是一个例子。真正的 INSERT 更长。但是,在这种情况下,它的工作方式相同。在插入记录之前,需要很长时间。请记住,我说的是数百万条记录(即使有 100,000 条记录,我也会得到相同的结果。
我在下面编辑了我的答案。我记得现在插入的最快方法是平面文件复制。
因此,与其制作插入脚本,不如制作一个纯文本文件并将其复制进去。您将所有位保存为 (), b
【参考方案1】:
试图了解情况。是否有数百万行 INSERT INTO... 语句?如果是这样,那么这可能是延迟开始实际插入的原因 - 实际上必须读取巨型脚本文件的所有行。
是否有一个 INSERT 语句可以执行类似 INSERT INTO...SELECT FROM...WHERE... 的操作?如果是这种情况,那么在优化器执行读取源行的工作时可能会有延迟。
还有什么?你能多分享一些关于脚本中的确切 SQL 语句和所涉及的表吗?
【讨论】:
是的,如果脚本是一堆INSERT INTO mytbl values (1,2,3);
,那将会非常缓慢,因为每一行都是单独解析和运行的。
是的,没错! SQL 语句是 'INSERT INTO PRUMIG (ZONSDE, ZONCDE, PACSDE, PACCDE) VALUES (12345678, 12345.678, 12345678, 12345.678)' 10000 INSERT 或超过 100,000 运行脚本差别很大。而且不成比例。该表没有索引,没有主键,没有日记。【参考方案2】:
如果从脚本运行,您可以做出的最有可能的主要改进是使用阻止的 INSERT。即insert many rows with each INSERT statement。
这是我运行的低效脚本中的前三个 INSERT 语句:
INSERT INTO MYLIB/MYDATA VALUES(10, 1, 'N')
;
INSERT INTO MYLIB/MYDATA VALUES(11, 1, 'Y')
;
INSERT INTO MYLIB/MYDATA VALUES(11, 2, 'Y')
;
完整的脚本包含 100,000 条 INSERT 语句。两次运行的时间分别为 20:34 和 20:32。
我对脚本进行了编辑,以创建一个更高效的示例。我更改了每个语句终止符,以便每个语句插入四个额外的行。前三行看起来像这样:
INSERT INTO MYLIB/MYDATA VALUES(10, 1, 'N')
,(11, 1, 'Y'),(13, 1, 'N'),(14, 1, 'N'),(14, 2, 'Y');
INSERT INTO MYLIB/MYDATA VALUES(11, 1, 'Y')
,(11, 1, 'Y'),(13, 1, 'N'),(14, 1, 'N'),(14, 2, 'Y');
INSERT INTO MYLIB/MYDATA VALUES(11, 2, 'Y')
,(11, 1, 'Y'),(13, 1, 'N'),(14, 1, 'N'),(14, 2, 'Y');
我将脚本剪掉,只剩下 20,000 条 INSERT 语句(仍然插入 100,000 行,一次插入 5 行)。
两个运行时间是 1:51 和 2:09,对于相同的行数,运行时间基本上减少了 90%。如果我生成一次执行 10 或 50 行的 INSERT,可能会有更好的改进。
通过一些简单的格式可以更好地查看表单:
INSERT INTO MYLIB/MYDATA VALUES(10, 1, 'N'),
(11, 1, 'Y'),
(13, 1, 'N'),
(14, 1, 'N'),
(14, 2, 'Y');
INSERT INTO MYLIB/MYDATA VALUES(11, 1, 'Y'),
(11, 1, 'Y'),
(13, 1, 'N'),
(14, 1, 'N'),
(14, 2, 'Y');
INSERT INTO MYLIB/MYDATA VALUES(11, 2, 'Y'),
(11, 1, 'Y'),
(13, 1, 'N'),
(14, 1, 'N'),
(14, 2, 'Y');
您也许可以自动编辑您的脚本以获得类似的结果。
【讨论】:
太棒了!!我会试试,我会发布我的结果。谢谢!【参考方案3】:对于简单地插入数据,最快的方法是使用 AS400 命令 并将数据平面复制到文件中。使用 sql 的最快插入将是多次插入。 *DLY 索引维护时的任何一种方法都会更快。
以下是复制平面文件的方法。这是文本文件中的数据与表格的数据布局完全一致的位置。表a是txt文件。
CPYF FROMFILE(A)
TOFILE(B)
MBROPT(*ADD)
FMTOPT(*NOCHK)
对于基于物理文件(表)的每个逻辑文件(索引),您可以通过将访问路径维护设置为 *DLY 来加快批量插入的速度。忽略访问路径未更改的任何警告消息。这是因为索引是针对唯一键的,不能关闭(*DLY)。您需要独占访问表和索引才能使用此过程。
CHGLF file(myLib/myFile1) MAINT(*DLY)
CHGLF file(myLib/myFile2) MAINT(*DLY)
CHGLF file(myLib/myFile3) MAINT(*DLY)
CHGLF file(myLib/myFile4) MAINT(*DLY)
在此处进行整体插入。完成后,根据物理文件(表)将每个逻辑文件(索引)的访问路径维护更改回 *IMMED。
CHGLF file(myLib/myLogicalFile1) MAINT(*IMMED)
CHGLF file(myLib/myLogicalFile2) MAINT(*IMMED)
CHGLF file(myLib/myLogicalFile3) MAINT(*IMMED)
CHGLF file(myLib/myLogicalFile4) MAINT(*IMMED)
您可以使用 DSPDBR 显示数据库关系命令获取基于物理文件(表)的所有逻辑文件(索引)的列表。
DSPDBR FILE(myLib/myTable)
【讨论】:
上述答案很有用,但请注意,它将经过的时间从一个进程(INSERT)移动到另一个进程(重建索引)。这样做的缺点是难以获得对生产表的 *EXCL 访问权限。 *EXCL 很烂,是的,它移动了处理。但它更快。在具有 20 个索引的系统上,速度可能快 20 倍。 IDK 为什么,但是数据库创建新索引比在插入期间维护索引要快。即使您添加了重新索引作业的时间(顺便说一句,它会在单独的作业中自动运行,以便您的应用程序可以继续运行),上述方法将在比插入更短的时间内完成,同时保持访问路径。 您的应用程序确实会继续运行,但每个想要使用这些逻辑文件的应用程序都将等待索引完全重建。如果唯一的应用程序是在月底打印一些报告的批处理程序,这不是问题,但如果其中一个逻辑是您的订单输入历史查找,则一个大问题。与所有事情一样,这取决于。我只是希望 OP 有足够的信息来做出明智的决定。 嘿@BuckCalabro 我说你需要这个方法的独占访问权限。如果某些东西打开了一个设置为 *dly 的文件,它将被索引。它是自动的,是的,会有一点延迟,但不像为插入的每条记录更新 20 个文件时的延迟。假设有 20 个索引。 @buck 该表没有逻辑文件、索引、主键、约束,甚至没有日记。这个想法是在插入数据的那一刻不要浪费更多的时间。所以,在这种情况下,这是不适用的。【参考方案4】:谢谢,@danny117!!! 这样可行!! 但是,代替使用 RUNSQLSTM,我将使用 shell 命令 DB2 Utility。它真的很快,虽然它有一些问题(错误控制)。 A bit more info
我使用它的方式非常简单: 我在 IFS (QSH) 上创建了一个 shell 批处理文件。文件里面,只需要一句话:
db2 -t -v -f $* LIB
(其中 LIB 是您正在运行的脚本的目标 LIB)。 而且,我使用参数调用它,在这种情况下,只是我要运行的脚本的路径。
当然,它为您提供了很多选项,因为批处理文件可以执行许多不同的任务。 我使用 STRQSH 命令从 CL 运行所有内容。 这是我的 CLP 资源:
DCL VAR(&CONF1) TYPE(*CHAR) STG(*AUTO) LEN(250) +
VALUE('/BATCH/Batch_pruebas_migracion/bin/rundb2.s+
h +
usr/INIGREDI/PRUEBAS_MIG/PRUMIG_opt-1000000.SQL')
CLRPFM INIGREDI/PRUMIG
OVRPRTF FILE(QPRINT) OUTQ(QEZJOBLOG) MAXRCDS(*NOMAX) +
SPLFNAME(PRUMIG) EXPDATE(*DAYS) DAYS(7)
STRQSH CMD(&CONF1)
我将进一步解释 CLP。 &CONF1 包含我想使用 STRQSH 执行的命令。您需要将整个路径写入 shell 批处理文件。 并且,在这种情况下,在空格之后是参数。在这种情况下,此参数是我要运行的 SQL 脚本的路径。 我听从了您的建议,并且该脚本已经过优化(让我说优化它并不总是一个好主意,这取决于您要插入的记录的内容和大小)。
“OVRTPRTF”已完成,因为运行 DB2 实用程序会创建一个假脱机文件。它真的很大,因为它至少包含 sql 脚本中每个句子的一行(您可以更改详细模式,但最少为 1 行)。 而且,为了不忘记我系统上的那个大假脱机文件,它已编程在 7 天后自动删除。
这真的很快,特别是与 RUNSQLSTM 相比。 尽管 FTP 或 CPYFRMSTMF 速度更快,但使用数字字段管理它们有点困难,特别是当它们包含负数或小数时。 (但那是另一段历史了)。
而且,正如@danny117 所说,优化 SQL 脚本时的改进确实令人难以置信。在某些情况下,我有 2000% 的速度差异。 如果您从目标文件中删除日记,这也很重要。或者,当然,逻辑文件、约束或任何意味着数据库引擎在传输过程中需要做更多工作的东西。
感谢大家的帮助。如您所见,我从不同的答案中汲取了想法。 如果您需要任何关于我如何达到目标的进一步解释,请不要犹豫,问!
【讨论】:
以上是关于Db2 for i - RUNSQLSTM - 插入包含数百万条记录的脚本的主要内容,如果未能解决你的问题,请参考以下文章
DB2 for IBM i (iSeries) 日期 - 需要比较(当前日期 - 1)
通过 Data Studio 为 DB2 for IBM i (iSeries) 构建存储过程失败