2010-11-18 11 views
39

Oświadczenie jestJak radzić sobie z (być może) wartościami pustymi w PreparedStatement?

SELECT * FROM tableA WHERE x = ? 

a parametr jest włożona przez java.sql.PreparedStatement „instr”

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

Jeśli y jest null, instrukcja zwraca nie wierszy w każdym przypadku, ponieważ jest zawsze x = null false (powinno być x IS NULL). Jednym rozwiązaniem byłoby

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

Ale wtedy trzeba ustawić ten sam parametr dwukrotnie. Czy istnieje lepsze rozwiązanie?

Dzięki!

+0

Nie używam przygotowanych wyciągów. Używam ciągów SQL, a następnie zamieniam '= NULL' na' Is Null' w ciągu SQL. Działa jak marzenie. –

+19

To nie jest bezpieczne i może być podatne na ataki przy użyciu wstrzyknięć. – aioobe

Odpowiedz

29

Zawsze robiłem to tak, jak pokazujesz w swoim pytaniu. Ustawienie tego samego parametru dwa razy nie jest tak wielkim trudem, prawda?

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

Dzięki Paul za notatkę. Więc głosujesz na "to jest najlepsza praktyka"? – Zeemee

+0

Nigdy nie znalazłem lepszego sposobu. –

+4

Posiadanie dwóch oddzielnych instrukcji SQL może być lepsze, jeśli wydajność jest ważna, ponieważ ścieżka dostępu będzie różna w zależności od tego, czy chcesz mieć wartość NULL, czy nie. – Thilo

5

po prostu użyć 2 różne stwierdzenia:

komunikat 1:

SELECT * FROM tableA WHERE x is NULL 

komunikat 2:

SELECT * FROM tableA WHERE x = ? 

można sprawdzić zmienną i zbudować odpowiednią deklarację w zależności od stanu. Myślę, że to sprawia, że ​​kod jest bardziej przejrzysty i łatwiejszy do zrozumienia.

EDYTUJ Nawiasem mówiąc, czemu nie skorzystać z procedur przechowywanych? Wtedy możesz obsłużyć wszystkie te logiki NULL w SP i możesz uprościć rzeczy na frontowym wywołaniu.

+0

Dzięki dcp, wygląda to prosto do mnie. Ale wyobraź sobie, że moje stwierdzenie nie ma 1 parametru, ale 5. Potrzebowałbym 5^2 stwierdzeń = 25! – Zeemee

+0

Więc to pozwoli uniknąć problemu, że nadal mówi '=' zamiast 'IS'? – aioobe

+1

@dcp, wydaje się, że zmieniłeś swoją odpowiedź z jednej rzeczy na coś zupełnie innego. Czemu? –

0

Jeśli używasz na przykład MySQL prawdopodobnie można zrobić coś takiego:

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

Następnie yo może zrobić:

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

Trzeba by sprawdzić swój wyjaśnić plan, aby sprawdzić, czy poprawia twoją wydajność. Oznaczałoby to jednak, że pusty łańcuch jest równy zeru, więc nie jest przyznany, aby pasował do twoich potrzeb.

+1

Dzięki Knubo, ale nie mogę się upewnić, że puste ciągi nie są używane jako wartość. – Zeemee

+0

Jeśli naprawdę potrzebujesz wydajności, możesz wybrać jakąś wartość, na którą jesteś pewny, że nigdy nie będziesz obecny w teście. To byłby hack, więc wolałbym tego uniknąć, gdybyś mógł. (Na przykład, może to oznaczać, że twoje dane nigdy nie zawierają jednego €, więc możesz to sprawdzić zamiast "." – Knubo

8

Istnieje dość nieznany operator ANSI-SQL IS DISTINCT FROM, który obsługuje wartości NULL. Może być używany w następujący sposób:

SELECT * FROM tableA WHERE x NOT IS DISTINCT FROM ? 

Należy ustawić tylko jeden parametr. Niestety nie jest to obsługiwane przez MS SQL Server (2008).

Innym rozwiązaniem mogłoby być, jeśli jest to wartość, która jest i nigdy nie będzie używany („XXX”):

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

Wypróbowałem to za pomocą MySQL + PHP PDO i zwróciło 'false' przy próbie przygotowania instrukcji :( – Pere

+1

[IS DISTINCT FROM] (https://www.postgresql.org/docs/current/static/functions-comparison.html) działa dobrze z PostgreSQL –

+0

W starszych wersjach Oracle, NVL może być używany do tego samego, ale COALESCE jest przyjemniejsze, jeśli jest dostępne (zobacz https://stackoverflow.com/questions/950084/oracle-differences-between-nvl-and-coalesce). –

0

W Oracle 11g, robię to w ten sposób, ponieważ x = null technicznie ocenia się UNKNOWN:

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

wyrażenie przed OR dba o zrównanie NULL NULL, wówczas wyrażenie po dba o wszystkich innych możliwości. LNNVL zmienia UNKNOWN do TRUE, TRUE do FALSE i FALSE do TRUE, która jest dokładnym przeciwieństwem tego, co chcemy, stąd NOT.

Przyjęte rozwiązanie nie działało dla mnie w Oracle w niektórych przypadkach, gdy było częścią większego wyrażenia, z udziałem NOT.