0

数値IDフィールドとXMLTypeフィールドの同じフィールドを持つ行をすべて返すカーソルがたくさんあります。これらのカーソルの1つにアクセスするたびに(各カーソルにはアクセス用の独自の関数があります)、同じパターンを実行します。

--query behind cursor is designed to no more than one row.
for rec in c_someCursor(in_searchKey => local_search_key_value) loop
    v_id := rec.ID
    v_someXMLVar := rec.XMLDataField
end loop;

if v_someXMLVar is null then
    /* A bunch of mostly-standard error handling and logging goes here */
end if;

exception
    /* all cursor access functions have the same error-handling */
end;

パターンがより明白になるにつれて、それを単一の機能に集中させることが理にかなっています。

    function fn_standardCursorAccess(in_cursor in t_xmlCursorType, in_alt in XMLType) return XMLType is
            v_XMLData XMLType;
        begin
            dbms_application_info.set_module(module_name => $$PLSQL_UNIT, action_name => 'fn_standardCursorAccess');
            loop
                fetch in_cursor
                    into v_XMLData;
                exit when in_cursor%notfound;
            end loop;
            /*some additional standard processing goes here*/
            return v_XML;
        exception
        /*standard exception handling happens here*/
    end;

私が遭遇した問題は、この関数を呼び出すことです。私は今それをこのように呼ぶ必要があります:

open v_curs for select /*blah blah blah*/ where key_field = x and /*...*/;
v_data := fn_standardCursorAccess(v_curs,alt);
close v_curs;

私がやりたいのは、次のように呼ぶことです。

open v_curs for c_getSomeData(x);
v_data := fn_standardCursorAccess(v_curs,alt);
close v_curs;

...コードへの変更の量を最小限に抑えるためです(これらのカーソルをすべて、それらに依存する関数にカットアンドペーストする必要はありません。複数の関数が同じカーソルに依存している場合は、これを新しい関数でラップする必要があります)。

残念ながら、これは機能しません。Oracleは次のようなエラーを返します。

Error: PLS-00222: no function with name 'C_GETSOMEDATA' exists in this scope

私がやろうとしていることは可能ですか?

(Oracleバージョンは10.2です)

編集: 私がしていることを説明するためのより良い方法は、明示的なカーソルへの参照を、カーソルによって返されるデータに対していくつかの一般的なルーチンを実行する関数に渡すことだと思います。明示カーソルでopen-forステートメントを使用できないようですが、明示カーソルへの参照を取得して、その参照を関数に渡すことができる他の方法はありますか?たぶん私がこの問題に取り組むことができる他の方法がありますか?

編集: R Van Rijnの返信に対する私の以前の返信からのコピーと貼り付け:

パッケージ仕様でカーソルを宣言し、パッケージ名で参照してみました:open v_curs for PKG.c_getSomeData(x); ...これにより、PKG.c_getSomeDataは関数または配列である必要があるという新しいエラーが発生します。そのように使用されます。

更新: 私はここでDBAに話しました、彼はrefカーソルが明示カーソルを指すようにすることは不可能であると言います。結局、これはできないようです。残念。:(

4

5 に答える 5

1

私はあなたの要求を見つけるのが難しいことを告白します。あなたはたくさんのコードを投稿しましたが、私のコメントで示唆したように、問題を明らかにする部分ではありません。したがって、おそらく次のことはかなりオフビームです。しかし、それは興味深い問題です。

次のコードは、一般的な一般的なREF CURSORを定義し、さまざまなクエリからの特定のデータを入力して、標準化された方法で処理する方法を示しています。繰り返しになりますが、これがビジネスロジックに適合しない場合はお詫び申し上げます。その場合は、質問を編集して、私がブルマを作った場所を説明してください。

これが一般的なrefカーソルです。..。

create or replace package type_def is
    type xml_rec is record (id number, payload xmltype);
    type xml_cur is ref cursor return xml_rec;
end type_def;
/

これが標準プロセッサです

create or replace procedure print_xml_cur 
    ( p_cur in type_def.xml_cur )
is
    lrec type_def.xml_rec;
begin
    loop
        fetch p_cur into lrec;
        exit when p_cur%notfound;
        dbms_output.put_line('ID='||lrec.id);
        dbms_output.put_line('xml='||lrec.payload.getClobVal());
    end loop;
    close p_cur;
end print_xml_cur;
/

異なるデータで標準カーソルを返す2つのプロシージャ...

create or replace function get_emp_xml
    ( p_id in emp.deptno%type )
    return type_def.xml_cur
is
    return_value type_def.xml_cur;
begin
    open return_value for 
        select deptno
               , sys_xmlagg(sys_xmlgen(ename))
        from emp
        where deptno = p_id
        group by deptno;
    return return_value;
end get_emp_xml;
/

create or replace function get_dept_xml
    ( p_id in dept.deptno%type )
    return type_def.xml_cur
is
    return_value type_def.xml_cur;
begin
    open return_value for 
        select deptno
               , sys_xmlagg(sys_xmlgen(dname))
        from dept
        where deptno = p_id
        group by deptno;
    return return_value;
end get_dept_xml;
/

それでは、すべてをまとめましょう...。

SQL> set serveroutput on size unlimited
SQL>
SQL> exec print_xml_cur(get_emp_xml(40))
ID=40
xml=<?xml
version="1.0"?>
<ROWSET>
<ENAME>GADGET</ENAME>
<ENAME>KISHORE</ENAME>
</ROWSET>


PL/SQL procedure successfully completed.

SQL> exec print_xml_cur(get_dept_xml(20))
ID=20
xml=<?xml version="1.0"?>
<ROWSET>
<DNAME>RESEARCH</DNAME>
</ROWSET>


PL/SQL procedure successfully completed.

SQL>
于 2010-01-23T00:41:19.483 に答える
1

エラーPLS-00222について:

関数'c_getSomeData'として参照されている識別子が宣言されていないか、実際には別のオブジェクトを表しています(たとえば、プロシージャとして宣言されている可能性があります)。

識別子のスペルと宣言を確認してください。また、宣言がブロック構造に正しく配置されていることを確認してください

これは、実際にいくつかの値を返す関数を作成する必要があることを意味します。

于 2010-01-22T17:52:15.263 に答える
1

OK、オラクルからの短い答えは「できません!」です。

私からの簡単な答えは次のとおりです。「ええ、オラクルが私を止めようとしているように!だから、できます。実は...うん!」

では、どのようにして明示カーソルを参照で渡すことができますか?CURSOR()コンストラクトを使用して別のカーソルにネストすることによって!

例えば)

CREATE OR REPLACE package CFSDBA_APP.test_Cursor
as
   function get_cursor(ed_id number) return sys_refcursor;
end;
/

CREATE OR REPLACE package body CFSDBA_APP.test_Cursor
as
   function get_cursor(ed_id number) return sys_refcursor
   is
      test_Cur sys_refcursor;

      cursor gettest is
        select CURSOR( -pass our actual query back as a nested CURSOR type
           select ELCTRL_EVNT_ELCTRL_DISTRCT_ID, 
                  ELECTORAL_DISTRICT_ID, 
                  ELECTORAL_EVENT_ID 
           from  ELCTRL_EVNT_ELCTRL_DISTRCT
           where electoral_District_id = ed_id)
        from dual;
   begin
      open gettest;
      fetch gettest into test_Cur;
      return test_Cur;       
   end;
end;
/

では、このソリューションの問題は何ですか?漏れがあります!外側のgettestカーソルは閉じられません。これは、閉じず、クライアントは、メインカーソルではなく、選択されたネストされたカーソルへの参照のみを閉じるためです。また、親のclosignは、参照によって返されたネストされたカーソルを強制的に閉じるため、自動的に閉じることはできません。クライアントがそれを使用していない可能性があります。

したがって、ネストされたカーソルを返すには、カーソルを開いたままにしておく必要があります。

また、ユーザーがed_idの新しい値を使用してget_Cursorを再度呼び出そうとすると、パッケージ内のセッションの永続性により、カーソルハンドルがまだ使用中であり、エラーが発生することがわかります。

これで、最初に明示カーソルをチェックして閉じることで、これを修正できます。

  if gettest%isopen then
    close gettest;
  end if;
  open gettest;
  fetch gettest into test_Cur;
  return test_Cur;       

しかし、それでも-ユーザーがこれを二度と呼び出さない場合はどうなりますか?Oracleがカーソルをガベージ収集するまでの時間はどれくらいですか?そして、この構成を使用する関数を呼び出すセッションを実行しているユーザーの数は、カーソルを使用した後もカーソルを開いたままにしますか?開いているカーソルをすべて置いたままにしておくには、huuuuuugeのオーバーヘッドを頼りにするとよいでしょう。

いいえ、明示的に閉じるにはユーザーにコールバックを実行させる必要があります。そうしないと、データベースが詰まってしまいます。ただし、これを行うには、両方の関数がアクセスできるように明示カーソルのスコープを変更する必要があります。したがって、関数スコープではなく、パッケージスコープで作成する必要があります。

CREATE OR REPLACE package CFSDBA_APP.test_Cursor
as
   function get_cursor(ed_id number) return sys_refcursor;
   function close_cursor return sys_refcursor;
end;
/

CREATE OR REPLACE package body CFSDBA_APP.test_Cursor
as

   cursor l_gettest(p_ed_id in number) is
        select CURSOR(
         select ELCTRL_EVNT_ELCTRL_DISTRCT_ID, ELECTORAL_DISTRICT_ID, ELECTORAL_EVENT_ID 
         from  ELCTRL_EVNT_ELCTRL_DISTRCT
         where electoral_District_id = p_ed_id)
        from dual;


   function get_cursor(ed_id number) return sys_refcursor
   is
      l_get_Cursor sys_refcursor;
   begin
      open l_gettest (ed_id);
      fetch l_gettest into l_get_Cursor;
      return l_get_cursor;       
   end;

   function close_cursor return sys_refcursor
   is
   begin
      if l_gettest%isopen then
         close l_gettest;
      end if;
      return pkg_common.generic_success_cursor;
   end;      

end;
/

OK、リークを塞いだ。ハード解析の代わりにネットワークラウンドトリップが必要になることを除いて、...ああ待ってください-また、このレベルで宣言された明示カーソルにバインド変数を埋め込むことを除いて、おそらくそれ自体のスコープの問題が発生します。これが私たちの理由でしたそもそもこれをやりたかったのです!

ああ、そしてセッションプーリング環境では、2人のユーザーがお互いのカーソルを踏むことができますか?セッションをプールに戻す前にopen-fetch-closeを実行することにあまり注意を払っていない場合、非常に興味深い(そしてデバッグが不可能な)結果が生じる可能性があります。

そして、クライアントコードのメンテナがこれに非常に熱心であるとどれだけ信頼しますか?うん、私も。

つまり、簡単な答えは次のとおりです。はい、オラクルができないと言っているにもかかわらず、少しこっそりとそれを行うことができます。

より良い答えは次のとおりです。しかし、しないでください!余分なラウンドトリップと、メモリリークやデータの問題を引き起こすクライアントコードエラーの可能性があるため、これは非常に恐ろしい提案です。

于 2011-02-09T17:04:33.137 に答える
1

このテストスクリプトと出力は、あなたがやろうとしていることを表していますか?open v_curs for c_getSomeData(x);カーソル変数=を関数からの出力に設定する代わりに。

私たちのテストデータ:

set serveroutput on

--create demo table
drop table company;
create table company 
(
 id number not null,
 name varchar2(40)
);

insert into company (id, name) values (1, 'Test 1 Company');
insert into company (id, name) values (2, 'Test 2 Company');
insert into company (id, name) values (3, 'Test 3 Company');

commit;

パッケージを作成する

create or replace package test_pkg as

  type cursor_type is ref cursor;

  function c_getSomeData(v_companyID number) return cursor_type;

end test_pkg;
/

create or replace package body test_pkg as

  function c_getSomeData(v_companyID number) return cursor_type
  is 
    v_cursor cursor_type;
  begin

    open v_cursor for
    select id,
           name
      from company
     where id = v_companyID;

    return v_cursor;
  end c_getSomeData;

end test_pkg;
/

手順を実行する

declare
  c test_pkg.cursor_type;
  v_id company.id%type;
  v_name company.name%type;
begin
  c := test_pkg.c_getSomeData(1);

  loop 
    fetch c
    into  v_id, v_name;
    exit when c%notfound;
    dbms_output.put_line(v_id || ' | ' || v_name);
  end loop;

  close c;

end;
/

1 | Test 1 Company

PL/SQL procedure successfully completed.
于 2010-01-22T22:07:28.307 に答える
0

私がやりたかったこと(open-forステートメントに既存の明示カーソルを参照させる)は、Oracleでは許可されていないようです。:(

于 2010-01-25T16:07:34.327 に答える