我想在PL / SQL中创建一个包含5个步骤的过程。步骤1和2首先执行并返回ID
。在第3步中,我们有一个SELECT
语句,其条件返回ID
。然后,我希望获取SELECT
语句的所有结果,并在另一个JOIN
语句的SELECT
中使用它们,并再次使用THOSE结果在第3 SELECT
语句中JOIN
。从我所看到的情况来看,我无法在CURSOR
语句中使用JOIN
。我的一些同事建议我将结果保存在CURSOR
中,然后使用循环迭代每一行并将该数据用于下一个SELECT
。然而,由于我要做2选择,这将创建一个巨大的内部循环分支,这正是我试图避免的。
另一个建议是使用Temprary Tables来存储数据。但是,许多用户可以同时执行此过程,并且表的数据会相互冲突。现在我正在查看根据会话过滤掉数据的LOCAL临时表,但我不确定我是否想为我的程序创建虚拟表,因为我想避免在模式中留下垃圾(此过程适用于应用程序的自定义部分)。有这样做的标准方法吗?有什么想法吗?
样品:
DECLARE
USERID INT := 1000000;
TEXT1 VARCHAR(100);
TEXT_INDEX INT;
CURSOR NODES IS SELECT * FROM NODE_TABLE WHERE DESCRIPTION LIKE TEXT || '%';
CURSOR USERS IS SELECT * FROM USERGROUPS JOIN NODES ON NODES.ID = USERGROUPS.ID;
BEGIN
SELECT TEXT INTO TEXT1 FROM TABLE_1 WHERE ID = USERID;
TEXT_INDEX = INSTR(TEXT, '-');
TEXT = SUBSTR(TEXT, 0, TEXT_INDEX);
OPEN NODES;
OPEN USERS;
END;
注意:这不起作用。 Oracle不支持游标之间的连接。 注意2:这可以在一个查询中完成,但为了参数(在我的实际用例中),我想在一个过程中打破这些步骤。示例代码描述了我尝试实现游标之间的IF连接的工作原理。但是他们没有,我正在寻找替代方案。
答案 0 :(得分:1)
我最终使用了一个函数(虽然也可以使用一个过程)以及表。我学到的东西应该注意:
MY_TABLE%ROWTYPE
之类的函数,即使类似信息似乎可用,也是不可接受的。您必须创建自定义类型MY_TABLE%ROWTYPE
才能返回它。 Oracle将声明类型的表与%ROWTYPE
的表区别对待。起初这让我感到很困惑,但从我收集的内容来看,这就是它的运作方式。
DECLARE TYPE MY_CUSTOM_TABLE IS TABLE OF MY_TABLE%ROWTYPE;
声明MY_TABLE行类型的集合。为了添加到此,我们必须使用查询MY_TABLE的SQL语句中的BULK COLLECT INTO
。生成的集合不能在JOIN语句中使用,这是不可查询的,并且不能由函数返回。
DECLARE
CREATE TYPE MY_CUSTOM_TYPE AS OBJECT (COL_A NUMBER, COL_B NUMBER);
CREATE TYPE MY_CUSTOM_TABLE AS TABLE OF MY_CUSTOM_TYPE;
my_custom_tab MY_CUSTOM_TABLE;
这创建了my_custom_tab,它是表(不是集合),如果使用TABLE(my_custmo_tab)
语句中的FROM
查询,则可以查询。作为在模式中预先声明的表,可以从函数返回。但是,它不能使用BULK COLLECT INTO
填充,因为它不是集合。我们必须使用正常的SELECT INTO
语句。但是,如果我们想要使用包含2个数字列的现有表中的数据填充它,我们不能简单地执行SELECT * INTO my_custom_tab FROM DOUBLE_NUMBER_TABLE
,因为my_custom_tab
尚未初始化且不包含足够的行接收数据。如果我们不知道查询返回了多少行,我们就无法对其进行初始化。填充表格的技巧是使用CAST
命令并将我们的选择结果集转换为MY_CUSTOM_TABLE
,然后添加它。
SELECT CAST(MULTISET(SELECT COL_A, COL_B FROM DOUBLE_NUMBER_TABLE) AS MY_CUSTOM_TABLE) INTO my_custom_tab FROM DUAL
现在,我们可以通过使用TABLE()
函数轻松地在查询等中使用my_custom_tab。
SELECT * FROM TABLE(my_custom_tab)
有效。
答案 1 :(得分:-1)
您可以通过多种方式进行此类分解,但所有这些分解在与单个SQL语句的同步中都会显着降低性能。 可维护性改进也值得怀疑,具体情况取决于具体情况 要查看所有可能性,请查看documentation。
以下是一些基于简单逻辑的可能变体:
<强> 1。流水线
准备功能使用的类型:
create or replace type TUserRow as object (
username varchar2(30),
user_id number,
created date
)
/
create or replace type TTableRow as object (
owner varchar2(30),
table_name varchar2(30),
status varchar2(8),
logging varchar2(3)
-- some other useful fields here
)
/
create or replace type TUserList as table of TUserRow
/
create or replace type TTableList as table of TTableRow
/
通过用户ID查找前缀的简单功能:
create or replace function GetUserPrefix(piUserId in number) return varchar2
is
vUserPrefix varchar2(30);
begin
select substr(username,1,3) into vUserPrefix
from all_users
where user_id = piUserId;
return vUserPrefix;
end;
/
搜索用户的功能:
create or replace function GetUsersPipe(
piNameStart in varchar2
)
return TUserList pipelined
as
vUserList TUserList;
begin
for cUsers in (
select *
from
all_users
where
username like piNameStart||'%'
)
loop
pipe row( TUserRow(cUsers.username, cUsers.user_id, cUsers.created) ) ;
end loop;
return;
end;
搜索表的函数:
create or replace function GetUserTablesPipe(
piUserNameStart in varchar2
)
return TTableList pipelined
as
vTableList TTableList;
begin
for cTables in (
select *
from
all_tables tab_list,
table(GetUsersPipe(piUserNameStart)) user_list
where
tab_list.owner = user_list.username
)
loop
pipe row ( TTableRow(cTables.owner, cTables.table_name, cTables.status, cTables.logging) );
end loop;
return;
end;
代码中的用法:
declare
vUserId number := 5;
vTableCount number;
begin
select count(1) into vTableCount
from table(GetUserTablesPipe(GetUserPrefix(vUserId)));
dbms_output.put_line('Users with name started with "'||GetUserPrefix(vUserId)||'" owns '||vTableCount||' tables');
end;
<强> 2。简单的表格功能
此解决方案使用与上述流水线函数相同的类型。
搜索用户的功能:
create or replace function GetUsers(piNameStart in varchar2) return TUserList
as
vUserList TUserList;
begin
select TUserRow(username, user_id, created)
bulk collect into vUserList
from
all_users
where
username like piNameStart||'%'
;
return vUserList;
end;
/
搜索表的函数:
create or replace function GetUserTables(piUserNameStart in varchar2) return TTableList
as
vTableList TTableList;
begin
select TTableRow(owner, table_name, status, logging)
bulk collect into vTableList
from
all_tables tab_list,
table(GetUsers(piUserNameStart)) user_list
where
tab_list.owner = user_list.username
;
return vTableList;
end;
/
代码中的用法:
declare
vUserId number := 5;
vTableCount number;
begin
select count(1) into vTableCount
from table(GetUserTables(GetUserPrefix(vUserId)));
dbms_output.put_line('Users with name started with "'||GetUserPrefix(vUserId)||'" owns '||vTableCount||' tables');
end;
第3。 cursor - xml - cursor
它是一个特定的案例,可以在没有用户定义类型的情况下实现,但性能损失很大,涉及不需要的类型转换,并且可维护性低。
搜索用户的功能:
create or replace function GetUsersRef(
piNameStart in varchar2
)
return sys_refcursor
as
cUserList sys_refcursor;
begin
open cUserList for
select * from all_users
where username like piNameStart||'%'
;
return cUserList;
end;
搜索表的函数:
create or replace function GetUserTablesRef(
piUserNameStart in varchar2
)
return sys_refcursor
as
cTableList sys_refcursor;
begin
open cTableList for
select
tab_list.*
from
(
XMLTable('/ROWSET/ROW'
passing xmltype(GetUsersRef(piUserNameStart))
columns
username varchar2(30) path '/ROW/USERNAME'
)
) user_list,
all_tables tab_list
where
tab_list.owner = user_list.username
;
return cTableList;
end;
代码中的用法:
declare
vUserId number := 5;
vTableCount number;
begin
select count(1) into vTableCount
from
XMLTable('/ROWSET/ROW'
passing xmltype(GetUserTablesRef(GetUserPrefix(vUserId)))
columns
table_name varchar2(30) path '/ROW/TABLE_NAME'
)
;
dbms_output.put_line('Users with name started with "'||GetUserPrefix(vUserId)||'" owns '||vTableCount||' tables');
end;
当然,所有变体都可以混合使用,但SQL至少在简单的情况下看起来更好:
declare
vUserId number := 5;
vUserPrefix varchar2(100);
vTableCount number;
begin
-- Construct prefix from Id
select max(substr(user_list.username,1,3))
into vUserPrefix
from
all_users user_list
where
user_list.user_id = vUserId
;
-- Count number of tables owned by users with name started with vUserPrefix string
select
count(1) into vTableCount
from
all_users user_list,
all_tables table_list
where
user_list.username like vUserPrefix||'%'
and
table_list.owner = user_list.username
;
dbms_output.put_line('Users with name started with "'||vUserPrefix||'" owns '||vTableCount||' tables');
end;
P.S。所有代码仅用于演示目的:无优化等。