La ALTER TABLE ... ALTER COLUMN
El comando es muy poderoso. Puede usarlo para cambiar el tipo de datos, la longitud, la precisión, la escala, la nulabilidad, la intercalación de una columna... y muchas otras cosas además.
Sin duda es más conveniente que la alternativa:crear una nueva tabla y migrar los datos cada vez que sea necesario un cambio. Sin embargo, no se puede hacer mucho para ocultar la complejidad subyacente. Junto con una gran cantidad de restricciones sobre lo que es incluso posible con este comando, siempre está la cuestión del rendimiento.
En última instancia, las tablas se almacenan como una secuencia de bytes con algunos metadatos en otras partes del sistema para describir qué significa cada uno de esos bytes y cómo se relacionan con cada una de las diversas columnas de la tabla. Cuando le pedimos a SQL Server que cambie algún aspecto de la definición de una columna, debe verificar que los datos existentes sean compatibles con la nueva definición. También necesita determinar si el diseño físico actual debe cambiar.
Según el tipo de cambio y la configuración de la base de datos, un ALTER COLUMN
el comando deberá realizar una de las siguientes acciones:
- Cambie los metadatos solo en las tablas del sistema.
- Verifique la compatibilidad de todos los datos existentes y luego cambie los metadatos.
- Reescriba algunos o todos los datos almacenados para que coincidan con la nueva definición.
La opción 1 representa el caso ideal desde el punto de vista del rendimiento. Solo requiere unos pocos cambios en las tablas del sistema y una cantidad mínima de registro. La operación aún requerirá una modificación de esquema restrictiva Sch-M
lock, pero los cambios en los metadatos se completarán muy rápidamente, independientemente del tamaño de la tabla.
Cambios solo de metadatos
Hay una serie de casos especiales a tener en cuenta, pero como resumen general, las siguientes acciones solo requieren cambios en los metadatos:
- Pasando de
NOT NULL
aNULL
para el mismo tipo de datos. - Aumentando el tamaño máximo de un
varchar
,nvarchar
ovarbinary
columna (excepto paramax
).
Mejoras en SQL Server 2016
El tema de esta publicación son los cambios adicionales que están habilitados solo para metadatos desde SQL Server 2016 en adelante . No se necesitan cambios en la sintaxis y no es necesario modificar los ajustes de configuración. Obtiene estas mejoras no documentadas de forma gratuita.
Las nuevas capacidades apuntan a un subconjunto de longitud fija tipos de datos. Las nuevas habilidades se aplican a las tablas de almacenamiento de filas en las siguientes circunstancias:
- La compresión debe estar habilitada:
- En todos los índices y particiones , incluido el montón base o el índice agrupado.
- Cualquier
ROW
oPAGE
compresión. - Los índices y las particiones pueden usar una mezcla de estos niveles de compresión. Lo importante es que no hay índices o particiones sin comprimir.
- Cambiando de
NULL
aNOT NULL
está no permitido . - Los siguientes cambios de tipo entero son compatibles:
smallint
ainteger
obigint
.integer
abigint
.smallmoney
amoney
(usa la representación de enteros internamente).
- Los siguientes cambios de tipo binario y de cadena son compatibles:
char(n)
achar(m)
ovarchar(m)
nchar(n)
anchar(m)
onvarchar(m)
binary(n)
abinary(m)
ovarbinary(m)
- Todo lo anterior solo para
n < m
ym != max
- Los cambios de intercalación no están permitidos
Estos cambios pueden ser solo de metadatos porque el diseño de datos binarios subyacentes no cambia cuando Descriptor de columna se utiliza el formato de fila (de ahí la necesidad de compresión). Sin compresión, el almacenamiento de filas usa el FixedVar original representación, que no puede adaptarse a estos cambios de tipos de datos de longitud fija sin reescribir el diseño físico.
Puede notar que tinyint
se omite de la lista de tipos enteros. Esto se debe a que no está firmado, mientras que los otros tipos de enteros están todos firmados, por lo que no es posible un cambio solo de metadatos. Por ejemplo, un valor de 255 puede caber en un byte para tinyint
, pero requiere dos bytes en cualquiera de los formatos firmados. Los formatos firmados pueden contener -128 a +127 en un byte cuando están comprimidos.
Ejemplo de entero
Una aplicación muy útil de esta mejora es cambiar el tipo de datos de una columna con IDENTITY
propiedad.
Digamos que tenemos la siguiente tabla de montón usando compresión de filas (la compresión de página también funcionaría):
DROP TABLE IF EXISTS dbo.Test; GO CREATE TABLE dbo.Test ( id integer IDENTITY NOT NULL, some_value integer NOT NULL ) WITH (DATA_COMPRESSION = ROW);
Agreguemos 5 millones de filas de datos. Esto será suficiente para que sea obvio (desde el punto de vista del rendimiento) si cambiar el tipo de datos de la columna es una operación solo de metadatos o no:
WITH Numbers AS ( SELECT n = ROW_NUMBER() OVER (ORDER BY @@SPID) FROM sys.all_columns AS AC1 CROSS JOIN sys.all_columns AS AC2 ORDER BY n OFFSET 0 ROWS FETCH FIRST 5 * 1000 * 1000 ROWS ONLY ) INSERT dbo.Test WITH (TABLOCKX) ( some_value ) SELECT N.n FROM Numbers AS N;
A continuación, volveremos a sembrar la IDENTITY
para que parezca que estamos a punto de quedarnos sin valores que quepan en un integer
:
DBCC CHECKIDENT ( N'dbo.Test', RESEED, 2147483646 );
Podemos agregar una fila más con éxito:
INSERT dbo.Test (some_value) VALUES (123456);
Pero intentar agregar otra fila:
INSERT dbo.Test (some_value) VALUES (7890);
Resultados en un mensaje de error:
Mensaje 8115, Nivel 16, Estado 1, Línea 1Error de desbordamiento aritmético al convertir IDENTIDAD al tipo de datos int.
Podemos arreglar eso convirtiendo la columna a bigint
:
ALTER TABLE dbo.Test ALTER COLUMN id bigint NOT NULL;
Gracias a las mejoras en SQL Server 2016, este comando cambia solo los metadatos y se completa inmediatamente. El anterior INSERT
declaración (la que arrojó el error de desbordamiento aritmético) ahora se completa con éxito.
Esta nueva capacidad no resuelve todos los problemas relacionados con cambiar el tipo de una columna con IDENTITY
propiedad. Todavía necesitaremos descartar y volver a crear cualquier índice en la columna, volver a crear cualquier clave externa que haga referencia, etc. Eso está un poco fuera del alcance de esta publicación (aunque Aaron Bertrand ha escrito sobre eso antes). Poder cambiar el tipo como una operación solo de metadatos ciertamente no duele. Con una planificación cuidadosa, los demás pasos necesarios se pueden hacer lo más eficientes posible, por ejemplo, utilizando registros mínimos o ONLINE
. operaciones.
Cuidado con la sintaxis
Asegúrese de siempre especificar NULL
o NOT NULL
al cambiar los tipos de datos con ALTER COLUMN
. Digamos, por ejemplo, que también queríamos cambiar el tipo de datos de some_value
columna en nuestra tabla de prueba de integer NOT NULL
a bigint NOT NULL
.
Cuando escribimos el comando, omitimos el NULL
o NOT NULL
calificador:
ALTER TABLE dbo.Test ALTER COLUMN some_value bigint;
Este comando se completa correctamente como un cambio solo de metadatos, pero también elimina el NOT NULL
restricción. La columna ahora es bigint NULL
, que no es lo que pretendíamos. Este comportamiento está documentado, pero es fácil pasarlo por alto.
Podríamos intentar arreglar nuestro error con:
ALTER TABLE dbo.Test ALTER COLUMN some_value bigint NOT NULL;
Esto no un cambio de solo metadatos. No se nos permite cambiar de NULL
a NOT NULL
(Consulte la tabla anterior si necesita un repaso de las condiciones). SQL Server deberá verificar todos los valores existentes para asegurarse de que no haya valores nulos. Luego reescribirá físicamente cada fila de la mesa. Además de ser lentas en sí mismas, estas acciones generan una gran cantidad de registros de transacciones, lo que puede tener efectos colaterales.
Como nota al margen, este mismo error no es posible para las columnas con IDENTITY
propiedad. Si escribimos una ALTER COLUMN
sentencia sin NULL
o NOT NULL
en ese caso, el motor asume amablemente que queríamos decir NOT NULL
porque la propiedad de identidad no está permitida en columnas anulables. Todavía es una gran idea no confiar en este comportamiento.
Siempre especifique NULL
o NOT NULL
con ALTER COLUMN
.
Colección
Se necesita especial cuidado cuando se modifica una columna de cadena que tiene una intercalación que no coincide con la predeterminada para la base de datos.
Por ejemplo, supongamos que tenemos una tabla con una intercalación que distingue mayúsculas de minúsculas y acentos (supongamos que el valor predeterminado de la base de datos es diferente):
DROP TABLE IF EXISTS dbo.Test2; GO CREATE TABLE dbo.Test2 ( id integer IDENTITY NOT NULL, some_string char(8) COLLATE Latin1_General_100_CS_AS NOT NULL ) WITH (DATA_COMPRESSION = ROW);
Agregue 5 millones de filas de datos:
WITH Numbers AS ( SELECT n = ROW_NUMBER() OVER (ORDER BY @@SPID) FROM sys.all_columns AS AC1 CROSS JOIN sys.all_columns AS AC2 ORDER BY n OFFSET 0 ROWS FETCH FIRST 5 * 1000 * 1000 ROWS ONLY ) INSERT dbo.Test2 WITH (TABLOCKX) ( some_string ) SELECT CONVERT(char(8), N.n) COLLATE Latin1_General_100_CS_AS FROM Numbers AS N;
Duplique la longitud de la columna de cadena usando el siguiente comando:
ALTER TABLE dbo.Test2 ALTER COLUMN some_string char(16) NOT NULL;
Recordamos especificar NOT NULL
, pero se olvidó de la intercalación no predeterminada. SQL Server asume que queríamos cambiar la intercalación al valor predeterminado de la base de datos (Latin1_General_CI_AS
para mi base de datos de prueba). Cambiar la intercalación evita que la operación sea solo de metadatos, por lo que la operación se ejecuta durante varios minutos, generando montones de registros.
Vuelva a crear la tabla y los datos usando el script anterior, luego intente ALTER COLUMN
comando de nuevo, pero especificando la intercalación no predeterminada existente como parte del comando:
ALTER TABLE dbo.Test2 ALTER COLUMN some_string char(16) COLLATE Latin1_General_100_CS_AS NOT NULL;
El cambio ahora se completa de inmediato, como una operación solo de metadatos. Al igual que con el NULL
y NOT NULL
sintaxis, vale la pena ser explícito para evitar accidentes. Este es un buen consejo en general, no solo para ALTER COLUMN
.
Compresión
Tenga en cuenta que la compresión debe especificarse explícitamente para cada índice y por separado para la tabla base si es un montón. Este es otro ejemplo en el que el uso de sintaxis abreviada o atajos puede evitar el resultado deseado.
Por ejemplo, la siguiente tabla no especifica la compresión explícita para la clave principal o la definición de índice en línea:
CREATE TABLE dbo.Test ( id integer IDENTITY NOT NULL PRIMARY KEY, some_value integer NOT NULL INDEX [IX dbo.Test some_value] ) WITH (DATA_COMPRESSION = PAGE);
La PRIMARY KEY
tendrá un nombre asignado, por defecto CLUSTERED
y ser PAGE
comprimido. El índice en línea será NONCLUSTERED
y no comprimido en absoluto. Esta tabla no estará habilitada para ninguna de las nuevas optimizaciones porque no todos los índices y particiones están comprimidos.
Una definición de tabla mucho mejor y más explícita sería:
CREATE TABLE dbo.Test ( id integer IDENTITY NOT NULL CONSTRAINT [PK dbo.Test id] PRIMARY KEY CLUSTERED WITH (DATA_COMPRESSION = PAGE), some_value integer NOT NULL INDEX [IX dbo.Test some_value] NONCLUSTERED WITH (DATA_COMPRESSION = ROW) );
Esta tabla calificará para las nuevas optimizaciones porque todos los índices y particiones están comprimidos. Como se señaló anteriormente, mezclar tipos de compresión está bien.
Hay una variedad de formas de escribir esto CREATE TABLE
declaración de manera explícita, por lo que hay un elemento de preferencia personal. El punto importante a recordar es siempre ser explícito sobre lo que quieres. Esto se aplica a CREATE INDEX
separados declaraciones también.
Eventos extendidos y Trace Flag
Hay un evento extendido específicamente para el nuevo ALTER COLUMN
solo de metadatos operaciones admitidas en SQL Server 2016 en adelante.
El evento extendido es compressed_alter_column_is_md_only
en el Depuración canal. Sus campos de eventos son object_id
, column_id
y is_md_only
(verdadero/falso).
Este evento solo indica si una operación es solo de metadatos debido a las nuevas capacidades de SQL Server 2016. Las alteraciones de columna que eran solo de metadatos antes de 2016 mostrarán is_md_only = false
a pesar de seguir siendo solo de metadatos.
Otros eventos extendidos útiles para rastrear ALTER COLUMN
las operaciones incluyen metadata_ddl_alter_column
y alter_column_event
, tanto en el Analytic canal.
Si necesita deshabilitar las nuevas capacidades de SQL Server 2016 por cualquier motivo, se puede usar el indicador de seguimiento global (o de inicio) no documentado 3618. Esta marca de rastreo no es efectiva cuando se usa a nivel de sesión. No hay forma de especificar un indicador de seguimiento de nivel de consulta con un ALTER COLUMN
comando.
Pensamientos finales
Ser capaz de cambiar algunos tipos de datos enteros de longitud fija con un cambio de solo metadatos es una mejora muy bienvenida del producto. Requiere que la tabla ya esté completamente comprimida, pero eso se está volviendo más común de todos modos. Esto es especialmente cierto ya que la compresión se habilitó en todas las ediciones a partir de SQL Server 2016 Service Pack 1.
Las columnas de tipo cadena de longitud fija son probablemente mucho menos comunes. Parte de esto puede deberse a consideraciones un tanto desactualizadas, como el uso del espacio. Cuando se comprimen, las columnas de cadenas de longitud fija no almacenan espacios en blanco finales, lo que las hace tan eficientes como las columnas de cadenas de longitud variable desde el punto de vista del almacenamiento. Puede ser molesto recortar espacios para la manipulación o la visualización, pero si los datos suelen ocupar la mayor parte de la longitud máxima, los tipos de longitud fija pueden tener ventajas importantes, sobre todo en lo que respecta a las concesiones de memoria para cosas como la clasificación y el hash.
No todo son buenas noticias con la compresión habilitada. Mencioné anteriormente que SQL Server a veces puede realizar un cambio solo de metadatos después de verificar que todos los valores existentes se convertirán correctamente al nuevo tipo. Este es el caso cuando se usa ALTER COLUMN
para cambiar de integer
a smallint
por ejemplo. Desafortunadamente, estas operaciones actualmente no son solo metadatos para objetos comprimidos.
Agradecimientos
Un agradecimiento especial a Panagiotis Antonopoulos (Ingeniero de software principal) y Mirek Sztajno (Administrador sénior de programas) del equipo de productos de SQL Server por su asistencia y orientación durante la investigación y redacción de este artículo.
Ninguno de los detalles proporcionados en este trabajo debe considerarse como documentación oficial de Microsoft o declaraciones de productos.