預讀和 SQL-Variant 欄位
我有兩個包含完全相同數據的表。兩個表都有
bigint primary key identity column
60 列和 300 000 行。不同之處在於第二個表的所有列都有sql-variant
類型。我正在創建臨時表並從其中的兩個表中導入數據。當從
sql-variant
列中提取數據時,它被轉換為相應的 SQL 類型。從第一個表中提取數據是針對
1 sec
和從第二個表中提取的6 secs
。基本上,執行的差異在於估計:
在
read-ahead reads
計數中:我想知道為什麼
SQL Server
不能提前載入從sql-variant
欄位中讀取的數據(幾乎沒有read-ahead reads
)。此外,表的儲存大小幾乎相同:
為什麼
SQL Server
認為它應該閱讀67 GB
?列類型為:
16 x BIGINT 8 x DECIMAL(9,2) 36 x NVARCHAR(100)
該
dbcc dropcleanbuffers
命令每次在數據提取和填充之前使用。為了進行測試,您可以從這裡下載範例數據文件。然後,
- 執行
Tech05_01_TableDefinitions.sql
- 執行3次
Tech05_02_TablePupulation.sql
- 像這樣打開
Tech05_03_TestingInsertionInTempTable.sql
文件並執行一次:DECLARE @TableStructured SYSNAME = '[dbo].[NormalDataTypes_60Cols]'; DECLARE @TableFromWhichDataIsCoppied SYSNAME = '[dbo].[SQLVariant01_60Cols]';
有一次像這樣:
DECLARE @TableStructured SYSNAME = '[dbo].[NormalDataTypes_60Cols]'; DECLARE @TableFromWhichDataIsCoppied SYSNAME = '[dbo].[NormalDataTypes_60Cols]';
Tech05_03_TestingInsertionInTempTable.sql 文件存在一些問題。它使用一個名為
$$ dbo $$.$$ ConcatenateWithOrder $$沒有定義。我想我理解了您的意圖並對其進行了調整以產生一些插入語句。 要回答您的第一個問題,SQL Server 不能使用預讀來獲取 sql_variant 數據是不正確的。如果我
DBCC DROPCLEANBUFFERS;
在插入之前立即執行,我會從統計資訊 io 中獲得以下輸出:表“SQLVariant01_60Cols”。掃描計數 1,邏輯讀取 66311,物理讀取 3,預讀讀取 66307,lob 邏輯讀取 0,lob 物理讀取 0,lob 預讀讀取 0。
為了回答關於計劃中估計行大小的第二個問題,SQL Server 不會查看表使用的空間並除以行數。相反,查詢計劃包含基於表中數據類型的估計。有關更多詳細資訊,請閱讀Martin Smith 的這篇文章。
SQL Server 估計您的表的行大小為 235 KB。我可以接近:
240484.0 = 0.5 * (8016 * 60 + 8)
這與 235 KB 相比略有偏差,但我不知道如何準確計算 sql_variants 的空間估計值。但是您可以看到,當使用可以儲存許多不同數據類型的數據類型時,您可能會被高估。作為記錄,我認為該估計與您觀察到的性能問題沒有任何關係。
為了回答我認為您想問的問題(為什麼使用 sql_variant 的查詢比其他查詢慢得多?),我對其進行了一些測試,從 sql_variant 表中進行選擇時似乎有很大的 CPU 成本。我選擇了行而不是插入它們(在它們到達客戶端之前將它們丟棄)並發現 cpu 時間存在顯著差異。sql_variant 查詢的 CPU 時間為 3151 毫秒,總執行時間為 3268 毫秒。另一個查詢的 CPU 時間為 686 毫秒,總執行時間為 744 毫秒。CPU 時間的差異幾乎完全解釋了執行時間的差異。
使用 sql_variant 列時出現成本似乎不是不合理的,對吧?我不知道有什麼技巧可以避免支付成本。如果您覺得您的數據模型需要 sql_variant 列,我只能建議進行測試以確保您可以獲得足夠好的性能。