2010-11-18 10 views
39

La déclaration estComment gérer les valeurs null (peut-être) dans un PreparedStatement?

SELECT * FROM tableA WHERE x = ? 

et le paramètre est inséré par java.sql.PreparedStatement « stmt »

stmt.setString(1, y); // y may be null 

Si y est nulle, l'instruction renvoie aucune ligne dans tous les cas parce que x = null est toujours false (doit être x IS NULL). Une solution serait

SELECT * FROM tableA WHERE x = ? OR (x IS NULL AND ? IS NULL) 

Mais je dois définir le même paramètre deux fois. Y a-t-il une meilleure solution?

Merci!

+0

Je n'utilise pas d'instructions préparées. J'utilise des chaînes SQL, puis je remplace '= NULL' par' Is Null' dans la chaîne SQL. Fonctionne comme un charme. –

+19

Ce type n'est pas sûr et peut-être vulnérable aux attaques par injection. – aioobe

Répondre

29

Je l'ai toujours fait comme vous le montrez dans votre question. Définir le même paramètre deux fois n'est pas une si grande difficulté, n'est-ce pas?

SELECT * FROM tableA WHERE x = ? OR (x IS NULL AND ? IS NULL); 
+0

Merci Paul pour votre note. Donc vous votez pour 'ceci est la meilleure pratique'? – Zeemee

+0

Je n'ai jamais trouvé de meilleur moyen. –

+4

Avoir deux SQL distincts peut être préférable si la performance est importante, cependant, parce que le chemin d'accès sera différent selon que vous voulez IS NULL ou non. – Thilo

5

serait tout simplement utiliser 2 déclarations différentes:

Déclaration 1:

SELECT * FROM tableA WHERE x is NULL 

Déclaration 2:

SELECT * FROM tableA WHERE x = ? 

Vous pouvez vérifier votre variable et construire la déclaration appropriée en fonction de la condition. Je pense que cela rend le code beaucoup plus clair et plus facile à comprendre.

EDIT Par ailleurs, pourquoi ne pas utiliser des procédures stockées? Ensuite, vous pouvez gérer toute cette logique NULL dans le SP et vous pouvez simplifier les choses sur l'appel frontal.

+0

Merci dcp, cela me semble simple. Mais imaginez si ma déclaration n'a pas 1 paramètre, mais 5. J'aurais besoin de 5^2 Déclarations = 25! – Zeemee

+0

Donc, cela évitera le problème qu'il dit toujours '=' au lieu de 'IS'? – aioobe

+1

@dcp, vous semblez avoir changé votre réponse d'une chose à quelque chose de très différent. Pourquoi? –

0

Si vous utilisez par exemple MySQL, vous pouvez probablement faire quelque chose comme:

select * from mytable where ifnull(mycolumn,'') = ?; 

Alors yo pourrait faire:

stmt.setString(1, foo == null ? "" : foo); 

vous devez vérifier votre expliquer le plan pour voir si cela améliore votre performance. Cela signifierait que la chaîne vide est égale à null, donc il n'est pas accordé que cela corresponde à vos besoins.

+1

Merci Knubo, mais je ne peux pas m'assurer que les chaînes vides ne sont pas utilisées comme valeur. – Zeemee

+0

Si vous avez vraiment vraiment besoin de la performance, vous pouvez choisir une valeur que vous êtes sûr de ne jamais avoir à tester. Ce serait un hack de performance, donc je préfère éviter cela si vous le pouvez. (Par exemple, il se peut que vos données ne contiennent jamais un seul €, donc vous pouvez vérifier cela au lieu de ''.) – Knubo

8

L'opérateur ANSI-SQL IS DISTINCT FROM qui gère les valeurs NULL est tout à fait inconnu. Il peut être utilisé comme cela:

SELECT * FROM tableA WHERE x NOT IS DISTINCT FROM ? 

Un seul paramètre doit donc être défini. Malheureusement, ceci n'est pas supporté par MS SQL Server (2008).

Une autre solution pourrait être, s'il y a une valeur qui est et sera jamais utilisé (« XXX »):

SELECT * FROM tableA WHERE COALESCE(x, 'XXX') = COALESCE(?, 'XXX') 
+0

J'ai essayé avec MySQL + PHP PDO et j'ai aussi retourné 'false' en essayant de préparer la déclaration :( – Pere

+1

[EST DISTINCT DE] (https://www.postgresql.org/docs/current/static/functions-comparison.html) fonctionne bien avec PostgreSQL –

+0

Dans les anciennes versions d'Oracle, NVL peut être utilisé pour la même chose, mais COALESCE est plus agréable s'il est disponible (voir https://stackoverflow.com/questions/950084/oracle-differences-between-nvl-and-coalesce). –

0

Dans Oracle 11g, je le fais de cette façon parce que x = null évalue techniquement UNKNOWN:

WHERE (x IS NULL AND ? IS NULL) 
    OR NOT LNNVL(x = ?) 

l'expression avant la OR prend en charge assimilant NULL avec NULL, l'expression prend après soin de toutes les autres possibilités. LNNVL change UNKNOWN-TRUE, TRUE-FALSE et FALSE-TRUE, ce qui est exactement le contraire de ce que nous voulons, d'où le NOT.

La solution acceptée ne fonctionnait pas pour moi dans Oracle dans certains cas, quand il faisait partie d'une plus grande expression, impliquant un NOT.

Questions connexes