web-dev-qa-db-fra.com

Comment utiliser un type de table dans une instruction SELECT FROM?

Cette question est plus ou moins la même chose que this

Dans l'en-tête du package:
Déclaré le type de ligne suivant:

  TYPE exch_row IS RECORD(
    currency_cd VARCHAR2(9),
    exch_rt_eur NUMBER,
    exch_rt_usd NUMBER);


Et ce type de tableau:

  TYPE exch_tbl IS TABLE OF exch_row INDEX BY BINARY_INTEGER;


Ajout d'une variable:

exch_rt exch_tbl;


Dans le corps du paquet:
Remplissez cette variable de tableau avec quelques données.


Dans une procédure dans le corps du package:
Je veux utiliser la déclaration suivante:

CURSOR c0 IS
  SELECT i.*, rt.exch_rt_eur, rt.exch_rt_usd
  FROM item i, exch_rt rt
  WHERE i.currency = rt.exchange_cd


Comment faire cela dans Oracle?


Notes

En fait, je recherche la solution 'Table Variable' dans MSSQL:

DECLARE @exch_tbl TABLE
(
  currency_cd VARCHAR(9),
  exch_rt_eur NUMBER,
  exch_rt_usd NUMBER)
)

Et utilisez cette variable de table dans ma procédure stockée.

13
Stef Heyenrath

En SQL, vous ne pouvez utiliser que le type de table défini au niveau du schéma (pas au niveau du package ou de la procédure) et la table d'indexation (tableau associatif) ne peut pas être définie au niveau du schéma. Donc - vous devez définir une table imbriquée comme ceci

create type exch_row as object (
    currency_cd VARCHAR2(9),
    exch_rt_eur NUMBER,
    exch_rt_usd NUMBER);

create type exch_tbl as table of exch_row;

Et puis vous pouvez l'utiliser en SQL avec l'opérateur TABLE, par exemple:

declare
   l_row     exch_row;
   exch_rt   exch_tbl;
begin
   l_row := exch_row('PLN', 100, 100);
   exch_rt  := exch_tbl(l_row);

   for r in (select i.*
               from item i, TABLE(exch_rt) rt
              where i.currency = rt.currency_cd) loop
      -- your code here
   end loop;
end;
/
17

Avant Oracle 12C, vous ne pouvez pas sélectionner de tables définies par PL/SQL, uniquement à partir de tables basées sur des types SQL comme celui-ci:

CREATE OR REPLACE TYPE exch_row AS OBJECT(
currency_cd VARCHAR2(9),
exch_rt_eur NUMBER,
exch_rt_usd NUMBER);


CREATE OR REPLACE TYPE exch_tbl AS TABLE OF exch_row;

Dans Oracle 12C, il est désormais possible de sélectionner des tables PL/SQL définies dans une spécification de package.

14
Tony Andrews

Vous ne pouvez pas le faire dans une seule requête à l'intérieur du package - vous ne pouvez pas mélanger les types SQL et PL/SQL, et devez définir les types dans la couche SQL comme Tony, Marcin et Thio l'ont dit.

Si vous voulez vraiment que cela se fasse localement et que vous pouvez indexer le type de table par VARCHAR au lieu de BINARY_INTEGER, vous pouvez faire quelque chose comme ceci:

-- dummy ITEM table as we don't know what the real ones looks like
create table item(
    item_num number,
    currency varchar2(9)
)
/   

insert into item values(1,'GBP');
insert into item values(2,'AUD');
insert into item values(3,'GBP');
insert into item values(4,'AUD');
insert into item values(5,'CDN');

create package so_5165580 as
    type exch_row is record(
        exch_rt_eur number,
        exch_rt_usd number);
    type exch_tbl is table of exch_row index by varchar2(9);
    exch_rt exch_tbl;
    procedure show_items;
end so_5165580;
/

create package body so_5165580 as
    procedure populate_rates is
        rate exch_row;
    begin
        rate.exch_rt_eur := 0.614394;
        rate.exch_rt_usd := 0.8494;
        exch_rt('GBP') := rate;
        rate.exch_rt_eur := 0.9817;
        rate.exch_rt_usd := 1.3572;
        exch_rt('AUD') := rate;
    end;

    procedure show_items is
        cursor c0 is
            select i.*
            from item i;
    begin
        for r0 in c0 loop
            if exch_rt.exists(r0.currency) then
                dbms_output.put_line('Item ' || r0.item_num
                    || ' Currency ' || r0.currency
                    || ' EUR ' || exch_rt(r0.currency).exch_rt_eur
                    || ' USD ' || exch_rt(r0.currency).exch_rt_usd);
            else
                dbms_output.put_line('Item ' || r0.item_num
                    || ' Currency ' || r0.currency
                    || ' ** no rates defined **');
            end if;
        end loop;
    end;
begin
    populate_rates;
end so_5165580;
/

Donc, à l'intérieur de votre boucle, partout où vous vous seriez attendu à utiliser r0.exch_rt_eur, Vous utilisez plutôt exch_rt(r0.currency).exch_rt_eur, et la même chose pour USD. Test à partir d'un bloc anonyme:

begin
    so_5165580.show_items;
end;
/

Item 1 Currency GBP EUR .614394 USD .8494
Item 2 Currency AUD EUR .9817 USD 1.3572
Item 3 Currency GBP EUR .614394 USD .8494
Item 4 Currency AUD EUR .9817 USD 1.3572
Item 5 Currency CDN ** no rates defined **

Sur la base de la réponse que Stef a publiée, cela n'a pas du tout besoin d'être dans un package; les mêmes résultats pourraient être obtenus avec une instruction insert. En supposant que EXCH détient les taux de change d'autres devises par rapport à l'euro, y compris USD avec currency_key=1:

insert into detail_items
with rt as (select c.currency_cd as currency_cd,
        e.exch_rt as exch_rt_eur,
        (e.exch_rt / usd.exch_rt) as exch_rt_usd
    from exch e,
        currency c,
        (select exch_rt from exch where currency_key = 1) usd
    where c.currency_key = e.currency_key)
select i.doc,
    i.doc_currency,
    i.net_value,
    i.net_value / rt.exch_rt_usd AS net_value_in_usd,
    i.net_value / rt.exch_rt_eur as net_value_in_euro
from item i
join rt on i.doc_currency = rt.currency_cd;

Avec des articles d'une valeur de 19,99 GBP et 25,00 AUD, vous obtenez detail_items:

DOC DOC_CURRENCY NET_VALUE         NET_VALUE_IN_USD  NET_VALUE_IN_EURO
--- ------------ ----------------- ----------------- -----------------
1   GBP          19.99             32.53611          23.53426
2   AUD          25                25.46041          18.41621

Si vous souhaitez que le contenu en devises soit plus réutilisable, vous pouvez créer une vue:

create view rt as
select c.currency_cd as currency_cd,
    e.exch_rt as exch_rt_eur,
    (e.exch_rt / usd.exch_rt) as exch_rt_usd
from exch e,
    currency c,
    (select exch_rt from exch where currency_key = 1) usd
where c.currency_key = e.currency_key;

Et puis insérez en utilisant les valeurs de cela:

insert into detail_items
select i.doc,
    i.doc_currency,
    i.net_value,
    i.net_value / rt.exch_rt_usd AS net_value_in_usd,
    i.net_value / rt.exch_rt_eur as net_value_in_euro
from item i
join rt on i.doc_currency = rt.currency_cd;
4
Alex Poole

Merci pour toute aide à ce sujet. Je posterai ici ma solution:

En-tête du package

CREATE OR REPLACE PACKAGE X IS
  TYPE exch_row IS RECORD(
    currency_cd VARCHAR2(9),
    exch_rt_eur NUMBER,
    exch_rt_usd NUMBER);
  TYPE exch_tbl IS TABLE OF X.exch_row;

  FUNCTION GetExchangeRate RETURN X.exch_tbl PIPELINED;
END X;

Corps de l'emballage

CREATE OR REPLACE PACKAGE BODY X IS
  FUNCTION GetExchangeRate RETURN X.exch_tbl
    PIPELINED AS
    exch_rt_usd NUMBER := 1.0; --todo
    rw exch_row;
  BEGIN

    FOR rw IN (SELECT c.currency_cd AS currency_cd, e.exch_rt AS exch_rt_eur, (e.exch_rt / exch_rt_usd) AS exch_rt_usd
                 FROM exch e, currency c
                WHERE c.currency_key = e.currency_key
                  ) LOOP
      PIPE ROW(rw);
    END LOOP;
  END;


  PROCEDURE DoIt IS
  BEGIN
    DECLARE
      CURSOR c0 IS
        SELECT i.DOC,
               i.doc_currency,
               i.net_value,
               i.net_value / rt.exch_rt_usd AS net_value_in_usd,
               i.net_value / rt.exch_rt_eur AS net_value_in_euro,
          FROM item i, (SELECT * FROM TABLE(X.GetExchangeRate())) rt
         WHERE i.doc_currency = rt.currency_cd;

      TYPE c0_type IS TABLE OF c0%ROWTYPE;

      items c0_type;
    BEGIN
      OPEN c0;

      LOOP
        FETCH c0 BULK COLLECT
          INTO items LIMIT batchsize;

        EXIT WHEN items.COUNT = 0;
        FORALL i IN items.FIRST .. items.LAST SAVE EXCEPTIONS
          INSERT INTO detail_items VALUES items (i);

      END LOOP;

      CLOSE c0;

      COMMIT;

    EXCEPTION
      WHEN OTHERS THEN
        RAISE;
    END;
  END;

END X;

S'il-vous-plaît évaluez.

1
Stef Heyenrath

Dans les spécifications du package vous pouvez faire tout ce que vous avez mentionné mais vous n'êtes pas sûr de INDEX BY BINARY_INTEGER;

Dans le corps du paquet:

initialiser le tableau dans les déclarations:

exch_rt exch_tbl := exch_tbl();

afin d'ajouter un enregistrement à la collection locale, dans le bloc début-fin, vous pouvez faire:

exch_rt.extend;
                                one_row.exch_rt_usd := 2;
                                one_row.exch_rt_eur := 1;
                                one_row.currency_cd := 'dollar';
                                exch_rt(1) := one_row; -- 1 - number of row in the table - you can put a variable which will be incremented inside a loop 

afin d'obtenir des données de cette table, à l'intérieur du corps du package, vous pouvez utiliser:

select exch_rt_usd, exch_rt_eur, currency_cd from table(exch_rt)

prendre plaisir!

P.S. désolé pour une réponse tardive: D

0
t v