ddl语句通过dblink保存在clob变量中

时间:2017-03-01 16:24:27

标签: plsql ddl oracle12c dblink

我在db链接上执行ddl语句时遇到问题。 PL / SQL下面负责通过db链接在远程数据库上执行语句,但是我需要将ddl_statement变量作为clob,因为最大varchar2对它来说是不够的。目前它正在提高:

PLS-00564:在远程服务器

的调用中不允许使用Lob参数
DECLARE
  vi_handler        INTEGER;
  vi_numberOfRows   INTEGER;
BEGIN
  vi_handler := DBMS_SQL.open_cursor%s;
  DBMS_SQL.parse%s (vi_handler, :ddl_statement, DBMS_SQL.native);
  vi_numberOfRows := DBMS_SQL.execute%s (vi_handler);
  DBMS_SQL.close_cursor%s (vi_handler);
END;

提前感谢您的任何帮助

4 个答案:

答案 0 :(得分:1)

嗯,这很痛苦,如果你可以在远程端有一个你可以打电话的程序会更简单(我知道你不能!);但是通过一些努力,您可以远程运行匿名块,并让匿名块运行您的DDL。

这很令人困惑,因为您需要通过在此端运行的匿名块对远程数据库执行dbms_sql次调用; 您需要在本地数据库上执行dbms_sql调用才能远程运行匿名块。

目前我已在远程端使用r_为变量等添加前缀,并在本地末端使用l_作为前缀。我不确定是否足以让事情变得清晰,但这只是一个开始。

让我们使用您尝试的版本,使用带有简单包调用的本地CLOB而不是真正的DDL:

declare
  l_stmt clob;
  l_c pls_integer;
  l_rc pls_integer;
begin
  l_stmt := 'begin dbms_stats.gather_schema_stats(user); end;';

  l_c := dbms_sql.open_cursor@dblink;
  dbms_sql.parse@dblink(l_c, l_stmt, dbms_sql.native);
  l_rc := dbms_sql.execute@dblink(l_c);
  dbms_sql.close_cursor@dblink(l_c);
end;
/

正如预期的那样:

ORA-06550: line 9, column 31:
PLS-00564: lob arguments are not permitted in calls to remote server

正如评论中所建议的,也许我可以将CLOB拆分成块并重新组装。如果我实际上是在远程服务器上,我可以做这样的事情,现在手动切断我的陈述:

var b1 varchar2(20);
var b2 varchar2(20);
var b3 varchar2(20);

exec :b1 := 'begin dbms_stats.gat';
exec :b2 := 'her_schema_stats(use';
exec :b3 := 'r); end;';

declare
  r_stmt clob := :b1 || :b2 || :b3;
  r_c pls_integer;
  r_rc pls_integer;
begin
  r_c := dbms_sql.open_cursor;
  dbms_sql.parse(r_c, r_stmt, dbms_sql.native);
  r_rc := dbms_sql.execute(r_c);
  dbms_sql.close_cursor(r_c);
end;
/

PL/SQL procedure successfully completed.

所以现在我想远程运行同样的东西。该匿名块成为本地解析的语句:

declare
  l_stmt clob;
  l_remote_stmt varchar2(32767);
  l_c integer;
  l_rc integer;
begin
  l_stmt := 'begin dbms_stats.gather_schema_stats(user); end;';

  l_remote_stmt := q'[
declare
  r_stmt clob := :b1 || :b2 || :b3;
  r_c pls_integer;
  r_rc pls_integer;
  pragma autonomous_transaction;
begin
  r_c := dbms_sql.open_cursor;
  dbms_sql.parse(r_c, r_stmt, dbms_sql.native);
  r_rc := dbms_sql.execute(r_c);
  dbms_sql.close_cursor(r_c);
end;
]';

  l_c := dbms_sql.open_cursor@dblink;
  dbms_sql.parse@dblink(l_c, l_remote_stmt, dbms_sql.native);
  -- bind the chunks of CLOB
  dbms_sql.bind_variable@dblink(l_c, 'B1', dbms_lob.substr(l_stmt, 20, 1));
  dbms_sql.bind_variable@dblink(l_c, 'B2', dbms_lob.substr(l_stmt, 20, 21));
  dbms_sql.bind_variable@dblink(l_c, 'B3', dbms_lob.substr(l_stmt, 20, 41));
  -- execute the anonymous block (which makes its own dbms_sql calls) remotely
  l_rc := dbms_sql.execute@dblink(l_c);
  dbms_sql.close_cursor@dblink(l_c);
end;
/

PL/SQL procedure successfully completed.

我使用了20个字符块来模仿我之前手动完成的操作。对于真正的DDL,您可以这样做:

  dbms_sql.bind_variable@dblink(l_c, 'B1', dbms_lob.substr(l_stmt, 32767, 1));
  dbms_sql.bind_variable@dblink(l_c, 'B2', dbms_lob.substr(l_stmt, 32767, 32768));
  dbms_sql.bind_variable@dblink(l_c, 'B3', dbms_lob.substr(l_stmt, 32767, 65535));

...可能计算偏移而不是手动输入。对于大型CLOB,将更多连接的绑定变量添加到远程语句,并匹配bind_variable次调用。

此版本确定CLOB需要拆分的块数,并创建适当的远程语句,并以相同的方式进行绑定:

set serveroutput on
declare
  l_stmt clob;
  l_remote_stmt varchar2(32767);
  l_remote_args varchar2(4000);
  l_c integer;
  l_rc integer;
begin
  -- create valid dummy statement > 32k via comments
  l_stmt := 'begin dbms_stats.gather_schema_stats(user); /* ';
  -- random garbage as comments
  for i in 1..20 loop
    l_stmt := l_stmt || dbms_random.string('a', 4000);
  end loop;
  l_stmt := l_stmt || ' */ end;';
  -- for debug only
  dbms_output.put_line('l_stmt size: ' || length(l_stmt)
    || ' chunks ' || ceil(length(l_stmt)/32767));

  -- build remote bind list dynamically
  l_remote_args := ' to_clob(:b1)';
  for i in 2..ceil(length(l_stmt)/32767) loop
    l_remote_args := l_remote_args || ' || to_clob(:b' || i || ')';
  end loop;
  -- for debug only
  dbms_output.put_line('l_remote_args: ' || l_remote_args);

  -- build remote statement, including constructed list of binds
  l_remote_stmt := q'[
declare
  r_stmt clob := ]' || l_remote_args || q'[;
  r_c pls_integer;
  r_rc pls_integer;
  pragma autonomous_transaction;
begin
  r_c := dbms_sql.open_cursor;
  dbms_sql.parse(r_c, r_stmt, dbms_sql.native);
  r_rc := dbms_sql.execute(r_c);
  dbms_sql.close_cursor(r_c);
end;
]';

  l_c := dbms_sql.open_cursor@dblink3;

  -- parse full remote statement including generated binds  
  dbms_sql.parse@dblink3(l_c, l_remote_stmt, dbms_sql.native);

  -- bind variables using chunks of original CLOB
  for i in 1..ceil(length(l_stmt)/32767) loop
    dbms_sql.bind_variable@dblink3(l_c, 'B' || i,
      dbms_lob.substr(l_stmt, 32767, ((i-1) * 32767) + 1));
    l_remote_args := l_remote_args || ' || :b' || i;
  end loop;
  l_rc := dbms_sql.execute@dblink3(l_c);

  dbms_sql.close_cursor@dblink3(l_c);
end;
/

l_stmt size: 80055 chunks 3
l_remote_args:  to_clob(:b1) || to_clob(:b2) || to_clob(:b3)

PL/SQL procedure successfully completed.

答案 1 :(得分:1)

看看这个,它可能会有所帮助 https://github.com/HowdPrescott/Lob_Over_DBLink

感谢@Phonolog,明白了。 而且你是对的,它实际上并没有回答这个问题,而是指向可能有所帮助的方向。

那怎么样。
你能否将dbms_sql解析重载与dbms_sql.varchar2a类型(varchar2表(32767))一起使用?
按照这个例子:

DECLARE  
  vi_handler        INTEGER;  
  vi_numberOfRows   INTEGER;  
  ddl_statement     DBMS_SQL.varchar2a@some_dblink;  
BEGIN  
  ddl_statement(1) := 'create or replace function get_dual return varchar2 as ';  
  ddl_statement(2) := 'v_dual varchar2(1);';  
  ddl_statement(3) := 'begin ';  
  ddl_statement(4) := '    select * into v_dual from dual;';  
  ddl_statement(5) := '    return v_dual;';  
  ddl_statement(6) := 'end get_dual;';  
  vi_handler := DBMS_SQL.open_cursor@some_dblink;  
  DBMS_SQL.parse@some_dblink (vi_handler, ddl_statement, 1, ddl_statement.count, true, DBMS_SQL.native);  
  vi_numberOfRows := DBMS_SQL.execute@some_dblink (vi_handler);  
  DBMS_SQL.close_cursor@some_dblink (vi_handler);  
END; 

答案 2 :(得分:0)

我通过将DDL写入文件,然后使用sqlplus包含该文件来完成此操作。以下是我向所有Oracle实例分发管理包的示例:

  

c:\ temp \ distribute.sql保存我的DDL

sqlplus /nolog
connect /@db1 as sysdba
@c:\temp\distribute.sql
connect /@db2 as sysdba
@c:\temp\distribute.sql
...
connect /@dbN as sysdba
@c:\temp\distribute.sql

答案 3 :(得分:0)

谢谢你的帮助。我发送了一个负责执行ddls的包 通过dblink远程服务器(该软件包适合varchar2),然后我从1.db上通过dblink从all_source获取软件包sourec。它正在工作,因为在视图中记录了一行包。