我有一个工作场所问题,我正在寻找一个简单的解决方案。 我试图在较小的场景中复制它。
简短问题
我想在nvl
子句中使用in
。目前我有一个由名称组成的输入字符串。它用在如下的where子句中
and column_n = nvl(in_parameter,column_n)
现在我想在同一输入参数中传递多个逗号分隔值。因此,如果我用=
替换in
,并将输入逗号分隔的字符串转换为行,我就不能使用nvl
子句。
详细问题
让我们考虑一个Employee表emp1。
Emp1
+-------+-------+
| empno | ename |
+-------+-------+
| 7839 | KING |
| 7698 | BLAKE |
| 7782 | CLARK |
+-------+-------+
现在这是现有存储过程的简单版本
create or replace procedure emp_poc(in_names IN varchar2)
as
cnt integer;
begin
select count(*)
into cnt
from emp1
where
ename = nvl(in_names,ename); --This is one of the condition where we will make the change.
dbms_output.put_line(cnt);
end;
因此,这将给出作为输入参数传递的员工数量。但是如果我们传递null,它将返回nvl
的整个雇员数量。
因此,这些过程调用将呈现给定的输出。
Procedure Call Output
exec emp_poc('KING') 1
exec emp_poc('JOHN') 0
exec emp_poc(null) 3
现在我想要实现的是添加其他功能。因此exec emp_poc('KING,BLAKE')
应该给我2
。所以我想方法将逗号分隔的字符串拆分为行并在过程中使用它。
因此,如果我将where
条款更改为in
create or replace procedure emp_poc2(in_names IN varchar2)
as
cnt integer;
begin
select count(*)
into cnt
from emp1
where
ename in (select trim(regexp_substr(in_names, '[^,]+', 1, level))
from dual
connect by instr(in_names, ',', 1, level - 1) > 0
);
dbms_output.put_line(cnt);
end;
所以exec emp_poc2('KING','BLAKE')
给了我2
。但是,传递null
会将结果显示为0
。不过我希望3
与emp_proc
一样
我不能将nvl
与in
一起使用,因为它希望子查询返回单个值。
我能想到的一种方法是在变量中重建整个查询,基于输入参数,然后使用execute immediate
。但是我使用一些变量来收集值,而execute immediate
很难实现它。
我再次强调,这是一个复杂过程的简单版本,我们捕获许多变量,并且它连接了许多表,并且在where子句中有多个AND
条件。
关于如何使这项工作的任何想法。
答案 0 :(得分:4)
这可能对您有所帮助
CREATE OR REPLACE PROCEDURE emp_poc2(in_names IN varchar2)
AS
cnt integer;
BEGIN
SELECT COUNT(*) INTO cnt
FROM emp1
WHERE
in_names IS NULL
OR ename IN (
SELECT TRIM(REGEXP_SUBSTR(in_names, '[^,]+', 1, level))
FROM dual
CONNECT BY INSTR(in_names, ',', 1, level - 1) > 0
);
dbms_output.put_line(cnt);
END;
其他方式可以使用IF ELSE或UNION ALL
答案 1 :(得分:1)
如果您的真实代码要复杂得多,那么使用适当的集合类型可能会大大提高您的代码可读性。
在下面的示例中,我创建了一个用户定义的类型str_list_t
,它是一个真正的字符串集合。
我还在sql查询中使用公用表表达式(CTE)来增强可读性。在这个简单的例子中,CTE对可读性的好处并不明显,但对于所有非平凡的查询,它是一个有价值的工具。
测试数据
create table emp1(empno number, empname varchar2(10));
insert into emp1 values(5437, 'GATES');
insert into emp1 values(5438, 'JOBS');
insert into emp1 values(5439, 'BEZOS');
insert into emp1 values(5440, 'MUSK');
insert into emp1 values(5441, 'CUBAN');
insert into emp1 values(5442, 'HERJAVEC');
commit;
支持数据类型
create or replace type str_list_t is table of varchar2(4000 byte);
/
<强>子程序强>
create or replace function emp_count(p_emps in str_list_t) return number is
v_count number;
v_is_null_container constant number :=
case
when p_emps is null then 1
else 0
end;
begin
-- you can also test for empty collection (that's different thing than a null collection)
with
-- common table expression (CTE) gives you no benefit in this simple example
emps(empname) as (
select * from table(p_emps)
)
select count(*)
into v_count
from emp1
where v_is_null_container = 1
or empname in (select empname from emps)
;
return v_count;
end;
/
show errors
运行示例
SQL> select 2 as expected, emp_count(str_list_t('BALLMER', 'CUBAN', 'JOBS')) as emp_count from dual
union all
select 0, emp_count(str_list_t()) from dual
union all
select 6, emp_count(null) from dual
;
EXPECTED EMP_COUNT
---------- ----------
2 2
0 0
6 6