Oracle PL / SQL - Como criar uma variável de matriz simples?

128

Eu gostaria de criar uma variável de matriz na memória que possa ser usada no meu código PL / SQL. Não consigo encontrar nenhuma coleção no Oracle PL / SQL que use memória pura, todas elas parecem estar associadas a tabelas. Eu estou olhando para fazer algo parecido com isto no meu PL / SQL (sintaxe C #):

string[] arrayvalues = new string[3] {"Matt", "Joanne", "Robert"};

Edição: Oracle: 9i

contactmatt
fonte
2
Veja: Coleções e registros PL / SQL
user272735
1
A referência "tabela" tende a ser uma ressaca da antiga nomeação de tabelas PL / SQL. VARRAYs, matrizes associativas e tabelas aninhadas declaradas são todos tipos de matrizes na memória.
Ollie

Respostas:

244

Você pode usar o VARRAY para uma matriz de tamanho fixo:

declare
   type array_t is varray(3) of varchar2(10);
   array array_t := array_t('Matt', 'Joanne', 'Robert');
begin
   for i in 1..array.count loop
       dbms_output.put_line(array(i));
   end loop;
end;

Ou TABLE para uma matriz ilimitada:

...
   type array_t is table of varchar2(10);
...

A palavra "tabela" aqui não tem nada a ver com tabelas de banco de dados, de maneira confusa. Ambos os métodos criam matrizes na memória.

Com qualquer um destes, você precisa inicializar e estender a coleção antes de adicionar elementos:

declare
   type array_t is varray(3) of varchar2(10);
   array array_t := array_t(); -- Initialise it
begin
   for i in 1..3 loop
      array.extend(); -- Extend it
      array(i) := 'x';
   end loop;
end;

O primeiro índice é 1 e não 0.

Tony Andrews
fonte
75
"confusamente" resume o Oracle
m.edmondson 17/11/2015
Eu insiro nas tabelas da mesma maneira que as matrizes? iemy_array(0) := 'some string';
Abdul
@TonyAndrews array.extend();EXTEND adiciona um slot a uma matriz limitada regular? Nesse caso, seu tamanho já é dinâmico, portanto, uma tabela (matriz ilimitada) não seria necessária.
Abdul
2
@ Abdul, não, não. Eu nunca uso VARRAYs normalmente, mas ao testar o código acima, verifiquei o que acontece se você tentar estender varray(3)4 vezes - você recebe um erro "subscrito fora do limite".
Tony Andrews
2
Gostaria de poder votar esta resposta várias vezes @TonyAndrews desde que você cobriu o array.extend(). Em todos os lugares em que eu olhei, isso não foi mostrado e foi a parte mais importante para poder adicionar mais de um item (pelo meu entendimento, ainda novo para matrizes no SQL).
Jonathan Van Dam
61

Você pode simplesmente declarar um DBMS_SQL.VARCHAR2_TABLE para manter uma matriz de comprimento variável na memória indexada por um BINARY_INTEGER:

DECLARE
   name_array dbms_sql.varchar2_table;
BEGIN
   name_array(1) := 'Tim';
   name_array(2) := 'Daisy';
   name_array(3) := 'Mike';
   name_array(4) := 'Marsha';
   --
   FOR i IN name_array.FIRST .. name_array.LAST
   LOOP
      -- Do something
   END LOOP;
END;

Você pode usar uma matriz associativa (costumava ser chamada de tabelas PL / SQL), pois elas são uma matriz na memória.

DECLARE
   TYPE employee_arraytype IS TABLE OF employee%ROWTYPE
        INDEX BY PLS_INTEGER;
   employee_array employee_arraytype;
BEGIN
   SELECT *
     BULK COLLECT INTO employee_array
     FROM employee
    WHERE department = 10;
   --
   FOR i IN employee_array.FIRST .. employee_array.LAST
   LOOP
      -- Do something
   END LOOP;
END;

A matriz associativa pode conter qualquer composição de tipos de registro.

Espero que ajude, Ollie.

Ollie
fonte
17
A condição de iteração aumenta VALUE_ERRORquando a coleção está vazia. Eu sugiro que prefiro usar FOR i IN 1 .. employee_array.COUNTneste caso
unziberla
A versão de j-Chomel ( stackoverflow.com/a/40579334/1915920 ) com base sys.odcivarchar2listabaixo tem a vantagem de que você também tem um construtor na mão, por exemplo, para inicialização função param padrão:sys.odcivarchar2list('val1','val2')
Andreas Dietrich
11

Outra solução é usar um Oracle Collection como um Hashmap:

declare 
-- create a type for your "Array" - it can be of any kind, record might be useful
  type hash_map is table of varchar2(1000) index by varchar2(30);
  my_hmap hash_map ;
-- i will be your iterator: it must be of the index's type
  i varchar2(30);
begin
  my_hmap('a') := 'apple';
  my_hmap('b') := 'box';
  my_hmap('c') := 'crow';
-- then how you use it:

  dbms_output.put_line (my_hmap('c')) ;

-- or to loop on every element - it's a "collection"
  i := my_hmap.FIRST;

  while (i is not null)  loop     
    dbms_output.put_line(my_hmap(i));      
    i := my_hmap.NEXT(i);
  end loop;

end;
J. Chomel
fonte
11

Você também pode usar um oracle defined collection

DECLARE 
  arrayvalues sys.odcivarchar2list;
BEGIN
  arrayvalues := sys.odcivarchar2list('Matt','Joanne','Robert');
  FOR x IN ( SELECT m.column_value m_value
               FROM table(arrayvalues) m )
  LOOP
    dbms_output.put_line (x.m_value||' is a good pal');
  END LOOP;
END;

Eu usaria matriz na memória. Mas com a .COUNTmelhoria sugerida pela uziberia:

DECLARE
  TYPE t_people IS TABLE OF varchar2(10) INDEX BY PLS_INTEGER;
  arrayvalues t_people;
BEGIN
  SELECT *
   BULK COLLECT INTO arrayvalues
   FROM (select 'Matt' m_value from dual union all
         select 'Joanne'       from dual union all
         select 'Robert'       from dual
    )
  ;
  --
  FOR i IN 1 .. arrayvalues.COUNT
  LOOP
    dbms_output.put_line(arrayvalues(i)||' is my friend');
  END LOOP;
END;

Outra solução seria usar um Hashmap como o @Jchomel fez aqui .

NB:

Com o Oracle 12c, você pode consultar matrizes diretamente agora !

Jika
fonte
1

Exemplos de programas da seguinte forma e fornecidos no link também https://oracle-concepts-learning.blogspot.com/

tabela plsql ou matriz associada.

        DECLARE 
            TYPE salary IS TABLE OF NUMBER INDEX BY VARCHAR2(20); 
            salary_list salary; 
            name VARCHAR2(20); 
        BEGIN 
           -- adding elements to the table 
           salary_list('Rajnish') := 62000; salary_list('Minakshi') := 75000; 
           salary_list('Martin') := 100000; salary_list('James') := 78000; 
           -- printing the table name := salary_list.FIRST; WHILE name IS NOT null 
            LOOP 
               dbms_output.put_line ('Salary of ' || name || ' is ' || 
               TO_CHAR(salary_list(name))); 
               name := salary_list.NEXT(name); 
            END LOOP; 
        END; 
        /
sudhirkondle
fonte