[SQL] 詭異的資料列溢位處理

[SQL] 詭異的資料列溢位處理

這幾天有同事傳來一個怪問題,在 SQL Server 2008 R2 的資料庫內,對其中一個 Table 中某個欄位下 Update 指令時,居然彈出一個錯誤訊息

 

無法建立大小為 8081 的資料列,這個大小大於允許的資料列大小上限 8060

 

正常來說,在 SQL Server 2005 之後就沒有這樣的限制了。於是當下就很職業性的詢問一下 SQL Server 版本 , 資料庫相容性設定 , 相關 SET 的參數設定 , 有沒有做過 DBCC CHECKDB 檢查…. 等問題。對方也很快地就提供相關資訊,看起來一切都是很 OK ,於是就很例行性地建立起一個相同的環境來做個測試,請對方在 Management Studio 裡面透過 [資料庫] –> [ 工作] –> [產生指令碼] –> [選取特定的資料庫物件] ,然後挑選有異常的 Table ,接著在發行方式中選擇 [進階] ,把 [要編寫指令碼的資料類型] 這裡選擇 [結構描述和資料] ,把有問題的結構和資料轉出 SCRIPT,希望可以用一個比較單純的環境來測試出問題。

 

但是很不幸的,那樣有異常的 Script 把它放到別台電腦上去執行,或者是在該機器上另外建立資料庫測試,也都不會有任何異常,資料都可以正常的 Insert 和 Update。於是我們懷疑是不是資料庫有異常,決定將資料庫備份出來,把其他不相關的 Table 都給 DROP ,只剩下一個有異常的 Table。這一次就比較好運了,的確在這樣的狀況下,我們可以還原問題,就開始找方法來看問題的原因。

 

這中間我們發現,這個有異常的 Table 如果我們重新建立一個新的,則不會發生有同樣的錯誤訊息,因此我們調整前面所產生的指令碼,在相同的資料庫內另外建立一個名稱不同但是欄位相同的 Table,此時利用 SQL Server 的系統物件來檢查一下兩個 Table 的 metadata,指令如下:

select name,object_id,max_column_id_used
  from sys.tables 

 

這時候會出現很特別的狀況

image_thumb1_thumb[1]

 

這兩個 Table 的 max_column_id_used 居然會不同 ?! 於是我們想到另用另外一個系統物件,來檢查到底 Table 正確的欄位數是多少,指令如下:

with realdata( object_id, column_count ) as 
(
select object_id,COUNT(*) from sys.columns
  group by object_id
)
select a.name,a.max_column_id_used,b.column_count
  from sys.tables a
  join realdata b on a.object_id = b.object_id

 

image_thumb11_thumb[1]

 

這下總算找到可能的原因了,也許是因為 metadata 的異常,導致我們在 Insert 資料的時候造成 SQL Server 誤判,但是否這個是真正的原因我就不清楚了,實在很難重現當初是怎麼產生那個有異常的 Table 了,但至少讓我們有個方向可以去判斷有可能有異常的 Table,於是我們重新整理一下 SQL 指令,方便以後可以去針對一些客戶或者是有類似狀況發生時,可以做個整體性的掃瞄了。有需要的朋友可以參考以下的 SQL 指令:

 

create table ##X( db sysname, name sysname, max_column int, column_count int )

declare @sql varchar(1024);
set @sql =      'with realdata( object_id, column_count ) as ';
set @sql = @sql+'( select object_id,COUNT(*) from [?].sys.columns group by object_id ) ' ;
set @sql = @sql+'insert ##X  ' ;
set @sql = @sql+'select "?" as [database], a.name,a.max_column_id_used,b.column_count ';
set @sql = @sql+'from [?].sys.tables a join realdata b on a.object_id = b.object_id '; 
set @sql = @sql+' where a.max_column_id_used<>b.column_count';

EXECUTE master.sys.sp_MSforeachdb @sql
select * from ##X

 有需要知道有關於 SQL Server 8K Page 限制的朋友,可以參考 MSDN 上面詳細的說明:

http://msdn.microsoft.com/zh-tw/library/ms186981(v=sql.105).aspx