Sql-Server
為什麼向視圖添加列會破壞表值函式?
我有一個 TVP
myTVP
實現為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
(儘管事實上Bar
和Bash
甚至沒有相同的類型)。展示問題的匿名計劃:https ://www.brentozar.com/pastetheplan/?id=ryJLJmqdl
注意
Object2
和Object1
是相同的,但是最左邊的嵌套循環的輸出列表在兩個計劃之間是完全不同的。筆記:
- 沒有使用中的計劃提示。沒有設置跟踪標誌。
- CheckDB 沒有發現錯誤。
- 使用 option(recompile) 沒有解決問題。
- 在 TVP 上執行右鍵點擊更改而不進行任何更改修復了該問題。
- 在名稱更改的 TVP 上執行右鍵點擊更改修復了該問題,但新的 TVP 與之前的名稱相同。
- 使用 SQL 2014 標準版。
問題:
- 向視圖添加新列以更改視圖的哪一列被對該視圖的其他引用使用是否正常?
- 在什麼情況下向視圖添加列會導致這種行為?
是的,這是意料之中的。使用的函式和視圖
SELECT *
最終會儲存與底層列相關的部分元數據本身,因此它們很容易混淆。我在 2009 年的以下文章中談到了這個問題:在我最近的 GroupBy 展示中:
你如何避免這個問題?
- 不要
SELECT *
在對像中使用。- 製作你的函式和視圖,這意味著你不能修改底層對象而不修改引用它們的對象(這也有防止雙贏
WITH SCHEMABINDING
的副作用)。*
- 在這種特定情況下,您可以修復對象,以便
Bar
使用以下方法返回正確的數據:EXEC sys.sp_refreshsqlmodule @name = N'dbo.myTVP';
順便說一句,文件
sys.sp_refreshsqlmodule
描述了這個確切的場景:更新目前數據庫中指定的非模式綁定儲存過程、使用者定義函式、視圖、DML 觸發器、數據庫級 DDL 觸發器或伺服器級 DDL 觸發器的元數據。這些對象的持久元數據(例如參數的數據類型)可能會由於其底層對象的更改而過時。
順便說一句,您不應該使用
FROM myTVP()
而是始終指定模式,例如FROM dbo.myTVP()
. 我在舊文章中也談到了這一點。