Write, Read and Update Oracle CLOBs with PL/SQL

Posted by robertphyatt on Geeks with Blogs See other posts from Geeks with Blogs or by robertphyatt
Published on Wed, 24 Mar 2010 12:41:51 GMT Indexed on 2010/03/24 20:53 UTC
Read the original article Hit count: 969

Filed under:

Fun with CLOBS! If you are using Oracle, if you have to deal with text that is over 4000 bytes, you will probably find yourself dealing with CLOBs, which can go up to 4GB. They are pretty tricky, and it took me a long time to figure out these lessons learned. I hope they will help some down-trodden developer out there somehow.

Here is my original code, which worked great on my Oracle Express Edition: (for all examples, the first one writes a new CLOB, the next one Updates an existing CLOB and the final one reads a CLOB back)

CREATE OR REPLACE PROCEDURE PRC_WR_CLOB (
       p_document      IN VARCHAR2,
       p_id            OUT NUMBER)

IS
     lob_loc CLOB;

BEGIN

   INSERT INTO TBL_CLOBHOLDERDDOC (CLOBHOLDERDDOC)
       VALUES (empty_CLOB())
       RETURNING CLOBHOLDERDDOC, CLOBHOLDERDDOCID INTO lob_loc, p_id;

   DBMS_LOB.WRITE(lob_loc, LENGTH(UTL_RAW.CAST_TO_RAW(p_document)),
1, UTL_RAW.CAST_TO_RAW(p_document));


END;
/


CREATE OR REPLACE PROCEDURE PRC_UD_CLOB (
       p_document      IN VARCHAR2,
       p_id            IN NUMBER)

IS
     lob_loc CLOB;

BEGIN

       SELECT CLOBHOLDERDDOC INTO lob_loc FROM TBL_CLOBHOLDERDDOC
       WHERE CLOBHOLDERDDOCID = p_id FOR UPDATE;

   DBMS_LOB.WRITE(lob_loc, LENGTH(UTL_RAW.CAST_TO_RAW(p_document)),
1, UTL_RAW.CAST_TO_RAW(p_document));

END;
/



CREATE OR REPLACE PROCEDURE PRC_RD_CLOB (
   p_id IN NUMBER,
   p_clob OUT VARCHAR2)
IS

   lob_loc  CLOB;

BEGIN

   SELECT CLOBHOLDERDDOC INTO lob_loc
   FROM   TBL_CLOBHOLDERDDOC
   WHERE  CLOBHOLDERDDOCID = p_id;

   p_clob := UTL_RAW.CAST_TO_VARCHAR2(DBMS_LOB.SUBSTR(lob_loc,
DBMS_LOB.GETLENGTH(lob_loc), 1));

END;
/

As you can see, I had originally been casting everything back and forth between RAW formats using the UTL_RAW.CAST_TO_VARCHAR2() and UTL_RAW.CAST_TO_RAW() functions all over the place, but it had the nasty side effect of working great on my Oracle express edition on my developer box, but having all the CLOBs above a certain size display garbage when read back on the Oracle test database server .

So...I kept working at it and came up with the following, which ALSO worked on my Oracle Express Edition on my developer box:

 

CREATE OR REPLACE PROCEDURE PRC_WR_CLOB (
    p_document      IN VARCHAR2,
    p_id        OUT NUMBER)

IS
      lob_loc CLOB;

BEGIN

    INSERT INTO TBL_CLOBHOLDERDOC (CLOBHOLDERDOC)
        VALUES (empty_CLOB())
        RETURNING CLOBHOLDERDOC, CLOBHOLDERDOCID INTO lob_loc, p_id;

    DBMS_LOB.WRITE(lob_loc, LENGTH(p_document), 1, p_document);

 
END;
/

CREATE OR REPLACE PROCEDURE PRC_UD_CLOB (
    p_document      IN VARCHAR2,
    p_id        IN NUMBER)

IS
      lob_loc CLOB;

BEGIN

    SELECT CLOBHOLDERDOC INTO lob_loc FROM TBL_CLOBHOLDERDOC
    WHERE CLOBHOLDERDOCID = p_id FOR UPDATE;

    DBMS_LOB.WRITE(lob_loc, LENGTH(p_document), 1, p_document);

END;
/


CREATE OR REPLACE PROCEDURE PRC_RD_CLOB (
    p_id IN NUMBER,
    p_clob OUT VARCHAR2)
IS

    lob_loc  CLOB;

BEGIN

    SELECT CLOBHOLDERDOC INTO lob_loc
    FROM   TBL_CLOBHOLDERDOC
    WHERE  CLOBHOLDERDOCID = p_id;

    p_clob := DBMS_LOB.SUBSTR(lob_loc, DBMS_LOB.GETLENGTH(lob_loc), 1);

END;
/

Unfortunately, by changing my code to what you see above, even though it kept working on my Oracle express edition, everything over a certain size just started truncating after about 7950 characters on the test server!

Here is what I came up with in the end, which is actually the simplest solution and this time worked on both my express edition and on the database server (note that only the read function was changed to fix the truncation issue, and that I had Oracle worry about converting the CLOB into a VARCHAR2 internally):

CREATE OR REPLACE PROCEDURE PRC_WR_CLOB (
       p_document      IN VARCHAR2,
       p_id            OUT NUMBER)

IS
     lob_loc CLOB;

BEGIN

   INSERT INTO TBL_CLOBHOLDERDDOC (CLOBHOLDERDDOC)
       VALUES (empty_CLOB())
       RETURNING CLOBHOLDERDDOC, CLOBHOLDERDDOCID INTO lob_loc, p_id;

   DBMS_LOB.WRITE(lob_loc, LENGTH(p_document), 1, p_document);


END;
/


CREATE OR REPLACE PROCEDURE PRC_UD_CLOB (
       p_document      IN VARCHAR2,
       p_id            IN NUMBER)

IS
     lob_loc CLOB;

BEGIN

       SELECT CLOBHOLDERDDOC INTO lob_loc FROM TBL_CLOBHOLDERDDOC
       WHERE CLOBHOLDERDDOCID = p_id FOR UPDATE;

   DBMS_LOB.WRITE(lob_loc, LENGTH(p_document), 1, p_document);

END;
/



CREATE OR REPLACE PROCEDURE PRC_RD_CLOB (
   p_id IN NUMBER,
   p_clob OUT VARCHAR2)
IS

BEGIN

   SELECT CLOBHOLDERDDOC INTO p_clob
   FROM   TBL_CLOBHOLDERDDOC
   WHERE  CLOBHOLDERDDOCID = p_id;

END;
/

 

I hope that is useful to someone!

© Geeks with Blogs or respective owner