使用IN()提高查询性能


14

我有以下SQL查询:

SELECT
  Event.ID,
  Event.IATA,
  Device.Name,
  EventType.Description,
  Event.Data1,
  Event.Data2
  Event.PLCTimeStamp,
  Event.EventTypeID
FROM
  Event
INNER JOIN EventType ON EventType.ID = Event.EventTypeID
INNER JOIN Device ON Device.ID = Event.DeviceID
WHERE
  Event.EventTypeID IN (3, 30, 40, 41, 42, 46, 49, 50)
  AND Event.PLCTimeStamp BETWEEN '2011-01-28' AND '2011-01-29'
  AND Event.IATA LIKE '%0005836217%'
ORDER BY Event.ID;

我在Event表上也有该列的索引TimeStamp。我的理解是由于该IN()语句未使用该索引。所以我的问题是,有没有一种方法可以为此特定IN()语句创建索引以加快查询速度?

我还尝试Event.EventTypeID IN (2, 5, 7, 8, 9, 14)为上的索引添加过滤器TimeStamp,但是在查看执行计划时,它似乎并未使用该索引。任何建议或对此的见解将不胜感激。

下面是图形计划:

执行计划

这是.sqlplan文件的链接


我们也可以看看执行计划吗?:)
dezso 2012年

1
并请发布带有.sqlplan扩展名的实际执行计划(未估算)。大多数人只想发布图形化计划的屏幕快照,而这远没有用处。
亚伦·伯特兰

好的,我添加了执行计划以及更新了SQL查询。
SandersKY 2012年

@SandersKY最好内联.sqlplan文件,以将与该问题相关的所有内容保留在同一站点上。
TrygveLaugstøl2012年

1
@trygvis-由于帖子的长度限制,这通常是不可能的。羞耻堆栈交换不支持内部托管帖子附件。
马丁·史密斯

Answers:


18

给定的表格具有以下一般形式:

CREATE TABLE Device 
(
    ID integer PRIMARY KEY
);

CREATE TABLE EventType
(
    ID integer PRIMARY KEY, 
    Name nvarchar(50) NOT NULL
);

CREATE TABLE [Event]
(
    ID integer PRIMARY KEY, 
    [TimeStamp] datetime NOT NULL, 
    EventTypeID integer NOT NULL REFERENCES EventType, 
    DeviceID integer NOT NULL REFERENCES Device
);

以下索引很有用:

CREATE INDEX f1 
ON [Event] ([TimeStamp], EventTypeID) 
INCLUDE (DeviceID)
WHERE EventTypeID IN (2, 5, 7, 8, 9, 14);

对于查询:

SELECT
  [Event].ID,
  [Event].[TimeStamp],
  EventType.Name,
  Device.ID
FROM
  [Event]
INNER JOIN EventType ON EventType.ID = [Event].EventTypeID
INNER JOIN Device ON Device.ID = [Event].DeviceID
WHERE
  [Event].[TimeStamp] BETWEEN '2011-01-28' AND '2011-01-29'
  AND Event.EventTypeID IN (2, 5, 7, 8, 9, 14);

筛选器满足AND子句要求,索引的第一个键允许[TimeStamp]对筛选的对象进行查找,EventTypeIDs并且包括DeviceID列在内的所有索引使索引覆盖(因为DeviceID连接到Device表是必需的)。

计划完成

索引的第二个键- EventTypeID不是严格要求的(它也可以是一INCLUDEd列);由于这里所述的原因,我已将其包含在密钥中。通常,我建议人们至少从过滤索引子句中选择列。INCLUDEWHERE


根据问题中更新的查询和执行计划,我同意SSMS建议的索引更一般,可能是此处更好的选择,除非EventTypeIDsAaron在回答中也提到过滤的列表是静态的:

CREATE TABLE Device 
(
    ID integer PRIMARY KEY,
    Name nvarchar(50) NOT NULL UNIQUE
);

CREATE TABLE EventType
(
    ID integer PRIMARY KEY, 
    Name nvarchar(20) NOT NULL UNIQUE,
    [Description] nvarchar(100) NOT NULL
);

CREATE TABLE [Event]
(
    ID integer PRIMARY KEY, 
    PLCTimeStamp datetime NOT NULL,
    EventTypeID integer NOT NULL REFERENCES EventType, 
    DeviceID integer NOT NULL REFERENCES Device,
    IATA varchar(50) NOT NULL,
    Data1 integer NULL,
    Data2 integer NULL,
);

建议的索引(如果合适,则声明为唯一):

CREATE UNIQUE INDEX uq1
ON [Event]
    (EventTypeID, PLCTimeStamp)
INCLUDE 
    (DeviceID, IATA, Data1, Data2, ID);

执行计划中的基数信息(未记录的语法,在生产系统中不使用):

UPDATE STATISTICS dbo.Event WITH ROWCOUNT = 4042700, PAGECOUNT = 400000;
UPDATE STATISTICS dbo.EventType WITH ROWCOUNT = 22, PAGECOUNT = 1;
UPDATE STATISTICS dbo.Device WITH ROWCOUNT = 2806, PAGECOUNT = 28;

更新的查询(在此特定情况下,重复表的IN列表EventType将对优化器有所帮助):

SELECT
  Event.ID,
  Event.IATA,
  Device.Name,
  EventType.Description,
  Event.Data1,
  Event.Data2,
  Event.PLCTimeStamp,
  Event.EventTypeID
FROM
  Event
INNER JOIN EventType ON EventType.ID = Event.EventTypeID
INNER JOIN Device ON Device.ID = Event.DeviceID
WHERE
  Event.EventTypeID IN (3, 30, 40, 41, 42, 46, 49, 50)
  AND EventType.ID IN (3, 30, 40, 41, 42, 46, 49, 50)
  AND Event.PLCTimeStamp BETWEEN '2011-01-28' AND '2011-01-29'
  AND Event.IATA LIKE '%0005836217%'
ORDER BY Event.ID;

预计执行计划:

第二计划

您得到的计划可能会有所不同,因为我使用的是猜测的统计数据。总的目的是为优化器提供尽可能多的信息,并在400万行[Event]表中提供一种有效的访问方法(索引)。


8

大部分成本是聚集索引扫描,除非该表真的很宽或者您实际上并不需要输出中的所有这些列,否则我相信SQL Server这是当前方案中的最佳路径,并且没有其他更改。它确实使用范围扫描(标记为CI搜寻)来缩小其感兴趣的行的范围,但是由于输出的原因,即使使用您创建的过滤索引,它仍将需要查找或CI扫描扫描针对此范围,即使在那种情况下,CI扫描可能仍然是最便宜的(或者至少SQL Server如此估计)。

执行计划确实告诉您该索引将非常有用:

CREATE NONCLUSTERED INDEX ix_EventTypeID_PLCTimeStamp_WithIncludes
  ON [dbo].[Event] ([EventTypeID],[PLCTimeStamp])
  INCLUDE ([ID],[DeviceID],[Data1],[Data2],[IATA]);

尽管取决于您的数据偏斜,但反过来可能更好一些,例如:

CREATE NONCLUSTERED INDEX ix_PLCTimeStamp_EventTypeID_WithIncludes
  ON [dbo].[Event] ([PLCTimeStamp],[EventTypeID])
  INCLUDE ([ID],[DeviceID],[Data1],[Data2],[IATA]);

但我会同时测试两者,以确保哪一个更好—这些索引中的任何一个与您现在拥有的索引之间的差异可能只是微不足道的(我们知道的变量太多),并且您必须考虑到另外一个索引需要额外的维护,这可能会明显影响您的DML操作(插入/更新/删除)。您也可以考虑按照@SQLKiwi的建议在该索引中包括过滤条件,但前提是这是您经常搜索的EventTypeID值的集合。如果该设置随时间变化,则过滤后的索引仅对该特定查询有用。

由于行数如此之低,我不得不怀疑当前的性能可能有多糟糕?该查询返回3行(但没有任何迹象表明它拒绝了多少行)。表中有几行?


4

我只是发现在我执行执行计划时,SQL Server 2008 R2实际上提出了索引建议。该建议索引使查询运行速度提高了约90%。

它建议的索引如下:

CREATE NONCLUSTERED INDEX [INDEX_spBagSearch] ON [dbo].[Event] 
(
    [EventTypeID] ASC,
    [PLCTimeStamp] ASC
)
INCLUDE ( [ID],
[DeviceID],
[Data1],
[Data2],
[IATA]) WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]
GO
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.