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:缺少 sql_function

DB2 for IBM i (iSeries) 日期 - 需要比较(当前日期 - 1)

通过 Data Studio 为 DB2 for IBM i (iSeries) 构建存储过程失败

使用 SQL 和 DB2 for i 进行日期算术

与 DB2 for i 的 JDBC 连接 - 只有单一模式的表可用

DB2 for i 中的语句打开时间、语句提取时间和语句关闭时间是啥?