替代在Oracle 11g中使用NVL和IN子句

时间:2017-01-17 07:23:26

标签: sql oracle plsql oracle11g

我有一个工作场所问题,我正在寻找一个简单的解决方案。 我试图在较小的场景中复制它。

简短问题

我想在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。不过我希望3emp_proc一样 我不能将nvlin一起使用,因为它希望子查询返回单个值。

我能想到的一种方法是在变量中重建整个查询,基于输入参数,然后使用execute immediate。但是我使用一些变量来收集值,而execute immediate很难实现它。

我再次强调,这是一个复杂过程的简单版本,我们捕获许多变量,并且它连接了许多表,并且在where子句中有多个AND条件。

关于如何使这项工作的任何想法。

2 个答案:

答案 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