【问题标题】:How to use a table type in a SELECT FROM statement?如何在 SELECT FROM 语句中使用表类型?
【发布时间】:2011-07-07 03:21:06
【问题描述】:

这个问题和this差不多

在包头中:
声明了以下行类型:

  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;


添加了一个变量:

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 中使用此表变量。

【问题讨论】:

  • 正如其他人所说,您不能对包中声明的类型执行此操作。目前尚不清楚您为什么要这样做,而不是使用真实表格来保存汇率,或者您想对所选数据做什么。这是在游标中吗?您真的在寻找您选择的特定货币的数据吗?您是否根据汇率操纵项目价值?更多背景信息可能有助于激发替代方法。

标签: oracle select row


【解决方案1】:

在包装规格中你可以做你提到但不确定INDEX BY BINARY_INTEGER;

在包体中:

在声明中初始化表:

exch_rt exch_tbl := exch_tbl();

为了将记录添加到本地集合中,您可以在开始 - 结束块中执行以下操作:

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 

为了从这个表中获取数据,你可以在包体内使用:

select exch_rt_usd, exch_rt_eur, currency_cd from table(exch_rt)

享受吧!

附:抱歉回答晚了:D

【讨论】:

    【解决方案2】:

    在 Oracle 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 表中进行选择。

    【讨论】:

    • 如果我没有使用现有表的所有列,是否还需要声明一个类型?
    • @zygimantus 对不起,我不明白你的问题,你能解释更多吗?
    • 例如,我可以声明这种类型TYPE my_type IS TABLE OF my_table%ROWTYPE,而不是使用您描述的自定义列吗?
    • 在 PL/SQL 中是的,您可以 - 如果使用 12C,也可以从该类型的变量中进行选择。
    • 好的。感谢您提供信息。
    【解决方案3】:

    您不能在包内的单个查询中执行此操作 - 您不能混合 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,对于美元也是如此。从匿名块测试:

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

    根据 Stef 发布的答案,这根本不需要放在包中;使用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;
    

    【讨论】:

    • 感谢您提供详细信息,请在问题中查看我更新的--notes--。
    • @Stef:我认为这是最接近的等价物,但工作方式并不完全相同。仍然不确定为什么不为此使用常规表,但如果您想要不同会话的不同值,您也可以查看全局临时表。我认为流水线函数的类型也会有同样的问题。
    • this earlier question 上有一些链接,但并没有真正解决您想要做的事情。
    【解决方案4】:

    感谢您在此问题上提供的所有帮助。我将在这里发布我的解决方案:

    包头

    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;
    

    请查看。

    【讨论】:

    • 我不确定当它无法编译时这是一个怎样的解决方案:AS net_value_in_euro 之后有一个额外的逗号,而batchsize 没有定义。并且 doit 未在包头中声明,因此无法运行。我认为这应该是对问题的编辑而不是答案。但是你为什么要在 PL/SQL 中这样做呢?这可以在单个 update 语句中完成。
    • insert 更新了我的答案,它创建了与doit 过程相同的detail_items 记录。
    • @Alex,它不会编译,因为它只是提示如何创建它。也许这很令人困惑。我的原因是:1)没有视图:我不想用那些小的帮助视图“污染”数据库。 2) 我想使用 PL/SQL,因为要记录批处理错误,并且需要处理大量数据。
    • 好的,如果你喜欢使用 PL/SQL,那么这将起作用。您仍然可以使用我的插入版本(来自with rt as)中的选择部分作为光标,并避免使用 PL/SQL 表和函数,这似乎增加了不必要的复杂性。但我偏离了原来的问题。
    【解决方案5】:

    在 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 中使用 TABLE 操作符,例如:

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

    【讨论】:

    • 有没有办法在函数中使用表格类型?
    • 是的。您可以在函数中使用表类型,其方式与此答案中的匿名 PL/SQL 块中的方式完全相同
    • 实际上您可以使用包规范/正文中几乎所有描述的对象。唯一的事情-您不能将本地类型创建为对象。包裹只允许作为记录
    猜你喜欢
    • 1970-01-01
    • 2018-06-01
    • 1970-01-01
    • 2015-10-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多