Sqlserver
 sql >> Base de données >  >> RDS >> Sqlserver

Nouvelles modifications des colonnes de métadonnées uniquement dans SQL Server 2016

Le ALTER TABLE ... ALTER COLUMN la commande est très puissante. Vous pouvez l'utiliser pour modifier le type de données d'une colonne, sa longueur, sa précision, son échelle, sa capacité à être nul, son classement... et bien d'autres choses encore.

C'est certainement plus pratique que l'alternative :créer une nouvelle table et migrer les données chaque fois qu'un changement est nécessaire. Néanmoins, il n'y a pas grand-chose à faire pour masquer la complexité sous-jacente. Outre un grand nombre de restrictions sur ce qui est même possible avec cette commande, il y a toujours la question des performances.

En fin de compte, les tables sont stockées sous la forme d'une séquence d'octets avec des métadonnées ailleurs dans le système pour décrire la signification de chacun de ces octets et leur relation avec chacune des différentes colonnes de la table. Lorsque nous demandons à SQL Server de modifier certains aspects de la définition d'une colonne, il doit vérifier que les données existantes sont compatibles avec la nouvelle définition. Il doit également déterminer si la disposition physique actuelle doit changer.

Selon le type de changement et la configuration de la base de données, un ALTER COLUMN la commande devra effectuer l'une des actions suivantes :

  1. Modifier les métadonnées dans les tables système uniquement.
  2. Vérifiez la compatibilité de toutes les données existantes, puis modifiez les métadonnées.
  3. Réécrivez tout ou partie des données stockées pour correspondre à la nouvelle définition.

L'option 1 représente le cas idéal du point de vue des performances. Il ne nécessite que quelques modifications des tables système et une quantité minimale de journalisation. L'opération nécessitera toujours une modification de schéma restrictive Sch-M verrouiller, mais les modifications des métadonnées elles-mêmes se termineront très rapidement, quelle que soit la taille de la table.

Modifications des métadonnées uniquement

Il existe un certain nombre de cas particuliers à surveiller, mais en résumé, les actions suivantes ne nécessitent que des modifications des métadonnées :

  • Passer de NOT NULL à NULL pour le même type de données.
  • Augmentation de la taille maximale d'un varchar , nvarchar , ou varbinary colonne (sauf à max ).

Améliorations dans SQL Server 2016

Le sujet de cet article est les modifications supplémentaires qui sont activées pour les métadonnées uniquement à partir de SQL Server 2016 . Aucune modification de la syntaxe n'est nécessaire et aucun paramètre de configuration ne doit être modifié. Vous obtenez gratuitement ces améliorations non documentées.

Les nouvelles fonctionnalités ciblent un sous-ensemble de la longueur fixe Types de données. Les nouvelles capacités s'appliquent aux tables de magasin de lignes dans les circonstances suivantes :

  • La compression doit être activée :
    • Sur tous les index et partitions , y compris le tas de base ou l'index clusterisé.
    • Soit ROW ou PAGE compression.
    • Les index et les partitions peuvent utiliser un mélange de ces niveaux de compression. L'important est qu'il n'y ait pas d'index ou de partitions non compressés.
  • Modification de NULL à NOT NULL n'est pas autorisé .
  • Les changements de type entier suivants sont pris en charge :
    • smallint en integer ou bigint .
    • integer en bigint .
    • smallmoney en money (utilise la représentation entière en interne).
  • Les modifications de type chaîne et binaire suivantes sont pris en charge :
    • char(n) en char(m) ou varchar(m)
    • nchar(n) à nchar(m) ou nvarchar(m)
    • binary(n) en binary(m) ou varbinary(m)
    • Tout ce qui précède uniquement pour n < m et m != max
    • Les modifications de classement ne sont pas autorisées

Ces modifications peuvent concerner uniquement les métadonnées, car la disposition des données binaires sous-jacentes ne change pas lorsque le descripteur de colonne format de ligne est utilisé (d'où le besoin de compression). Sans compression, le magasin de lignes utilise l'original FixedVar représentation, qui ne peut pas accepter ces changements de type de données de longueur fixe sans réécrire la mise en page physique.

Vous remarquerez peut-être que tinyint est omis de la liste des types entiers. En effet, il n'est pas signé, alors que les autres types d'entiers sont tous signés, de sorte qu'une modification des métadonnées uniquement n'est pas possible. Par exemple, une valeur de 255 peut tenir dans un octet pour tinyint , mais nécessite deux octets dans l'un des formats signés. Les formats signés peuvent contenir -128 à +127 dans un octet lorsqu'ils sont compressés.

Exemple d'entier

Une application très pratique de cette amélioration est de changer le type de données d'une colonne avec le IDENTITY propriété.

Supposons que nous ayons la table de tas suivante utilisant la compression de ligne (la compression de page fonctionnerait également) :

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);

Ajoutons 5 millions de lignes de données. Cela suffira à indiquer clairement (du point de vue des performances) si la modification du type de données de la colonne est une opération portant uniquement sur les métadonnées ou non :

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;

Ensuite, nous allons réamorcer le IDENTITY pour donner l'impression que nous sommes presque sur le point de manquer de valeurs pouvant tenir dans un integer :

DBCC CHECKIDENT
(
    N'dbo.Test',
    RESEED,
    2147483646
);

Nous pouvons ajouter une ligne supplémentaire avec succès :

INSERT dbo.Test
    (some_value)
VALUES
    (123456);

Mais essayez d'ajouter une autre ligne :

INSERT dbo.Test
    (some_value)
VALUES
    (7890);

Entraîne un message d'erreur :

Msg 8115, Niveau 16, État 1, Ligne 1
Erreur de débordement arithmétique lors de la conversion de IDENTITY en type de données int.

Nous pouvons résoudre ce problème en convertissant la colonne en bigint :

ALTER TABLE dbo.Test
ALTER COLUMN id bigint NOT NULL;

Grâce aux améliorations de SQL Server 2016, cette commande modifie les métadonnées uniquement , et se termine immédiatement. Le précédent INSERT instruction (celle qui a lancé l'erreur de dépassement arithmétique) se termine maintenant avec succès.

Cette nouvelle capacité ne résout pas tous les problèmes liés à la modification du type d'une colonne avec l'IDENTITY biens. Nous devrons toujours supprimer et recréer tous les index sur la colonne, recréer toutes les clés étrangères de référence, etc. Cela sort un peu du cadre de cet article (bien qu'Aaron Bertrand en ait déjà parlé). Pouvoir changer le type en tant qu'opération de métadonnées uniquement ne fait certainement pas de mal. Avec une planification minutieuse, les autres étapes requises peuvent être rendues aussi efficaces que possible, par exemple en utilisant une connexion minimale ou ONLINE opérations.

Soyez prudent avec la syntaxe

Assurez-vous de toujours spécifiez NULL ou NOT NULL lors du changement de types de données avec ALTER COLUMN . Disons par exemple que nous voulions également changer le type de données de some_value colonne dans notre table de test à partir de integer NOT NULL à bigint NOT NULL .

Lorsque nous écrivons la commande, nous omettons le NULL ou NOT NULL qualificatif :

ALTER TABLE dbo.Test
ALTER COLUMN some_value bigint;

Cette commande se termine avec succès en tant que modification des métadonnées uniquement, mais supprime également le NOT NULL contrainte. La colonne est maintenant bigint NULL , ce qui n'est pas notre intention. Ce comportement est documenté, mais il est facile de l'ignorer.

Nous pourrions essayer de corriger notre erreur avec :

ALTER TABLE dbo.Test
ALTER COLUMN some_value bigint NOT NULL;

Ce n'est pas un changement de métadonnées uniquement. Nous ne sommes pas autorisés à changer de NULL à NOT NULL (reportez-vous au tableau précédent si vous avez besoin d'un rappel sur les conditions). SQL Server devra vérifier toutes les valeurs existantes pour s'assurer qu'aucune valeur nulle n'est présente. Il va alors réécrire physiquement chaque ligne du tableau. En plus d'être lentes en elles-mêmes, ces actions génèrent beaucoup de journal des transactions, ce qui peut avoir des répercussions.

En remarque, cette même erreur n'est pas possible pour les colonnes avec le IDENTITY biens. Si nous écrivons un ALTER COLUMN instruction sans NULL ou NOT NULL dans ce cas, le moteur suppose utilement que nous voulions dire NOT NULL car la propriété d'identité n'est pas autorisée sur les colonnes nullables. C'est toujours une bonne idée de ne pas compter sur ce comportement.

Spécifiez toujours NULL ou NOT NULL avec ALTER COLUMN .

Collation

Une attention particulière est nécessaire lors de la modification d'une colonne de chaîne dont le classement ne correspond pas à la valeur par défaut de la base de données.

Par exemple, supposons que nous ayons une table avec un classement sensible à la casse et aux accents (en supposant que la base de données par défaut est différente) :

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);

Ajoutez 5 millions de lignes de données :

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;

Doublez la longueur de la colonne de chaîne à l'aide de la commande suivante :

ALTER TABLE dbo.Test2
ALTER COLUMN some_string char(16) NOT NULL;

Nous avons pensé à spécifier NOT NULL , mais j'ai oublié le classement autre que celui par défaut. SQL Server suppose que nous voulions changer le classement par défaut de la base de données (Latin1_General_CI_AS pour ma base de données de test). La modification du classement empêche l'opération de se limiter aux métadonnées. L'opération s'exécute donc pendant plusieurs minutes, générant des tas de journaux.

Recréez la table et les données à l'aide du script précédent, puis essayez la commande ALTER COLUMN commande à nouveau, mais en spécifiant le classement existant autre que le classement par défaut dans le cadre de la commande :

ALTER TABLE dbo.Test2
ALTER COLUMN some_string 
    char(16) COLLATE Latin1_General_100_CS_AS NOT NULL;

La modification s'effectue désormais immédiatement, en tant qu'opération portant uniquement sur les métadonnées. Comme avec le NULL et NOT NULL syntaxe, il vaut mieux être explicite pour éviter les accidents. C'est un bon conseil en général, pas seulement pour ALTER COLUMN .

Compression

Veuillez noter que la compression doit être explicitement spécifiée pour chaque index, et séparément pour la table de base s'il s'agit d'un tas. Ceci est un autre exemple où l'utilisation d'une syntaxe abrégée ou de raccourcis peut empêcher le résultat souhaité.

Par exemple, le tableau suivant ne spécifie pas de compression explicite pour la clé primaire ou la définition d'index en ligne :

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 aura un nom attribué, par défaut CLUSTERED ,et être PAGE comprimé. L'index en ligne sera NONCLUSTERED et pas compressé du tout. Cette table ne sera activée pour aucune des nouvelles optimisations car tous les index et partitions ne sont pas compressés.

Une définition de table bien meilleure et plus explicite serait :

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)        
);

Cette table sera qualifiée pour les nouvelles optimisations car tous les index et partitions sont compressés. Comme indiqué précédemment, mélanger les types de compression est correct.

Il existe plusieurs façons d'écrire ce CREATE TABLE déclaration de manière explicite, il y a donc un élément de préférence personnelle. Le point important à retenir est de toujours être explicite sur ce que vous voulez. Ceci s'applique à CREATE INDEX séparé déclarations également.

Événements étendus et indicateur de suivi

Il existe un événement étendu spécifiquement pour le nouveau ALTER COLUMN réservé aux métadonnées opérations prises en charge dans SQL Server 2016 et versions ultérieures.

L'événement étendu est compressed_alter_column_is_md_only dans le Débogage canal. Ses champs d'événement sont object_id , column_id , et is_md_only (vrai/faux).

Cet événement indique uniquement si une opération est réservée aux métadonnées en raison des nouvelles capacités de SQL Server 2016. Les modifications de colonne qui étaient réservées aux métadonnées avant 2016 afficheront is_md_only = false même s'il s'agit toujours de métadonnées uniquement.

Autres événements étendus utiles pour suivre ALTER COLUMN les opérations incluent metadata_ddl_alter_column et alter_column_event , à la fois dans Analytique chaîne.

Si vous devez désactiver les nouvelles fonctionnalités de SQL Server 2016 pour une raison quelconque, l'indicateur de trace global (ou de démarrage) non documenté 3618 peut être utilisé. Cet indicateur de trace n'est pas efficace lorsqu'il est utilisé au niveau de la session. Il n'y a aucun moyen de spécifier un indicateur de trace au niveau de la requête avec un ALTER COLUMN commande.

Réflexions finales

La possibilité de modifier certains types de données entiers de longueur fixe avec une modification des métadonnées uniquement est une amélioration du produit très appréciée. Cela nécessite que la table soit déjà entièrement compressée, mais cela devient de toute façon de plus en plus courant. Cela est d'autant plus vrai que la compression a été activée dans toutes les éditions à partir de SQL Server 2016 Service Pack 1.

Les colonnes de type chaîne de longueur fixe sont probablement beaucoup moins courantes. Cela peut être dû en partie à des considérations quelque peu dépassées comme l'utilisation de l'espace. Lorsqu'elles sont compressées, les colonnes de chaîne de longueur fixe ne stockent pas les blancs de fin, ce qui les rend tout aussi efficaces que les colonnes de chaîne de longueur variable du point de vue du stockage. Il peut être ennuyeux de réduire les espaces pour la manipulation ou l'affichage, mais si les données occupent généralement la majeure partie de la longueur maximale, les types à longueur fixe peuvent présenter des avantages importants, notamment en ce qui concerne les allocations de mémoire pour des choses comme le tri et le hachage.

Ce ne sont pas toutes de bonnes nouvelles avec la compression activée. J'ai mentionné précédemment que SQL Server peut parfois effectuer une modification des métadonnées uniquement après avoir vérifié que toutes les valeurs existantes seront converties avec succès dans le nouveau type. C'est le cas lors de l'utilisation de ALTER COLUMN pour passer de integer en smallint par exemple. Malheureusement, ces opérations ne concernent pas actuellement les métadonnées uniquement pour les objets compressés.

Remerciements

Remerciements particuliers à Panagiotis Antonopoulos (Ingénieur logiciel principal) et Mirek Sztajno (Senior Program Manager) de l'équipe produit SQL Server pour leur aide et leurs conseils lors de la recherche et de la rédaction de cet article.

Aucun des détails donnés dans ce travail ne doit être considéré comme une documentation Microsoft officielle ou des déclarations de produit.