如何提高SQL Server中日期时间过滤的性能?

How to improve performance for datetime filtering in SQL Server?

我在使用datetime列进行过滤时遇到问题。

我尝试了这两种方法:

1
2
datefield < '2013-03-15 17:17:55.179'
datefield < CAST('2013-03-15 17:17:55.179' AS datetime)

我有一个拥有超过3000.000个主要对象的大型数据库。

所以我需要提高我的datetime过滤的性能。我正在阅读有关Unix时间戳的内容(将所有datetime转换为Unix时间戳,然后按此Unix字段进行筛选)。

我认为这比用datetime过滤更好。但如果有人知道其他的方法,我会感激的。

我的问题是:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
SELECT TOP (100)  ev.Title AS Event_name, po.Name AS POI_name,
po.Address, po.City, po.Region, po.Country, po.Latitude, po.Longitude, ev.Start_time,
(SELECT ID_Category FROM SubCategory s WHERE ev.ID_SubCategory = s.ID_SubCategory) AS ID_Category,
ev.ID_SubCategory, ev.ID_Event, ev.ID_Channel, IDChanelEvent,
ev.FavoriteCount, po.gmtOffset, v.IsFavorite, v1.IsFavorite  
FROM Events ev
JOIN POI po ON ev.ID_POI = po.ID_POI
JOIN (SELECT et.id_event AS joinIdEv FROM EventTagLink et, tags t
 WHERE t.id_tag = et.id_tag
 AND ( t.Title = N'music' )
 ) AS joinEvents
 ON joinEvents.joinIdEv = ev.ID_Event
LEFT JOIN Viewed v ON v.ID_Event = ev.ID_Event AND v.ID_User = 1 AND v.IsFavorite = 1 LEFT JOIN Viewed v1 ON v1.ID_Event = ev.ID_Event AND v1.ID_User = 1 AND v1.IsFavorite = 0
WHERE
--ev.GmtStop_time > '2013-03-15 14:17:55.188' AND
po.Latitude > 41.31423 AND po.Latitude < 61.60511
AND  po.Longitude > -6.676602 AND po.Longitude < 17.04498  
AND ev.ID_SubCategory IN (3, 12, 21, 4, 30, 13, 22, 6, 14, 40, 23, 7, 32, 15, 41, 8, 50, 33, 16, 42, 25, 9, 34, 17, 35, 18, 44, 27, 36, 19, 45, 28, 37, 46, 29, 38, 47, 39, 48, 49, 10, 1, 11, 2, 20)
--AND ev.GmtStart_time< '2013-03-15 17:17:55.179'
AND v1.IsFavorite IS NULL

在我评论的时候过滤。

如果我关闭这些过滤器,请求持续时间是几秒钟。如果我打开它们,那么请求持续时间超过25秒。

  • 筛选日期时间的执行计划
  • 不带日期时间筛选器的执行计划

所以有很多关于执行计划、索引等的讨论。但是Unix时间戳呢,这也是我提出这个问题的主要原因。它会提高datetime过滤的性能吗?

事先谢谢。


当涉及到MSQL中datetime上的索引时,一个建议就是索引占用空间会影响搜索时间(是的,这似乎很明显……但是请继续阅读)。

在对日期时间进行索引时,这一点很重要,例如"2015-06-05 22:47:20.102",索引必须说明日期时间内的每个位置。这变得非常大的空间和体积。我使用的一种成功方法是创建一个新的日期时间列,并通过将时间四舍五入到小时,然后在此新列上构建索引来填充数据。示例"2015-06-05 22:47:20.102"翻译为"2015-06-05 22:00:00.000"。通过采用这种方法,我们将保留详细数据,并且可以在这个新列中显示它或通过搜索来使用它,这将使我们获得大约10倍(至少)的返回结果的速度。这是因为索引不必考虑分钟、秒和毫秒字段。


您需要首先查看您的执行计划,以了解SQL Server正在做什么。更可能的是,您只需要添加一个索引。像这样的小转换几乎从来都不是查询速度慢的原因。索引是修复查询的良好第一站。

您不需要将此设置为聚集索引。将其设为聚集索引意味着您不需要执行查找,但对于只有100行的情况,查找速度非常快。我会按这个顺序将datetime和子类别放入非聚集索引中。

如果您要订购,还应该确保它在索引中。因为每个表只使用一个索引是有意义的,所以需要确保所有相关列都以正确的顺序位于同一索引中。

但首先,要有实际的执行计划!


为了获得更好的性能,我建议您创建新索引:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
CREATE INDEX x1 ON LiveCity.dbo.Tags(Title) INCLUDE(ID_Tag)
CREATE INDEX x2 ON LiveCity.dbo.Tags(ID_Event, GmtStart_time, GmtStop_time)
  INCLUDE(
          FavoriteCount,
          ID_Channel,
          ID_POI,
          ID_SubCategory,
          IDChanelEvent,
          Start_time,
          Title
          )
CREATE INDEX x ON LiveCity.dbo.POI(ID_POI, Latitude, Longitude)
  INCLUDE(
          Address,
          City,
          Country,
          gmtOffset,
          Name,
          Region
          )

这将帮助您避免RID查找操作并提高查询的整体性能。


试试这个-

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
;WITH cte AS (
     SELECT IsFavorite, ID_Event  
     FROM Viewed
     WHERE ID_User = 1
)
SELECT TOP (100)
      Event_name = ev.Title
    , POI_name = po.Name
    , po.[address]
    , po.City
    , po.Region
    , po.Country
    , po.Latitude
    , po.Longitude
    , ev.start_time
    , s.ID_Category
    , ev.ID_SubCategory
    , ev.ID_Event
    , ev.ID_Channel
    , IDChanelEvent
    , ev.FavoriteCount
    , po.gmtOffset
    , v.IsFavorite
    , IsFavorite = NULL
FROM [events] ev
JOIN POI po ON ev.ID_POI = po.ID_POI
LEFT JOIN SubCategory s ON ev.ID_SubCategory = s.ID_SubCategory
LEFT JOIN cte v ON v.ID_Event = ev.ID_Event AND v.IsFavorite = 1
WHERE po.Latitude BETWEEN 41.31423 AND 61.60511
     AND po.Longitude BETWEEN -6.676602 AND 17.04498
     AND ev.ID_SubCategory IN (3, 12, 21, 4, 30, 13, 22, 6, 14, 40, 23, 7, 32, 15, 41, 8, 50, 33, 16, 42, 25, 9, 34, 17, 35, 18, 44, 27, 36, 19, 45, 28, 37, 46, 29, 38, 47, 39, 48, 49, 10, 1, 11, 2, 20)
     AND v1.IsFavorite IS NULL
     AND EXISTS(
          SELECT 1
          FROM EventTagLink et
          WHERE t.Title = 'music'
               AND et.joinIdEv = ev.ID_Event
     )
     AND NOT EXISTS (
          SELECT *
          FROM cte v1
          WHERE v1.ID_Event = ev.ID_Event AND v1.IsFavorite = 0
     )


在datetime字段上创建集群索引肯定会有所帮助。我们之前也遇到过同样的问题。我们通过在datetime列上创建索引来解决这个问题。