为什么添加 where 子句时查询会变慢?

Ben*_*nCr 2 performance sql-server-2008-r2 query-performance

我有两个数据库,并且都对具有相同索引的同一个表有相同的视图。

该视图从位置表中选择给定 IMEI 的顶部位置。

CREATE VIEW [dbo].[LatestDeviceLocation]
AS 
SELECT DISTINCT t.Imei, t.Accuracy, t.UserId, t.Lat, t.Lng, t.Timestamp
FROM (SELECT Imei, MAX(Timestamp) AS latest
    FROM      dbo.DeviceLocation
    GROUP BY Imei) AS m INNER JOIN
    dbo.DeviceLocation AS t ON t.Imei = m.Imei AND t.Timestamp = m.latest
GO
Run Code Online (Sandbox Code Playgroud)

我正在使用一个非常简单的 select 查询视图,其中包含一个非常简单的 where 子句。

SELECT TOP 1000 [Imei]
      ,[Accuracy]
      ,[UserId]
      ,[Lat]
      ,[Lng]
      ,[Timestamp]
  FROM [dbo].[LatestDeviceLocation]
  Where [Timestamp] > '2015-02-19T00:00:00.000Z' AND [Timestamp] < '2015-02-26T23:59:59.999Z'
Run Code Online (Sandbox Code Playgroud)

在我的实时服务器上,当我查询我的视图时,我会在 < 1 秒内取回数据。当我添加一个Where [Timestamp] > '2015-02-19T00:00:00.000Z' AND [Timestamp] < '2015-02-26T23:59:59.999Z'跳转到大约 1 分钟的 where 子句时。

在我的测试服务器上,它有 10 倍多的数据(如果 Imei 编号与实时站点 25 相同,则有 35 万多个位置共享),无论是否使用 where 子句,查询都会在 < 1 秒内返回数据。

我找了锁,没看到。

我重新创建了索引,以防它损坏并且没有区别。

我已经完全删除了索引,性能没有改变。

这是我在两台服务器上使用的索引。

/****** Object:  Index [GangHeatMapIndex]    Script Date: 02/26/2015 22:38:38 ******/
CREATE NONCLUSTERED INDEX [GangHeatMapIndex] ON [dbo].[DeviceLocation] 
(
    [UserId] ASC,
    [Timestamp] ASC,
    [Imei] ASC
)
INCLUDE ( [DeviceLocationId],
[Accuracy],
[Lat],
[Lng]) 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
Run Code Online (Sandbox Code Playgroud)

编辑:我刚刚意识到我没有在正确的地方寻找锁。它在查询时取出对象锁。我正在尝试解决如何在视图中内置“无锁”的情况下编写我的视图。

编辑2:我附上了执行计划,顶部是索引,底部没有。

执行计划

编辑 3:更多执行计划,这次全部在实时服务器上,重新添加索引,有和没有 where 子句。

执行计划 - 有索引,没有哪里。

执行计划 - 有索引,有哪里。

编辑4:

我已将视图更改为使用公用表表达式,如下所示,性能要好得多。

WITH cte 
       AS (SELECT Rank() 
                    OVER ( 
                      partition BY dloc.[Imei]
                      ORDER BY dloc.[Timestamp], devicelocationid DESC) AS arank,
                 dloc.*
           FROM   [dbo].[DeviceLocation] AS dloc) 
  SELECT [Imei], [Accuracy], [UserId], [Lat], [Lng], [Timestamp]
  FROM   cte 
  WHERE  arank = 1
Run Code Online (Sandbox Code Playgroud)

通过防止在最终结果中发生任何重复,在订单中包含设备 DeviceLocationId。

Joi*_*dio 5

编辑- 下面假设查询将返回的行数(如果不存在限制)超过限制。(如......它通常会返回 5000 行,但限制迫使它返回 1000)

任何时候您对查询返回的行数有限制时,您都不应该期望该查询的 TIMING 与性能有任何相关性。

例如,如果您采用这样的简单查询:

SELECT * FROM table_with_1m_rows;
Run Code Online (Sandbox Code Playgroud)

处理需要一段时间,因为它必须通过顺序扫描获取所有行。

如果我将其调整为:

SELECT TOP 1000 * FROM table_with_1m_rows;
Run Code Online (Sandbox Code Playgroud)

它会相对较快地返回,因为虽然它仍然执行顺序扫描,但在超过 1000 行后它可以停止。

如果我然后将其调整为:

SELECT TOP 1000 * FROM table_with_1m_rows WHERE col1 > 100;
Run Code Online (Sandbox Code Playgroud)

它将比之前的查询花费更长的时间,因为虽然它仍然执行顺序扫描,但在返回 1000 行之前很可能必须扫描 1000 多行。

无论数据库需要使用顺序扫描还是索引扫描,以上所有内容都适用。

如果您真的想对查询的性能进行故障排除,则需要删除 TOP 1000,然后查看您的查询计划并查看性能下降的位置...(在这种情况下,很可能缺少有用的索引)

  • 根据他们使用行的方式(例如在 SSMS 中通过非常慢的网络连接),某处将有一个临界点,不过滤和返回更多行实际上会更慢 - 我不知道那是哪里,但是向 SSMS 返回 10 亿行的查询不一定比返回 1000 行的过滤查询更快。过滤查询是否比未过滤查询花费的时间更长,反之亦然,这并没有绝对的定论。 (2认同)