sql >> Base de Datos >  >> RDS >> Sqlserver

Nuevos cambios en la columna solo de metadatos en SQL Server 2016

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:

  1. Cambie los metadatos solo en las tablas del sistema.
  2. Verifique la compatibilidad de todos los datos existentes y luego cambie los metadatos.
  3. 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 a NULL para el mismo tipo de datos.
  • Aumentando el tamaño máximo de un varchar , nvarchar o varbinary columna (excepto para max ).

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 o PAGE 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 a NOT NULL está no permitido .
  • Los siguientes cambios de tipo entero son compatibles:
    • smallint a integer o bigint .
    • integer a bigint .
    • smallmoney a money (usa la representación de enteros internamente).
  • Los siguientes cambios de tipo binario y de cadena son compatibles:
    • char(n) a char(m) o varchar(m)
    • nchar(n) a nchar(m) o nvarchar(m)
    • binary(n) a binary(m) o varbinary(m)
    • Todo lo anterior solo para n < m y m != 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 1
Error 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.