2010-11-18 11 views
39

L'affermazione èCome gestire (forse) valori null in una PreparedStatement?

SELECT * FROM tableA WHERE x = ? 

e il parametro viene inserito tramite java.sql.PreparedStatement 'stmt'

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

Se y è nullo, l'istruzione restituisce alcuna riga in ogni caso, perché è sempre x = null falso (dovrebbe essere x IS NULL). Una soluzione potrebbe essere

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

Ma poi devo impostare due volte lo stesso parametro. C'è una soluzione migliore?

Grazie!

+0

Non uso istruzioni preparate. Uso le stringhe SQL e quindi sostituisco '= NULL' con' Is Null' nella stringa SQL. Funziona come un fascino. –

+19

Questo non è sicuro dal tipo e potenzialmente vulnerabile agli attacchi per iniezione. – aioobe

risposta

29

L'ho sempre fatto come mostri nella tua domanda. Impostare lo stesso parametro due volte non è un disagio così grande, vero?

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

Grazie Paul per il tuo messaggio. Quindi vota per "questa è la migliore pratica"? – Zeemee

+0

Non ho mai trovato un modo migliore. –

+4

Avere due SQL separati potrebbe essere preferibile se le prestazioni sono importanti, tuttavia, poiché il percorso di accesso sarà diverso a seconda che si desideri IS NULL o meno. – Thilo

5

sarebbe solo utilizzare 2 diverse dichiarazioni:

Dichiarazione 1:

SELECT * FROM tableA WHERE x is NULL 

Dichiarazione 2:

SELECT * FROM tableA WHERE x = ? 

È possibile controllare la variabile e creare l'istruzione adeguata a seconda della condizione. Penso che questo renda il codice molto più chiaro e più facile da capire.

EDIT A proposito, perché non utilizzare le stored procedure? Quindi puoi gestire tutta questa logica NULL nell'SP e puoi semplificare le cose nella chiamata front-end.

+0

Grazie a dcp, questo mi sembra semplice. Ma immagina se la mia affermazione non ha 1 parametro, ma 5. Avrei bisogno di 5^2 istruzioni = 25! – Zeemee

+0

Quindi questo eviterà il problema che dice ancora '=' invece di 'IS'? – aioobe

+1

@ dcp, sembra che tu abbia cambiato la tua risposta da una cosa a qualcosa di completamente diverso. Perché? –

0

Se si utilizza per esempio mysql si potrebbe forse fare qualcosa di simile:

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

Poi anni potrebbe fare:

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

si dovrà controllare il vostro spiegare piano per vedere se migliora le tue prestazioni. Tuttavia, ciò significherebbe che la stringa vuota è uguale a null, quindi non è garantita per soddisfare le tue esigenze.

+1

Grazie a Knubo, ma non posso assicurarmi che le stringhe vuote non vengano utilizzate come valore. – Zeemee

+0

Se hai davvero bisogno delle prestazioni, potresti scegliere un valore che sei sicuro di non essere mai presente per testare. Questo sarebbe un trucco per le prestazioni, quindi preferirei evitarlo se potessi. (Ad esempio, potrebbe essere che i tuoi dati non contengano mai un singolo €, quindi potresti controllare quello invece di "." – Knubo

8

C'è un operatore ANSI-SQL piuttosto sconosciuto IS DISTINCT FROM che gestisce valori NULL. Può essere usato così:

SELECT * FROM tableA WHERE x NOT IS DISTINCT FROM ? 

Quindi è necessario impostare solo un parametro. Sfortunatamente, questo non è supportato da MS SQL Server (2008).

Un'altra soluzione potrebbe essere, se c'è un valore che è e non sarà mai utilizzato ('XXX'):

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

Provato con MySQL + PHP PDO e anche restituito 'false' quando prova a preparare la frase :( – Pere

+1

[IS DISTINCT FROM] (https://www.postgresql.org/docs/current/static/functions-comparison.html) funziona bene con PostgreSQL –

+0

Nelle vecchie versioni di Oracle, NVL può essere utilizzato per la stessa cosa, ma COALESCE è più bello se disponibile (vedi https://stackoverflow.com/questions/950084/oracle-differences-tra between-nvl-and-coalesce). –

0

In Oracle 11g, lo faccio in questo modo perché x = null viene valutata tecnicamente a UNKNOWN:

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

l'espressione prima della OR prende cura di equiparare NULL con NULL, allora l'espressione dopo prende cura di tutte le altre possibilità. LNNVL cambia UNKNOWN-TRUE, TRUE-FALSE e FALSE-TRUE, che è l'esatto opposto di ciò che vogliamo, da cui il NOT.

La soluzione accettata non funzionava per me in Oracle in alcuni casi, quando faceva parte di un'espressione più ampia, che implicava un NOT.