SQL Server使用非聚集索引(尽管有聚集索引)
我在一个名为SQL Server使用非聚集索引(尽管有聚集索引),sql,sql-server,indexing,sql-execution-plan,Sql,Sql Server,Indexing,Sql Execution Plan,我在一个名为Shopper的表上有两个索引 聚集索引: CREATE CLUSTERED INDEX [CI_EMail_ShopperNumID] ON [dbo].[Shopper] ([EMail] ASC, [ShopperNumID] ASC) DROP INDEX IF EXISTS [nci_wi_Shopper_D8E9A1BB0660D0838F923BB8587C7115] ON [dbo].[Shopper] GO 非聚集索引 CREATE NONCLUSTERED
Shopper
的表上有两个索引
聚集索引:
CREATE CLUSTERED INDEX [CI_EMail_ShopperNumID]
ON [dbo].[Shopper] ([EMail] ASC, [ShopperNumID] ASC)
DROP INDEX IF EXISTS [nci_wi_Shopper_D8E9A1BB0660D0838F923BB8587C7115]
ON [dbo].[Shopper]
GO
非聚集索引
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'
SELECT ShopperNumID
FROM shopper
WHERE Email = '87.kl@abcxyz.com'
在分析执行计划时,我注意到正在使用非聚集索引:
现在,我删除非聚集索引:
CREATE CLUSTERED INDEX [CI_EMail_ShopperNumID]
ON [dbo].[Shopper] ([EMail] ASC, [ShopperNumID] ASC)
DROP INDEX IF EXISTS [nci_wi_Shopper_D8E9A1BB0660D0838F923BB8587C7115]
ON [dbo].[Shopper]
GO
然后重新运行我的选择,注意到聚集索引(最终)正在使用中
有人能解释一下为什么优化引擎使用(庞大的)非聚集索引而不是(首选的)聚集索引吗
微软SQL Server 2016(RTM-GDR)(KB3194716)-13.0.1722.0(X64)Windows 10 Pro 6.3(内部版本14393:)上的开发者版(64位) 更新: 根据收到的输入,为了进一步评估这一点,我在表上创建了另一个非聚集索引,非常类似于已经存在的聚集索引
CREATE NONCLUSTERED INDEX [NCI_EMail_ShopperNumID]
ON [dbo].[Shopper] ([EMail] ASC, [ShopperNumID] ASC)
目前,该表有3个索引可支持mySELECT
:
SELECT ShopperNumID
FROM shopper
WHERE Email = '87.kl@abcxyz.com'
SELECT ShopperNumID
FROM shopper
WHERE Email = '87.kl@abcxyz.com'
时,选择:
SELECT ShopperNumID
FROM shopper
WHERE Email = '87.kl@abcxyz.com'
SELECT ShopperNumID
FROM shopper
WHERE Email = '87.kl@abcxyz.com'
在分析执行计划时,我注意到正在使用新创建的非聚集索引:
似乎优化器坚持使用非聚集索引,不管发生什么 之所以使用非聚集索引,是因为它经过优化,可以根据电子邮件查找行
您可能认为它很庞大,但它被键入电子邮件
这一事实使它非常适合您的查询,即使它包含表中的每一列
您可能没有意识到的是,聚集索引同样庞大,因为它隐式地包含了表中的每个字段。因此,在最坏的情况下(不要设计这样的东西),两个索引都键入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;
From:聚集索引根据数据行的键值在表或视图中排序和存储数据行。这些是索引定义中包含的列。每个表只能有一个聚集索引,因为数据行本身只能按一个顺序排序
非聚集索引覆盖(包括)其他指定的列,因此在引用任何包含的列时不需要返回表。看见实际上,非聚集索引就像创建一个包含列的新表,按索引列排序
就您的查询而言,聚集索引和非聚集索引几乎完全相同,唯一的区别是聚集索引额外按[ShopperNumID]排序。也许查询优化器选择非聚集索引是因为它名义上更适合。在这种情况下,更好的匹配并不一定意味着更好的性能
假设聚集索引和非聚集索引都位于同一存储介质上,则非聚集索引会占用空间,但不会提供额外的性能值。基本上,它是一个索引的六个或另一个索引的六个
聚集索引和非聚集索引的电子邮件地址都具有b树结构。因此,两者都可以很快找到匹配的电子邮件地址
那么,优化器如何选择获取哪一个呢?在这两种情况下,如果有一条记录,那么将获取一个页面(数据页面或索引叶页面)。选择非聚集索引可能是任意的
但是,优化器不知道一个电子邮件地址匹配多少条记录。因此,它必须根据电子邮件匹配的数量做出决定。如果非聚集索引只有两列,那么这将是一个无需思考的问题。索引页面将包含更多的记录(因为“记录”只有两列),因此与电子邮件匹配的记录将位于更少的页面上
不过,在您的例子中,非聚集索引是包含所有列的覆盖索引。也许索引页上比数据页上更适合这些内容(数据页上有一些开销,可能比索引页上的开销更大)
那么,我们到哪里去了?基本操作是搜索b树(这两种索引类型相同),然后读取匹配的记录。在大多数情况下,这两种索引结构在这些操作中相当等效。SQL Server可能会稍微偏爱非聚集索引,因为索引页上的记录多于数据页上的记录(这是一个猜测)。首先,请查看查询计划以查看使用了哪些索引。查询优化器试图最小化IO,但它可以做一些有趣的事情。一般来说,非聚集索引比聚集索引小。如果优化器可以看到非聚集索引可以使用更少的读取来回答查询,那么这就是问题的答案。例外情况是,如果非聚集索引包含表中的所有列。我想这可能是你问题的重点
虽然在某些用例中,在聚集索引中使用字符串是有意义的,但请记住,聚集索引始终包含在每个非聚集索引中。如果不是唯一的,你希望你的聚集索引是小的和有选择性的,看起来ShopperNumbId会满足这个标准,但是我们没有你的完整表。考虑从您的聚集索引中删除电子邮件地址。< /P>
CREATE NONCLUSTERED INDEX [NCI_EMail_ShopperNumID]
ON [dbo].[Shopper] ([EMail] ASC, [ShopperNumID] ASC)
如果您需要,应用程序需要根据电子邮件地址查找记录,为c创建最小的完整覆盖索引