MSSQL: null + string, un cocktail da evitare

Oggi ti parlerò di un problema che potrebbe manifestarsi se hai a che fare con la scrittura di query su Microsoft SQL Server, in particolare riguardo la concatenazione delle stringhe con NULL.

Dopo averci perso ore in debug, spero di fare cosa gradita a tutti spiegando cosa ho scoperto e come porvi rimedio.

Molti utilizzano la concatenazione di stringhe, ad esempio:

SELECT name + ' ' + surname AS fullname FROM table

con una tabella table come la seguente

+---------+----------+
|name     |surname   |
+---------+----------+
!Mario    |Rossi     !
+---------+----------+

la suddetta query restituirebbe un solo record così formattato:

+-----------+
|fullname   |
+-----------+
!Mario Rossi!
+-----------+

Niente di strano fin qui.

Un campo a NULL

Se la tabella fosse invece:

+---------+----------+
|name     |surname   |
+---------+----------+
!Mario    |NULL      !
+---------+----------+

Sei sicuro di sapere cosa restituirebbe la medesima query?

Prima di rispondere pensaci un attimo.

Una questione di driver

La risposta alla domanda precedente è: DIPENDE… da CHI interroga il database!

Prendi in considerazione un’installazione di default di MSSQL, qualunque versione:

Caso 1

Eseguendo la query dall’Enterprise Manager il risultato sarà:

+-----------+
|fullname   |
+-----------+
!NULL       !
+-----------+

perché l’opzione CONCAT_NULL_YIELDS_NULL di MSSQL per default è impostata a ON.

Caso 2

Eseguendo la query da un driver JDBC sul medesimo sistema, il risultato “potrebbe” essere:

+-----------+
|fullname   |
+-----------+
!NULL       !
+-----------+

che è uguale al caso precedente, ma la scelta di onorare l’opzione CONCAT_NULL_YIELDS_NULL di MSSQL da parte del drive JDBC è, sembra, a discrezione di chi ha scritto il driver, e, se la documentazione non lo specifica o non esamini direttamente il sorgente del driver, non lo saprai mai.

Caso 3

Eseguendo la query tramite un driver come FreeTDS, distribuito ad esempio con il package libsybdb5 v0.91-6 (attualmente su Ubuntu e derivati), il risultato è:

+-----------+
|fullname   |
+-----------+
!'Mario '   ! (gli apici non sono nell'output,
+-----------+  servono solo a far notare lo spazio!)

Per provarlo da shell, anche senza tabelle, ti basterà eseguire via shell il comando fisql che trovi nel pacchetto freetds-bin sostituendo le parole in corsivo con i parametri del tuo database:

$ fisql -D databasename -S databasehost -U username
Password: *******
1>> select 'string1' + NULL + 'string2'
2>> go
---------------
string1string2
(1 rows affected)

Come si può notare il risultato NON è NULL come nei casi precedenti e stai usando direttamente il driver suddetto, senza ulteriori “interpretazioni” da parte di altri strati di codice.

Non è scontato che il driver utilizzato onori il valore dell’opzione CONCAT_NULL_YIELDS_NULL di MSSQL riguardo la concatenazione di null e stringhe.

Ciò detto, per stare al sicuro dalle scelte implementative di chi ha scritto il dirver, devi metterti nella condizione di non concatenare MAI una stringa e NULL.

Come? Prendendo l’esempio precedente, cambiando da così:

SELECT name + ' ' + surname AS fullname FROM table

a così:

SELECT ISNULL(name, '') + ' ' + ISNULL(surname,'') AS fullname FROM table

In questo modo qualunque NULL sarà sostituito da una stringa vuota, o da quello che preferisci.

In alternativa, puoi cambiare strategia e restituire NULL al posto della stringa concatenata, simulando via SQL il comportamento nativo di MSSQL, ovvero:

SELECT CASE WHEN name IS NULL OR surname IS NULL THEN NULL ELSE
name + ' ' + surname END AS fullname FROM table

In questo modo riceverai sempre NULL, che sia onorata o meno l’opzione dal driver utilizzato.

Cambiare CONCAT_NULL_YIELDS_NULL?

Potresti essere tentato di impostare tale opzione a OFF, in modo che MSSQL restituisca sempre il risultato del caso 3, ma… Microsoft non pensa che sia una buona idea, documentandolo ufficialmente.

In conclusione quindi le uniche opzioni rimaste sembrano essere le due suggerite prima, oppure, puoi evitare del tutto la concatenazione in SQL e implementarla solo nel tuo codice applicativo.

comments powered by HyperComments