The Will Will Web

記載著 Will 在網路世界的學習心得與技術分享

SQL Server: 內部錯誤。提供給讀取資料行值的緩衝區太小。

前陣子用了 SQL Server 2005 中極少人使用的 Query Notification 功能,且我下的 SQL 指令明明就很單純,只是做一個簡單的 SELECT 查詢卻會引來 SQL Server 的內部錯誤,自己奮戰了一個多月無解,最後還是靠微軟的技術支援中心幫我解了這個難題。

如果你安裝的是中文版的 SQL Server 2005 的話訊息的話,那麼訊息就一定會只有中文,我想全世界只有我這篇文章是中文且可參考的資料,查這些資料真是累死我了,早知道就早點打電話尋求支援。

同一種錯誤,我從各方取得各種不同的錯誤訊息如下:

Event Log 得到的錯誤

內部錯誤。提供給讀取資料行值的緩衝區太小。請執行 DBCC CHECKDB,以檢查是否有任何損毀。

將錯誤訊息嘗試翻譯成英文關鍵字才能找到相關資料,完整的英文訊息如下:

Internal error. Buffer provided to read column value is too small. Run DBCC CHECKDB to check for any corruption."

從 InnerException 得到的錯誤:

嚴重錯誤 682 發生於 06 22 2009 5:28PM。請記錄錯誤和時間,並連絡您的系統管理員。

英文錯誤訊息為:

Warning: Fatal error 682 occurred at Feb 8 2007 11:49AM. Note the error and time, and contact your system administrator

透過 SQL Server Profiler 得到的錯誤:

Error: 682, Severity: 22, State: 148

因為且這個錯誤訊息是直接從 SQL Server 傳出來的,所以我直覺的研判是 SQL Server 的 Bug,也確實有查到這個這個錯誤,不過早在 Service Pack 2 就修復了,目前 SQL Server 2005 出到 Service Pack 3,建議開發人員要隨時升級到最新版,避免無謂的時間浪費。如果你的資料庫真的出問題,可以參考這篇文章來救你。

最後,我發現問題出現在我宣告 SqlCommand 的寫法造成了這個錯誤:

foreach (System.Data.Common.DbParameter dbp in dc.GetCommand(q).Parameters)
{
    cmd.Parameters.Add(new SqlParameter(dbp.ParameterName, dbp.Value));
}

最後修正成以下程式碼才解決這個問題:

foreach (System.Data.Common.DbParameter dbp in dc.GetCommand(q).Parameters)
{
    if (dbp.DbType == System.Data.DbType.AnsiString 
	 || dbp.DbType == System.Data.DbType.String)
    {
        SqlParameter pa = new SqlParameter();
        pa.DbType = dbp.DbType;
        pa.ParameterName = dbp.ParameterName;
        pa.Value = dbp.Value;
        pa.Size = 2000;   // 這裡是非常非常重要的關鍵!
        cmd.Parameters.Add(pa);
    }
    else
    {
        cmd.Parameters.Add(new SqlParameter(dbp.ParameterName, dbp.Value));
    }
}

原理說明(以我個人的理解來說,可能並非原本的SQL SERVER 的設計邏輯)

使用 SqlCacheDependency 必須先準備 SqlCommand 物件,所有 SqlCommand 中傳入的參數會變成快取資料表(暫存資料表)的索引欄位,而這些參數會綁定(Binding)該欄位的長度,以確保欄位不會變動。

當 SqlCommand 物件中的 SqlParameter 參數沒有明確指定長度時,預設就會是第一筆回傳值的長度,不過當該欄位屬「變動長度」的欄位時,就可能會發生「第一筆資料欄位長度是 8 個字元,但第二筆的資料長度是 9 個字元」的情況,以導致出現「提供給讀取資料行值的緩衝區太小」的問題。

後記

即便如此,我覺得 SQL Server 應該要能處理這種情況,這種判斷不應該在 AP 層做才對,不知道微軟會不會聽到我的心聲?還是我的觀念有誤?

不過我最近還真是「幸運」,讓我學了這麼多鬼東西。 = =''

相關連結