前陣子用了 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 層做才對,不知道微軟會不會聽到我的心聲?還是我的觀念有誤?
不過我最近還真是「幸運」,讓我學了這麼多鬼東西。 = =''
相關連結