SQL Server 有一个叫做“多列统计”的东西,但这并不是人们认为的意思。
让我们看一下下面的示例表:
CREATE TABLE BadStatistics
(
IsArchived BIT NOT NULL,
Id INT NOT NULL IDENTITY PRIMARY KEY,
Mystery VARCHAR(200) NOT NULL
);
CREATE NONCLUSTERED INDEX BadIndex
ON BadStatistics (IsArchived, Mystery);
这样,我们就在我们拥有的两个索引上创建了两个统计信息:
BadIndex 的统计数据:
+--------------+----------------+-------------------------+
| All density | Average Length | Columns |
+--------------+----------------+-------------------------+
| 0.5 | 1 | IsArchived |
+--------------+----------------+-------------------------+
| 4.149378E-06 | 37 | IsArchived, Mystery |
+--------------+----------------+-------------------------+
| 4.149378E-06 | 41 | IsArchived, Mystery, Id |
+--------------+----------------+-------------------------+
+--------------+------------+---------+---------------------+----------------+
| RANGE_HI_KEY | RANGE_ROWS | EQ_ROWS | DISTINCT_RANGE_ROWS | AVG_RANGE_ROWS |
+--------------+------------+---------+---------------------+----------------+
| 0 | 0 | 24398 | 0 | 1 |
+--------------+------------+---------+---------------------+----------------+
| 1 | 0 | 216602 | 0 | 1 |
+--------------+------------+---------+---------------------+----------------+
聚集索引的统计信息:
+--------------+----------------+---------+
| All density | Average Length | Columns |
+--------------+----------------+---------+
| 4.149378E-06 | 4 | Id |
+--------------+----------------+---------+
+--------------+------------+---------+---------------------+----------------+
| RANGE_HI_KEY | RANGE_ROWS | EQ_ROWS | DISTINCT_RANGE_ROWS | AVG_RANGE_ROWS |
+--------------+------------+---------+---------------------+----------------+
| 1 | 0 | 1 | 0 | 1 |
+--------------+------------+---------+---------------------+----------------+
| 240999 | 240997 | 1 | 240997 | 1 |
+--------------+------------+---------+---------------------+----------------+
| 241000 | 0 | 1 | 0 | 1 |
+--------------+------------+---------+---------------------+----------------+
(我用随机样本数据填充了表,其中大约十分之一的行未归档。之后我运行了完整的扫描统计更新。)
为什么两列统计的直方图只使用一列?我知道很多人都写过它确实如此,但理由是什么?在这种情况下,它会使整个直方图的用处大大降低,因为第一列只有两个值。为什么统计数据会被这样任意限制?
请注意,这个问题不是指多维直方图,它是完全不同的野兽。它是关于单维直方图,单维是包含各自多列的元组。
背景
当前的 SQL Server 模型仅使用单列直方图和多列密度信息。单列直方图用于估计合适谓词的选择性,例如
a = 1
orb > 50
。具有多个谓词的查询只是将各个选择性(与假设)结合起来以产生估计的整体选择性。例如,请参阅我的文章Cardinality Estimation: Combining Density Statistics
多列密度通过为多个相等谓词和聚合基数分组提供弱相关信息来进一步通知模型。
与索引相关的统计信息是该模型的一个机会性附加组件:引擎在构建索引时也可以收集(通常是全扫描)统计信息。SQL Server 自动为其他键构造前导列直方图和密度信息。
索引中非前导列的直方图可以由查询处理器根据需要自动构建,或者预先
sp_createstats
与@indexonly
选项一起使用(等等)。多列直方图
组合单列统计数据(如上)时所做的假设可能会或可能不会足够好地模拟数据的实际情况。在许多情况下,可用的选项(指数退避、独立性、最小选择性)会产生“足够好”的估计。
我们还将过滤统计信息(和索引)作为低基数前导列索引的自然解决方案,例如问题示例。将这些推向逻辑极端会使我们更接近问题所不涉及的多维统计数据。
当可用的建模选项无法提供合适的估计时,在某些情况下,多列统计直方图确实可以为合适的索引谓词提供更好的选择性估计。在不同的列中组合不同的数据类型有一些困难,但没有什么不可克服的。
我们还需要索引键的每个级别的直方图(以获得最佳结果);因此,对于那个索引,除了当前的单列直方图之外
(a, b, c)
,还意味着直方图。(a, b)
(a, b, c)
(a)
还需要修改用于检测过时统计信息的机制以维护受影响的多列直方图。这些直方图最终可能会比单列统计信息更频繁地重建,这仅仅是因为对更多列的修改会影响它们。
所有这些都增加了大小、复杂性和维护开销。
可以使用在引用多列的精心构造的计算列上创建的统计信息来模拟多列统计信息(在有限的范围内)。查询需要在计算列上包含谓词(或基础公式的精确文本匹配)以利用该统计信息。可能只有非常有限的情况下这种方法是可行的。然而,它有一些与自动多列直方图相同的实现问题。
归根结底,唯一能肯定 SQL Server 不支持多列统计信息的人将是设计者自己。如果您认为您可以为该领域的产品改进提出强有力的理由并具有广泛的适用性,您可以在Connect上或通过您的正常支持渠道提出建议。
脚注
直方图仍然提供有关前列中值分布的有用信息:构建统计信息时,有 24,398 行
IsArchived
是false,有 216,602 行是true。此外,统计对象告诉我们有 (1 / 0.5) = 2 个不同的值
IsArchived
,(1 / 4.149378E-06) ~= 241000 个不同的值,(IsArchived, Mystery)
平均行大小为 37 字节,并且频率(IsArchived, Mystery, Id)
相同每行 4 个额外字节。这些都是很好的通用信息,可以与其他列的统计信息相结合,在具有多个谓词的查询中产生选择性估计(如前所述)。