导出为插入语句:但在 SQL Plus 中,该行覆盖了 2500 个字符!

Posted

技术标签:

【中文标题】导出为插入语句:但在 SQL Plus 中,该行覆盖了 2500 个字符!【英文标题】:EXPORT AS INSERT STATEMENTS: But in SQL Plus the line overrides 2500 characters! 【发布时间】:2010-04-29 08:56:05 【问题描述】:

我必须将 Oracle 表导出为 INSERT STATEMENTS。

但是这样生成的 INSERT STATEMENTS 会覆盖 2500 个字符。

我必须在 SQL Plus 中执行它们,所以我收到一条错误消息。

这是我的 Oracle 表:

CREATE TABLE SAMPLE_TABLE
(
   C01   VARCHAR2 (5 BYTE) NOT NULL,
   C02   NUMBER (10) NOT NULL,
   C03   NUMBER (5) NOT NULL,
   C04   NUMBER (5) NOT NULL,
   C05   VARCHAR2 (20 BYTE) NOT NULL,
   c06   VARCHAR2 (200 BYTE) NOT NULL,
   c07   VARCHAR2 (200 BYTE) NOT NULL,
   c08   NUMBER (5) NOT NULL,
   c09   NUMBER (10) NOT NULL,
   c10   VARCHAR2 (80 BYTE),
   c11   VARCHAR2 (200 BYTE),
   c12   VARCHAR2 (200 BYTE),
   c13   VARCHAR2 (4000 BYTE),
   c14   VARCHAR2 (1 BYTE) DEFAULT 'N' NOT NULL,
   c15   CHAR (1 BYTE),
   c16   CHAR (1 BYTE)
);

假设:

a) 我有义务将表格数据导出为 INSERT STATEMENTS;我被允许使用 UPDATE 语句,以避免 SQL*Plus 错误“sp2-0027 input is too long(>2499 characters)”;

b) 我有义务使用 SQL*Plus 来执行生成的脚本。

c) 请假设每条记录都可以包含特殊字符:CHR(10)、CHR(13) 等;

d) 我不能使用 SQL 加载器;

e) 我无法导出然后导入表:我只能通过 SQL Plus 使用 INSERT / UPDATE 语句添加“增量”。

【问题讨论】:

有一点不清楚,这很重要。您是否有 c13 的 value 超过 2499 个字符的任何插入/更新?或者,您是否只是有一些 statement 的长度超过 2499 个字符的语句? (前者难,后者易修) 答案如下:在名为 SAMPLE_TABLE 的 Oracle 表中,我有 80 条记录,其中 VARCHAR2(4000) 类型的 C13 字段的长度为 3762 个字符。这意味着,当然,C13 的 VALUE 超过了 2499 个字符。在这种情况下,我们可以观察到 STATEMENT 的长度也超过了 2499 个字符。 @厨房里的鸡 - 目标表的主键是什么? 请假设源表和目标表没有主键。 您只知道名为 C13 的字段的值有时会达到 4000 个字符。 【参考方案1】:

哇,这些限制非常有限,但我认为可能有办法解决它。我认为您可能必须为此编写自己的小脚本。

我自己会使用带有 JDBC 的 Java(但任何可以连接和读取数据库并输出字符串的语言都可以),编写一个小程序来检索数据库中每一行的记录集。然后,对于这些行中的每一行:

用完整数据构造一个插入语句。如果小于 2,000 字节,则只需将其输出到文件并移至下一行。

否则为每个字段创建一个插入语句,但将 c13 字段保留为 ''(空)。

然后,只要您的 c13input 字符串大于 2000 个字符,输出 "update tbl set c13 = c13 || '" + c13input.substring (0,2000) + "' where ..." 形式的更新语句(附加接下来的 2000 个字符),然后执行 c13input = c13input.substring(2000) 删除这些字符从你的字符串。

一旦c13input的长度小于或等于2000个字符,只需输出一个最终更新即可。

这使您可以将各个 SQL 语句保持在 2000 个字符左右,并有效地执行正确的 SQL 以重新填充另一个数据库表。

这就是我所说的类型(对于只包含一个主键 c1 和一个大 honkin' varchar c13 的表):

rowset r = db.exec ("select * from oldtable");
while r.next != NO_MORE_ROWS:
    string s = "insert into newtable (c1,c13) values ('" +
        r.get("c1") + "','" + r.get("c13") + "')"
    if s.len() < 2000:
        print s
    else:
        s = "insert into newtable (c1,c13) values ('" + r.get("c1") + "','')"
        print s
        f = r.get("c13")
        while f.len() > 2000:
            s = "update newtable set c13 = c13 || '" + f.substring(0,2000) + ')"
            f = f.substring(2000)
            print s
        endwhile
        s = "update newtable set c13 = c13 || '" + f + ')"
        print s
    endif
endwhile

显然,您可能需要对字符串进行变形以允许插入特殊字符 - 我不确定 Oracle 期望这些格式是什么格式,但希望它是传递字符串的简单问题(r.get("c13") 如果长度完整插入的数量小于 2000,f.substring(0,2000)f(如果您也在构建更新)到辅助函数来执行此操作。

如果变形可能会增加打印行的大小,您可能希望将阈值降回 1000 以确保安全,以确保变形字符串不会导致行大于 PL/SQL 限制.

对不起,如果这看起来令人费解,但您所说的限制对我们有点不利。可能有更好的方法,但我想不出符合您的所有标准的方法。


更新:看来你比原来想象的更更多:如果你不得不限制自己使用 SQL 来生成脚本除了运行它,还有一种方法,虽然很痛苦。

您可以使用 SQL 生成 SQL。使用我上面提到的表格和c1c13,你可以这样做:

select
    'insert into newtable (c1,c13) values ("' ||
    c1 ||
    '","");'
from oldtable;
# Xlates to: insert into newtable (c1,c13) values ("[c1]","");

这将为您提供所有基线 insert 语句,用于复制除 c13 列之外的所有内容。

然后您需要做的是生成更多用于设置c13 的语句。为长度小于等于 1000 的所有值更新 c13(简单集):

select
    'update newtable set c13 = "' ||
    c13 ||
    '" where c1 = "' ||
    c1 ||
    '";'
from oldtable where length(c13) <= 1000;
# Xlates to: update newtable set c13 = "[c13]" where c1 = "[c1]";
#   but only for rows where length([c13]) <= 1000

然后,对于 1001 到 2000 个字符之间的所有值(设置然后追加)到 update c13:

select
    'update newtable set c13 = "' ||
    substring(c13,1,1000) ||
    '" where c1 = "' ||
    c1 ||
    '";'
from oldtable where length(c13) > 1000 and length(c13) <= 2000;
select
    'update newtable set c13 = c13 || "' ||
    substring(c13,1001,1000) ||
    '" where c1 = "' ||
    c1 ||
    '";'
from oldtable where length(c13) > 1000 and length(c13) <= 2000;
# Xlates to: update newtable set c13 =        "[c13a]" where c1 = "[c1]";
#            update newtable set c13 = c13 || "[c13b]" where c1 = "[c1]";
#   but only for rows where length([c13]) > 1000 and <= 2000
#   and [c13a]/[c13b] are the first/second thousand chars of c13.

以此类推,长度为 2001 到 3000 和 3001 到 4000。

可能需要进行一些调整。我很高兴为您提供解决问题的方法,但我对完成这样一个怪物的愿望充其量是最小的:-)

它能完成工作吗?是的。漂亮吗?我会说这是一个响亮的“不!”但是,鉴于您的限制,这可能是您希望的最好结果。


作为概念证明,这是 DB2 中的一个 SQL 脚本(虽然没有特殊功能,但它应该可以在具有 lengthsubstr 等效项的任何 DBMS 中正常工作):

# Create table and populate.

DROP TABLE XYZ;
COMMIT;
CREATE TABLE XYZ (F1 VARCHAR(1),F2 VARCHAR(20));
COMMIT;
INSERT INTO XYZ VALUES ('1','PAX');
INSERT INTO XYZ VALUES ('2','GEORGE');
INSERT INTO XYZ VALUES ('3','VLADIMIR');
INSERT INTO XYZ VALUES ('4','ALEXANDRETTA');
SELECT * FROM XYZ ORDER BY F1;

# Create initial insert statem,ents.

SELECT 'INSERT INTO XYZ (F1,F2) VALUES (' || F1 ','''');' 
    FROM XYZ;

# Updates for 1-5 character F2 fields.

SELECT 'UPDATE XYZ SET F2 = ''' || F2 ||
    ''' WHERE F1 = ''' || F1 || ''';'
    FROM XYZ WHERE LENGTH(F2) <= 5;

# Updates for 6-10 character F2 fields.

SELECT 'UPDATE XYZ SET F2 = ''' || SUBSTR(F2,1,5) ||
    ''' WHERE F1 = ''' || F1 || ''';'
    FROM XYZ WHERE LENGTH(F2) > 5 AND LENGTH(F2) <= 10;

SELECT 'UPDATE XYZ SET F2 = F2 || ''' || SUBSTR(F2,6) ||
    ''' WHERE F1 = ''' || F1 || ''';'
    FROM XYZ WHERE LENGTH(F2) > 5 AND LENGTH(F2) <= 10;

# Updates for 11-15 character F2 fields.

SELECT 'UPDATE XYZ SET F2 = ''' || SUBSTR(F2,1,5) ||
    ''' WHERE F1 = ''' || F1 || ''';'
    FROM XYZ WHERE LENGTH(F2) > 10 AND LENGTH(F2) <= 15;

SELECT 'UPDATE XYZ SET F2 = F2 || ''' || SUBSTR(F2,6,5) ||
    ''' WHERE F1 = ''' || F1 || ''';'
  FROM XYZ WHERE LENGTH(F2) > 10 AND LENGTH(F2) <= 15;

SELECT 'UPDATE XYZ SET F2 = F2 || ''' || SUBSTR(F2,11) || 
    ''' WHERE F1 = ''' || F1 || ''';'
    FROM XYZ WHERE LENGTH(F2) > 10 AND LENGTH(F2) <= 15;

这会生成以下几行:

> DROP TABLE XYZ;
> COMMIT;
> CREATE TABLE XYZ (F1 VARCHAR(1),F2 VARCHAR(20));
> COMMIT;
> INSERT INTO XYZ VALUES ('1','PAX');
> INSERT INTO XYZ VALUES ('2','GEORGE');
> INSERT INTO XYZ VALUES ('3','VLADIMIR');
> INSERT INTO XYZ VALUES ('4','ALEXANDRETTA');
> SELECT * FROM XYZ;
    F1  F2
    --  ------------
    1   PAX
    2   GEORGE
    3   VLADIMIR
    4   ALEXANDRETTA

> SELECT 'INSERT INTO XYZ (F1,F2) VALUES (' || F1 || ','''');'
> FROM XYZ;
    INSERT INTO XYZ (F1,F2) VALUES (1,'');
    INSERT INTO XYZ (F1,F2) VALUES (2,'');
    INSERT INTO XYZ (F1,F2) VALUES (3,'');
    INSERT INTO XYZ (F1,F2) VALUES (4,'');

> SELECT 'UPDATE XYZ SET F2 = ''' || F2 ||
> ''' WHERE F1 = ''' || F1 || ''';'
> FROM XYZ WHERE LENGTH(F2) <= 5;
    UPDATE XYZ SET F2 = 'PAX' WHERE F1 = '1';

> SELECT 'UPDATE XYZ SET F2 = ''' || SUBSTR(F2,1,5) ||
> ''' WHERE F1 = ''' || F1 || ''';'
> FROM XYZ WHERE LENGTH(F2) > 5 AND LENGTH(F2) <= 10;
    UPDATE XYZ SET F2 = 'GEORG' WHERE F1 = '2';
    UPDATE XYZ SET F2 = 'VLADI' WHERE F1 = '3';

> SELECT 'UPDATE XYZ SET F2 = F2 || ''' || SUBSTR(F2,6) ||
> ''' WHERE F1 = ''' || F1 || ''';'
> FROM XYZ WHERE LENGTH(F2) > 5 AND LENGTH(F2) <= 10;
    UPDATE XYZ SET F2 = F2 || 'E' WHERE F1 = '2';
    UPDATE XYZ SET F2 = F2 || 'MIR' WHERE F1 = '3';

> SELECT 'UPDATE XYZ SET F2 = ''' || SUBSTR(F2,1,5) ||
> ''' WHERE F1 = ''' || F1 || ''';'
> FROM XYZ WHERE LENGTH(F2) > 10 AND LENGTH(F2) <= 15;
    UPDATE XYZ SET F2 = 'ALEXA' WHERE F1 = '4';

> SELECT 'UPDATE XYZ SET F2 = F2 || ''' || SUBSTR(F2,6,5) ||
> ''' WHERE F1 = ''' || F1 || ''';'
> FROM XYZ WHERE LENGTH(F2) > 10 AND LENGTH(F2) <= 15;
    UPDATE XYZ SET F2 = F2 || 'NDRET' WHERE F1 = '4';

> SELECT 'UPDATE XYZ SET F2 = F2 || ''' || SUBSTR(F2,11) ||
> ''' WHERE F1 = ''' || F1 || ''';'
> FROM XYZ WHERE LENGTH(F2) > 10 AND LENGTH(F2) <= 15;
    UPDATE XYZ SET F2 = F2 || 'TA' WHERE F1 = '4';

拆分输出行,我们得到:

INSERT INTO XYZ (F1,F2) VALUES (1,'');
INSERT INTO XYZ (F1,F2) VALUES (2,'');
INSERT INTO XYZ (F1,F2) VALUES (3,'');
INSERT INTO XYZ (F1,F2) VALUES (4,'');
UPDATE XYZ SET F2 = 'PAX' WHERE F1 = '1';
UPDATE XYZ SET F2 = 'GEORG' WHERE F1 = '2';
UPDATE XYZ SET F2 = 'VLADI' WHERE F1 = '3';
UPDATE XYZ SET F2 = F2 || 'E' WHERE F1 = '2';
UPDATE XYZ SET F2 = F2 || 'MIR' WHERE F1 = '3';
UPDATE XYZ SET F2 = 'ALEXA' WHERE F1 = '4';
UPDATE XYZ SET F2 = F2 || 'NDRET' WHERE F1 = '4';
UPDATE XYZ SET F2 = F2 || 'TA' WHERE F1 = '4';

它应该给你原始的行,尽管是以迂回的方式。


这是我在解决任何一个问题时所付出的最大努力,所以我会向你道别,除非有人向我指出任何严重错误。

祝你的项目好运,祝你好运。

【讨论】:

我写过我需要一个 PL/SQL 解决方案。我不知道Java。此外,我无权在生产环境中运行 Java 类。我只能在 PL/SQL 语言中选择/插入/更新。我被授权创建一个 JAVA PACKAGE,这意味着一个使用 JAVA 类的包 PL/SQL。你能把你的脚本转换成一个 JAVA 包,在 ORACLE PL/SQL 中编译吗? 没有。如果您也仅限于使用 PL/SQL 创建脚本,那么您基本上会遇到 非常 的艰难时期。它是一种用于编写 SQL 脚本的好语言,但对于通用的东西却不是那么好。您说您“有义务使用 SQL*Plus 执行如此生成的脚本”,并且没有提及实际制作脚本的要求,这就是我建议我的方法的原因。 Oracle 允许您非常轻松地通过 JDBC 进行连接,并且 Java 工具是免费的,所以这仍然是我的答案,因为将自己限制在 PL/SQL 上要比学习 Java/JDBC 困难得多。 话虽如此,我会给你一个开始(注意我的编辑),但这将是一条漫长的曲折之路:-) 您的解决方案似乎很有趣...我期待分析和测试它:如果它有效,我会接受答案。任何其他线索总是值得赞赏的。 @Chicken:你似乎习惯于对你的问题引出长而深入的答案。 :)【参考方案2】:

您可以使用 Jailer 工具 (http://jailer.sf.net) 将表格数据导出为 INSERT STATEMENTS。

【讨论】:

据我了解的问题,他导出它们没有问题,但生成的脚本的导入/执行由于行过长而导致问题。 你说得对,但我提到的工具将行换行,以便可以使用 SQL*Plus 导入数据。 我确认将记录导出为 INSERT STATEMENTS 没有问题,因为我使用的是 TOAD(由 Quest Software 提供)。下午我会尝试使用 Jailer 工具,看看用 Jailer 导出我的表数据会发生什么。目标是使用 Jailer 生成的 INSERT STATEMENTS 不会给出 SQL*Plus 错误“sp2-0027 input is too long(>2499 characters)”。 很抱歉,Jailer 工具太难用了,我想要一个PL/SQL 中的解决方案,不使用外部工具。用户必须有机会自己解决这个问题。 此外,我无法使用该工具连接到我的 Oracle Database 10G R2...

以上是关于导出为插入语句:但在 SQL Plus 中,该行覆盖了 2500 个字符!的主要内容,如果未能解决你的问题,请参考以下文章

我的 SQL Plus 插入语句中的字符无效

SQL:如果存在则更新,否则插入...但对于具有不同值的多行

为啥即使使用提交后 sql plus 也不会保存新行?

如何在 PL/SQL 中使用 select 语句为 SQL*Plus 变量赋值?

将数据从 oracle sql plus 导出到 csv 时,Hypen 转换为问号

MySQL 工作台:如何将特定列导出为更新语句?