【问题标题】:Oracle Sql Developer "string literal too long" errorOracle Sql Developer“字符串文字太长”错误
【发布时间】:2011-03-30 13:22:40
【问题描述】:

我想在 Oracle SQL Developer 中针对 Oracle 10g 服务器运行以下 SQL:

WITH openedXml AS (
  SELECT extractvalue(column_value, '/theRow/First') FIRST,
         extractvalue(column_value, '/theRow/Last') LAST,
         to_number(extractvalue(column_value, '/theRow/Age')) Age
    FROM TABLE(XMLSequence(XMLTYPE('
  <theRange>
    <theRow><First>Bob</First><Last>Smith</Last><Age>30</Age></theRow>
    <theRow><First>Sue</First><Last>Jones</Last><Age>34</Age></theRow>
...
...
...
    <theRow><First>Tom</First><Last>Anderson</Last><Age>39</Age></theRow>
    <theRow><First>Ali</First><Last>Grady</Last><Age>45</Age></theRow>
  </theRange>
  ').extract('/theRange/theRow')))
)
SELECT *
FROM openedxml
WHERE age BETWEEN 30 AND 35;

当我尝试运行它时,我收到以下错误:

Error at Command Line:1 Column:0 Error report: SQL Error: ORA-01704: string literal too long
01704. 00000 -  "string literal too long"
*Cause:    The string literal is longer than 4000 characters.
*Action:   Use a string literal of at most 4000 characters.
           Longer values may only be entered using bind variables.

我的字符串有时会超过 4000 个字符。关于如何解决这个问题的任何想法?

【问题讨论】:

    标签: sql oracle


    【解决方案1】:

    您无法使用“普通”SQL 解决此问题。 (但我很高兴被证明是错误的)

    您将需要某种编程语言(例如 Java、存储过程)来处理这个问题。

    另一种方法是将 XML 数据上传到表中(可以使用 SQL*Loader 完成)并在查询中使用列值。

    这是真正让我抓狂的 Oracle 限制之一。 20 年前这可能还可以接受,但现在……

    【讨论】:

    • 我希望你也错了,但我希望你是对的。
    【解决方案2】:

    您需要使用 CLOB 作为 XMLTYPE() 的输入,而不是 VARCHAR。

    使用 dbms_lob.loadclobfromfile 从文件中加载 xml,或将 xml 分解为 32000 个字符块并附加到 CLOB。

    DECLARE
       xmlClob CLOB;
    BEGIN
    /* Build Clob here */
    
    WITH openedXml AS (
      SELECT extractvalue(column_value, '/theRow/First') FIRST,
             extractvalue(column_value, '/theRow/Last') LAST,
             to_number(extractvalue(column_value, '/theRow/Age')) Age
        FROM TABLE(XMLSequence(XMLTYPE(xmlClob).extract('/theRange/theRow')))
    )
    SELECT *
    FROM openedxml
    WHERE age BETWEEN 30 AND 35;
    END;
    

    【讨论】:

    • 如何构建“Clob”?提供完整的解决方案。
    【解决方案3】:

    这么大的一大块 XML 是从哪里来的?我假设你没有输入它。

    通常我会看一个读取源代码并将其转换为 CLOB 的程序。这可能是客户端上的 perl/python/whatever 脚本,也可能是从 Web 服务器中提取值的服务器端例程。

    【讨论】:

    • XML 来自几个不同的地方。目前我正在构建一个包含 XML 的 SQL 脚本,然后在 SQL Server 中执行它。我想修改一些东西以在 Oracle 中运行,但我遇到了一些问题。
    【解决方案4】:

    如果每个部分少于 4000 个字符,您可以使用插入/更新的 sql 解决方法。

    1 将插入作为插入,第一部分是最多 4000 个字符的 sql 文字 2 将附加部分作为更新,将前一部分与下一部分连接起来,下一部分最多 4000 个字符 3 重复第 2 步,直到所有大的 sql 文字都更新完毕。

    例子,

    Insert into
    test_large_literal (col1, col2)
    values
    (<key val>, <first part of large sql literal>);
    
    update
    test_large_literal
    set
    col2 = col2 || <second part large sql literal>
    where
    col1 = <key val>;
    ...
    ...
    update
    test_large_literal
    set
    col2 = col2 || <last part large sql literal>
    where
    col1 = <key val>;
    

    【讨论】:

    • 你也可以INSERT ... VALUES (TO_CLOB('&lt;first part&gt;') || TO_CLOB('&lt;second part&gt;') || TO_CLOB(...));但是如果你真的有很多部分,这会变得很慢,除非你使用括号来确保连接相同长度的块(比如(((A||B)||(C||D))||((E||F)||(G||H)))),所以最好不要使用这个肮脏的hack :)
    【解决方案5】:

    一种可能的解决方法是使用 PL/SQL 块:

    DECLARE
      xml VARCHAR2(32000) := 
     '<theRange>
        <theRow><First>Bob</First><Last>Smith</Last><Age>30</Age></theRow>
        <theRow><First>Sue</First><Last>Jones</Last><Age>34</Age></theRow>
    ...
    ...
    ...
        <theRow><First>Tom</First><Last>Anderson</Last><Age>39</Age></theRow>
        <theRow><First>Ali</First><Last>Grady</Last><Age>45</Age></theRow>
      </theRange>';
    
      CURSOR C (p1 INTEGER, p2 INTEGER) IS
      SELECT * FROM (
        SELECT extractvalue(column_value, '/theRow/First') FIRST,
               extractvalue(column_value, '/theRow/Last') LAST,
               to_number(extractvalue(column_value, '/theRow/Age')) Age
          FROM TABLE(XMLSequence(XMLTYPE(xml).extract('/theRange/theRow'))))
      )
       WHERE age BETWEEN p1 AND p2;
    BEGIN
      FOR R IN C (30,35) LOOP
        dbms_output.put_line(R.First||', '||R.Last||', '||R.Age);
      END LOOP;
    END;
    

    (完全未经测试)

    编辑:

    作为插入,您可以尝试:

    DECLARE
          xml VARCHAR2(32000) := 
         '<theRange>
            <theRow><First>Bob</First><Last>Smith</Last><Age>30</Age></theRow>
            <theRow><First>Sue</First><Last>Jones</Last><Age>34</Age></theRow>
        ...
        ...
        ...
            <theRow><First>Tom</First><Last>Anderson</Last><Age>39</Age></theRow>
            <theRow><First>Ali</First><Last>Grady</Last><Age>45</Age></theRow>
          </theRange>';
    BEGIN
      INSERT INTO temp_table(last,first,age)
      SELECT last, first, age FROM (
        SELECT extractvalue(column_value, '/theRow/First') FIRST,
               extractvalue(column_value, '/theRow/Last') LAST,
               to_number(extractvalue(column_value, '/theRow/Age')) Age
          FROM TABLE(XMLSequence(XMLTYPE(xml).extract('/theRange/theRow'))))
      )
       WHERE age BETWEEN 30 AND 35;
    END;
    

    【讨论】:

    • 有什么方法可以将结果插入到我可以从中选择的临时表中?
    • 另一个问题。 32,000是这种东西的极限吗?如果我超过了这个数量,我是否需要将它分成 32,000 个字符块。
    • 32k (32767) 是 iirc,PL/SQL 中 varchar2 变量的绝对大小限制(以字节为单位)。所以你需要使用块。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-12-06
    • 1970-01-01
    • 2012-12-06
    • 2016-10-16
    • 2012-07-03
    • 2020-03-20
    • 1970-01-01
    相关资源
    最近更新 更多