我在名为Shopper
的表上有两个索引。
聚集索引:
CREATE CLUSTERED INDEX [CI_EMail_ShopperNumID]
ON [dbo].[Shopper] ([EMail] ASC, [ShopperNumID] ASC)
非聚集指数
CREATE NONCLUSTERED INDEX [nci_wi_Shopper_D8E9A1BB0660D0838F923BB8587C7115]
ON [dbo].[Shopper] ([EMail] ASC)
INCLUDE ([DateCreated], [FirstName], [LastLoginDate], [LastName],
[MaxEmailVolume], [ShopperNumID], [ShopperSourceCD], [ShopperSourceOther])
我运行一个非常简单的SELECT
:
SELECT ShopperNumID
FROM shopper
WHERE Email = '87.kl@abcxyz.com'
在分析执行计划时,我注意到正在使用非聚集索引:
现在,我删除了非聚集索引:
DROP INDEX IF EXISTS [nci_wi_Shopper_D8E9A1BB0660D0838F923BB8587C7115]
ON [dbo].[Shopper]
GO
并重新运行我的选择以注意聚集索引(最终)正在使用
有人可以解释为什么优化引擎正在使用(庞大的)非聚集索引而不是(首选)聚簇索引吗?
Microsoft SQL Server 2016(RTM-GDR)(KB3194716) - 13.0.1722.0(X64)
Windows 10 Pro 6.3(Build 14393:)上的Developer Edition(64位)
更新 根据收到的输入,为了进一步评估,我在表上创建了另一个非聚集索引,与现有的聚簇索引非常相似。
CREATE NONCLUSTERED INDEX [NCI_EMail_ShopperNumID]
ON [dbo].[Shopper] ([EMail] ASC, [ShopperNumID] ASC)
目前,该表有3个索引可以支持我的SELECT
:
答案 0 :(得分:2)
正在使用非聚集索引,因为它已针对根据Email
查找行进行了优化。
您可能认为它很笨重,但它在Email
上键入的事实使其成为查询的理想选择,即使它包含表格中的每一列。
您可能没有意识到聚集索引同样庞大,因为它隐含地包含表中的每个字段。因此,在最糟糕的情况下(不要设计类似的东西),您可以在Email
上键入两个索引,并且两个索引都包含每一列。优化者可以选择使用其中之一。
如果使用此脚本,它可以显示非聚簇索引和聚簇索引实际使用了多少空间:
SELECT o.NAME AS TableOrViewName,
i.name As IndexName,
i.type_desc As IndexType,
i.index_id As IndexOrdinal,
s.Name AS SchemaName,
p.rows AS RowCounts,
p.data_compression_desc As CompressionType,
SUM(a.total_pages) * 8 / 1024.0 AS ObjectSpaceMB,
SUM(a.used_pages) * 8 / 1024.0 AS UsedSpaceMB
FROM sys.objects As o
LEFT JOIN sys.indexes i ON o.OBJECT_ID = i.object_id
JOIN sys.partitions p ON i.object_id = p.OBJECT_ID AND i.index_id = p.index_id
JOIN sys.allocation_units a ON p.partition_id = a.container_id
LEFT JOIN sys.schemas s ON o.schema_id = s.schema_id
WHERE o.NAME NOT LIKE 'dt%'
AND o.is_ms_shipped = 0
AND i.OBJECT_ID > 255
GROUP BY o.Name,
i.name,
i.type_desc,
i.index_id,
s.Name,
p.data_compression_desc,
p.Rows;
答案 1 :(得分:1)
基本上,它是六分之一或六分之一。
您的聚簇索引和非聚集索引都具有电子邮件地址的b树结构。因此,要么能够非常快速地找到匹配的电子邮件地址。
然后,优化器如何选择要获取的内容?好吧,在这两种情况下,如果有一条记录,那么就会获取一个页面(数据页面或索引页面页面)。也许选择非聚集索引是任意的。
但是,优化程序不知道电子邮件地址匹配的记录数。因此,它必须根据电子邮件匹配的数量做出决定。如果非聚集索引只有两列,那么这将是一个明智的选择。索引页面将包含更多记录(因为“记录”只有两列),因此与电子邮件匹配的记录将在更少的页面上。
但是,在您的情况下,非聚集索引是包含所有列的覆盖索引。也许更多这些适合索引页面而不是数据页面(数据页面上有一些开销,它可能不仅仅是索引页面上的开销)。
那么,我们在哪里得到了什么?基本操作是搜索b树(两种索引类型都相同),然后读取匹配的记录。在大多数情况下,这两个索引结构在这些操作中将非常相同。 SQL Server可能对非聚集索引略有偏好,因为索引页面上的记录比数据页面上的更多(这是猜测)。
答案 2 :(得分:0)
From MSDN: Clustered and Nonclustered Indexes Described:聚簇索引根据键值对表或视图中的数据行进行排序和存储。这些是索引定义中包含的列。每个表只能有一个聚簇索引,因为数据行本身只能按一个顺序排序。
非聚集索引覆盖(包括)其他指定列,因此在引用任何包含的列时不需要返回到表。见MSDN:Create Indexes with Included Columns。实际上,非聚簇索引就像创建一个包含列的新表,按索引列排序。
对于您的查询,聚簇索引和非聚簇索引非常接近,唯一的区别是聚簇索引还按[ShopperNumID]排序。也许查询优化器正在选择非聚簇索引,因为它名义上更合适。在这种情况下,更好的拟合并不一定意味着更好的表现。
假设聚簇索引和非聚簇索引都位于同一存储介质上,则非聚簇索引会占用空间,但不会提供额外的性能值。
答案 3 :(得分:0)
首先,对查看查询计划以查看正在使用的索引表示赞赏。查询优化器尝试最小化IO,但它可以做一些有趣的事情。一般来说,非聚集索引小于聚簇索引。如果优化器可以看到非聚集索引可以使用较少的读取来回答查询,那么这就是您的问题的答案。如果非聚集索引包含表中的所有列,则例外。我怀疑这可能是你问题的重点。
虽然肯定会在聚集索引中使用字符串的用例,但请记住,聚簇索引始终包含在每个非聚集索引中。您希望聚集索引小而且有选择性(如果不是唯一的),看起来ShopperNumbId符合此条件,但我们没有完整的表格。请考虑从聚集索引中删除电子邮件地址。
如果您的应用程序需要根据电子邮件地址查找记录,那么为您需要的列创建最小的完整覆盖索引将为您提供最佳性能,这就是nci_wi_Shopper_D8E9A1BB0660D0838F923BB8587C7115所显示的内容。