为什么我的索引不会自动使用?

时间:2012-03-04 22:59:23

标签: sql sql-server indexing

我有一张桌子

Archive(VarId SMALLINT, Timestamp DATETIME, Value FLOAT)

VarId不是唯一的。该表包含测量值。我在Timestamp上有一个聚集索引。现在我需要在特定日期之前找到特定VarId的测量值。所以我这样做:

SELECT TOP(1) * 
FROM Archive 
WHERE VarId = 135 
  AND Timestamp < '2012-06-01 14:21:00'
ORDER BY Timestamp DESC; 

如果没有此类测量,则此查询将搜索整个表。所以我在(VarId, Timestamp)上引入了另一个索引。

我的问题是:SQL Server似乎并不关心它,查询仍然需要永远。当我明确说明'WITH (INDEX = <id>)'时,它可以正常工作。我能做什么,以便SQL Server自动使用我的索引?

我正在使用SQL Server 2005。

3 个答案:

答案 0 :(得分:3)

这有不同的可能性。 我会尽力帮你隔离它们:

  1. 可能是SQL Server偏爱您的新聚集索引(很可能是主键)。解决此问题的一种方法是使用NonClustered主键并在其他两个字段(varid和timestamp)上对索引进行聚类。也就是说,如果您不希望varid和timestamp成为PK。

  2. 另外,查看(估计的)执行计划可能有所帮助。

  3. 但我相信#1只有这两个字段是最常用(查询)索引才能正常工作。为了确定是否是这种情况,最好分析哪些用户最有可能使用(来自http://sqlblog.com/blogs/louis_davidson/archive/2007/07/22/sys-dm-db-index-usage-stats.aspx):

  select
          ObjectName = object_schema_name(indexes.object_id) + '.' + object_name(indexes.object_id),
          indexes.name,
          case when is_unique = 1 then 'UNIQUE ' else '' end + indexes.type_desc,
          ddius.user_seeks,
          ddius.user_scans,
          ddius.user_lookups,
          ddius.user_updates
        from
          sys.indexes
        left join sys.dm_db_index_usage_stats ddius on (
          indexes.object_id = ddius.object_id
            and indexes.index_id = ddius.index_id
            and ddius.database_id = db_id()
        )
WHERE object_schema_name(indexes.object_id) != 'sys' -- exclude sys objects AND object_name(indexes.object_id) LIKE 'Archive' order by ddius.user_seeks + ddius.user_scans + ddius.user_lookups desc

祝你好运

答案 1 :(得分:1)

我的猜测是您的索引设计是个问题。你在DATETIME字段上有一个CLUSTERED索引,我怀疑它不是唯一的数据,就像VarId一样,因此你没有将它声明为UNIQUE。因为它不是唯一的,所以存在一个隐藏的4字节“唯一符号”字段(因此,无论您不给它唯一的数据,每行都可以是物理上唯一的),并且具有相同DATETIME值的行在该组中基本上是随机的。相同的DATETIME值(因此即使缩小时间仍需要扫描该分组)。你还有一个关于VarId,Timestamp的NONCLUSTERED索引。 NONCLUSTERED索引包含来自CLUSTERED索引的数据,因此内部的NONCLUSTERED索引实际上是:VarId,Timestamp,Timestamp(来自CLUSTERED索引)。所以你可以在NONCLUSTERED索引中省略Timestamp列,它对优化器来说都是一样的,但从某种意义上来说它会更好,因为它会是一个更小的索引。

因此,您的物理布局基于日期,而VarId值分布在这些日期。因此,就数据页而言,VarId = 135可以分散很远。是的,您的非聚集索引会将它们组合在一起,但优化器可能正在考虑您需要所有字段(“SELECT *”部分)和Timestamp&lt; '2012-06-01 14:21:00'条件除此之外似乎得到了你需要的大部分,而不是找到几行并进行书签查找以获得“值”字段来实现“SELECT * ”。很可能,如果你只做“SELECT TOP(1)VarId,Timestamp”,它更可能使用你的NONCLUSTERED索引而不需要“INDEX =”提示。

影响整体性能的另一个问题可能是ORDER BY正在以DESC顺序请求时间戳,如果你有ASC顺序的CLUSTERED索引,那么它将与你正在寻找的方向相反(至少在这个查询中) )。当然,在这种情况下,如果它是在DESC顺序中的话,可以在NONCLUSTERED索引中使用Timestamp。

我的建议是重新考虑CLUSTERED索引。仅仅判断此查询(其他查询/使用可能会改变建议),请尝试删除NONCLUSTERED索引并首先使用Timestamp字段以DESC顺序重新创建CLUSTERED索引,并且还使用VarId重新创建CLUSTERED索引,以便可以对UNIQUE进行删除。所以:

CREATE UNIQUE CLUSTERED INDEX [UIX_Archive_Timestamp_VarId] 
ON Archive (Timestamp DESC, VarId ASC)

当然,这假设Timestamp和VarId组合是唯一的。如果没有,那么仍然尝试不使用UNIQUE关键字。

更新

一起提取所有这些信息和建议:

在设计索引时,您需要考虑数据的分布和用于与之交互的用例。通常需要考虑很多,并且几种不同的方法在理论上看起来会很好。您需要尝试一些方法,对其进行剖析/测试,并查看哪些方法在现实中效果最佳。如果不知道你正在做什么的所有方面以及正在发生什么,以及计划使用和/或修改这个表,我怀疑在原始问题中没有出现过,那么就没有“总是这样”的方法。< / p>

因此,为了开始这段旅程,您按日期排序记录,并查看自然发生的日期和日期范围,以便使Timestamp首先获得更多您正在做的事情并减少碎片,特别是如果定义为DESC in创造。在那一点上只有VarId的NC索引就可以了,即使是展开,也可以查看特定VarId的一组行。所以也许从那里开始(改变CLUSTERED索引的方向顺序并从NC索引中删除Timestamp)。了解这些变化与现有结构的对比情况。然后尝试将VarId字段移动到CLUSTERED索引中并删除NC索引。你说组合也不是唯一的,但确实增加了行排序的可预测性。看看它是如何工作的。这张表有没有更新?如果不是,并且Value字段以及Timestamp和VarId将是唯一的,则尝试将其添加到CLUSTERED索引并确保使用UNIQUE关键字创建。通过查看实际执行计划并在运行查询之前使用SET STATISTICS IO ON查看这些不同方法的工作原理,并了解不同方法之间的逻辑读取如何比较。

希望这会有所帮助:)

答案 2 :(得分:0)

您可能需要分析表以收集统计信息,因此优化程序可以确定是否使用索引。