2011-03-02 48 views
13

這個問題是更多或更少的作爲this如何在SELECT FROM語句中使用表類型?

在包標頭中的相同:
聲明以下行類型:

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


而這個表類型:

TYPE exch_tbl IS TABLE OF exch_row INDEX BY BINARY_INTEGER; 


增加了一個var iable:

exch_rt exch_tbl; 


在封裝體:
填充一些數據該表格變量。


在封裝體的過程:
我想用下面的語句:

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 


如何做到這一點的Oracle?


注意

其實我正在尋找在MSSQL的 '表變量' 的解決方案:

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

,用我的StoredProcedure這裏面表變量。

+0

正如其他人所說,你不能用包中聲明的類型來做到這一點。目前尚不清楚爲什麼你要這樣做,而不是使用真正的表來保存匯率,或者你想要對選定的數據做什麼。這是在遊標?你真的在尋找你選擇的特定貨幣的數據嗎?您是否根據匯率操縱項目價值?更多的上下文可能有助於激發替代方法。 – 2011-03-02 11:48:58

回答

13

此前甲骨文12C不能從PL/SQL定義的表中選擇,只能從基於SQL類型這樣的表:

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; 

在Oracle 12C,現在可以從PL/SQL表中選擇這是在包規範中定義的。

+0

如果我不使用現有表的所有列,是否需要聲明類型? – zygimantus 2016-11-21 12:07:58

+0

@zygimantus對不起,但我不明白你的問題,你能解釋更多嗎? – 2016-11-21 12:12:31

+0

例如,我可以使用您的descibed自定義列來聲明這種類型'TYPE my_type IS TABLE OF my_table%ROWTYPE'嗎? – zygimantus 2016-11-21 12:17:52

14

在SQL中,您只能使用在模式級別(而不是在程序包或過程級別)定義的表類型,並且無法在模式級別定義索引表(關聯數組)。所以 - 你必須像這樣定義

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; 

嵌套表,那麼您可以在SQL與表操作使用它,例如:

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

有沒有辦法在函數中使用表類型? – sabertooth1990 2017-04-04 17:25:19

+0

是的。你可以在函數中使用表類型,就像這個答案中的匿名PL/SQL塊一樣。 – 2017-04-05 07:46:04

4

你不能做一個單一的查詢中包 - 你不能混用SQL和PL/SQL類型,並且需要像Tony,Marcin和Thio所說的那樣在SQL層中定義類型。

如果你真的想這個在本地完成的,你可以指數VARCHAR代替BINARY_INTEGER表類型,你可以做這樣的事情:

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

所以你的循環,只要你會預期到內使用r0.exch_rt_eur您改爲使用exch_rt(r0.currency).exch_rt_eur,而USD則相同。從一個匿名塊測試:

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

基於答案燕姿發佈,這並不需要在一個包在所有; insert聲明可以實現相同的結果。假設EXCH保存與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; 

隨着項目價值19.99英鎊和25.00澳元,你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 

如果你要使用的貨幣東西要更可重複使用,你可以創建一個視圖:

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; 

然後插入使用值在:

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; 
+0

感謝您的詳細信息,請參閱我的更新 - 註釋 - 在問題中。 – 2011-03-02 14:49:52

+0

@Stef:我認爲這是最接近的等價物,但不完全一樣。仍然不確定爲什麼你不會爲此使用常規表格,但是如果您需要不同會話的不同值,則還可以查看全局臨時表格。我認爲流水線函數會與類型有相同的問題。 – 2011-03-02 15:06:08

+0

[此早期問題]上有一些鏈接(http:// stackoverflow。com/questions/670461/does-oracle-have-an-equivalent-of-sql-servers-table-variables),但並沒有真正解決你想要做的事情。 – 2011-03-02 15:21:42

1

感謝您在這個問題上的所有幫助。我會在這裏發佈我的解決方案:

程序包頭

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; 

包體

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; 

請仔細閱讀。

+0

我不確定這是不是編譯時的解決方案:'AS net_value_in_euro'後面有一個額外的逗號,'batchsize'沒有定義。 'doit'沒有在包頭中聲明,所以無法運行。我認爲這應該是對問題的修改,而不是回答。但是你爲什麼要用PL/SQL來做這件事?這可以在一個'update'語句中完成。 – 2011-03-02 17:08:17

+0

用'insert'更新了我的答案,它創建了與'doit'過程相同的'detail_items'記錄。 – 2011-03-02 17:49:22

+0

@Alex,它不能編譯,因爲它只是一個提示如何創建它。也許這是令人困惑的。我的理由是:1)沒有看法:我不想用這些小幫手的觀點來「污染」數據庫。 2)我想使用PL/SQL,因爲記錄了批量錯誤以及需要處理的大量數據。 – 2011-03-03 06:54:21

相關問題