Sql server 在我的例子中,为什么SQL选择了不正确的索引?

Sql server 在我的例子中,为什么SQL选择了不正确的索引?,sql-server,tsql,sql-server-2008,sql-execution-plan,Sql Server,Tsql,Sql Server 2008,Sql Execution Plan,我有一个有两个索引的表;一种是多列聚集索引,位于3列上: ( symbolid int16, bartime int32, typeid int8 ) 第二个是非集群的 ( bartime int16 ) 我尝试运行的select语句是: SELECT symbolID, vTrdBuy FROM mvTrdHidUhd WHERE typeID = 1 AND barDateTime = 44991 AND symbo

我有一个有两个索引的表;一种是多列聚集索引,位于3列上:

(
   symbolid int16,
   bartime int32,
   typeid int8
) 
第二个是非集群的

(
   bartime int16
)
我尝试运行的select语句是:

    SELECT symbolID, vTrdBuy
    FROM mvTrdHidUhd 
    WHERE typeID = 1 
    AND barDateTime = 44991 
    AND symbolid in (1010,1020,1030,1040,1050,1060) 
我使用SQLManagementStudio编辑器在sql2008上运行此查询,并启用实际执行计划,我发现sql使用第二个索引和propse为三列(symbolid、bartime、typeid)创建了一个新索引,但没有聚集!!!(我认为这是非聚集索引,因为已经有了聚集索引)

这个选择是错误的,我再次运行了相同的查询,并强制SQL使用集群索引(使用“with index”),性能应该会更好

我这里有两个问题,一个与此行为有关,另一个与查询本身有关

  • 为什么SQL选择了错误的索引并支持相同的索引
  • “where”
    条件下,我应该使用哪一个来获得更好的性能
  • (101010201030104010501060)中的符号

    (SYMBOLD=1010或SYMBOLD=1020..等)

    (符号介于(1010和1060)之间)

    测试后


    我发现,当我将where条件从use IN改为use>=和=时,更新表/索引上的统计信息可能会使它选择正确的索引如果可能,使用1010和1050之间的
    符号。在
    =
    =
    >
    之间使用
    ,索引列的顺序可能会影响优化器是否选择您的索引。指示索引为(symbolid int16、bartime int32、typeid int8),但symbolid是where子句中最不明显的值。这将需要对您拥有的6个值进行6次索引查找

    我可能会从between语句开始,但只有使用数据、服务器、索引等进行测试才能证明是最好的情况

    如果要创建另一个索引,请尝试对这些列执行另外两个顺序


    如其他地方所述,更新您的统计数据

    您还可以在(symbolid、bartime、typeid、mvTrdBuy)上尝试覆盖索引

    您的查询引用了四列:

    • 象征
    • vTrdBuy
    • 类型ID
    • 业余时间
    而聚集索引只覆盖了其中的三个

    • 符号化
    • vTrdBuy
    • typeID
    • barDateTime
    SQL Server忽略该索引的原因是它对它毫无用处。索引首先按
    symbolID
    排序,您不需要特定的symbolID,而是一组随机值。这意味着它必须读取整个表

    聚集索引中的下一列是
    vTrdBuy
    。这并不是用来帮助它跳到它真正想要的行

    查看查询,有两列非常具体地限制了要返回的行:

    WHERE typeID = 1
    AND barDateTime = 44991 
    
    创建一个以typeIDbarDateTime开头的索引对于帮助SQL Server跳转到您感兴趣的行非常有用

    第一个SQL Server可以直接跳到指定的行

    typeID = 1. 
    
    一旦到了那里,它就可以直接跳到酒吧

    barDateTime = March 8, 2023
    
    它可以通过直接搜索索引来实现这一点,因为索引是按其中的列排序的。这是非常快的,它消除了大多数行被查看

    如果要创建索引,请执行以下操作:

    (
       typeID
       barDateTime
       symbolID
    )
    
    CREATE INDEX IX_mvTrdHidUhd_FancyCovering ON mvTrdHidUhd 
    (
       typeID, barDateTime, symbolID, vTrdBuy
    )
    
    如果查询返回很多行,它可能仍然没有用处。为了完成SELECT语句,SQL Server仍然需要vTrdBuy值。它必须通过跳转表中与条件匹配的每一行(称为书签查找)来完成此操作。如果有太多的行(比如>500),SQL Server会忘记索引,只扫描整个表,因为这样会更快

    如果要阻止书签查找,则不必返回到表中查找缺少的值,并希望在索引中包含该值:

    (
       typeID
       barDateTime
       symbolID
    )
    
    CREATE INDEX IX_mvTrdHidUhd_FancyCovering ON mvTrdHidUhd 
    (
       typeID, barDateTime, symbolID, vTrdBuy
    )
    
    现在,您有了一个索引,它按照SQL Server想要的顺序包含SQL Server想要的所有内容,并且您不必弄乱物理表的物理排序顺序(即集群)

    SELECT  symbolID, vTrdBuy
    FROM    mvTrdHidUhd 
    WHERE   typeID = 1 
            AND barDateTime = 44991 
            AND symbolid IN (1010,1020,1030,1040,1050,1060)
    
    聚集索引的单个连续范围不包含此条件

    这些行:

    1010, 44991, 1
    1010, 50000, 1
    1020, 44991, 1
    
    将按顺序出现在索引中,但您的查询将选择第一个和第三个,跳过第二个

    SQL Server
    可以在谓词数量有限的情况下使用
    聚集索引查找
    ,如在
    案例中的
    。在这种情况下,它使用多个范围:

    SELECT  symbolID, vTrdBuy
    FROM    mvTrdHidUhd 
    WHERE   (typeID = 1 
            AND barDateTime = 44991 
            AND symbolid = 1010)
            OR
            (typeID = 1 
            AND barDateTime = 44991 
            AND symbolid = 1010)
            OR …
    
    但是,如果
    symbolid
    上的
    范围介于
    之间,它无法构造如此有限数量的谓词,这就是为什么它会返回到效率较低的
    聚集索引扫描
    (它扫描
    symbolid
    ,只是过滤出错误的结果)

    在这种情况下,非聚集索引的性能更好

    您可以这样重写查询:

    SELECT  symbolID, vTrdBuy
    FROM    (
            SELECT  DISTINCT symbolid
            FROM    mvTrdHidUhd 
            WHERE   symbolid BETWEEN 1010 AND 1050
            ) s
    JOIN    mvTrdHidUhd m
    ON      m.symbolid = s.symbolid
            AND m.typeID = 1 
            AND m.barDateTime = 44991
    

    ,它也将在您的表上使用
    聚集索引Seek
    ,既可以构建
    不同符号的列表,也可以加入此列表。

    首先在where子句中选择限制最严格的列(输出的行数最少)。我将对其进行测试,但这意味着我必须强制sql使用正确的索引,而不是依赖于它的选择?不,如果您的统计数据正确,并且索引和查询设计正确,那么您就不需要在查询中强制使用某些索引。您说了我所说的,但使用了数据示例+1谢谢你这么好的解释,但你没有回答我的第一个问题?@Ahmed:它选择非聚集索引是因为它认为它会更有选择性。至于建议:请检查SQL Server是否建议完全相同的列顺序?根据定义,聚集索引始终覆盖所有查询。这样说是不对的: