web-dev-qa-db-fra.com

Sélection de valeurs dans la variable / tableau Oracle Table?

Suite à ma dernière question ( Variables de table dans Oracle PL/SQL? ) ...

Une fois que vous avez des valeurs dans un tableau/table, comment les récupérer à nouveau? Utiliser de préférence une instruction select ou quelque chose de similaire?

Voici ce que j'ai jusqu'à présent:

declare
    type array is table of number index by binary_integer;
    pidms array;
begin
    for i in    (
                select distinct sgbstdn_pidm
                from sgbstdn
                where sgbstdn_majr_code_1 = 'HS04'
                and sgbstdn_program_1 = 'HSCOMPH'
                )
    loop
        pidms(pidms.count+1) := i.sgbstdn_pidm;
    end loop;

    select *
    from pidms; --Oracle DOESN'T LIKE THIS BIT!!!
end;

Je sais que je peux les produire en utilisant dbms_output.putline (), mais j'espère obtenir un jeu de résultats comme je le ferais en le sélectionnant dans n'importe quelle autre table.

Merci d'avance, Matt

11
Sonny Boy

Vous pourriez avoir besoin d'une TABLE TEMPORAIRE GLOBALE.

Dans Oracle, ils sont créés une fois, puis lorsqu'ils sont invoqués, les données sont privées pour votre session.

Lien de documentation Oracle

Essayez quelque chose comme ça ...

CREATE GLOBAL TEMPORARY TABLE temp_number
   ( number_column   NUMBER( 10, 0 )
   )
   ON COMMIT DELETE ROWS;

BEGIN 
   INSERT INTO temp_number
      ( number_column )
      ( select distinct sgbstdn_pidm 
          from sgbstdn 
         where sgbstdn_majr_code_1 = 'HS04' 
           and sgbstdn_program_1 = 'HSCOMPH' 
      ); 

    FOR pidms_rec IN ( SELECT number_column FROM temp_number )
    LOOP 
        -- Do something here
        NULL; 
    END LOOP; 
END; 
/
15
PaulJ

Dans Oracle, les moteurs PL/SQL et SQL maintiennent une certaine séparation. Lorsque vous exécutez une instruction SQL dans PL/SQL, elle est transmise au moteur SQL, qui n'a aucune connaissance des structures spécifiques à PL/SQL comme les tables INDEX BY.

Ainsi, au lieu de déclarer le type dans le bloc PL/SQL, vous devez créer un type de collection équivalent dans le schéma de base de données:

CREATE OR REPLACE TYPE array is table of number;
/

Ensuite, vous pouvez l'utiliser comme dans ces deux exemples dans PL/SQL:

SQL> l
  1  declare
  2    p  array := array();
  3  begin
  4    for i in (select level from dual connect by level < 10) loop
  5      p.extend;
  6      p(p.count) := i.level;
  7    end loop;
  8    for x in (select column_value from table(cast(p as array))) loop
  9       dbms_output.put_line(x.column_value);
 10    end loop;
 11* end;
SQL> /
1
2
3
4
5
6
7
8
9

PL/SQL procedure successfully completed.

SQL> l
  1  declare
  2    p  array := array();
  3  begin
  4    select level bulk collect into p from dual connect by level < 10;
  5    for x in (select column_value from table(cast(p as array))) loop
  6       dbms_output.put_line(x.column_value);
  7    end loop;
  8* end;
SQL> /
1
2
3
4
5
6
7
8
9

PL/SQL procedure successfully completed.

Exemple supplémentaire basé sur des commentaires

Sur la base de votre commentaire sur ma réponse et sur la question elle-même, je pense que c'est ainsi que je l'appliquerais. Utilisez un package pour que les enregistrements puissent être récupérés une fois dans la table réelle et stockés dans un package privé global; et avoir une fonction qui renvoie un curseur ref ouvert.

CREATE OR REPLACE PACKAGE p_cache AS
  FUNCTION get_p_cursor RETURN sys_refcursor;
END p_cache;
/

CREATE OR REPLACE PACKAGE BODY p_cache AS

  cache_array  array;

  FUNCTION get_p_cursor RETURN sys_refcursor IS
    pCursor  sys_refcursor;
  BEGIN
    OPEN pCursor FOR SELECT * from TABLE(CAST(cache_array AS array));
    RETURN pCursor;
  END get_p_cursor;

  -- Package initialization runs once in each session that references the package
  BEGIN
    SELECT level BULK COLLECT INTO cache_array FROM dual CONNECT BY LEVEL < 10;
  END p_cache;
/
11
Dave Costa

Le type de tableau SQL n'est pas nécessaire. Pas si le type d'élément est primitif. (Varchar, numéro, date, ...)

Échantillon très basique:

declare
  type TPidmList is table of sgbstdn.sgbstdn_pidm%type;
  pidms TPidmList;
begin
  select distinct sgbstdn_pidm
  bulk collect into pidms
  from sgbstdn
  where sgbstdn_majr_code_1 = 'HS04'
  and sgbstdn_program_1 = 'HSCOMPH';

  -- do something with pidms

  open :someCursor for
    select value(t) pidm
    from table(pidms) t;
end;

Lorsque vous souhaitez le réutiliser, il peut être intéressant de savoir à quoi cela ressemblerait. Si vous émettez plusieurs commandes, celles-ci pourraient être regroupées dans un package. L'astuce de la variable de package privé ci-dessus a ses inconvénients. Lorsque vous ajoutez des variables à un package, vous lui donnez l'état et maintenant il n'agit plus comme un groupe de fonctions sans état mais comme une sorte étrange d'instance d'objet singleton.

par exemple. Lorsque vous recompilez le corps, il lèvera des exceptions dans les sessions qui l'ont déjà utilisé auparavant. (parce que les valeurs des variables ont été invalidées)

Cependant, vous pouvez déclarer le type dans un package (ou globalement en sql) et l'utiliser comme paramètre dans les méthodes qui devraient l'utiliser.

create package Abc as
  type TPidmList is table of sgbstdn.sgbstdn_pidm%type;

  function CreateList(majorCode in Varchar, 
                      program in Varchar) return TPidmList;

  function Test1(list in TPidmList) return PLS_Integer;
  -- "in" to make it immutable so that PL/SQL can pass a pointer instead of a copy
  procedure Test2(list in TPidmList);
end;

create package body Abc as

  function CreateList(majorCode in Varchar, 
                      program in Varchar) return TPidmList is
    result TPidmList;
  begin
    select distinct sgbstdn_pidm
    bulk collect into result
    from sgbstdn
    where sgbstdn_majr_code_1 = majorCode
    and sgbstdn_program_1 = program;

    return result;
  end;

  function Test1(list in TPidmList) return PLS_Integer is
    result PLS_Integer := 0;
  begin
    if list is null or list.Count = 0 then
      return result;
    end if;

    for i in list.First .. list.Last loop
      if ... then
        result := result + list(i);
      end if;
    end loop;
  end;

  procedure Test2(list in TPidmList) as
  begin
    ...
  end;

  return result;
end;

Comment l'appeler:

declare
  pidms constant Abc.TPidmList := Abc.CreateList('HS04', 'HSCOMPH');
  xyz PLS_Integer;
begin
  Abc.Test2(pidms);
  xyz := Abc.Test1(pidms);
  ...

  open :someCursor for
    select value(t) as Pidm,
           xyz as SomeValue
    from   table(pidms) t;
end;
1
Robert Giesecke