Archives pour la catégorie T-SQL

Modifier les colonnes TEXT en VARCHAR(MAX)

Le type de données TEXT est déprécié depuis SQL Server 2005. Outre les problèmes de gestion dans le code et sa non compatibilité avec plusieurs fonctions de chaînes, et est gourmand en espace disque et ralentit les opérations de lecture et d’écriture, parce que le moteur de stockage doit créer une allocation spécifique aux LOB pour chaque ligne insérée.
Le type qui le remplace s’appelle VARCHAR(MAX), et l’allocation dans la page ou en LOB se fait dynamiquement selon le contenu inséré dans la colonne. Dans la pratique, cela prend beaucoup moins d’espace en base.

Voici un code se basant sur une vue de catalogue, pour générer les instructions ALTER TABLE pour convertir les types de données, et ensuite pour reconstruire les tables (à partir de SQL Server 2008). Vous pouvez au besoin modifier le code pour accommoder des colonnes NTEXT, et IMAGE (à remplacer par VARBINARY(MAX)) si vous en avez.

SELECT
    'ALTER TABLE ['+TABLE_SCHEMA+'].['+TABLE_NAME+'] ALTER COLUMN ['+COLUMN_NAME+'] VARCHAR(MAX) '
    +CASE IS_NULLABLE WHEN 'YES' THEN 'NULL' ELSE 'NOT NULL' END + ';'
FROM INFORMATION_SCHEMA.COLUMNS
WHERE DATA_TYPE = 'text';

SELECT
    'ALTER TABLE ['+TABLE_SCHEMA+'].['+TABLE_NAME+'] REBUILD;'
FROM INFORMATION_SCHEMA.COLUMNS
WHERE DATA_TYPE = 'text'
GROUP BY TABLE_SCHEMA, TABLE_NAME;

fonction UPDATE() dans un déclencheur

Attention à la fonction UPDATE() utilisée dans les déclencheures (triggers).
Ne pensez pas que cela vous indique que la valeur de la colonne a changé, mais simplement
que la colonne a été mentionnée dans l’instruction. Un exemple :

USE tempdb;
GO

CREATE TABLE dbo.Contact (Id INT, Name VARCHAR(50))
GO

INSERT INTO dbo.Contact
VALUES (1, 'Fillon'), (2, 'Copé');

CREATE TRIGGER atr_u_Contact
ON dbo.Contact
AFTER UPDATE
AS BEGIN
    IF UPDATE(Name)
        PRINT 'ok'
    ELSE
        PRINT 'non'
END

UPDATE dbo.Contact
SET Name = Name
WHERE Id = 2;
-- affiche oui

UPDATE dbo.Contact
SET Id = Id
WHERE Id = 2;
-- affiche non

Donc il vaut mieux tester s’il y a eu réellement une modification avant d’exécuter tout le reste. Par exemple, à la place de ceci :

CREATE TRIGGER atr_u_Contact
ON dbo.Contact
AFTER UPDATE
AS BEGIN
    IF UPDATE(Name)
        INSERT INTO ContactHistory (Id, Name)
        SELECT Id, Name
        FROM deleted;
END

ceci :

CREATE TRIGGER atr_u_Contact
ON dbo.Contact
AFTER UPDATE
AS BEGIN
    IF @@ROWCOUNT = 0 RETURN

    IF UPDATE(Name)
        INSERT INTO ContactHistory (Id, Name)
        SELECT d.Id, d.Name
        FROM deleted d
        JOIN inserted i ON d.Id = i.Id
        WHERE d.Name  i.Name;
END

Ici nous ajoutons aussi, en toute première ligne (c’est important), l’instruction

IF @@ROWCOUNT = 0 RETURN

pour tester si le déclencheur est appelé pour une bonne raison. En effet, une instruction comme celle-ci:

UPDATE dbo.Contact
SET Name = UPPER(Name)
WHERE 1 = 0;

n’affecterait aucune ligne, mais appellerait pourtant le déclencheur, qui lancerait des requêtes pour rien. Le test sur @@ROWCOUNT permet de sortir immédiatement du trigger si aucune ligne n’a été affectée.