问题是,如何将 CLOB 数据从一个源数据库传输到另一个具有 DBLink 的 Oracle 数据库。
Oracle无法使用DBLinks传输CLOB数据,那么除了:将Oracle中的字段扩展为Varchar2 32.767个字符(Oracle 12的新功能)之外,我们还能有什么样的解决方案。
首先你需要临时表:
create global temporary table TBL_TMP_CLOB
(
c_clob CLOB
)
第二次使用“从选择插入”:
INSERT INTO schema.remote_table@dblink(rem_clob) SELECT * FROM TBL_TMP_CLOB;
我发布了一个 Github 项目,该项目通过 dblink 查询 CLOB 和 BLOB。 https://github.com/HowdPrescott/Lob_Over_DBLink
这是独立函数中的 CLOB 部分:
create or replace function dblink_clob(
p_dblink in varchar2
, p_clob_col in varchar2
, p_rid in urowid
)
return clob is
/** A function to fetch a CLOB column over a dblink **/
/** Laurence Prescott 25-Aug-17 **/
/** select dblink_clob('some_dblink', 'some_clob_column', rowid)
from some_table@some_dblink;
Note: Does not work on tables that have a virtual column (eg. xmltype).
**/
c_chunk_size constant pls_integer := 4000;
v_chunk varchar2(4000);
v_remote_table varchar2(128);
v_clob clob;
v_pos pls_integer := 1;
begin
dbms_lob.createtemporary(v_clob, true, dbms_lob.call);
execute immediate 'select object_name from user_objects@' ||p_dblink
|| ' where data_object_id = dbms_rowid.rowid_object(:bv_rid) '
into v_remote_table using cast (p_rid as rowid);
loop
execute immediate
'select dbms_lob.substr@' ||p_dblink|| '(' ||p_clob_col|| ', ' ||c_chunk_size
|| ', ' ||v_pos|| ') from ' ||v_remote_table|| '@' ||p_dblink|| ' where rowid = :rid '
into v_chunk using p_rid;
begin dbms_lob.append(v_clob, v_chunk);
exception when others then
if sqlcode = -6502 then exit; else raise; end if;
end;
if length(v_chunk) < c_chunk_size then exit; end if;
v_pos := v_pos + c_chunk_size;
end loop;
return v_clob;
end dblink_clob;
我认为这个例子是相当不言自明的,但这里有一些描述。
该函数依赖于这样一个事实:您可以通过 dblink 调用远程数据库上的函数/过程 - 在本例中为 dbms_lob.substr()。
首先,它使用对象 ID(编码到 rowid 中)查找远程表名称。这样就不必将远程表名称作为另一个参数传递。
另请注意,p_rid 参数是一个 urowid,因为它是来自远程数据库的 rowid。这就是为什么需要铸造它。
然后,CLOB 被提取并以 4000 字节块的形式重建,这是 PL/SQL 中 varchar2 的最大大小。这些块是 varchar2 的,可以通过 dblink 传递。
读取最后一个 CLOB 后,满足 if length(v_chunk) < c_chunk_size ... 子句(然后“块”缓冲区将不会被填充)。
当 CLOB 的长度是 4000 的倍数时,需要捕获ORA-06502的异常,此时即使没有更多数据,也不满足“if”子句。
您可以仅依靠此捕获并完全删除“if”子句。但我做了一些性能测试,发现大多数情况下最好保留它。
Howd 可能在 4 年前就有了正确的方法,但我需要将他的方法更新为如下所示才能使其工作(我简化了他的方法供我自己使用,因此以下内容可能无法编译或工作,但你明白了,在 12.1.0.2.0 中不再需要第一个查询):
create or replace function dblink_clob(
p_dblink in varchar2
, v_remote_table in varchar2
, p_clob_col in varchar2
, p_rid in urowid
)
return clob is
/** A function to fetch a CLOB column over a dblink **/
/** Laurence Prescott 25-Aug-17 **/
/** select dblink_clob('some_dblink', 'some_clob_column', rowid)
from some_table@some_dblink;
Note: Does not work on tables that have a virtual column (eg. xmltype).
**/
c_chunk_size constant pls_integer := 4000;
v_chunk varchar2(4000);
v_clob clob;
v_pos pls_integer := 1;
begin
dbms_lob.createtemporary(v_clob, true, dbms_lob.call);
loop
execute immediate
'select dbms_lob.substr@' ||p_dblink|| '(' ||p_clob_col|| ', ' ||c_chunk_size
|| ', ' ||v_pos|| ') from ' ||v_remote_table|| '@' ||p_dblink|| ' where rowid = :rid '
into v_chunk using p_rid;
begin dbms_lob.append(v_clob, v_chunk);
exception when others then
if sqlcode = -6502 then exit; else raise; end if;
end;
if length(v_chunk) < c_chunk_size then exit; end if;
v_pos := v_pos + c_chunk_size;
end loop;
return v_clob;
end dblink_clob;