Sql server 在我的例子中,为什么SQL选择了不正确的索引?
我有一个有两个索引的表;一种是多列聚集索引,位于3列上: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
(
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”),性能应该会更好
我这里有两个问题,一个与此行为有关,另一个与查询本身有关
“where”
条件下,我应该使用哪一个来获得更好的性能我发现,当我将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
创建一个以typeID和barDateTime开头的索引对于帮助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是否建议完全相同的列顺序?根据定义,聚集索引始终覆盖所有查询。这样说是不对的: