Oracle PL / SQL: ¿cómo crear una variable de matriz simple?

128

Me gustaría crear una variable de matriz en memoria que pueda usarse en mi código PL / SQL. No puedo encontrar ninguna colección en Oracle PL / SQL que use memoria pura, todas parecen estar asociadas con tablas. Estoy buscando hacer algo como esto en mi PL / SQL (sintaxis C #):

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

Editar: Oracle: 9i

contactmatt
fuente
1
La referencia de "tabla" tiende a ser una resaca de los antiguos nombres de tablas PL / SQL. Las VARRAY, las matrices asociativas y las tablas anidadas declaradas son todos tipos de matrices en memoria.
Ollie

Respuestas:

244

Puede usar VARRAY para una matriz de tamaño fijo:

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;

O TABLE para una matriz ilimitada:

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

La palabra "tabla" aquí no tiene nada que ver con las tablas de la base de datos, de manera confusa. Ambos métodos crean arreglos en memoria.

Con cualquiera de estos debe inicializar y extender la colección antes de agregar 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;

El primer índice es 1, no 0.

Tony Andrews
fuente
75
"confusamente" casi resume Oracle
m.edmondson
¿Me inserto en tablas de la misma manera que las matrices? es decirmy_array(0) := 'some string';
Abdul
@TonyAndrews array.extend();¿ EXTENDE agrega una ranura a una matriz acotada regular? En ese caso, ya es de tamaño dinámico, por lo que no sería necesaria una tabla (matriz sin límites).
Abdul
2
@ Abdul, no, no lo hace. Nunca uso VARRAY normalmente, pero cuando probé el código anterior, verifiqué lo que sucede si intentas extenderlo varray(3)4 veces: obtienes un error de "subíndice fuera de límite".
Tony Andrews
2
Ojalá pudiera votar esta respuesta varias veces @TonyAndrews desde que cubriste el array.extend(). Todos los lugares donde miré no mostraron esto y fue la parte más importante para poder agregar más de un elemento (según tengo entendido, todavía nuevo en los arreglos en SQL).
Jonathan Van Dam
61

Simplemente puede declarar un DBMS_SQL.VARCHAR2_TABLE para contener una matriz de longitud variable en memoria indexada por un 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;

Podría usar una matriz asociativa (solía llamarse tablas PL / SQL) ya que son una matriz en memoria.

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;

La matriz asociativa puede contener cualquier composición de tipos de registro.

Espero que ayude, Ollie.

Ollie
fuente
17
La condición de iteración aumenta VALUE_ERRORcuando la colección está vacía. Sugeriría usar más bien FOR i IN 1 .. employee_array.COUNTen este caso
unziberla
La versión de j-chomel ( stackoverflow.com/a/40579334/1915920 ) basada en la sys.odcivarchar2listsiguiente tiene la ventaja de que también tiene un constructor a mano, por ejemplo, para la inicialización predeterminada de la función param:sys.odcivarchar2list('val1','val2')
Andreas Dietrich
11

Otra solución es usar una Colección Oracle como 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
fuente
11

También puedes usar un 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;

Usaría una matriz en memoria. Pero con la .COUNTmejora sugerida por 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;

Otra solución sería usar un Hashmap como lo hizo @Jchomel aquí .

NÓTESE BIEN:

¡Con Oracle 12c puede incluso consultar matrices directamente ahora !

Jika
fuente
1

Programas de muestra de la siguiente manera y proporcionados en el enlace también https://oracle-concepts-learning.blogspot.com/

tabla plsql o matriz asociada.

        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
fuente