Warning: file_get_contents(/data/phpspider/zhask/data//catemap/7/sql-server/24.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
SQL Server使用非聚集索引(尽管有聚集索引)_Sql_Sql Server_Indexing_Sql Execution Plan - Fatal编程技术网

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个索引可支持my
SELECT

SELECT ShopperNumID
FROM shopper
WHERE Email = '87.kl@abcxyz.com'
SELECT ShopperNumID
FROM shopper
WHERE Email = '87.kl@abcxyz.com'
  • 聚集索引[CI_EMail_ShopperNumID]
  • 非聚集索引[nci_wi_Shopper_D8E9A1BB0660D0838F923BB8587C7115]
  • 非聚集索引[NCI_EMail_ShopperNumID]
  • 现在,当我运行相同的
    时,选择

    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创建最小的完整覆盖索引