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.
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;
/
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.
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;
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.
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