Oracle 如何在SELECTFROM语句中使用表类型?
这个问题和我的问题差不多 在包标题中:Oracle 如何在SELECTFROM语句中使用表类型?,oracle,select,row,Oracle,Select,Row,这个问题和我的问题差不多 在包标题中: 声明了以下行类型: 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; 在包体中: 用一些数据填充
声明了以下行类型:
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)
)
并在my StoredProcess中使用此表变量。在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;
-- 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;
/
在Oracle 12C中,现在可以从包规范中定义的PL/SQL表中进行选择。在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;
/
您不能在包内的单个查询中完成这项工作-您不能混合使用SQL和PL/SQL类型,并且需要像Tony、Marcin和Thio所说的那样在SQL层中定义类型 如果您确实希望在本地执行此操作,并且可以通过VARCHAR而不是BINARY_INTEGER索引表类型,您可以执行以下操作:
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;
-- 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\u rt\u eur
,您都会使用exch\u rt(r0.currency).exch\u rt\u 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\u 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澳元的项目,您可以获得详细信息\u项目
:
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;
谢谢大家在这个问题上的帮助。我将在此处发布我的解决方案:
包头
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;
请查看。在软件包规范中您可以完成您提到的所有操作,但不确定是否按二进制\u整数索引代码>
在包体中:
在声明中初始化表:
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正如其他人所说,您不能对包中声明的类型执行此操作。不清楚您为什么要这样做,而不是使用一个真实的表来保存汇率,或者您想对所选数据做什么。这是光标吗?您真的在从您的选择中查找特定货币的数据吗?您是否根据汇率操纵项目值?更多的上下文可能有助于激发其他方法。感谢您提供的详细信息,请参阅问题中我更新的--notes.@Stef:我认为这是最接近的等效方法,但并不完全相同。仍然不确定为什么不使用常规表进行此操作,但如果希望不同会话使用不同的值,也可以查看全局临时表。我认为流水线函数的类型也会有同样的问题。上面有一些链接,但并没有真正解决您要做的事情。我不确定这在它不编译时是如何解决的:您在后面有一个额外的逗号,作为欧元中的净值,并且没有定义batchsize
。并且doit
未在包头中声明,因此无法运行。我认为这应该是对问题的编辑,而不是回答。但为什么要在PL/SQL中这样做呢?这可以在一个update
语句中完成。用insert
更新了我的答案,它创建了与doit
过程相同的detail\u项
记录。@Alex,它不编译,因为它只是一个如何创建它的提示。也许这让人困惑。我的理由是:1)没有视图:我不想用那些小的辅助视图“污染”数据库。2) 我想使用PL/SQL,因为它记录了批处理错误,并且需要处理大量的数据。好的,如果你坚持使用PL/SQL,那么这就行了。您仍然可以使用我的插入版本中的select part(从和rt as
)作为光标,并避免使用PL/SQL表和函数,这似乎增加了不必要的复杂性。但我偏离了原来的问题。如果我没有使用现有表的所有列,我是否需要声明类型?@zygimantus抱歉,我不理解你的问题,你能解释更多吗?例如,我能不能声明这种类型类型my_type是my_TABLE%ROWTYPE的TABLE
,而不是使用你描述的自定义列?在PL/SQL中,你可以-如果使用12C,你也可以从该类型的变量中选择。Ok。谢谢提供信息。有没有办法在函数中使用表类型?是的。您可以在函数中使用表类型,其方式与此答案中的匿名PL/SQL块完全相同。实际上,您可以使用包规范/正文中描述的几乎所有对象。唯一的问题是,不能将本地类型创建为对象。仅允许包使用as记录