为什么向视图添加列会损坏表值函数?

Bri*_*ian 1 sql-server view sql-server-2014 set-returning-functions

我有一个 TVPmyTVP实现为select val.* from vw_MyView val. 前几天,我修改了vw_MyView,将: 替换
select Foo,Bar from DB
select Foo, Bash, Bar from DB

这有一个奇怪的副作用:调用select Bar from myTVP()返回一个名为Bar填充了内容的列Bash(尽管事实上BarBash甚至没有相同的类型)。

展示问题的匿名计划:https://www.brentozar.com/pastetheplan/?id=ryJLJmqdl

请注意,Object2Object1是相同的,但两个计划之间最左侧嵌套循环的输出列表完全不同。

笔记:

  • 使用中没有计划提示。未设置跟踪标志。
  • CheckDB 没有发现错误。
  • 使用选项(重新编译)并没有解决问题。
  • 在 TVP 上运行右键单击 alter 且不进行任何更改修复了该问题。
  • 在名称更改的 TVP 上运行右键单击 alter 修复了该问题,但新的 TVP 除名称外与之前的相同。
  • 使用SQL 2014标准版。

问题:

  1. 向视图添加新列以更改对该视图的其他引用所使用的视图列是否正常?
  2. 在什么情况下向视图添加列会导致这种行为?

Aar*_*and 5

是的,这是预期的。使用的函数和视图SELECT *最终会存储与底层列相关的部分元数据本身,因此它们很容易感到困惑。我在 2009 年的以下帖子中讨论过这个问题:

在我最近的 GroupBy 演示中:

如何避免这个问题?

  1. 不要SELECT *在对象中使用。
  2. 创建你的函数和视图,这意味着你不能修改底层对象而不修改引用它们的对象(这也有防止双赢WITH SCHEMABINDING的副作用)。*
  3. Bar在这种特定情况下,您可以使用以下方法修复该对象,以便返回正确的数据:

    EXEC sys.sp_refreshsqlmodule @name = N'dbo.myTVP';
    
    Run Code Online (Sandbox Code Playgroud)

顺便说一句,文档sys.sp_refreshsqlmodule描述了这个确切的场景:

更新当前数据库中指定的非架构绑定存储过程、用户定义函数、视图、DML 触发器、数据库级 DDL 触发器或服务器级 DDL 触发器的元数据。这些对象的持久元数据(例如参数的数据类型)可能会因其底层对象的更改而变得过时。

顺便说一句,您不应该使用FROM myTVP()而是始终指定模式,例如FROM dbo.myTVP()我在一篇旧文章中也谈到过这一点。