miércoles, 3 de diciembre de 2014

Convertir columna NULL en columna NOT NULL


1. EL PROBLEMA

Parece una tontería (y en el fondo, lo es), pero esta mañana me he enfrentado a la necesidad de tener que cambiar el diseño de una tabla para especificar la restricción sobre una columna, que inicialmente podía contener valores nulos, para que fuera una columna NOT NULL. Coser y cantar, me he dicho... Pero no. La cosa se ha complicado un poco.

Figura 1. Situación de partida: actualmente la columna id_concurso puede ser NULL. Queremos cambiar esto

Antes de nada, habrá que rellenar de datos esta columna, para asegurarnos de que no hay ningún registro con NULL en ese campo. Esto es de cajón (no puedo indicar que sea NOT NULL una columna que contiene datos NULL), así que asumimos que esto ya lo hemos hecho (por ejemplo, se pueden actualizar todos los NULL a un valor especial como cero o un número negativo).


2. PRIMER INTENTO: EL DISEÑADOR GRÁFICO DE TABLAS

Como la base de datos está en una instancia de SQL Server, en primer lugar, he intentado hacer el cambio desde la interfaz gráfica del SQL Server 2008 Management Studio, la herramienta oficial de administración de bases de datos de Microsoft, asumiendo que la cosa iba a ser cosa de desmarcar el checkbox y guardar.

Figura 2. Desmarcar el checkbox de "Permitir valores NULL" y ya está. ¿O no?

Pero... cuando lo he intentado, ¡mi gozo en un pozo!

Figura 3. Mensaje de error. No se puede hacer desde la interfaz gráfica. ¡OMG!


3. SEGUNDO INTENTO: DDL

Bueno. No es la primera vez que esta herramienta, el Management Studio, me da estos disgustos. A veces impide hacer cosas desde la interfaz gráfica (como alterar la PRIMARY KEY) pero me permite hacerlas escribiendo la correspondiente instrucción DDL en una ventana de consulta. Así que intentaré hacerlo así.

Después de intentar recordar (sin éxito, mi memoria no da para mucho) la sintaxis de la instrucción ALTER TABLE para hacer esto, me he tenido que lanzar a una búsqueda en Internet, asumiendo que lo iba a encontrar en menos de un minuto. Pero...

... buscando... buscando... buscando...

...tras unos minutos dando vueltas, me encuentro con que aparentemente NO se puede hacer en un único paso (ah, quizás mi memoria no era tan mala después de todo). Aquí tenéis una explicación (en inglés) de cómo se puede hacer para Firebird, pero imagino que el proceso puede valer para cualquier otro motor de BD. Básicamente, los pasos son:

1. Crear una nueva columna NOT NULL
2. Rellenar de datos esta columna con los datos de la columna original
3. Eliminar la columna original
4. Renombrar la columna nueva con el nombre de la columna original

OJO: si lo miráis en el ejemplo de Firebird que os he enlazado, la sintaxis que allí aparece no es válida para SQL Server (no sé si es que con Firebird vale, o es que directamente han cometido algún error sintáctico).

Se entiende, ¿no?

Ok, manos a la obra.


Paso 1. Crear nueva columna temporal

Al ejecutar la primera instrucción, obtengo el mensaje de error siguiente:

Figura 4. Mensaje de error. No puedo crear una columna NOT NULL. 

Claro, qué despiste. Si crea una columna nueva NOT NULL, ¿qué valores le asignaríamos a los registros existentes en esa columna? Así que tendremos que especificar un valor DEFAULT para esos registros, dejando la instrucción así (esto no lo tuvieron en cuenta en el ejemplo del Firebird)

1. ALTER TABLE mitabla ADD columna_nueva INTEGER NOT NULL DEFAULT 0


Figura 5. Ahora sí: nueva columna no nula creada. Datos por defecto a 0 (cero)


Paso 2. Rellenar los datos

Figura 6. Actualizar el nuevo campo a partir del antiguo. Sin problemas


Paso 3. Eliminar la columna original

Figura 7. Eliminar columna


Paso 4. Renombrar la columna

Figura 8. Vaya, la sintaxis en SQL Server no es 'RENAME COLUMN'.

Un par de consultas al patito y veo que en SQL Server, para renombrar una columna de una tabla, hay que usar el procedimiento sp_rename,

Figura 9. Renombrar una columna con sp_rename. ¿Por qué no me funciona?

Bueno, como veréis en la figura 9, no me ha funcionado, seguramente por algún error de sintaxis. Me he vuelto loco probando distintas maneras: con comillas sencillas, con comillas dobles, con el nombre simple de la tabla, con el nombre de la tabla totalmente cualificado (precedido de esquema y propietario)... y no he conseguido ejecutar el sp_rename. Lo que sí he comprobado es que con el diseñador, en la interfaz gráfica, sí que te deja modificar el nombre de la columna, así que he optado por esa opción, la fácil. Pero agradecería si alguien me sabe decir por qué no me ha funcionado ninguna de las opciones anteriores.

Figura 10. Podemos renombrar la columna desde la vista Diseño de la tabla


4. CONCLUSIÓN

Aunque en principio parece una operación trivial modificar el estatus de una columna de una tabla que acepta valores nulos a la situación en que no los acepta, a la hora de la verdad la cosa se puede complicar un poco.

Como resumen, aquí están los pasos que yo he dado en SQL Server 2008 Management Studio

1. ALTER TABLE mitabla ADD columna_nueva INTEGER NOT NULL DEFAULT 0;
2. UPDATE mitabla SET columna_nueva = columna_original
3. ALTER TABLE mitabla DROP COLUMN columna_original
4. Renombrar 'columna_nueva' a 'columna_original' en el diseñador gráfico

En la explicación en inglés que os he puesto arriba introducen un COMMIT entre los pasos 1 y 2 y otro entre los pasos 2 y 3. Me parece una muy buena idea, aunque os dejo a vosotros el pensar por qué.


EPILOGO

Después de tener esta entrada escrita, encuentro (concretamente, aquí) que hay una forma muuuuuuuucho más simple de hacer lo que yo quería

ALTER TABLE dbo.mitabla ALTER COLUMN columna_original INTEGER NOT NULL

La he probado y funciona perfectamente

Figura 11. ¡Haber empezado por ahí, hombre!


A la vista del resultado, a punto he estado de borrar esta entrada, pero no lo he hecho por varias razones

1) Ilustra un proceso que ocurre muchas veces en informática. Uno se plantea una forma de hacer las cosas, y cuando está cerca del final, descubre otra forma más sencilla de hacerlo. Mi opinión es que no hay que ser nostálgico, no te aferres a "tu" código por el hecho de que tú lo has "parido". Si hay una forma mejor, o más sencilla, tira lo viejo y quédate con lo mejor.

2) En el ejemplo del Firebird que encontré, se indicaba que con ese motor de BD había que hacerlo así, con la secuencia de cuatro pasos que os he puesto arriba. No he probado si también allí funciona la forma "simple", pero si no funciona, habría que usar la "compleja". Lo mismo vale para otros SGBD (Sistemas Gestores de Bases de Datos, o motores).

3) La secuencia de cuatro pasos indicada tiene sentido desde el punto de vista pedagógico, para aquellas personas que están aprendiendo temas de administración de bases de datos.

4) Como digo a veces, el lema de este blog es "Lo explico para enterarme yo". Es decir, si no me hubiera planteado explicar el problema paso a paso, quizás no hubiera encontrado la solución simple. Al menos, queda aquí para mí como una lección aprendida.

5) Y además, ya que la tenía escrita no iba a borrarla, ¿verdad? ;-) (esto es una broma, como digo en el punto 1: "no te aferres, no te aferres...").

2 comentarios:

  1. Jajaja, he tenido este problema, y me leí todo tu post. No podía hacer el cambio por la interfaz gráfica, estando ya en SQL Server 2014, pero con la instrucción se pudo de imnediato. Así que al final la solución, fuera la que fuese, vale.

    ResponderEliminar
  2. Me alegro de que te haya sido útil. Uno nunca deja de pelear para superar estos pequeños escollos en esta profesión... ¡ains!

    ResponderEliminar