首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 數據庫知識 >  >> SQL Server 資料庫 >> 關於SqlServer

SQL Server 2000 視圖使用 * 號隱患:ALTER VIEW 導致欄位順序錯位實測

     編輯:關於SqlServer

現象重現

在 SQL Server 2000 中,視圖定義使用 SELECT * 參考另一視圖/資料表,當基礎視圖/資料表欄位順序變更(ALTER VIEW 或 ALTER TABLE)時,上層視圖**不會自動重新綁定欄位對應**,導致資料錯位、型別衝突甚至靜默回傳錯誤結果。

實測腳本

USE Northwind;
GO

-- 基礎視圖:明確列出欄位
CREATE VIEW vCustomersA AS
SELECT CustomerID, CompanyName, ContactName, ContactTitle,
       Address, City, Region, PostalCode, Country, Phone, Fax
FROM dbo.Customers;
GO

-- 衍生視圖:使用 SELECT *
CREATE VIEW vCustomersB AS
SELECT * FROM vCustomersA;
GO

-- 測試查詢:正常
SELECT * FROM vCustomersA WHERE CustomerID = 'ALFKI';
SELECT * FROM vCustomersB WHERE CustomerID = 'ALFKI';
-- 兩者結果一致

-- 變更基礎視圖欄位順序:CompanyName 與 ContactName 互換
ALTER VIEW vCustomersA AS
SELECT CustomerID, ContactName, CompanyName, ContactTitle,
       Address, City, Region, PostalCode, Country, Phone, Fax
FROM dbo.Customers;
GO

-- 再次查詢:vCustomersB 欄位錯位!
SELECT * FROM vCustomersA WHERE CustomerID = 'ALFKI';  -- 正確
SELECT * FROM vCustomersB WHERE CustomerID = 'ALFKI';  -- CompanyName 欄顯示 ContactName 值

根因分析

  • SQL Server 2000 視圖編譯時將 * 展開為當時的欄位清單,並將**欄位序數 (Ordinal Position)** 寫入系統目錄 (syscolumns)。
  • ALTER VIEW 重新編譯基礎視圖,產生新的欄位序數,但**不會級聯更新** 參考它的上層視圖元資料。
  • 上層視圖仍按舊序數讀取資料列,造成欄位值錯位。

影響範圍

場景是否受影響說明
視圖巢狀參考 (VIEW → VIEW)✅ 是最常見,如範例
視圖參考資料表 (VIEW → TABLE)✅ 是ALTER TABLE ... ALTER COLUMN 改變順序同樣觸發
內嵌表值函數 (Inline TVF)✅ 是本質上同視圖
SQL Server 2005+❌ 否引入延遲綁定/重新綁定機制,執行時自動解析最新結構描述

最佳實踐:杜絕視圖使用 * 號

  1. 永遠明確列出欄位:CREATE VIEW v AS SELECT Col1, Col2, Col3 FROM Table。
  2. 使用 sp_refreshview / sp_refreshsqlmodule:若必須維護舊系統且無法立即改寫,在 ALTER VIEW 基礎物件後,立即對所有相依視圖執行 EXEC sp_refreshview 'vCustomersB' 強制重新綁定。
  3. 架構變更流程:變更基礎表/視圖欄位 → 產生相依物件清單 (sp_depends / sys.sql_expression_dependencies) → 批次 sp_refreshview → 迴歸測試。
  4. 升級至 SQL Server 2005+:根治此類元資料不同步問題。

延伸:系統函數 sp_refreshview 實戰

-- 取得所有直接/間接參考 vCustomersA 的視圖
SELECT DISTINCT OBJECT_SCHEMA_NAME(referencing_id) + '.' + OBJECT_NAME(referencing_id) AS DependentView
FROM sys.sql_expression_dependencies
WHERE referenced_id = OBJECT_ID('vCustomersA');

-- 動態批次刷新
DECLARE @sql NVARCHAR(MAX) = N'';
SELECT @sql += 'EXEC sp_refreshview ''' + OBJECT_SCHEMA_NAME(referencing_id) + '.' + OBJECT_NAME(referencing_id) + ''';'
FROM sys.sql_expression_dependencies
WHERE referenced_id = OBJECT_ID('vCustomersA');
EXEC sp_executesql @sql;

總結

SQL Server 2000 視圖 SELECT * 是定時炸彈:ALTER VIEW/TABLE 靜默破壞上層視圖語義,且無編譯錯誤預警。鐵律:**視圖定義永遠明確列出欄位**,配合 sp_refreshview 自動化腳本,才能在舊版本環境安全演進結構描述。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved