令人信服的SQL服务器在聚集索引上向后搜索仅插入模式

时间:2014-04-10 15:31:23

标签: sql sql-server sql-server-2008

我们的数据库有许多表格,这些表格遵循"仅插入"架构。行添加到最后,"当前"然后,可以通过查找每个逻辑密钥的最近插入行来找到该值。

以下是一个例子:

CREATE TABLE [dbo].[SPOTQUOTE](
[ID] [numeric](19, 0) NOT NULL,
[QUOTETYPE] [varchar](255) NOT NULL,
[QUOTED_UTC_TS] [datetime] NOT NULL,
[QUOTED_UTC_MILLIS] [smallint] NOT NULL,
[RECEIVED_UTC_TS] [datetime] NOT NULL,
[RECEIVED_UTC_MILLIS] [smallint] NOT NULL,
[VALUE_ASK] [float] NULL,
[VALUE_BID] [float] NULL,
[FEEDITEM_ID] [numeric](19, 0) NOT NULL,
[SAMPLING] [int] NOT NULL,
 CONSTRAINT [SPOTQUOTE_pk1] PRIMARY KEY CLUSTERED 
(
[ID] ASC
)WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, IGNORE_DUP_KEY = OFF,            ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]
) ON [PRIMARY]

GO

此表的逻辑键是" feeditem_id"。但是,我们可以执行历史查询,而是只使用" ID"作为实际的物理钥匙。

因此,我们知道每个不同的feeditem_id的max(id)将在表的末尾找到,而不是在开头。

查询表格时,我们希望找到最新的"更新每个" feeditem_id",这是"逻辑键"对于这张桌子。

以下是我们想要的查询:

select feeditem_id, max(id)
from spotquote
group by feeditem_id
having feeditem_id in (827, 815, 806)

这样我们就可以获得每个feeditem_id的最新ID。

不幸的是,SQL Server 2008为此查询生成了一个次优查询计划。

根据我对SQL的理解,这是为最大id选择的事实,这是主要的群集密钥,这意味着最佳查询计划是:

  • 从表格末尾开始
  • 向后走,跟踪每个feeditem_id
  • 到目前为止遇到的最大ID
  • 一旦找到每个feeditem_id的Id,就停止

我希望这会非常快。

第一个问题:我是否可以通过某种方式明确告诉SQL服务器执行上述查询计划?

我试过了:

SELECT feeditem_id, max(ID) as latest from SPOTQUOTE with (index(SPOTQUOTE_pk1)) group by feeditem_id
having FEEDITEM_ID in (827, 815, 806)

但是,在实践中,它似乎执行得更慢。

我想知道"聚集索引扫描"正在向前走,而不是倒退...有没有办法可以确认这是不是正在发生的事情?

如何确认此聚簇索引扫描是否从表的后面开始工作,以及如何说服SQL服务器向后搜索聚簇索引?

更新

问题确实是当我执行分组时,聚集索引扫描不会向后搜索。

相反,以下SQL查询基本上生成了正确的查询计划:

select
FEEDITEM_ID, MAX(id) from
(select top 100 * from
SPOTQUOTE
where FEEDITEM_ID in (827,815,806)
order by ID desc) s
group by feeditem_id

我可以在管理工作室看到" Ordered = True"和"扫描方向=后退":

Screenshot from Management Studio

它执行速度非常快 - 2毫秒 - 而且几乎可以肯定"的工作原理。

我只是想要"停止"一旦找到每个Feed id的条目而不是前100个条目。

令人沮丧的是,似乎没有办法告诉SQL服务器执行这个明显更高效的查询。

如果我通过"做一个正常的"组。使用feeditem_id和id上的适当索引,速度更快 - 大约300毫秒 - 但仍然比向后聚集索引扫描慢100倍。

A normal group by with appropriate index defined achieves 300 ms

1 个答案:

答案 0 :(得分:1)

SQL Server无法生成截至2012年的此类查询计划。重写查询:

SELECT ids.feeditem_id, MaxID
FROM (VALUES (827), (815), (806)) ids(feeditem_id)
CROSS APPLY (
 select TOP 1 ID AS MaxID
 from spotquote sq
 where sq.feeditem_id = ids.feeditem_id
 ORDER BY ID DESC
) x

这会产生一个计划,可以根据您指定的ID查找spotquote表。这是我们能做的最好的事情。只要您感兴趣的所有组至少有一个值,SQL Server就无法中止聚合。