web-dev-qa-db-fra.com

Les vues SQL Server mettent-elles à jour leurs types de données automatiquement en fonction des tables qui les nourrissent?

Quelqu'un a créé à tort un tas de champs NCHAR dans des tables de base de données. J'ai remarqué cela et, après avoir examiné la question, nous déplacons ces champs vers Nvarchars et couper les valeurs à l'intérieur.

Dans mon script, je l'ai actuellement limité pour tirer uniquement les tables qui ont des types NCHAR, en supposant que les vues alimentées par les tableaux sauront mettre à jour leurs valeurs. Cette hypothèse est-elle correcte, ou devrais-je inclure des vues dans cette liste?

Pour référence, le script de sa forme actuelle est ci-dessous (principalement tiré cette excellente réponse SO ):

declare @tn nvarchar(128)
declare @cn nvarchar(128)
declare @ln int

declare @sql as nvarchar(1000)

declare c cursor for 
    select cols.table_name,cols.column_name,cols.character_maximum_length 
    from information_schema.columns cols
        inner join information_schema.tables tabs 
            on (cols.TABLE_SCHEMA = tabs.TABLE_SCHEMA and cols.TABLE_NAME = tabs.TABLE_NAME)
    where cols.data_type ='nchar' and tabs.TABLE_TYPE = 'BASE TABLE' 

open c
fetch next from c into @tn, @cn, @ln

while @@FETCH_STATUS = 0
begin

    set @sql = 'alter table ' + @tn + ' alter column ' 
        + @cn + ' nvarchar(' + convert(nvarchar(50), @ln) + ')'
    exec sp_executesql @sql

    set @sql = 'update ' + @tn + ' set ' + @cn + ' = LTRIM(RTRIM(' + @cn + '))'
    exec sp_executesql @sql

    fetch next from c into @tn, @cn, @ln
end

close c
deallocate c

Références

4
SeanKilleen

Un exemple simple:

CREATE TABLE dbo.x(a INT, b NCHAR(4));
GO
CREATE VIEW dbo.vx AS 
  SELECT a, b FROM dbo.x;
GO

ALTER TABLE dbo.x ALTER COLUMN a TINYINT;
ALTER TABLE dbo.x ALTER COLUMN b NVARCHAR(4);
GO

SELECT a,b INTO #blat FROM dbo.vx;
GO

EXEC tempdb.dbo.sp_columns N'#blat';
GO

DROP VIEW dbo.vx;
DROP TABLE dbo.x, #blat;

Sortie partielle:

COLUMN_NAME    TYPE_NAME    PRECISION
-----------    ---------    ---------
a              tinyint      3
b              nvarchar     4

Donc, en termes pratiques, oui, toute interaction avec la vue devrait générer les nouveaux types de données.

cela dit, je voudrais toujours appel sp_refreshview Pour des vues qui font référence à une table qui a changé (et, en fait, j'utilise souvent WITH SCHEMABINDING Donc, je ne peux pas modifier une table sans connaître les points de vue et d'autres objets qu'il affecte - cela peut rendre le développement/le déploiement de cow-boy douloureux, mais je suppose que c'est un peu le point).

Vous pouvez construire le script pour rafraîchir toutes les vues référencées de manière dynamique de celle-ci (ceci est pour une seule table; vous devrez intégrer cela dans votre script existant pour la rendre dynamique pour toutes les tables concernées):

DECLARE @sql NVARCHAR(MAX) = N'';

SELECT @sql += N'EXEC sp_refreshview ''' 
  + QUOTENAME(s.name) + '.' + QUOTENAME(v.name) + ''';'
FROM sys.sql_expression_dependencies AS d
INNER JOIN sys.views AS v
ON d.referencing_id = v.[object_id]
INNER JOIN sys.schemas AS s
ON v.[schema_id] = s.[schema_id]
WHERE d.referenced_id = OBJECT_ID('dbo.whatever')
GROUP BY s.name, v.name;

EXEC sp_executesql @sql;

Une autre raison pour laquelle vous devez toujours rafraîchir toutes les vues après avoir changé la table (s) de base, surtout Si vos vues utilisent SELECT *:

CREATE TABLE dbo.x(a INT, b NCHAR(4));
GO
CREATE VIEW dbo.vx AS 
  SELECT * FROM dbo.x;
GO

SELECT * INTO #b1 FROM dbo.vx;
GO

ALTER TABLE dbo.x ALTER COLUMN a TINYINT;
ALTER TABLE dbo.x ALTER COLUMN b NVARCHAR(4);
GO

SELECT * INTO #b2 FROM dbo.vx;
GO

ALTER TABLE dbo.x ADD d INT;
GO

SELECT * INTO #b3 FROM dbo.vx;
GO

EXEC sp_rename N'dbo.x.b', N'c', N'COLUMN';
EXEC sp_rename N'dbo.x.d', N'b', N'COLUMN';
GO

SELECT * INTO #b4 FROM dbo.vx;
GO

EXEC tempdb.dbo.sp_columns N'#b1';
EXEC tempdb.dbo.sp_columns N'#b2';
EXEC tempdb.dbo.sp_columns N'#b3';
EXEC tempdb.dbo.sp_columns N'#b4';
GO

DROP TABLE #b1, #b2, #b3, #b4;
GO

Résultats partiels:

-- initial:

#b1____    a    int        10
#b1____    b    nchar      4

-- correct:

#b2____    a    tinyint    3
#b2____    b    nvarchar   4

-- missing new column d:

#b3____    a    tinyint    3
#b3____    b    nvarchar   4

-- missing column c, b still points at "old" b:

#b4____    a    tinyint    3
#b4____    b    nvarchar   4

Mais alors si nous rafraîchissons la vue:

EXEC sp_refreshview N'dbo.vx';
GO

SELECT * INTO #b5 FROM dbo.vx;

EXEC tempdb.dbo.sp_columns N'#b5';

DROP VIEW dbo.vx;
DROP TABLE dbo.x, #b5;

Résultats:

#b5____    a     tinyint     3
#b5____    c     nvarchar    4
#b5____    b     int         10

Notez que les types de données sont corrects maintenant, mais les colonnes ne sont pas dans l'ordre à attendre.

9
Aaron Bertrand