我有一张桌子
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。
答案 0 :(得分:3)
这有不同的可能性。 我会尽力帮你隔离它们:
可能是SQL Server偏爱您的新聚集索引(很可能是主键)。解决此问题的一种方法是使用NonClustered主键并在其他两个字段(varid和timestamp)上对索引进行聚类。也就是说,如果您不希望varid和timestamp成为PK。
另外,查看(估计的)执行计划可能有所帮助。
但我相信#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)
您可能需要分析表以收集统计信息,因此优化程序可以确定是否使用索引。