NULL 值**零儲存空間** (不佔用頁面位元組)。int, datetime, money, uniqueidentifier 等),不支援 varchar(max), xml, geometry 等 LOB 型別。WHERE col IS NOT NULL) 大幅縮小索引體積。每個資料型別有「平衡點」,超過此 NULL 比例才節省空間:
| 資料型別 | 最小 NULL 比例 | 備註 |
|---|---|---|
| bit | 98% | 1 bit 儲存,開銷極大 |
| tinyint | 86% | |
| smallint | 76% | |
| int | 64% | |
| bigint | 52% | |
| real | 64% | |
| float | 52% | |
| smallmoney | 64% | |
| money | 52% | |
| smalldatetime | 64% | |
| datetime | 52% | |
| uniqueidentifier | 43% | 16 位元組固定,開銷相對較小 |
| date | 69% | SQL 2008 新型別 |
| 其他/不定長 | 60% (預設) |
USE AdventureWorks;
GO
SET NOCOUNT ON;
CREATE TABLE #SparseThreshold (DataType VARCHAR(50), PctNull INT);
INSERT INTO #SparseThreshold VALUES
('bit',98),('tinyint',86),('smallint',76),('int',64),('bigint',52),
('real',64),('float',52),('smallmoney',64),('money',52),
('smalldatetime',64),('datetime',52),('uniqueidentifier',43),('date',69);
CREATE TABLE #NullStats (ColName VARCHAR(500), NullCount INT, DataType VARCHAR(50), TotalRows INT);
DECLARE @sql NVARCHAR(MAX) = N'';
SELECT @sql += N'INSERT INTO #NullStats SELECT ''' + QUOTENAME(TABLE_SCHEMA) + N'.' + QUOTENAME(TABLE_NAME) + N'.' + QUOTENAME(COLUMN_NAME) + N''', COUNT(*), ''' + DATA_TYPE + N''', (SELECT COUNT(*) FROM ' + QUOTENAME(TABLE_SCHEMA) + N'.' + QUOTENAME(TABLE_NAME) + N') FROM ' + QUOTENAME(TABLE_SCHEMA) + N'.' + QUOTENAME(TABLE_NAME) + N' WHERE ' + QUOTENAME(COLUMN_NAME) + N' IS NULL;'
FROM INFORMATION_SCHEMA.COLUMNS c
JOIN sys.objects o ON c.TABLE_NAME = o.name AND c.TABLE_SCHEMA = SCHEMA_NAME(o.schema_id)
WHERE o.type = 'U' AND c.DATA_TYPE IN (SELECT DataType FROM #SparseThreshold);
-- 可加 WHERE 限定 schema/table
EXEC sp_executesql @sql;
SELECT n.ColName, n.NullCount, n.TotalRows,
CAST(n.NullCount * 1.0 / n.TotalRows * 100 AS DECIMAL(5,2)) AS NullPct,
ISNULL(t.PctNull, 60) AS ThresholdPct,
CASE WHEN n.NullCount * 1.0 / n.TotalRows >= ISNULL(t.PctNull, 60) * 0.01 THEN '建議 SPARSE' ELSE '不建議' END AS Recommendation
FROM #NullStats n
LEFT JOIN #SparseThreshold t ON n.DataType = t.DataType
WHERE n.NullCount > 0
ORDER BY NullPct DESC;
DROP TABLE #NullStats, #SparseThreshold;
-- 新增稀疏欄位
CREATE TABLE CustomerInfo (
CustomerID INT PRIMARY KEY,
Address_Line1 VARCHAR(100) NOT NULL,
Address_Line6 VARCHAR(100) SPARSE NULL -- 直接宣告
);
-- 現有欄位轉換 (需重建表/欄位,因無 ALTER COLUMN ... SPARSE)
-- 方法: 新增稀疏欄位 → 更新資料 → 刪除舊欄位 → 重命名
ALTER TABLE CustomerInfo ADD Address_Line6_New VARCHAR(100) SPARSE NULL;
UPDATE CustomerInfo SET Address_Line6_New = Address_Line6;
ALTER TABLE CustomerInfo DROP COLUMN Address_Line6;
EXEC sp_rename 'CustomerInfo.Address_Line6_New', 'Address_Line6', 'COLUMN';
客戶地址表:Line1-3 必填,Line4-5 常用,Line6-8 極少用 (如 5% 填寫)。
→ Line6-8 宣告為 SPARSE,Line4-5 保持一般欄位。