سؤال

 TYPE ref_cur IS REF CURSOR;
 ref_cur_name    ref_cur;
 TYPE tmptbl IS  TABLE OF ref_cur_name%ROWTYPE;
 n_tmptbl        tmptbl;

I tried this code but can't get it thru compiler . Is there a way to store the results of ref cursor into a table ?

NOTE-I need a table because i need to access the column of ref cursor . Using dbms_sql to access records of ref cursor is a bit tough for me .

UPDATE :

/* Formatted on 8/1/2013 4:09:08 PM (QP5 v5.115.810.9015) */
CREATE OR REPLACE PROCEDURE proc_deduplicate (p_tblname   IN VARCHAR2,
                                               p_cname     IN VARCHAR2,
                                               p_cvalue    IN VARCHAR2)
IS
   v_cnt          NUMBER;

   TYPE ref_cur IS REF CURSOR;
   ref_cur_name   ref_cur;


   v_str1         VARCHAR2 (4000);
   v_str2         VARCHAR2 (4000);
   v_str3         VARCHAR2 (4000);
BEGIN
   v_str1 :=
         'SELECT ROWID v_rowid FROM '
      || p_tblname
      || ' WHERE '
      || p_cname
      || '='''
      || p_cvalue
      || '''';


   BEGIN
      v_str2 :=
            'SELECT   COUNT ( * )

         FROM  '
         || p_tblname
         || ' WHERE  '
         || p_cname
         || ' = '''
         || p_cvalue
         || '''';
      logerrors ('proc_deduplicate',
                 'count exception',
                 SQLCODE,
                 v_str2 || SQLERRM,
                 'e');

      EXECUTE IMMEDIATE v_str2 INTO   v_cnt;
   EXCEPTION
      WHEN OTHERS
      THEN
         logerrors ('proc_deduplicate',
                    'count exception',
                    SQLCODE,
                    SQLERRM,
                    'e');
   END;

   IF v_cnt IS NOT NULL
   THEN
     OPEN ref_cur_name FOR v_str1;

      LOOP
         IF v_cnt = 1
         THEN
            EXIT;
         ELSE
            BEGIN
               v_str3 :=
                     'DELETE FROM '
                  || p_tblname
                  || ' WHERE   ROWID = v_rowid '; 
 -- THIS IS THE PROBLEM . i just created an alias above for rowid keyword but i guess, DBMS sql will have to be used  after all . 


               EXECUTE IMMEDIATE v_str3;
            EXCEPTION
               WHEN OTHERS
               THEN
                  logerrors (
                     '                                                            proc_deduplicate
      ',
                     '                                                            delete exception
      ',
                     SQLCODE,
                     SQLERRM,
                     '                                                            e
      '
                  );
            END;
         END IF;

         v_cnt := v_cnt - 1;
      END LOOP;
   END IF;
EXCEPTION
   WHEN OTHERS
   THEN
      logerrors (
         '                                    proc_deduplicate',
         '                                    final exception
      ',
         SQLCODE,
         SQLERRM,
         '                                    e'
      );
END;
/
هل كانت مفيدة؟

المحلول 2

As far as I understand what you're doing, you just need to parameterise the delete:

...
   v_str3         VARCHAR2 (4000);
   v_rowid        ROWID;
BEGIN
...
     OPEN ref_cur_name FOR v_str1;

      LOOP
         FETCH ref_cur_name INTO v_rowid;
         EXIT WHEN ref_cur_name%NOTFOUND;
         IF v_cnt = 1
         THEN
            EXIT;
         ELSE
            BEGIN
               v_str3 :=
                     'DELETE FROM '
                  || p_tblname
                  || ' WHERE   ROWID = :v_rowid '; 

               EXECUTE IMMEDIATE v_str3 USING v_rowid;
...

You need to fetch the ref_cur_name into a variable, which needs to be declared obviously, and then use that as a bind variable value in the delete.

You should do the same thing with the p_cvalue references in the other dynamic SQL too. You could probably make this much simpler, with a single delete and no explicit count, in a single dynamic statement:

CREATE OR REPLACE PROCEDURE proc_deduplicate (p_tblname   IN VARCHAR2,
                                               p_cname     IN VARCHAR2,
                                               p_cvalue    IN VARCHAR2)
IS
BEGIN
   execute immediate 'delete from ' || p_tblname
      || ' where ' || p_cname || ' = :cvalue'
      || ' and rowid != (select min(rowid) from ' || p_tblname
          || ' where ' || p_cname || ' = :cvalue)'
      using p_cvalue, p_cvalue;
END proc_deduplicate;
/

SQL Fiddle.

If you wanted to know or report how many rows were deleted, you could refer to SQL%ROWCOUNT after the execute immediate.

نصائح أخرى

By issuing TYPE ref_cur IS REF CURSOR you are declaring a weak cursor. Weak cursors return no specified types. It means that you cannot declare a variable that is of weak_cursor%rowtype, simply because a weak cursor does not return any type.

declare
  type  t_rf  is ref cursor;
  l_rf  t_rf;
  type  t_trf is table of l_rf%rowtype;
  l_trf t_trf;
begin
  null;
end;

ORA-06550: line 4, column 27:
PLS-00320: the declaration of the type of this expression is incomplete or malformed
ORA-06550: line 4, column 3:
PL/SQL: Item ignored    

If you specify return type for your ref cursor, making it strong, your PL/SQL block will compile successfully:

 SQL> declare                            -- strong cursor
  2    type  t_rf  is ref cursor return [table_name%rowtype][structure]; 
  3    l_rf  t_rf;                                         
  4    type  t_trf is table of l_rf%rowtype;
  5    l_trf t_trf;
  6  begin
  7    null;
  8  end;
  9  /

PL/SQL procedure successfully completed

Strong ref cursor returns defined values, but weak is free to anything, totally dynamic.

But we can't define rowtype variable on weak ref cur e.g.

declare 
 refcur sys_refcursor;
 emprec refcur%rowtype; --it'll yield error

while

declare
 type empref is ref cursor returns employees%rowtype;
 empcur empref;
 emprec empcur%rowtype; --it'll work fine.

This is very useful, now we can define collection on them and many other advantage if you talk about practically.

No. You're trying to declare the type against an instance of the cursor anyway, so you'd be closer with:

TYPE tmptbl IS TABLE OF ref_cur%ROWTYPE;

but you still can't do that, you'd get PLS-00310: with %ROWTYPE attribute, 'REF_CUR' must name a table, cursor or cursor-variable.

A ref cursor is weakly-typed, so the compiler doesn't know what a record would look like. You could open the ref cursor for different results depending on the logic in the block, or a dynamic query, and the compiler would have no way to know what to expect in advance.

The PL/SQL documentation state that %rowtype can apply to an explicit cursor, a strong cursor variable, or a table or view. And this compares strong and weak cursor variables.

If you know what your query will be you can declare a record type with those fields, or against a table %rowtype if you're be querying a single table. Since you're using dbms_sql I guess you won't know that though. Maybe if you updated your question with more information about what you're actually trying to do there would be some other approaches you could try.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top