我正在编写一些 pl/sql 来生成 pdf 报告,这些报告作为 blob 存储在 oracle 表中。我需要遍历这个表,其中有一列用于文件名和 blob,并将 blob 作为一个文件写入操作系统,该文件在表中具有相应的文件名。我几乎已经完成了这段代码,但遇到了麻烦:
ORA-06550: line 13, column 59:
PL/SQL: ORA-00904: "SIMS_PROD"."PUBLISH_RPT_NEW"."RPT_FILE_NAME": invalid identifier
ORA-06550: line 13, column 12:
PL/SQL: SQL Statement ignored
06550. 00000 - "line %s, column %s:\n%s"
Cause: Usually a PL/SQL compilation error.
Action:
我确实阅读了网站上的帖子:如何从 Oracle BLOB 字段中提取文件? - 但是 - 这仅适用于一个文件 - 我的表包含数百行,每行都有一个 blob 和关联的文件名 - 它是遍历这张表让我很伤心。
我需要明确地为模式名称、表和列添加前缀,因为我是以 DBA 用户身份登录的,而不是以模式本身的所有者身份登录的。这是我的代码 - 我在这里遗漏了什么或做错了什么。预先感谢社区的任何帮助 - 非常感谢。
DECLARE
t_blob BLOB;
t_len NUMBER;
t_file_name VARCHAR2(100);
t_output utl_file.file_type;
t_totalsize NUMBER;
t_position NUMBER := 1;
t_chucklen NUMBER := 4096;
t_chuck RAW(4096);
t_remain NUMBER;
BEGIN
FOR X IN (SELECT SIMS_PROD.publish_rpt_new.RPT_FILE_NAME, SIMS_PROD.publish_rpt_new.RPT_CONTENTS FROM SIMS_PROD.PUBLISH_RPT)
LOOP
-- Get length of blob
SELECT dbms_lob.Getlength (SIMS_PROD.publish_rpt_new.RPT_CONTENTS), SIMS_PROD.publish_rpt_new.RPT_FILE_NAME INTO t_totalsize, t_file_name FROM SIMS_PROD.publish_rpt_new;
t_remain := t_totalsize;
-- The directory TEMPDIR should exist before executing
t_output := utl_file.Fopen ('PDF_REP', t_file_name, 'wb', 32760);
-- Get BLOB
SELECT SIMS_PROD.publish_rpt_new.RPT_CONTENTS INTO t_blob FROM SIMS_PROD.publish_rpt_new;
-- Retrieving BLOB
WHILE t_position < t_totalsize
LOOP
dbms_lob.READ (t_blob, t_chucklen, t_position, t_chuck);
utl_file.Put_raw (t_output, t_chuck);
utl_file.Fflush (t_output);
t_position := t_position + t_chucklen;
t_remain := t_remain - t_chucklen;
IF t_remain < 4096 THEN t_chucklen := t_remain;
END IF;
END LOOP;
END LOOP;
END;
尝试用这个替换第 13 行:
FOR X IN (SELECT RPT_FILE_NAME, RPT_CONTENTS FROM SIMS_PROD.PUBLISH_RPT)
这将是一个太晚的答案,但至少你会知道你在哪里做错了。问题是如果你使用:
FOR X IN (SELECT a, b from table) LOOP
您必须在此循环的下一个语句中使用
X.a
才能正确引用所选行的值。
所以在您的代码中,您必须将
SIMS_PROD.publish_rpt_new.RPT_CONTENTS
更改为 X.SIMS_PROD.publish_rpt_new.RPT_CONTENTS
。
我还没有阅读您的其余代码,所以也许还有更多错误。
最近做过类似的事情, 联系我以获取更多详细信息
但要点是这个
也许您可以考虑单独编写一个过程,将 BLOB 保存为文件(任何表中的任何 BLOB),然后循环遍历您的表,传递 BLOB、目录和文件名。这样就可以独立使用了。
这是一个函数(由于其他原因它是一个函数 - 你可以将它更改为过程),就像描述的那样:
Function BLOB2FILE (mBLOB BLOB, mDir VARCHAR2, mFile VARCHAR2) RETURN VarChar2
IS
BEGIN
Declare
utlFile UTL_FILE.FILE_TYPE;
utlBuffer RAW(32767);
utlAmount BINARY_INTEGER := 32767;
utlPos INTEGER := 1;
utlBlobLen INTEGER;
mRet VarChar2(100);
Begin
utlBlobLen := DBMS_LOB.GetLength(mBLOB);
utlFile := UTL_FILE.FOPEN(mDir, mFile,'wb', 32767);
--
WHILE utlPos <= utlBlobLen LOOP
DBMS_LOB.READ(mBLOB, utlAmount, utlPos, utlBuffer);
UTL_FILE.PUT_RAW(utlFile, utlBuffer, TRUE);
utlPos := utlPos + utlAmount;
END LOOP;
--
UTL_FILE.FCLOSE(utlFile);
mRet := 'OK - file created' || mFile;
RETURN mRet;
Exception
WHEN OTHERS THEN
IF UTL_FILE.IS_OPEN(utlFile) THEN
UTL_FILE.FCLOSE(utlFile);
END IF;
mRet := 'ERR - CLOB_ETL.BLOB2FILE error message ' || Chr(10) || SQLERRM;
RETURN mRet;
End;
END BLOB2FILE;
如果您可以从任何表中选择您的 BLOBS,只需循环并将其传递给具有目录和文件名的函数/过程...