在我的Oracle存储过程中,有一个基于选择的查询,不是那么复杂,只有几个联接表,带有附加的普通过滤器和单个排序依据。
查询包含一个用户(按名称排序),该用户具有一些属性,每个用户可能有几行。
但是我需要添加一些计算出的额外列。
将组中第一行的“ flags”列设置为1,且名称相同(其他行均为0)。
累积列“权利”确实将同一字段“权利”的前一行值与其他列和当前行值(在本例中为“组”)结合在一起,并且也应按用户重新设置
例如:
john; admin; 1; admin
john; poweruser; 0; admin | poweruser
ken; guest; 1; guest
ted; developer; 1; developer
ted; user; 0; developer | user
ted; techwriter; 0; developer | user | techwriter
我需要执行一些格式化操作,并从存储过程中返回结果。 不知道如何设置所需的值并从存储过程中返回该值。
我应该将初始查询声明为游标,循环遍历并设置(是否可能?对于非数据库字段?)以及如何从存储过程中返回此游标?
答案 0 :(得分:2)
您还可以将其他一些分析函数与LISTAGG()一起使用以完成此操作。由于您不提供测试数据,因此我将用EMP表进行演示:
select deptno, ename,
case rn when 1 then 1 else 0 end flags,
case cnt when rn
then enames
else substr(enames, 1, instr(enames, ' | ', 1, rn) - 1)
end enames
from (
select deptno, ename,
row_number() over(partition by deptno order by ename) rn,
count(*) over(partition by deptno) cnt,
listagg(ename, ' | ') within group(order by ename) over(partition by deptno) enames
from emp
) a;
DEPTNO ENAME FLAGS ENAMES
10 CLARK 1 CLARK
10 KING 0 CLARK | KING
10 MILLER 0 CLARK | KING | MILLER
20 ADAMS 1 ADAMS
20 FORD 0 ADAMS | FORD
20 JONES 0 ADAMS | FORD | JONES
20 SCOTT 0 ADAMS | FORD | JONES | SCOTT
20 SMITH 0 ADAMS | FORD | JONES | SCOTT | SMITH
30 ALLEN 1 ALLEN
30 BLAKE 0 ALLEN | BLAKE
30 JAMES 0 ALLEN | BLAKE | JAMES
30 MARTIN 0 ALLEN | BLAKE | JAMES | MARTIN
30 TURNER 0 ALLEN | BLAKE | JAMES | MARTIN | TURNER
30 WARD 0 ALLEN | BLAKE | JAMES | MARTIN | TURNER | WARD
最好的问候,炖阿什顿
答案 1 :(得分:2)
我知道您已经有了适合您的答案。我将其添加为后代。
SQL MODEL
子句可以很好地解决此问题。这是一个使用DBA_ROLE_PRIVS
表的示例,该表存在于每个Oracle数据库中,并且具有与帖子中数据相似的结构和概念。显然,您将DBA_ROLE_PRIVS
替换为表名。
select grantee,
granted_role,
DECODE(rn,1,1,0) flag,
role_list
FROM dba_role_privs
MODEL
PARTITION BY (grantee)
DIMENSION BY (ROW_NUMBER() OVER ( PARTITION BY grantee ORDER BY granted_role) AS rn)
MEASURES (CAST(NULL AS VARCHAR2(4000)) as role_list, granted_role)
RULES UPSERT
( role_list[1] = granted_role[1],
role_list[rn>1] = role_list[cv(rn)-1] || ',' || granted_role[cv(rn)]);
出于安全原因,我不会从数据库中发布示例结果,但我认为它们与您的要求相符。
答案 2 :(得分:1)
不幸的是,listagg()
的分析版本不允许使用window子句,这会使它相当简单。
您可以使用递归子查询分解来模拟这一点,但是由于您没有真正的排序标准(除了名称之外),因此需要添加一些东西来代替它,例如行号或排名函数;也可以在CTE中:
with cte (name, right, rn) as (
select name,
right,
row_number() over (partition by name order by null)
from your_data
),
rcte (name, right, rn, flag, rights) as (
select name, right, rn, 1, right
from cte
where rn = 1
union all
select c.name, c.right, c.rn, 0, r.rights || ' | ' || c.right
from rcte r
join cte c on c.name = r.name and c.rn = r.rn + 1
)
select name, right, flag, rights
from rcte
order by name, rn;
NAME RIGHT FLAG RIGHTS
---- ---------- ---------- ------------------------------
john admin 1 admin
john poweruser 0 admin | poweruser
ken guest 1 guest
ted developer 1 developer
ted user 0 developer | user
ted techwriter 0 developer | user | techwriter
your_data
是您的查询现在正在执行的操作;整个过程可能只需要在末尾加上rn
计算就可以放在CTE内,但是却看不到现有查询,这还不是很清楚。希望这将适合您的数据。
我的rn
计算是按null
排序的,这不是确定性的-它在一次查询执行中为您提供了一个固定值,但是如果再次运行,您可能会得到不同的值。由于权利的顺序似乎无关紧要,所以无论如何您都可以按顺序对它们进行排序,以得出确定的结果。可能会或可能不会更改输出(由于以上内容不确定,因此无论如何都可能与之匹配;有时...):
cte (name, right, rn) as (
select name,
right,
row_number() over (partition by name order by right)
from your_data
),
rcte (name, right, rn, rights) as (
select name, right, rn, right
from cte
where rn = 1
union all
select c.name, c.right, c.rn, r.rights || ' | ' || c.right
from rcte r
join cte c on c.name = r.name and c.rn = r.rn + 1
)
select name, right, case when rn = 1 then 1 else 0 end as flag, rights
from rcte
order by name, rn;
NAME RIGHT FLAG RIGHTS
---- ---------- ---------- ------------------------------
john admin 1 admin
john poweruser 0 admin | poweruser
ken guest 1 guest
ted developer 1 developer
ted techwriter 0 developer | techwriter
ted user 0 developer | techwriter | user
实际上,您可能还可以使用其他一些条件,例如要连接的表之一中的标志或序列;或者您可以通过case表达式根据正确的值应用自己的内容。
答案 3 :(得分:1)
这是一种如何转换存储过程返回的游标的可能性。
您必须执行三个步骤:
1)为原始光标定义行和表TYPE
2)定义表函数,返回原始光标
3)定义新的过程,该过程在查询表函数(第2点)并将其与其他源连接时打开新的游标
示例
原始程序
create procedure P1(cur OUT SYS_REFCURSOR) IS
begin
open cur for
select id, col from V1;
end;
/
1)定义类型
create or replace type t_row is object
(id int,
col VARCHAR2(5));
/
create or replace type t_table is table of t_row;
/
2)定义返回原始光标的表函数
create or replace function F1 return t_table PIPELINED as
cv sys_refcursor;
v_row v1%rowtype;
begin
P1(cv);
loop
FETCH cv INTO v_row;
EXIT WHEN cv%NOTFOUND;
PIPE ROW(t_row(v_row.id, v_row.col));
end loop;
close cv;
return;
end;
/
请注意,现在您可以使用SQL访问原始的光标:
select * from table(F1);
ID COL
---------- -----
1 A
2 B
3)定义执行转换的新过程
请注意,我忽略了您的详细信息,仅通过添加新的虚拟列来模拟转换
create procedure P2(cur OUT SYS_REFCURSOR) IS
begin
open cur for
select id, col, 'new' col2
from table(F1);
end;
/