我可以将“批量”INSERT发送给Oracle吗?

时间:2009-02-11 13:18:13

标签: sql ruby-on-rails ruby oracle

情景:

  • 我每天将一些数据加载到本地MySQL数据库中,大约200万行;
  • 我必须(拥有 - 这是审计/监管的事情)转移到“正确”管理的服务器,目前看起来是Oracle 10g;
  • 服务器位于不同的国家/地区:网络往返电流需要60-70毫秒;
  • 输入是非规范化形式的CSV文件:我在加载前对数据进行规范化,每行通常会在最多4个表中产生3-8个INSERT;
  • 加载脚本目前在Ruby中实现,使用ActiveRecord和fastercsv。我已经尝试过ar-extensions gem,但它假设MySQL样式的多值子句的想法是可行的。它没有。

编辑:非常有用的答案 - 谢谢!更多关于那个讨厌的输入文件。字段数是可变的,位置已经改变了几次 - 我当前的脚本通过分析标题行来确定内容(好吧,fastcsv和一个狡猾的转换器这样做)。因此,如果没有多个版本的加载文件,直接上传和后期处理SQL将无法运行,这很糟糕。它也是一个德国的CSV文件:分号分隔(没什么大不了的)和逗号表示的小数(相当大的交易,除非我们加载为VARCHAR和文本处理之后 - 呃)。

问题:

以大约7 /秒的速度装载200万行需要花费超过24小时!这可能是日常流程的一个缺点,更不用说用户希望能够以CSV格式提供大约5个小时后才能访问数据!

我考虑过每次网络旅行应用多个插入:相当笨拙的INSERT ALL...语法会很好,除了目前我正在使用序列为每一行应用唯一的id。它发生了

INSERT ALL
    INTO tablea (id,b,c) VALUES (tablea_seq.nextval,1,2)
    INTO tablea (id,b,c) VALUES (tablea_seq.nextval,3,4)
    INTO tablea (id,b,c) VALUES (tablea_seq.nextval,5,6)
SELECT 1 FROM dual;

(我说这是笨拙的吗?)尝试对所有三行使用相同的id。 Oracle docus似乎证实了这一点。

最新尝试是在一次执行中发送多个INSERT,例如:

    INSERT INTO tablea (id,b,c) VALUES (tablea_seq.nextval,1,2);
    INSERT INTO tablea (id,b,c) VALUES (tablea_seq.nextval,3,4);
    INSERT INTO tablea (id,b,c) VALUES (tablea_seq.nextval,5,6);

我还没有办法说服甲骨文接受这一点。

问题

  • 我错过了一些明显的东西吗? (如果情况确实如此,我会如此高兴!)
  • 如果我不能发送多个插页,我还能尝试什么?

为什么接受那个?

无论出于何种原因,我更喜欢让我的代码尽可能不受特定于平台的构造的影响:出现这个问题的一个原因是我正在从MySQL迁移到Oracle;由于地理原因,可能有一天会发生另一次移动,我无法确定该平台。因此,使我的数据库库可以使用文本SQL命令来实现合理的扩展是有吸引力的,并且PL / SQL块实现了这一点。现在,如果确实出现了另一个平台,则更改将仅限于在代码中更改适配器:一个单行,很可能。

6 个答案:

答案 0 :(得分:10)

如何将csv文件传送到oracle db服务器,使用SQLLoader将csv文件加载到临时表中,然后运行存储过程以在最终表中进行转换和插入?

答案 1 :(得分:4)

您可以使用:

insert into tablea (id,b,c)
 ( select tablea_seq.nextval,1,2 from dual union all
   select tablea_seq.nextval,3,4 from dual union all
   select tablea_seq.nextval,3,4 from dual union all
   select tablea_seq.nextval,3,4 from dual union all
   ...
 )

当我没记错的时候,这可以工作到最多1024行。

您也可以将其作为PL / SQL批处理指令发送:

BEGIN
 INSERT INTO tablea (id,b,c) VALUES (tablea_seq.nextval,1,2);
 INSERT INTO tablea (id,b,c) VALUES (tablea_seq.nextval,3,4);
 INSERT INTO tablea (id,b,c) VALUES (tablea_seq.nextval,5,6);
 ...
 COMMIT;
END

答案 2 :(得分:2)

我将使用SQL * Loader将原始CSV文件加载到数据库中的专用表而不对其进行规范化,然后针对表中的数据运行代码以将其规范化为各种所需的表。这将最大限度地减少往返次数,并且在Oracle社区中是一种非常传统的方法。

SQL * Loader在初始学习曲线方面可能有点挑战性,但如果你遇到困难,你可以很快再次发布。

http://download.oracle.com/docs/cd/B19306_01/server.102/b14215/part_ldr.htm#i436326

答案 3 :(得分:0)

我有一个快速的建议。我来自MySQL世界,但接受过Oracle培训,我认为这样可行。

在MySQL中,您可以使用单个insert语句插入多个记录。它看起来像这样:

INSERT INTO table_name (column_one, column_two, column_three, column_four)
VALUES
    ('a', 'one', 'alpha', 'uno'),        // Row 1
    ('b', 'two', 'beta', 'dos'),         // Row 2
    ('c', 'three', 'gamma', 'tres'),     // etc.
....
    ('z', 'twenty-six', 'omega', 'veintiséis');

现在显然你只能一次插入一个表,而你不想做200万条记录,但你可以轻松地一次做10或20或100(如果允许的数据包那么大)。您可能必须手动生成此内容,我不知道您使用的框架(或任何此类内容)是否支持为您制作此类代码。

在MySQL世界中,这种DRAMATICALLY加速了插入。我假设它同时执行所有索引更新,但它也阻止它必须在每个插入上重新解析SQL。

如果将它与预准备语句(因此SQL被缓存并且不必每次都被解析)和事务(以确保在必须跨多个插入时始终处于理智状态)桌子......我想你会做得很好。


NunoG是正确的,您可以将CSV直接加载到Oracle中。您可能最好在输入文件中读取,生成一组标准化的CSV文件(每个表一个),然后一次加载每个文件。

答案 4 :(得分:0)

不是通过网络执行SQL,而是将插入内容写入文本文件,通过网络移动并在那里本地运行。

答案 5 :(得分:0)

SQL * Loader是Oracle提供的实用程序,允许您将平面文件中的数据加载到一个或多个数据库表中。这比使用查询的插入快10-100倍。

http://www.orafaq.com/wiki/SQL*Loader_FAQ

如果SQL * Loader没有删除它,请尝试使用SQL * Loader可读格式格式化文件的小型预处理程序。