Sql-Server

為什麼向視圖添加列會破壞表值函式?

  • May 7, 2020

我有一個 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 沒有發現錯誤。
  • 使用 option(recompile) 沒有解決問題。
  • 在 TVP 上執行右鍵點擊更改而不進行任何更改修復了該問題。
  • 在名稱更改的 TVP 上執行右鍵點擊更改修復了該問題,但新的 TVP 與之前的名稱相同。
  • 使用 SQL 2014 標準版。

問題:

  1. 向視圖添加新列以更改視圖的哪一列被對該視圖的其他引用使用是否正常?
  2. 在什麼情況下向視圖添加列會導致這種行為?

是的,這是意料之中的。使用的函式和視圖SELECT *最終會儲存與底層列相關的部分元數據本身,因此它們很容易混淆。我在 2009 年的以下文章中談到了這個問題:

在我最近的 GroupBy 展示中:

你如何避免這個問題?

  1. 不要SELECT *在對像中使用。
  2. 製作你的函式和視圖,這意味著你不能修改底層對象而不修改引用它們的對象(這也有防止雙贏WITH SCHEMABINDING的副作用)。*
  3. 在這種特定情況下,您可以修復對象,以便Bar使用以下方法返回正確的數據:
EXEC sys.sp_refreshsqlmodule @name = N'dbo.myTVP';

順便說一句,文件sys.sp_refreshsqlmodule描述了這個確切的場景:

更新目前數據庫中指定的非模式綁定儲存過程、使用者定義函式、視圖、DML 觸發器、數據庫級 DDL 觸發器或伺服器級 DDL 觸發器的元數據。這些對象的持久元數據(例如參數的數據類型)可能會由於其底層對象的更改而過時。

順便說一句,您不應該使用FROM myTVP()而是始終指定模式,例如FROM dbo.myTVP(). 我在舊文章中也談到了這一點。

引用自:https://dba.stackexchange.com/questions/163773