即使对于IN列表中的表中不存在的元素,也返回结果

Fru*_*ner 0 sql oracle plsql

我试图找到返回结果集的最简单方法,该结果集指示表中是否存在某些值.考虑一下这个表:

id 
------
  1
  2
  3
  7
  23

我将收到一个ID列表,我需要回复相同的列表,指出表中存在哪些.如果我得到的列表如下:'1','2','3','4','8','23',我需要生成一个如下所示的结果集:

id  |  status
-------------
  1 | present
  2 | present
  3 | present
  4 | missing
  8 | missing
 23 | present

到目前为止,我已经设法使用以下方法UNPIVOT:

select id, 'present' as status
from my_table
where id in ('1','2','3')
union
select subq.v as id, 'missing' as status
from (
        select v
        from
        (
          (
            select '1' v1, '2' v2, '3' v3 from dual
          )
          unpivot
          (
            v
            for x in (v1,v2,v3)
          )
        )
      ) subq
where subq.v not in
(
   select id
   from my_table 
   where id in ('1','2','3')
);
Run Code Online (Sandbox Code Playgroud)

它看起来有点奇怪,但确实有效.这个问题的select '1' v1, '2' v2, '3' v3 from dual部分是:我不知道如何使用JDBC预处理语句填充它.ID列表不是固定的,因此对使用此查询的函数的每次调用都可以传递不同的ID列表.

有没有其他方法来完成这项工作?我想我错过了一些明显的东西,但我不确定......

(使用Oracle 11)

Ale*_*ole 5

从SQL端,您可以定义表类型并使用它来连接到您的真实数据,例如:

create type my_array_type as table of number
/

create or replace function f42 (in_array my_array_type)
return sys_refcursor as
  rc sys_refcursor;
begin
  open rc for
    select a.column_value as id,
      case when t.id is null then 'missing'
        else 'present' end as status
    from table(in_array) a
    left join t42 t on t.id = a.column_value
    order by id;

  return rc;
end f42;
/
Run Code Online (Sandbox Code Playgroud)

带有包装函数的SQL Fiddle演示,因此您可以直接查询它,它给出:

        ID STATUS             
---------- --------------------
         1 present              
         2 present              
         3 present              
         4 missing              
         8 missing              
        23 present              
Run Code Online (Sandbox Code Playgroud)

从Java中,您可以ARRAY根据表类型定义,从Java数组填充,并直接调用该函数; 你的单个参数绑定变量是ARRAY,你得到一个你可以正常迭代的结果集.

作为Java方面的概述:

int[] ids = { 1, 2, 3, 4, 8, 23 };
ArrayDescriptor aDesc = ArrayDescriptor.createDescriptor("MY_ARRAY_TYPE",
  conn);
oracle.sql.ARRAY ora_ids = new oracle.sql.ARRAY(aDesc, conn, ids);

cStmt = (OracleCallableStatement) conn.prepareCall("{ call ? := f42(?) }");
cStmt.registerOutParameter(1, OracleTypes.CURSOR);
cStmt.setArray(2, ora_ids);
cStmt.execute();
rSet = (OracleResultSet) cStmt.getCursor(1);

while (rSet.next())
{
    System.out.println("id " + rSet.getInt(1) + ": " + rSet.getString(2));
}
Run Code Online (Sandbox Code Playgroud)

这使:

id 1: present
id 2: present
id 3: present
id 4: missing
id 8: missing
id 23: present
Run Code Online (Sandbox Code Playgroud)

正如Maheswaran Ravisankar所提到的,这允许通过任意数量的元素; 你不需要知道在编译时有多少元素(或处理理论上的最大值),你不受限于一个IN或多个分隔字符串长度允许的最大表达式数,并且你不必撰写和分解字符串来传递多个值.


正如ThinkJet指出的那样,如果您不想创建自己的表类型,可以使用预定义的集合,在此处演示 ; 除了参数声明之外,main函数是相同的:

create or replace function f42 (in_array sys.odcinumberlist)
return sys_refcursor as
...    
Run Code Online (Sandbox Code Playgroud)

包装器函数以稍微不同的方式填充数组,但在Java端,您只需要更改此行:

ArrayDescriptor aDesc =
  ArrayDescriptor.createDescriptor("SYS.ODCINUMBERLIST", conn );
Run Code Online (Sandbox Code Playgroud)

使用它也意味着(正如ThinkJet也指出的那样!)您可以在不定义函数的情况下运行原始独立查询:

select a.column_value as id,
case when t.id is null then 'missing'
else 'present' end as status
from table(sys.odcinumberlist(1, 2, 3, 4, 8, 23)) a
left join t42 t on t.id = a.column_value
order by id;
Run Code Online (Sandbox Code Playgroud)

(SQL小提琴).

这意味着您可以直接从Java调用查询:

int[] ids = { 1, 2, 3, 4, 8, 23 };
ArrayDescriptor aDesc = ArrayDescriptor.createDescriptor("SYS.ODCINUMBERLIST", conn );
oracle.sql.ARRAY ora_ids = new oracle.sql.ARRAY(aDesc, conn, ids);

sql = "select a.column_value as id, "
    + "case when t.id is null then 'missing' "
    + "else 'present' end as status "
    + "from table(?) a "
    + "left join t42 t on t.id = a.column_value "
    + "order by id";
pStmt = (OraclePreparedStatement) conn.prepareStatement(sql);
pStmt.setArray(1, ora_ids);
rSet = (OracleResultSet) pStmt.executeQuery();

while (rSet.next())
{
    System.out.println("id " + rSet.getInt(1) + ": " + rSet.getString(2));
}
Run Code Online (Sandbox Code Playgroud)

...你可能更喜欢.

还有一个预定义的ODCIVARCHAR2LIST类型,如果你实际上正在传递字符串 - 你的原始代码似乎正在使用字符串,即使它们包含数字,所以不确定你真正需要的是什么.

因为这些类型被定义为VARRAY(32767)限制为32k值,所以定义自己的表会删除该限制; 但显然,只有你传递了很多价值观才有意义.

  • +1,但可以使用预定义的Oracle类型作为参数(在数字Id的情况下为`Sys.ODCINumberList`) (2认同)
  • @ThinkJet - 是的; 我可能过分关注JDBC的要求.我已经用独立查询和修改函数的那些点和演示更新了答案.我将添加一个不需要该功能的JDBC版本.谢谢! (2认同)