Sql 在Oracle 11g中使用NVL和IN子句的替代方法
我有一个工作场所的问题,我正在寻找一个简单的解决办法。 我试图在一个较小的场景中复制它 简言之,问题 我想在in子句中使用nvl。目前,我有一个由名称组成的输入字符串。它用于where子句,如下所示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 +-------+
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