PLSQL - 字符串缓冲区太小

Posted

技术标签:

【中文标题】PLSQL - 字符串缓冲区太小【英文标题】:PLSQL - character string buffer too small 【发布时间】:2018-04-03 13:29:48 【问题描述】:

我正在尝试运行以下查询来检查一堆包含 html 页面的 CLOB:

SELECT template_id, REGEXP_SUBSTR(clean_html, 'src="[^"]*\.js"', 1, LEVEL, 'i') AS match
  FROM (
    SELECT template_id,  REGEXP_REPLACE(html_text, '<!--.*?-->', '', 1, 0, 'n') AS clean_html
     FROM (
           SELECT t.id as template_id, dbms_lob.substr(t.data, 4000, 1) AS html_text
           FROM template t
     )
  )
CONNECT BY REGEXP_SUBSTR(clean_html, 'src="[^"]*\.js"', 1, LEVEL, 'i') IS NOT NULL
 GROUP BY template_id,
      clean_html,
      LEVEL
order by 1

但在运行查询后,我几乎直接收到“第 1 行的字符串缓冲区太小”。

如果我在只有几个模板的小数据集上运行它,它可以工作,但是一旦我在整个 HTML 模板列表上运行它,它就会抛出错误。

我认为该问题与 dbms_lob.substr(data, 4000, 1) 有关,并且可能与由于隐藏字符(或其他原因)超过 4000 字节的子字符串有关,但我不知道如何修复它。

如果我写 dbms_lob.substr(data, 2000, 1) 那么我的子字符串太小了,我会丢失 HTML 文件中的重要数据。如果我让它大于 4000,那么我会立即得到“缓冲区太小”的错误。

有人知道我该如何解决这个问题吗?理想情况下,我想浏览整个“数据”字段,而不仅仅是前 4000 个字符。但是,如果它适用于我的一长串 HTML 文件,前 4000 个字符就可以了。

谢谢

【问题讨论】:

如果在t.data 上不使用dbms_lob.substr 并在外面使用会怎样? 如果您的 HTML 实际上是格式良好的 XML,那么您可以将其解析为 XML,而不是尝试使用正则表达式。 为什么是GROUP BY 这段代码似曾相识……***.com/questions/49274050/… 另外,这个问题可能是相关的:***.com/questions/13819551/… 【参考方案1】:

我假设您使用 GROUP BY 来获取不同的值,对吧?在这里使用CONNECT BY 处理大量记录的问题在于它会返回大量重复项。但是GROUP BYDISTINCT 不是处理这个问题的正确方法。取出GROUP BY 并将以下行附加到您的查询中:

AND PRIOR template_id = template_id
AND PRIOR DBMS_RANDOM.VALUE IS NOT NULL

另外,我认为您想在最里面的查询中添加以下条件:

WHERE REGEXP_LIKE(t.data, 'src="[^"]*\.js"', 'i')

否则,查询将始终为template_id 的每个值返回至少一行。

我不太确定 CLOB 发生了什么,但我无法准确重现您的错误。

已编辑

我想我有一个解决办法。请尝试以下方法:

SELECT template_id, REGEXP_SUBSTR(clean_html, 'src="[^"]*\.js"', 1, LEVEL, 'i') AS match
  FROM (
    SELECT template_id,  REGEXP_REPLACE(html_text, '<!--.*?-->', '', 1, 0, 'n') AS clean_html
     FROM (
       SELECT t.id as template_id, t.data AS html_text
         FROM template t
        WHERE REGEXP_LIKE(t.data, 'src="[^"]*\.js"', 'i')
     )
  )
CONNECT BY TO_CHAR(REGEXP_SUBSTR(clean_html, 'src="[^"]*\.js"', 1, LEVEL, 'i')) IS NOT NULL
  AND PRIOR template_id = template_id
  AND PRIOR DBMS_RANDOM.VALUE IS NOT NULL;

我认为问题在于CONNECT BY 子句中的比较

CONNECT BY REGEXP_SUBSTR(clean_html, 'src="[^"]*\.js"', 1, LEVEL, 'i') IS NOT NULL

在这里比较 CHAR 比比较 CLOB 更好:

CONNECT BY TO_CHAR(REGEXP_SUBSTR(clean_html, 'src="[^"]*\.js"', 1, LEVEL, 'i')) IS NOT NULL

为了安全起见,您甚至可以在这里使用DBMS_LOB.SUBSTR()

CONNECT BY DBMS_LOB.SUBSTR(REGEXP_SUBSTR(clean_html, 'src="[^"]*\.js"', 1, LEVEL, 'i'), 4000, 1) IS NOT NULL

最后,我认为您不需要那么多级别的子查询。我认为您可以执行以下操作:

SELECT template_id, REGEXP_SUBSTR(clean_html, 'src="[^"]*\.js"', 1, LEVEL, 'i') AS match
  FROM (
    SELECT t.id AS template_id, REGEXP_REPLACE(t.data, '<!--.*?-->', '', 1, 0, 'n') AS clean_html
      FROM template t
     WHERE REGEXP_LIKE(t.data, 'src="[^"]*\.js"', 'i')
  )
CONNECT BY DBMS_LOB.SUBSTR(REGEXP_SUBSTR(clean_html, 'src="[^"]*\.js"', 1, LEVEL, 'i'), 4000, 1) IS NOT NULL
  AND PRIOR template_id = template_id
  AND PRIOR DBMS_RANDOM.VALUE IS NOT NULL;

希望这会有所帮助。

【讨论】:

嗨,大卫,这解决了问题!这也使我的查询运行得更快,谢谢!您对连接方式的所有假设也是正确的。这主要是由于我对 SQL 的了解有限。感谢您教我一些关于 PRIOR 的新知识。

以上是关于PLSQL - 字符串缓冲区太小的主要内容,如果未能解决你的问题,请参考以下文章

执行动态SQL报“字符串缓冲区太小”错误,请问各位高手怎么解决啊

plsql客户资料

oracle expdp导出报错“字符串缓冲区太小”

Oracle 数据访问 ORA-06512: 字符串缓冲区太小

ORA-19011: 字符串缓冲区太小

使用 wcsncpy_s 复制字符串时缓冲区太小