在PL / SQL函数中的EXECUTE IMMEDIATE中使用UDT变量

时间:2017-03-20 21:16:52

标签: sql oracle plsql execute-immediate

我正在使用Oracle 11g在PL / SQL上构建一个函数。

我正在尝试在EXECUTE IMMEDIATE语句中使用表变量,但它无法正常工作,如您所见:

ERROR at line 1:
ORA-00904: "CENTER_OBJECTS": invalid identifier
ORA-06512: at "HIGIIA.KNN_JOIN", line 18

我正在使用的代码是......

首先,类型定义

CREATE TYPE join_t IS OBJECT (
   inn                          char(40),
   out                        char(40)
);
/


CREATE TYPE join_jt IS TABLE OF join_t;
/

CREATE TYPE blob_t IS OBJECT (
   id           CHAR(40),
   fv           BLOB
);
/

CREATE TYPE blob_tt IS TABLE OF blob_t;
/

功能是:

create or replace FUNCTION knn_join (tab_inn IN varchar2, tab_out IN varchar2, blob_col1 IN varchar2, blob_col2 IN varchar2, dist_alg in VARCHAR2, kv in NUMBER ) RETURN join_jt
IS
var_fv BLOB;
var_id CHAR(40);
center_objects blob_tt := blob_tt();
retval join_jt := join_jt ();
join_table join_jt := join_jt();
sql_stmt1 varchar2(400);
sql_stmt2 varchar2(400);
BEGIN
    sql_stmt1 := 'SELECT blob_t(ROWIDTOCHAR(rowid),' || blob_col1 || ') FROM ' || tab_out;
    sql_stmt2 := 'SELECT join_t(ROWIDTOCHAR(r.rowid), center_objects(idx).id) FROM ' || tab_inn || ' r  WHERE ' || dist_alg || '_knn(r.' || blob_col2 || ',  center_objects(idx).' ||   blob_col1 || ')<=' || kv;
    dbms_output.put_line(sql_stmt2);    
    EXECUTE IMMEDIATE sql_stmt1 BULK COLLECT INTO center_objects;
    for idx in center_objects.first()..center_objects.last()
                loop
                --SELECT join_t(ROWIDTOCHAR(r.rowid), center_objects(idx).id) BULK COLLECT INTO join_table FROM londonfv r WHERE manhattan_knn(r.fv, center_objects(idx).fv) <=5;
                EXECUTE IMMEDIATE sql_stmt2 BULK COLLECT INTO join_table;   
            for idx2 in join_table.first()..join_table.last()
                   loop
                            retval.extend();
                        retval(retval.count()) := join_table(idx2);
                       end loop;
            end loop;
RETURN retval;
END;
/

运行该功能:

select * from TABLE(knn_join('london','cophirfv','fv','fv','manhattan',5)); 

我正在尝试使用运行语句'SELECT join_t(ROWIDTOCHAR(r.rowid),center_objects(idx).id)BULK COLLECT INTO join_table FROM london r WHERE manhattan_knn(r.fv,center_objects(idx).fv) &lt; = 5'使用EXECUTE IMMEDIATE,但它不起作用,因为我在其中使用了一个变量。

有人可以帮我一把吗?

提前致谢!

1 个答案:

答案 0 :(得分:1)

您无法在动态SQL语句中引用本地PL / SQL变量,因为它超出了动态调用使用的SQL上下文中的范围。你可以替换你的第一个电话:

SELECT join_t(ROWIDTOCHAR(r.rowid), center_objects(idx).id) FROM ' ...
带有绑定变量的

SELECT join_t(ROWIDTOCHAR(r.rowid), :id FROM ' ...
EXECUTE IMMEDIATE ... USING center_objects(idx).id ...

但是当对象属性也是可变的时,你也无法做到:

... ',  center_objects(idx).' ||   blob_col1 || ')<='...

尽管 - 至少在您已经显示的示例中 - 唯一可用的对象属性名称是fv,无论传递给函数的表列名称如何 - 因此可以进行硬编码;因此可以使用绑定变量:

... ',  :fv)<='...
EXECUTE IMMEDIATE ... USING center_objects(idx).id, center_objects(idx).fv ...

并且kv值也应该是绑定变量,因此您最终会得到:

create or replace FUNCTION knn_join (tab_inn IN varchar2, tab_out IN varchar2,
  blob_col1 IN varchar2, blob_col2 IN varchar2, dist_alg in VARCHAR2, kv in NUMBER )
RETURN join_jt
IS
  center_objects blob_tt := blob_tt();
  retval join_jt := join_jt ();
  join_table join_jt := join_jt();
  sql_stmt1 varchar2(400);
  sql_stmt2 varchar2(400);
BEGIN
  sql_stmt1 := 'SELECT blob_t(ROWIDTOCHAR(rowid),' || blob_col1 || ') FROM ' || tab_out;
  sql_stmt2 := 'SELECT join_t(ROWIDTOCHAR(r.rowid), :id) FROM ' || tab_inn || ' r  WHERE '
    || dist_alg || '_knn(r.' || blob_col2 || ',  :fv)<= :kv';
  dbms_output.put_line(sql_stmt1);    
  dbms_output.put_line(sql_stmt2);    
  EXECUTE IMMEDIATE sql_stmt1 BULK COLLECT INTO center_objects;
  for idx in center_objects.first()..center_objects.last()
  loop
    EXECUTE IMMEDIATE sql_stmt2 BULK COLLECT INTO join_table
    USING center_objects(idx).id, center_objects(idx).fv, kv;   
    for idx2 in join_table.first()..join_table.last()
    loop
      retval.extend();
      retval(retval.count()) := join_table(idx2);
    end loop;
  end loop;
  RETURN retval;
END;
/

据我所知,你仍然可以在动态SQL语句中进行连接,并消除循环以及对中间center_objectsjoin_table集合的需求:

create or replace FUNCTION knn_join (tab_inn IN varchar2, tab_out IN varchar2,
  blob_col1 IN varchar2, blob_col2 IN varchar2, dist_alg in VARCHAR2, kv in NUMBER )
RETURN join_jt
IS
  retval join_jt;
  sql_stmt varchar2(400);
BEGIN
  sql_stmt := 'SELECT join_t(ROWIDTOCHAR(tinn.rowid), ROWIDTOCHAR(tout.rowid))'
    || ' FROM ' || tab_inn || ' tinn JOIN ' || tab_out || ' tout'
    || ' ON ' || dist_alg || '_knn(tinn.fv, tout.fv) <= :kv';

  dbms_output.put_line(sql_stmt);
  EXECUTE IMMEDIATE sql_stmt BULK COLLECT INTO retval USING kv;
  RETURN retval;
END;
/

当你打电话给你时,显示:

select * from TABLE(knn_join('london','cophirfv','fv','fv','manhattan',5)); 

这相当于硬编码:

SELECT join_t(ROWIDTOCHAR(tinn.rowid), ROWIDTOCHAR(tout.rowid))
FROM london tinn
JOIN cophirfv tout
ON manhattan_knn(tinn.fv, tout.fv) <= 5

...所以我想您可以验证该硬编码版本是否能为您提供您期望的结果。 (当然,在问题中添加样本数据和预期结果会有所帮助。)

这种连接条件可能很昂贵,具体取决于函数的作用,每个表中的行数如何(因为每个表中的每一行都必须与另一个表中的每一行进行比较),无论你是否真的有其他过滤器等等。循环版本会更糟糕。没有更多的信息,无论如何都没有太多的事情要做。

另外,对象属性使用varchar2代替char会更正常;这也是the rowidtochar() function返回的数据类型。