2

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

(
   symbolid int16,
   bartime int32,
   typeid int8
) 

第二个是非聚集在

(
   bartime int16
)

我试图运行的选择语句是:

    SELECT symbolID, vTrdBuy
    FROM mvTrdHidUhd 
    WHERE typeID = 1 
    AND barDateTime = 44991 
    AND symbolid in (1010,1020,1030,1040,1050,1060) 

我使用 sql management studio 编辑器在 sql2008 上运行此查询并启用实际执行计划,我发现 sql 使用第二个索引和 propse 为三列(symbolid、bartime、typeid)创建一个新索引但非聚集!!!(我认为它说的是非聚集索引,因为已经有一个聚集索引)

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

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

  1. 为什么 SQL 选择错误的索引并支持相同的索引
  2. 在这种情况下我应该使用哪一个以"where"获得更好的性能

符号在 (1010,1020,1030,1040,1050,1060)

(符号 = 1010 或符号 = 1020 ..等)

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

测试后

我发现当我将 where 条件从使用 IN 更改为使用 >= 和 <= bartime 列上的非聚集索引比 3 列上的聚集索引提供更好的性能。

所以我有两种情况,如果 WHERE 使用 IN 最好使用聚集索引,如果它包含 >= 和 <= 它使用第二个。

4

6 回答 6

3
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 ServerClustered Index Seek如果谓词数量有限,则可以使用,例如您的IN情况。在这种情况下,它使用了多个范围:

SELECT  symbolID, vTrdBuy
FROM    mvTrdHidUhd 
WHERE   (typeID = 1 
        AND barDateTime = 44991 
        AND symbolid = 1010)
        OR
        (typeID = 1 
        AND barDateTime = 44991 
        AND symbolid = 1010)
        OR …

但是如果一个BETWEEN范围symbolid不能构造如此有限数量的谓词,这就是它恢复到较低效率的原因Clustered Index Scan(它会扫描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

,这也将Clustered Index Seek在您的表上使用,既可以建立列表,DISTINCT symbolid也可以加入该列表。

于 2009-06-03T16:15:49.100 回答
0

更新表/索引的统计信息可能使其选择正确的索引

于 2009-05-18T07:42:26.667 回答
0

symbolid BETWEEN 1010 AND 1050尽可能使用。与使用 or 相比,使用orBETWEEN=or>=>n<<=它们的组合AND通常会导致更好的性能和更好的索引选择。ORIN

于 2009-05-18T07:44:51.750 回答
0

索引列的顺序可能会影响优化器是否会选择您的索引。您指出索引是 (symbolid int16,bartime int32,typeid int8) 但 symbolid 是 where 子句中最不明显的值。这将需要对您拥有的 6 个值进行 6 次索引查找。

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

如果您要创建另一个索引,请尝试这些列的 2 个其他顺序。

如其他地方所述,更新您的统计信息

于 2009-05-18T07:48:14.083 回答
0

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

于 2009-05-19T13:26:00.400 回答
0

您的查询引用了四列:

  • 符号ID
  • vTrdBuy
  • 类型ID
  • 条日期时间

而聚集索引只涵盖其中的三个

  • 符号ID
  • vTrdBuy
  • 类型ID
  • 条日期时间

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
)

如果查询返回很多行,它仍然可能没有用。为了完成 SELECT 语句,SQL Server 仍然需要vTrdBuy值。它必须通过为符合条件的每一行(称为Bookmark Lookup)跳过表来执行此操作。如果行数过多(例如 > 500),SQL Server 将忘记索引并仅扫描整个表 - 因为它会更快。

您想阻止书签查找,通过让它不必回到表中查找缺失值,您希望将值包含在索引中:

CREATE INDEX IX_mvTrdHidUhd_FancyCovering ON mvTrdHidUhd 
(
   typeID, barDateTime, symbolID, vTrdBuy
)

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

于 2009-06-03T16:09:06.850 回答