You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

PLSQL字符字符串缓冲区过小报错:解析含HTML的CLOB查询问题

Fixing "character string buffer too small" Error When Extracting JS Sources from CLOB HTML

Hey there, let's figure out how to fix that "character string buffer too small" error you're hitting with your regex query on CLOB HTML data. Let's break down why this is happening first, then jump into practical solutions.

Why You're Seeing This Error

Your original query has a couple of key issues that trigger the buffer overflow:

  1. Truncated CLOB Data: You're using dbms_lob.substr(t.data, 4000, 1) which only grabs the first 4000 characters of your CLOB. Not only does this miss potential JS sources later in the HTML, but combining this with recursive CONNECT BY can create duplicate rows that overload string buffers.
  2. VARCHAR2 Limitations: Oracle's REGEXP_SUBSTR returns a VARCHAR2 by default (max 4000 characters in older versions, 32767 in 12c+ if enabled). If your JS source paths are long, or if the query generates too many repeated matches, you'll hit the buffer limit.
  3. Uncontrolled CONNECT BY: The way you're using CONNECT BY without proper correlation can generate massive numbers of duplicate rows, pushing the string buffer over its limit.

Solution 1: Use a Recursive CTE (Oracle 11g+)

This method safely traverses the entire CLOB without truncation, and avoids duplicate rows by tracking match positions step-by-step.

WITH js_src_matches AS (
    -- Initial seed: find the first JS source match in each template's cleaned HTML
    SELECT 
        template_id,
        clean_html,
        1 AS match_number,
        REGEXP_INSTR(clean_html, 'src="[^"]*\.js"', 1, 1, 0, 'i') AS start_pos,
        REGEXP_INSTR(clean_html, 'src="[^"]*\.js"', 1, 1, 1, 'i') AS end_pos
    FROM (
        -- Clean HTML by removing comments (works directly on CLOB)
        SELECT 
            t.id AS template_id,
            REGEXP_REPLACE(t.data, '<!--.*?-->', '', 1, 0, 'n') AS clean_html
        FROM template t
    )
    UNION ALL
    -- Recursive step: find the next match after the previous one
    SELECT 
        template_id,
        clean_html,
        match_number + 1,
        REGEXP_INSTR(clean_html, 'src="[^"]*\.js"', end_pos, 1, 0, 'i'),
        REGEXP_INSTR(clean_html, 'src="[^"]*\.js"', end_pos, 1, 1, 'i')
    FROM js_src_matches
    WHERE start_pos > 0
)
-- Extract the actual JS source string from the CLOB
SELECT 
    template_id,
    DBMS_LOB.SUBSTR(clean_html, end_pos - start_pos, start_pos) AS js_src
FROM js_src_matches
WHERE start_pos > 0
ORDER BY template_id, match_number;

How This Works:

  • The recursive CTE starts by finding the first match position for each template.
  • It then iteratively finds subsequent matches by starting from the end of the last match.
  • DBMS_LOB.SUBSTR extracts the exact match from the full CLOB, avoiding VARCHAR2 overflow where possible.

Solution 2: PL/SQL Block (For Large/Complex CLOBs)

If you're dealing with extremely large CLOBs or need more control over the extraction process, a PL/SQL block is a reliable alternative. It processes each template one at a time and avoids loading all matches into memory at once.

DECLARE
    -- Cursor to fetch each template's cleaned HTML
    CURSOR c_templates IS
        SELECT 
            id AS template_id,
            REGEXP_REPLACE(data, '<!--.*?-->', '', 1, 0, 'n') AS clean_html
        FROM template;
    v_start_pos NUMBER;
    v_end_pos   NUMBER;
    v_js_src    CLOB;
BEGIN
    FOR template_rec IN c_templates LOOP
        -- Find the first JS source match
        v_start_pos := REGEXP_INSTR(template_rec.clean_html, 'src="[^"]*\.js"', 1, 1, 0, 'i');
        
        -- Loop through all matches in the current template
        WHILE v_start_pos > 0 LOOP
            v_end_pos := REGEXP_INSTR(template_rec.clean_html, 'src="[^"]*\.js"', v_start_pos, 1, 1, 'i');
            v_js_src := DBMS_LOB.SUBSTR(template_rec.clean_html, v_end_pos - v_start_pos, v_start_pos);
            
            -- Do something with the result: insert into a table, print, etc.
            DBMS_OUTPUT.PUT_LINE('Template ID: ' || template_rec.template_id || ' | JS Source: ' || v_js_src);
            
            -- Move to the next match
            v_start_pos := REGEXP_INSTR(template_rec.clean_html, 'src="[^"]*\.js"', v_end_pos, 1, 0, 'i');
        END LOOP;
    END LOOP;
END;
/

How This Works:

  • The cursor iterates over each template, pulling its full cleaned CLOB.
  • We use REGEXP_INSTR to find the start and end positions of each JS source match.
  • DBMS_LOB.SUBSTR extracts the match as a CLOB, avoiding VARCHAR2 limits entirely.

Solution 3: Multiset Table (Oracle 12c+)

If you prefer a pure SQL approach and are on Oracle 12c or newer, you can use MULTISET to generate a list of matches per template, using a CLOB-compatible collection to avoid buffer issues.

SELECT 
    t.template_id,
    TO_CLOB(js_matches.COLUMN_VALUE) AS js_src
FROM (
    SELECT 
        id AS template_id,
        REGEXP_REPLACE(data, '<!--.*?-->', '', 1, 0, 'n') AS clean_html
    FROM template t
) t,
TABLE(
    CAST(
        MULTISET(
            SELECT REGEXP_SUBSTR(t.clean_html, 'src="[^"]*\.js"', 1, LEVEL, 'i')
            FROM dual
            CONNECT BY REGEXP_SUBSTR(t.clean_html, 'src="[^"]*\.js"', 1, LEVEL, 'i') IS NOT NULL
        ) AS SYS.ODCICLOBLIST
    )
) js_matches;

How This Works:

  • MULTISET generates a collection of matches for each template.
  • Using SYS.ODCICLOBLIST ensures matches are stored as CLOBs, avoiding VARCHAR2 length limits.
  • The TABLE function converts the collection into rows for easy querying.

Key Takeaways

  • Avoid truncating CLOBs: Remove dbms_lob.substr with a fixed length unless you're certain your HTML fits within that limit.
  • Use CLOB-compatible functions: Whenever possible, work with CLOBs directly instead of converting to VARCHAR2.
  • Control recursion: Use recursive CTEs or explicit loops instead of uncorrelated CONNECT BY to prevent duplicate rows and buffer overflow.

内容的提问来源于stack exchange,提问作者Piet Smet

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.21 08:21:51