SQL Server 视图是否根据提供给它们的表自动更新其数据类型?

Sea*_*een 4 sql-server datatypes view

有人错误地在数据库表中创建了一堆 nchar 字段。我注意到了这一点,在检查了这个问题之后,我们将这些字段移动到 nvarchar 类型并修剪里面的值。

在我的脚本中,我目前仅限于拉取具有 nchar 类型的表,假设由表提供的视图知道更新它们的值。这个假设是否正确,或者我也应该在这个列表中包含视图?

作为参考,当前形式的脚本如下(主要取自这个伟大的 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
Run Code Online (Sandbox Code Playgroud)

参考

Aar*_*and 10

一个简单的例子:

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;
Run Code Online (Sandbox Code Playgroud)

部分输出:

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;
Run Code Online (Sandbox Code Playgroud)

因此,实际上,是的,与视图的任何交互都应该产生新的数据类型。

也就是说,我总是会调用sp_refreshview任何引用已更改表的视图(事实上,我经常使用,WITH SCHEMABINDING这样我就无法在不知道它影响的视图和其他对象的情况下更改表 - 这可以使牛仔开发/部署很痛苦,但我想这就是重点)。

您可以构建脚本以动态刷新所有引用的视图,如下所示(这是针对单个表的;您需要将其合并到现有脚本中,以使其对所有受影响的表都是动态的):

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;
Run Code Online (Sandbox Code Playgroud)

更改基表后应始终刷新所有视图的另一个原因,特别是如果您的视图使用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
Run Code Online (Sandbox Code Playgroud)

部分结果:

-- 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
Run Code Online (Sandbox Code Playgroud)

但是如果我们刷新视图:

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;
Run Code Online (Sandbox Code Playgroud)

结果:

#b5____    a     tinyint     3
#b5____    c     nvarchar    4
#b5____    b     int         10
Run Code Online (Sandbox Code Playgroud)

请注意,现在数据类型是正确的,但列的顺序与您期望的顺序不同。