PL/SQL - 如何在 IN 子句中使用数组

Posted

技术标签:

【中文标题】PL/SQL - 如何在 IN 子句中使用数组【英文标题】:PL/SQL - How to use an array in an IN Clause 【发布时间】:2013-09-24 18:26:01 【问题描述】:

我正在尝试在 IN 子句中将一组输入值用于我的过程,作为游标 where 子句的一部分。我知道以前有人问过这个问题,但是我还没有看到如何使我的语法正确编译。

在封装规范中,类型是

TYPE t_brth_dt IS TABLE OF sourceTable.stdt_brth_dt%TYPE INDEX BY PLS_INTEGER;

sourceTable.std_brth_dt 是表中的日期列。

我的光标的简化版在包体中是-

 cursor DataCursor_Sort( p_brth_dt in t_brth_dt) is
    SELECT *
      FROM sourceTable 
     WHERE a.brth_dt IN (select column_value 
                           from table(p_brth_dt))

当我尝试编译它时,我收到以下错误。

[1]:(错误): PLS-00382: 表达式类型错误 [2]:(错误):PL/SQL:ORA-22905:无法访问非嵌套表项中的行

我知道这看起来与其他问题相似,但我不明白语法错误是什么。

【问题讨论】:

尝试 p_brth_dt in (select sourceTable.std_brth_dt from t_brth_dt) passing an array into oracle sql and using the array 的可能重复项 可悲的是(显然)企业级过程语言无法处理数组的简单概念。 使用 Clever Idea Widgetry 建议的流水线函数效果很好。我唯一需要添加的是一些代码来忽略异常 no_data_needed,因为我的程序抛出错误 ORA-06548。当 no_data_needed 然后为 null 时出现异常; 【参考方案1】:

为了在查询的from 子句中使用定义为嵌套表或关联数组的集合,正如@Alex Poole 正确指出的那样,您应该创建一个模式级别(SQL)类型或使用一个,当您打算使用日期列表时,您可以通过 ODCIConst 包 - odcidatelist 使用它。例如,您的光标定义可能如下所示:

cursor DataCursor_Sort(p_brth_dt in sys.odcidatelist) is
  select *
    from sourceTable 
   where a.brth_dt IN (select column_value 
                         from table(p_brth_dt))

cursor DataCursor_Sort(p_brth_dt in sys.odcidatelist) is
  select s.*
    from sourceTable      s
    join table(p_brth_dt) t
      on (s.brth_dt = t.column_value)

注意:在执行日期比较时,您应该考虑日期的时间部分。如果您只想比较日期部分,使用trunc() 函数去除时间部分可能会很有用。

【讨论】:

第二个语句中的错字 - t.brth_dt = s.column_value 应该是 s.brth_dt = t.column_value。 @0xdb 谢谢。【参考方案2】:

可以在IN 子句中间接使用 PL/SQL 定义的嵌套表类型(相对于 SQL 定义的嵌套表类型) PL/SQL 包中的SELECT 语句。您必须使用PIPELINED 函数作为中介。写起来感觉有点聪明,但我不相信它的基本用途。

CREATE OR REPLACE PACKAGE so18989249 IS

   TYPE date_plsql_nested_table_type IS TABLE OF DATE;
   dates date_plsql_nested_table_type;

   FUNCTION dates_pipelined RETURN date_plsql_nested_table_type PIPELINED;

   PROCEDURE use_plsql_nested_table_type;

END so18989249;
/

CREATE OR REPLACE PACKAGE BODY so18989249 IS

   FUNCTION dates_pipelined RETURN date_plsql_nested_table_type
      PIPELINED IS
   BEGIN
      IF (dates.count > 0)
      THEN
         FOR i IN dates.first .. dates.last
         LOOP
            IF (dates.exists(i))
            THEN
               PIPE ROW(dates(i));
            END IF;
         END LOOP;
      END IF;
   END;

   PROCEDURE use_plsql_nested_table_type IS
   BEGIN
      dates := NEW date_plsql_nested_table_type();

      -- tweak these values as you see fit to produce the dbms_output results you want
      dates.extend(5);
      dates(1) := DATE '2013-12-25';
      dates(2) := DATE '2013-01-01';
      dates(3) := DATE '2013-07-01';
      dates(4) := DATE '2013-09-03';
      dates(5) := DATE '2008-11-18';

      FOR i IN (SELECT o.owner,
                       o.object_name,
                       o.object_type,
                       to_char(o.last_ddl_time, 'YYYY-MM-DD') AS last_ddl
                  FROM all_objects o
                 WHERE trunc(o.last_ddl_time) IN 
                       (SELECT column_value FROM TABLE(dates_pipelined)) 
                       --uses pipeline function which uses pl/sql-defined nested table
      )
      LOOP
         dbms_output.put_line('"' || i.owner || '"."' || i.object_name || '" ("' || i.object_type || ') on ' || i.last_ddl);
      END LOOP;

   END;

END so18989249;
/

begin so18989249.use_plsql_nested_table_type; end;
/

【讨论】:

【参考方案3】:

类型必须在 SQL 级别创建,而不是在包中。 SQL 查询不知道如何使用 PL/SQL 中定义的任何类型。所以你必须这样做:

CREATE OR REPLACE TYPE t_brth_dt IS TABLE OF date;
/

... 并从您的包规范中删除该类型。 (或者至少给它们起不同的名称,并且它们在使用中不能互换)。因为它是在 SQL 级别,所以很遗憾,您也不能在声明中使用sourceTable.stdt_brth_dt%TYPE

【讨论】:

以上是关于PL/SQL - 如何在 IN 子句中使用数组的主要内容,如果未能解决你的问题,请参考以下文章

如何转换逗号分隔的 varchar 以用于 pl/sql 中的“IN”子句?

如何在选择语句的“NOT IN”子句中使用逗号分隔的字符串列表作为 pl/sql 存储的函数参数

PL/SQL - 在 Where In 子句中使用“列表”变量

如何使用PL / SQL更改您在FOR循环IN子句中查询的表

如何在使用变量的动态查询中指定 IN 子句?

如何在 PL/SQL 的 LIKE 子句中使用变量