2010-01-22 2 views
0

У меня есть много курсоров, которые возвращают строки с одинаковыми полями: поле числового идентификатора и поле XMLType. Каждый раз, когда я достигаю один из этих курсоров (каждый курсор теперь получил свою собственную функцию для доступа), я прохожу по той же схеме:Вопрос проектирования и реорганизации курсора

--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)

EDIT: Я думаю, что лучший способ описать то, что я делаю, это передать ссылку на явный курсор на функцию, которая будет выполнять некоторые общие процедуры на данные, возвращаемые курсором. Похоже, что я не могу использовать оператор open-for с курсором explcit, есть ли другой способ получить ссылку на явный курсор, чтобы я мог передать эту ссылку на функцию? Может быть, есть еще один способ, которым я мог бы подойти к этой проблеме?

EDIT: Копирование и вставка из моего предыдущего ответа на ответ R ван Рейн в:

Я попытался объявить курсор в спецификации пакета, и ссылаться на него с именем пакета: открытые v_curs для PKG.c_getSomeData (x); ... Это дает мне новую ошибку, говоря, что PKG.c_getSomeData должен быть функцией или массивом, которые будут использоваться таким образом.

UPDATE: Я говорил с нашим DBA здесь, он говорит, что это не возможно, чтобы иметь реф точку курсора в явный курсор. Похоже, я не могу этого сделать в конце концов. Облом. :(

+0

Не могли бы вы разместить объявление 'c_getSomeData', которое используете? –

+0

@Dougman: Есть много разных c_getSomeData. Все они следуют шаблону: 'Выберите T.ID, xmlelement (« SomeElt », ...) из T;' для многих разных 'T', конечно. – FrustratedWithFormsDesigner

+0

Проблема заключается в том, что ошибка заключается в том, что C_GETSOMEDATA не существует в пределах области действия. Вы опубликовали почти все, кроме кода, имеющего отношение к ошибке *. Это, вероятно, архитектурная проблема. Но без соответствующей информации нам трудно помочь. Все, что мы можем сказать наверняка, - это не внутренняя структура 'c_getSomeData': это то, как и где они объявлены. – APC

ответ

0

Это что то, что я хотел сделать (иметь ссылку на ссылку open-for существующий явный курсор), просто не допускается в Oracle. :(

1

относительно ошибки PLS-00222:.

Идентификатор которую ссылается как функция «c_getSomeData» не была объявлена ​​или фактически представляет другой объект (например, это может быть объявлена ​​как процедура)

Проверьте написание и объявление идентификатора. Кроме того, убедитесь, что объявление правильно размещено в блоке структуре

Это означает, что вы должны создать функцию, которая на самом деле возвращает некоторое значение (ы).

+0

c_getSomeData - это курсор, и объявление в порядке. Я попытался объявить курсор в спецификации пакета и ссылаться на него с именем пакета: 'open v_curs for PKG.c_getSomeData (x); ...' Это дает мне новую ошибку, говоря, что PKG.c_getSomeData должен быть функцией или массивом, который будет использоваться таким образом. Есть ли другой способ централизовать мой код обработки курсора с минимальным рефакторингом? – FrustratedWithFormsDesigner

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; 
/

Run Наша процедура

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. 
1

Признаюсь найти ваши требования мелочью трудно божественному. Вы отправили много кода, но, как я предложил в своем комментарии, не те части, которые освещали бы проблему. Так что, возможно, следующее вне балки. Но это интересный вопрос.

Следующий код показывает, как мы можем определить общий, генетический REF CURSOR, заполнить его конкретными данными из разных запросов, а затем обработать их стандартизованным способом. Опять же, я прошу прощения, если это не соответствует вашей бизнес-логике; если это так, отредактируйте свой вопрос, чтобы объяснить, где я сделал bloomer.

Вот общий курсор 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; 
/

и вот процессор standatd

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; 
/

две процедуры, которые возвращают стандартный курсор с различными данными ....

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> 
+0

Это более или менее то, что я сделал.Мой реальный вопрос (хотя я могу видеть, что путаница, возможно, не была такой ясной) была, если бы было возможно, чтобы мое заявление «открыто для» ссылалось на существующие явные курсоры, а не на то, вставьте инструкцию 'select' курсора в оператор' open-for'. Возможность сделать это позволит мне значительно сократить время, но DBA здесь подтвердил, что Oracle не позволяет использовать явные курсоры таким образом, поэтому то, что я пытаюсь сделать, просто невозможно. – FrustratedWithFormsDesigner

1

ОК, так что короткий ответ от Oracle является: «не может быть сделано!»

Короткий ответ от меня: «Да - как Oracle собирается остановить меня Так что да, вы можете!. ... но вам нужно быть подлым ... о да, и есть «но» или два ... на самом деле ... тьфу! »

Итак, как вы можете передать свой явный курсор ссылаться? к гнездованию его в другой курсор с помощью курсора() построить!

например)

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 никогда не закрывается, потому что мы его не закрываем, и клиент будет закрывать ссылку только на вложенный курсор, который был выбран для них, а не на главный курсор. И мы не можем закрыть его автоматически, потому что закрытие родителя принудительно закрывает вложенный курсор, который вы вернули по ссылке, - и вполне вероятно, что клиент не использовал его.

Поэтому мы должны оставить курсор открытым, чтобы вернуть вложенный курсор.

И если пользователь попытался снова вызвать get_Cursor с новым значением ed_id, они обнаружат, что постоянство сеанса в пакете означало, что дескриптор курсора все еще используется и возникает ошибка.

Теперь мы могли бы исправить, первой проверки и закрытия явного курсора:

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

Но еще - что, если пользователь никогда не называет это снова? Как долго до появления мусора Oracle собирает курсор? И сколько пользователей работает, сколько сеансов называет, сколько функций, которые используют эту конструкцию, будут оставлять курсоры открытыми после их завершения? Лучше рассчитывайте на huuuuuge над головой, чтобы оставить все эти открытые курсоры!

Нет, вам нужно, чтобы пользователи выполняли обратный вызов, чтобы явно закрыть его, или вы будете забивать базу данных. Но делать это потребует изменения сферы явного курсора, так что обе функции могут получить доступ к нему: Таким образом, мы должны сделать это в пакете объема, а не область видимости функции

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, заткнул утечку. За исключением того, что это стоило нам сети в оба конца вместо жесткого разбора, ... oh wait - и кроме того, что вложение переменной привязки в явный курсор, объявленный на этом уровне, вероятно, вызовет собственные проблемы, из-за чего мы хотел сделать это в первую очередь!

Ой, и в среде объединения сеансов могут два пользователя наступать друг на друга курсорами? ЕСЛИ они не очень осторожны в том, чтобы делать open-fetch-close, прежде чем возвращать сессию в пул - мы можем завершить некоторые действительно интересные (и невозможно отладить) результаты!

И сколько вы доверяете хранителям клиентского кода, чтобы быть экстравертичными на этом? Да, я тоже.

Итак, короткий ответ: Да, с небольшим подтекстом это можно сделать, несмотря на то, что Oracle говорит, что это невозможно.

Лучший ответ: Но, пожалуйста, не надо! Дополнительное путешествие в оба конца и потенциал утечки памяти и ошибок кода клиента, вызывающих проблемы с данными, делают это очень страшным предложением.

Смежные вопросы