Warning: file_get_contents(/data/phpspider/zhask/data//catemap/1/oracle/10.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Sql 在Oracle 11g中使用NVL和IN子句的替代方法_Sql_Oracle_Plsql_Oracle11g - Fatal编程技术网

Sql 在Oracle 11g中使用NVL和IN子句的替代方法

Sql 在Oracle 11g中使用NVL和IN子句的替代方法,sql,oracle,plsql,oracle11g,Sql,Oracle,Plsql,Oracle11g,我有一个工作场所的问题,我正在寻找一个简单的解决办法。 我试图在一个较小的场景中复制它 简言之,问题 我想在in子句中使用nvl。目前,我有一个由名称组成的输入字符串。它用于where子句,如下所示 and column_n = nvl(in_parameter,column_n) 现在我想在同一个输入参数中传递多个逗号分隔的值。因此,如果我将=替换为in,并将输入的逗号分隔字符串转换为行,那么就不能使用nvl子句 详细问题 让我们考虑一个雇员表EMP1../P> Emp1 +-------+

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

简言之,问题

我想在in子句中使用nvl。目前,我有一个由名称组成的输入字符串。它用于where子句,如下所示

and column_n = nvl(in_parameter,column_n)
现在我想在同一个输入参数中传递多个逗号分隔的值。因此,如果我将=替换为in,并将输入的逗号分隔字符串转换为行,那么就不能使用nvl子句

详细问题

让我们考虑一个雇员表EMP1../P>

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
现在我想实现的是添加另一个功能。所以执行官emp_poc'KING BLAKE应该给我2个。因此,我想出了一种将逗号分隔的字符串拆分为行的方法,并在过程中使用了这种方法

因此,如果我将where子句更改为in

所以执行官emp_poc2'KING','BLAKE'给了我2。但传递null将给出结果0。但是我想得到3,就像emp_proc的情况一样 我不能将nvl与in一起使用,因为它期望子查询返回单个值

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

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

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

这可能对你有帮助

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,这可能会对您有所帮助

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

如果您的实际代码要复杂得多,那么您的代码可读性可能会通过使用适当的集合类型而大大增强

在下面的示例中,我创建了一个用户定义的类型str\u list\t,它是字符串的真实集合

我还在sql查询中使用公共表表达式CTE来增强可读性。在这个简单的例子中,CTE对可读性的好处并不明显,但对于所有非平凡的查询来说,它是一个有价值的工具

测试数据

支持数据类型

子程序

示例运行


如果实际代码要复杂得多,那么使用适当的集合类型可能会大大提高代码的可读性

在下面的示例中,我创建了一个用户定义的类型str\u list\t,它是字符串的真实集合

我还在sql查询中使用公共表表达式CTE来增强可读性。在这个简单的例子中,CTE对可读性的好处并不明显,但对于所有非平凡的查询来说,它是一个有价值的工具

测试数据

支持数据类型

子程序

示例运行


很抱歉迟了答复。我成功地测试了它。谢谢很抱歉迟了答复。我成功地测试了它。谢谢_names数据类型中的输入变量是字符串还是可以改为集合/容器数据类型?这完全不是强制性的。在SQL或PL/SQL中,逗号分隔列表并不像在其他一些语言中那样是一种东西,它只是一个碰巧包含逗号的文本字符串。没有内置的方法将其视为文字列表、元素数组或其他任何东西。@WilliamRobertson您能说出一种语言,其中包含逗号的文本字符串不仅仅是文本字符串吗?还是我误读了你的评论?我脑子里有JavaScript和Bash。当然,这些变量并不完全将包含逗号的字符串变量视为数组,但它们以SQL不具备的方式非常透明地转换它们。是强制的吗?名称数据类型中的输入变量是字符串还是可以改为集合/容器数据类型?根本不是强制的。在SQL或PL/SQL中,逗号分隔列表并不像在其他一些语言中那样是一种东西,它只是一个碰巧包含逗号的文本字符串。没有内置的方法将其视为文字列表、元素数组或其他任何东西。@WilliamRobertson您能说出一种语言,其中包含逗号的文本字符串不仅仅是文本字符串吗?还是我误读了你的评论?我脑子里有JavaScript和Bash。诚然,这些函数并不完全将包含逗号的字符串变量视为数组,但它们以SQL所不具备的方式非常透明地对其进行转换。简单地将ename添加到表中并进行连接怎么样?@user272735:感谢您的回复和建议。我一定会去看看,然后
看看如何将其合并到实际过程中。@BobC我想你是在建议一个全局临时表?听起来很聪明——我从来没有想过这个选择!事实上,我不记得我曾经使用过GTT。我需要记下这件事。当我遇到这种情况时,过滤列表通常是一个简短的id号列表,最多十个,所以GTT听起来有点过分了,因为它是存储在磁盘上,而不是存储在内存中。但是,考虑到更大的数据量,可以选择一个GTT,甚至是一个带有密钥的常规堆表。很可能它不会进入磁盘,而是在缓冲区缓存中。简单地将珐琅添加到表并进行连接怎么样?@user272735:感谢您的回复和建议。我一定会检查它,然后看看如何将它合并到实际过程中。@BobC我猜你是在建议一个全局临时表?听起来很聪明——我从来没有想过这个选择!事实上,我不记得我曾经使用过GTT。我需要记下这件事。当我遇到这种情况时,过滤列表通常是一个简短的id号列表,最多十个,所以GTT听起来有点过分了,因为它是存储在磁盘上,而不是存储在内存中。但是,考虑到更大的数据量,可以选择一个GTT,甚至是一个带有密钥的常规堆表。很可能它无论如何都不会进入磁盘,而是在缓冲区缓存中。
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