Oracle PL / SQL - Comment créer une simple variable de tableau?

128

Je voudrais créer une variable de tableau en mémoire qui peut être utilisée dans mon code PL / SQL. Je ne trouve aucune collection dans Oracle PL / SQL qui utilise de la mémoire pure, elles semblent toutes être associées à des tables. Je cherche à faire quelque chose comme ça dans mon PL / SQL (syntaxe C #):

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

Modifier: Oracle: 9i

contactmatt
la source
1
La référence «table» a tendance à être un reliquat de l'ancien nom des tables PL / SQL. Les VARRAY, les tableaux associatifs et les tables imbriquées déclarées sont tous des types de tableaux en mémoire.
Ollie

Réponses:

244

Vous pouvez utiliser VARRAY pour un tableau de taille fixe:

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 pour un tableau illimité:

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

Le mot «table» ici n'a rien à voir avec les tables de base de données, ce qui prête à confusion. Les deux méthodes créent des tableaux en mémoire.

Avec l'un ou l'autre de ces éléments, vous devez à la fois initialiser et étendre la collection avant d'ajouter des éléments:

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;

Le premier indice est 1 et non 0.

Tony Andrews
la source
75
"déroutant" résume à peu près Oracle
m.edmondson
Dois-je insérer dans des tableaux de la même manière que des tableaux? iemy_array(0) := 'some string';
Abdul
@TonyAndrews EXTEND array.extend();ajoute-t-il un slot à un tableau borné normal? Dans ce cas, sa taille est déjà dynamique, donc une table (tableau illimité) ne serait pas nécessaire.
Abdul
2
@Abdul, non, non. Je n'utilise jamais VARRAYs normalement, mais en testant le code ci-dessus, j'ai vérifié ce qui se passe si vous essayez d'étendre varray(3)4 fois - vous obtenez une erreur "indice hors limite".
Tony Andrews
2
J'aimerais pouvoir voter cette réponse plusieurs fois @TonyAndrews depuis que vous avez couvert le array.extend(). Partout où je regardais, cela ne montrait pas cela et c'était la partie la plus importante pour pouvoir ajouter plus d'un élément (d'après ma compréhension, encore nouveau dans les tableaux en SQL).
Jonathan Van Dam
61

Vous pouvez simplement déclarer un DBMS_SQL.VARCHAR2_TABLE pour contenir un tableau de longueur variable en mémoire indexé par 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;

Vous pouvez utiliser un tableau associatif (anciennement appelé tables PL / SQL) car il s'agit d'un tableau en mémoire.

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;

Le tableau associatif peut contenir n'importe quelle composition de types d'enregistrement.

J'espère que ça aide, Ollie.

Ollie
la source
17
La condition d'itération se déclenche VALUE_ERRORlorsque la collection est vide. Je suggérerais plutôt d'utiliser FOR i IN 1 .. employee_array.COUNTdans ce cas
unziberla
La version de j-chomel ( stackoverflow.com/a/40579334/1915920 ) basée sur sys.odcivarchar2listci-dessous a l'avantage, que vous avez également un constructeur à portée de main, par exemple pour l'initialisation par défaut des paramètres de fonction:sys.odcivarchar2list('val1','val2')
Andreas Dietrich
11

Une autre solution consiste à utiliser une collection Oracle comme 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
la source
11

Vous pouvez également utiliser 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;

J'utiliserais un tableau en mémoire. Mais avec l' .COUNTamélioration suggérée par l'Ouzibérie:

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;

Une autre solution serait d'utiliser un Hashmap comme @Jchomel l'a fait ici .

NB:

Avec Oracle 12c, vous pouvez même interroger directement des tableaux dès maintenant !

Jika
la source
1

Exemples de programmes comme suit et fournis sur le lien également https://oracle-concepts-learning.blogspot.com/

table plsql ou tableau associé.

        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
la source