02 abril 2008

Restricción de unicidad con múltiples NULL

A estas alturas todavía caemos en el mismo error una y otra vez: creemos que una columna UNIQUE NULL permitirá múltiples valores NULL y sólo comprobará la unicidad para los que no lo sean. Partimos del supuesto de que NULL no es igual a NULL. Pero en SQL Server no sucede así, y sólo se permite 1 NULL (de hecho, dicen que en SQL Standard las columnas UNIQUE deben ser NOT NULL y no permitir ningún NULL). Tampoco lo permite ningún otro gestor de bases de datos, hasta la wikipedia lo dice cuando habla de NULL.
En este artículo la gente de SQL Server habla del tema y ofrece una solución: crear una vista sólo con los valores no nulos, y aplicar la unicidad a la columna de la vista. Esto repercute en la tabla si la vista se crea con SCHEMABINDING. Es una solución bastante simple, aunque requiere crear una vista para cada unicidad.
Otra solución propone utilizar un campo calculado en la misma tabla. El valor de ese campo será el de la columna a la que queremos imponer la unicidad cuando no sea null, y un valor distinto para cada fila cuando sea null (por ejemplo, la clave primaria, convertida a cadena cuando sea necesario). Es un sistema un poco más artificioso, pero al quedar todo contenido en la misma tabla me parece más atractivo. Pero tiene un problema: alguno de los valores de las filas que no son null puede coincidir con el valor de la clave primaria de alguna fila que sí es null, por lo que fallaría la unicidad, y habría que transformar la clave primaria al usarla en el campo calculado, como ponerle un prefijo si el campo es de texto, o hacerla negativa si es numérico. Pero esto no deja de ser arriesgado, y obliga a pensar cada caso, cuando usando la primera solución basada en una vista no hay que pensar nada, es más mecánica y por lo que acabamos de ver también es más robusta. La usaremos.
Por cierto, Access sí permite la opción Ignore Null en los índices, según se desprende del título de este artículo (muy bueno por cierto, ahí tenéis un resumen de las 2 soluciones anteriores más 2 nuevas, disparadores y dividir, que el propio autor descarta).

Etiquetas: ,