How to use a table type in a SELECT FROM statement?

后端 未结 5 696
花落未央
花落未央 2020-12-01 12:39

This question is more or less the same as this

In the package header :
Declared the following row type:

  TYPE exch_row IS RECORD(
             


        
5条回答
  •  陌清茗
    陌清茗 (楼主)
    2020-12-01 13:23

    You can't do it in a single query inside the package - you can't mix the SQL and PL/SQL types, and would need to define the types in the SQL layer as Tony, Marcin and Thio have said.

    If you really want this done locally, and you can index the table type by VARCHAR instead of BINARY_INTEGER, you can do something like this:

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

    So inside your loop, wherever you would have expected to use r0.exch_rt_eur you instead use exch_rt(r0.currency).exch_rt_eur, and the same for USD. Testing from an anonymous block:

    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 **
    

    Based on the answer Stef posted, this doesn't need to be in a package at all; the same results could be achieved with an insert statement. Assuming EXCH holds exchange rates of other currencies against the Euro, including USD with 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;
    

    With items valued at 19.99 GBP and 25.00 AUD, you get 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
    

    If you want the currency stuff to be more re-usable you could create a view:

    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;
    

    And then insert using values from that:

    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;
    

提交回复
热议问题