开发者

Getting index of element in PL/SQL collection

Is there a built-in function to determine the (first) index of an element in a PL/SQL collection?

Something like

DECLARE
  TYPE t_test IS TABLE OF VARCHAR2(1);
  v_test t_test;
BEGIN
  v_test := NEW t_test('A', 'B', 'A');
  dbms_output.put_line( 'A: ' || get_index( v_test, 'A' ) );
  dbms_output开发者_运维技巧.put_line( 'B: ' || get_index( v_test, 'B' ) );
  dbms_output.put_line( 'C: ' || get_index( v_test, 'C' ) );
END;

A: 1
B: 2
C: 

I can use Associative Arrays, Nested Tables or Varrays, whatever necessary. If the same element exists more than once, then the index of the first occurrence is sufficient.


Otherwise I'd have to do something like

CREATE FUNCTION get_index ( in_test IN t_test, in_value IN VARCHAR2 )
  RETURN PLS_INTEGER
AS
  i PLS_INTEGER;
BEGIN

i := in_test.FIRST;
  WHILE( i IS NOT NULL ) LOOP
    IF( in_test(i) = in_value ) THEN
      RETURN i;
    END IF;

    i := in_test.NEXT(i);
  END LOOP;

  RETURN NULL;

END get_index;


Not sure, if this really helps, or if you think it is more elegant:

create type t_test as table of varchar2(1);
/

DECLARE
--TYPE t_test IS TABLE OF VARCHAR2(1);
  v_test t_test;

  function get_index(q in t_test, c in varchar2) return number is
    ind number;
  begin
    select min(rn) into ind from (
      select column_value cv, rownum rn
       from table(q) 
    )
    where cv = c;

    return ind;
  end get_index;

BEGIN
  v_test := NEW t_test('A', 'B', 'A');

  dbms_output.put_line( 'A: ' || get_index( v_test, 'A' ) );
  dbms_output.put_line( 'B: ' || get_index( v_test, 'B' ) );
  dbms_output.put_line( 'C: ' || get_index( v_test, 'C' ) );
END;
/

show errors

drop type t_test;


I don't think there is a built-in function that searches a collection. However, if you know you will need to search a collection a lot, you could build an index. Adding element to the collection will be a bit more expensive, but looking for an element will be an O(1) operation (instead of O(n) for a brute force search). For example, you could use something like this:

SQL> DECLARE
  2     TYPE t_test IS TABLE OF VARCHAR2(1);
  3     TYPE t_test_r IS TABLE OF NUMBER INDEX BY VARCHAR2(1);
  4  
  5     v_test t_test;
  6     v_test_r t_test_r;
  7  
  8     FUNCTION get_index(p_test_r t_test_r,
  9                        p_element VARCHAR2) RETURN NUMBER IS
 10     BEGIN
 11        RETURN p_test_r(p_element);
 12     EXCEPTION
 13        WHEN no_data_found THEN
 14           RETURN NULL;
 15     END get_index;
 16  
 17     PROCEDURE add_element(p_test IN OUT t_test,
 18                           p_test_r IN OUT t_test_r,
 19                           p_element VARCHAR2) IS
 20     BEGIN
 21        p_test.extend;
 22        p_test(p_test.count) := p_element;
 23        p_test_r(p_element) := least(p_test.count,
 24                                     nvl(get_index(p_test_r, p_element),
 25                                         p_test.count));
 26     END add_element;
 27  BEGIN
 28     v_test := NEW t_test();
 29     add_element(v_test, v_test_r, 'A');
 30     add_element(v_test, v_test_r, 'B');
 31     add_element(v_test, v_test_r, 'A');
 32     dbms_output.put_line('A: ' || get_index(v_test_r, 'A'));
 33     dbms_output.put_line('B: ' || get_index(v_test_r, 'B'));
 34     dbms_output.put_line('C: ' || get_index(v_test_r, 'C'));
 35  END;
 36  /

A: 1
B: 2
C: 

PL/SQL procedure successfully completed

You could also define a record that contains both arrays and all functions/procedures to interact with arrays would use this record type.


When in doubt, consult the documentation ;) (here)

DECLARE
  TYPE aa_type_int IS TABLE OF INTEGER INDEX BY PLS_INTEGER;
  aa_int  aa_type_int;

  PROCEDURE print_first_and_last IS
  BEGIN
    DBMS_OUTPUT.PUT_LINE('FIRST = ' || aa_int.FIRST);
    DBMS_OUTPUT.PUT_LINE('LAST = ' || aa_int.LAST);
  END print_first_and_last;

BEGIN
  aa_int(1) := 3;
  aa_int(2) := 6;
  aa_int(3) := 9;
  aa_int(4) := 12;

  DBMS_OUTPUT.PUT_LINE('Before deletions:');
  print_first_and_last;

  aa_int.DELETE(1);
  aa_int.DELETE(4);

  DBMS_OUTPUT.PUT_LINE('After deletions:');
  print_first_and_last;
END;
/

Result:

Before deletions:
FIRST = 1
LAST = 4
After deletions:
FIRST = 2
LAST = 3
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜