Cambie rápidamente la columna NULL a NOT NULL

11

Tengo una tabla con millones de filas y una columna que permite valores NULL. Sin embargo, ninguna fila tiene actualmente un valor NULL para esa columna (puedo verificar esto bastante rápido con una consulta). Sin embargo cuando ejecuto el comando

ALTER TABLE MyTable ALTER COLUMN MyColumn BIGINT NOT NULL;

la consulta lleva una eternidad relativamente hablando. En realidad, toma entre 10 y 20 minutos, más del doble de tiempo que agregar una restricción de verificación. ¿Hay alguna manera de actualizar instantáneamente los metadatos de la tabla para esa columna, especialmente porque sé que ninguna fila tiene un valor NULL para esa columna?

Joseph Daigle
fuente
2
No (o al menos no utilizando métodos documentados / compatibles). Vea ¿Por qué ALTERAR COLUMNA a NO NULO causa un crecimiento masivo de archivos de registro?
Martin Smith
2
También podría estar esperando un Sch-Mbloqueo cuando se tarda "para siempre". ¿Miraste para ver si estaba esperando u ocupado?
Martin Smith
@ MartinSmith Aclaré lo que quiero decir con siempre . Estoy probando esto en un entorno de desarrollo sin que otras sesiones lleguen a la base de datos. Eventualmente se completa. Pero la respuesta que vinculó explica por qué lleva tanto tiempo. Si puede reformular su comentario como respuesta, lo aceptaré.
Joseph Daigle

Respuestas:

12

La respuesta de @ypercube logra esto parcialmente como un cambio de metadatos solamente.

Agregar la restricción con NOCHECKsignifica que no será necesario leer filas para verificarlo, y si está comenzando desde una posición donde la columna no contiene NULLvalores (y si sabe que no se agregará ninguno entre la verificación y la adición de la restricción), entonces, como los previene de restricción NULLvalores que se crean a partir de futuro INSERTo de UPDATEoperaciones, esto va a funcionar.

Sin embargo, agregar la restricción aún puede tener un impacto en las transacciones concurrentes. El ALTER TABLEtendrá que adquirir un Sch-Mbloqueo en primer lugar. Mientras espera esto, todos los demás accesos a la tabla serán bloqueados como se describe aquí .

Sch-MSin embargo, una vez que se obtiene el bloqueo, la operación debería ser bastante rápida.

Un problema con esto es que, incluso si sabe que la columna de hecho no tiene NULLseguridad, el optimizador de consultas no confía en la restricción, lo que significa que los planes pueden ser subóptimos.

CREATE TABLE T (X INT NULL)

INSERT INTO T 
SELECT ROW_NUMBER() OVER (ORDER BY @@SPID)
FROM master..spt_values

ALTER TABLE T WITH NOCHECK
  ADD  CONSTRAINT X_NOT_NULL 
    CHECK (X IS NOT NULL) ; 

SELECT *
FROM T 
WHERE X NOT IN (SELECT X FROM T)

Plan

Compare esto con el más simple

ALTER TABLE T ALTER COLUMN X INT NOT NULL

SELECT *
FROM T 
WHERE X NOT IN (SELECT X FROM T)

Plan

Un posible problema que puede encontrar al alterar la definición de columna de esta manera es que no solo necesita leer todas las filas para verificar que cumplan con la condición, sino que también puede terminar realizando actualizaciones registradas en las filas .

Una posible casa a mitad de camino podría ser agregar la restricción de verificación WITH CHECK. Esto será más lento de WITH NOCHECKlo que necesita para leer todas las filas, pero permite que el optimizador de consultas proporcione el plan más simple en la consulta anterior y debería evitar el posible problema de actualizaciones registradas.

Martin Smith
fuente
7

Podría, en lugar de alterar la columna, agregar una CHECKrestricción de tabla con la NOCHECKopción:

ALTER TABLE MyTable WITH NOCHECK
  ADD  CONSTRAINT MyColumn_NOT_NULL 
    CHECK (MyColumn IS NOT NULL) ;
ypercubeᵀᴹ
fuente
1
Esto evitaría futuras actualizaciones o inserciones que conforman la columna NULLpero que el optimizador de consultas no podría utilizar.
Martin Smith
@MartinSmith Oh, sí, acabo de leer la respuesta y los comentarios en la pregunta similar: ¿Cómo se agrega una columna NOT NULL a una tabla grande en SQL Server? Por favor, agregue una respuesta con los problemas o una solución mejor y eliminaré la mía.
ypercubeᵀᴹ
2
No tengo una mejor solución. Voté esto porque proporciona una solución parcial. Si todo lo que quiere hacer el OP es evitar datos no válidos, funcionará (y debería ser más rápido que ALTER COLUMNuna vez Sch-Mque se adquiere el bloqueo, esto no necesita escanear las filas en absoluto). Solo señalando que no es lo mismo (por ejemplo, si se usa en una NOT INconsulta, el plan será más complejo)
Martin Smith