Oracle
 sql >> Base de données >  >> RDS >> Oracle

Renvoie le résultat même pour les éléments de la liste IN qui n'existent pas dans la table

Du côté SQL, vous pouvez définir un type de table et l'utiliser pour joindre vos données réelles, quelque chose comme :

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;
/

Démo SQL Fiddle avec une fonction wrapper pour que vous puissiez l'interroger directement, ce qui donne :

        ID STATUS             
---------- --------------------
         1 present              
         2 present              
         3 present              
         4 missing              
         8 missing              
        23 present              

Depuis Java, vous pouvez définir un ARRAY en fonction du type de table, remplir à partir d'un tableau Java et appeler directement la fonction ; votre variable de liaison à paramètre unique est le ARRAY , et vous récupérez un ensemble de résultats sur lequel vous pouvez itérer normalement.

En guise d'aperçu du côté 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));
}

Ce qui donne :

id 1: present
id 2: present
id 3: present
id 4: missing
id 8: missing
id 23: present

Comme Maheswaran Ravisankar le mentionne, cela permet de passer n'importe quel nombre d'éléments; vous n'avez pas besoin de savoir combien d'éléments il y a au moment de la compilation (ou de gérer un maximum théorique), vous n'êtes pas limité par le nombre maximum d'expressions autorisées dans un IN ou par la longueur d'une seule chaîne délimitée, et vous n'avez pas à composer et décomposer une chaîne pour transmettre plusieurs valeurs.

Comme l'a souligné ThinkJet, si vous ne souhaitez pas créer votre propre type de table, vous pouvez utiliser une collection prédéfinie, illustrée ici; la fonction main est la même hormis la déclaration du paramètre :

create or replace function f42 (in_array sys.odcinumberlist)
return sys_refcursor as
...    

La fonction wrapper remplit le tableau légèrement différemment, mais du côté Java, vous n'avez qu'à modifier cette ligne :

ArrayDescriptor aDesc =
  ArrayDescriptor.createDescriptor("SYS.ODCINUMBERLIST", conn );

L'utilisation de cela signifie également (comme ThinkJet l'a également souligné !) que vous pouvez exécuter votre requête autonome d'origine sans définir de fonction :

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;

(Violon SQL).

Et cela signifie que vous pouvez appeler la requête directement depuis 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));
}

... que vous préférez peut-être.

Il existe un ODCIVARCHAR2LIST prédéfini tapez aussi, si vous passez réellement des chaînes - votre code d'origine semble fonctionner avec des chaînes même si elles contiennent des nombres, donc vous ne savez pas ce dont vous avez vraiment besoin.

Parce que ces types sont définis comme VARRAY(32767) vous êtes limité à 32 000 valeurs, tandis que la définition de votre propre table supprime cette restriction ; mais évidemment cela n'a d'importance que si vous transmettez beaucoup de valeurs.