优化我的SQL Server查询

优化我的SQL Server查询,sql,sql-server,sql-server-2008,Sql,Sql Server,Sql Server 2008,我有一个python应用程序,它ping设备作为测试,以检查它们是否处于活动状态 下面是我用来对每个设备进行分组的查询,并根据该设备的PingResults中的最后一条记录确定该设备是否在线 SELECT c.ID, c.DeviceName, c.GroupID, c.DeviceIP, p1.Status, p1.DateTime AS LastUpdate, DeviceGroups.GroupName FROM Devices AS

我有一个python应用程序,它ping设备作为测试,以检查它们是否处于活动状态

下面是我用来对每个设备进行分组的查询,并根据该设备的PingResults中的最后一条记录确定该设备是否在线

SELECT     
    c.ID, c.DeviceName, c.GroupID, c.DeviceIP, p1.Status, 
    p1.DateTime AS LastUpdate, DeviceGroups.GroupName
FROM         
    Devices AS c 
INNER JOIN
    PingResults AS p1 ON c.ID = p1.DeviceID 
INNER JOIN
    DeviceGroups ON c.GroupID = DeviceGroups.ID 
LEFT OUTER JOIN
    PingResults AS p2 ON c.ID = p2.DeviceID 
                      AND (p1.DateTime < p2.DateTime OR
                           p1.DateTime = p2.DateTime AND p1.DeviceID < p2.DeviceID)
WHERE     
    (p2.ID IS NULL)
当前执行计划

是否有任何方法可以优化或更改此查询以提高效率,因为以当前速度,当数据库填满时,运行此查询将花费太长时间


计划中最大的成本是哈希匹配(内部联接)

为了加快这一速度,请将参与联接的两个表(设备和设备组)索引到它们联接的列上


这会将哈希匹配更改为一个内部循环,这比您在计划中看到的要快得多。

计划中最大的成本是哈希匹配(内部连接)

LEFT OUTER JOIN  PingResults AS p2 
   ON c.ID = p2.DeviceID AND (p1.DateTime < p2.DateTime 
      OR p1.DateTime = p2.DateTime AND p1.DeviceID < p2.DeviceID)
WHERE     (p2.ID IS NULL)
为了加快这一速度,请将参与联接的两个表(设备和设备组)索引到它们联接的列上

这将把散列匹配更改为内部循环,这将比您在计划中看到的更快

LEFT OUTER JOIN  PingResults AS p2 
   ON c.ID = p2.DeviceID AND (p1.DateTime < p2.DateTime 
      OR p1.DateTime = p2.DateTime AND p1.DeviceID < p2.DeviceID)
WHERE     (p2.ID IS NULL)
并为每个表创建聚集索引

upd 再看一眼:

SELECT  c.ID,
        c.DeviceName,
        c.GroupID,
        c.DeviceIP,
        p1.[Status],
        p1.LastUpdate,
        DeviceGroups.GroupName
FROM    Devices                  AS c   
        INNER JOIN  DeviceGroups
            ON  c.GroupID = DeviceGroups.ID   
        CROSS APPLY(
            SELECT TOP 1 p1.[Status], p1.[DateTime] AS LastUpdate
            FROM PingResults  AS p1
            WHERE p1.DeviceID  = c.ID
            ORDER BY p1.DateTime DESC
        ) p1
无需两次“读取”PingResults

另一件事是:

ON c.ID = p2.DeviceID 
AND (p1.DateTime < p2.DateTime 
   OR p1.DateTime = p2.DateTime 
     AND p1.DeviceID < p2.DeviceID ---<<<<<<<<<<<<<<<<<<<<
)
c.ID=p2.DeviceID上的

和(p1.DateTime
upd
再看一眼:

SELECT  c.ID,
        c.DeviceName,
        c.GroupID,
        c.DeviceIP,
        p1.[Status],
        p1.LastUpdate,
        DeviceGroups.GroupName
FROM    Devices                  AS c   
        INNER JOIN  DeviceGroups
            ON  c.GroupID = DeviceGroups.ID   
        CROSS APPLY(
            SELECT TOP 1 p1.[Status], p1.[DateTime] AS LastUpdate
            FROM PingResults  AS p1
            WHERE p1.DeviceID  = c.ID
            ORDER BY p1.DateTime DESC
        ) p1
无需两次“读取”PingResults

另一件事是:

ON c.ID = p2.DeviceID 
AND (p1.DateTime < p2.DateTime 
   OR p1.DateTime = p2.DateTime 
     AND p1.DeviceID < p2.DeviceID ---<<<<<<<<<<<<<<<<<<<<
)
c.ID=p2.DeviceID上的

和(p1.DateTime
SELECT c.ID, c.DeviceName, c.GroupID, c.DeviceIP, p1.Status, 
       p1.DateTime AS LastUpdate, dg.GroupName
FROM Devices c INNER JOIN
     PingResults p1
     ON c.ID = p1.DeviceID INNER JOIN
     DeviceGroups dg
     ON c.GroupID = dg.ID LEFT OUTER JOIN
     PingResults p2
     ON c.ID = p2.DeviceID AND
        (p1.DateTime < p2.DateTime OR
         p1.DateTime = p2.DateTime AND p1.DeviceID < p2.DeviceID
        )
WHERE p2.ID IS NULL;
对于此查询:

SELECT c.ID, c.DeviceName, c.GroupID, c.DeviceIP, p1.Status, 
       p1.DateTime AS LastUpdate, dg.GroupName
FROM Devices c INNER JOIN
     PingResults p1
     ON c.ID = p1.DeviceID INNER JOIN
     DeviceGroups dg
     ON c.GroupID = dg.ID LEFT OUTER JOIN
     PingResults p2
     ON c.ID = p2.DeviceID AND
        (p1.DateTime < p2.DateTime OR
         p1.DateTime = p2.DateTime AND p1.DeviceID < p2.DeviceID
        )
WHERE p2.ID IS NULL;

在我看来,您应该使用行号:

;WITH CTE as
(
  SELECT  
    c.ID, 
    c.DeviceName, 
    c.GroupID, 
    c.DeviceIP, 
    p1.Status, 
    p1.DateTime,
    DeviceGroups.GroupName,
    row_number() over (partition by c.ID order by p1.DateTime desc) rn
  FROM
    Devices AS c
  INNER JOIN
    PingResults AS p1
  ON c.ID = p1.DeviceID
  INNER JOIN
    DeviceGroups
  ON c.GroupID = DeviceGroups.ID
)
SELECT 
  ID, 
  DeviceName, 
  GroupID, 
  DeviceIP, 
  Status, 
  DateTime LastUpdate,
  DeviceGroups.GroupName
FROM CTE
WHERE rn = 1

在我看来,您应该使用行号:

;WITH CTE as
(
  SELECT  
    c.ID, 
    c.DeviceName, 
    c.GroupID, 
    c.DeviceIP, 
    p1.Status, 
    p1.DateTime,
    DeviceGroups.GroupName,
    row_number() over (partition by c.ID order by p1.DateTime desc) rn
  FROM
    Devices AS c
  INNER JOIN
    PingResults AS p1
  ON c.ID = p1.DeviceID
  INNER JOIN
    DeviceGroups
  ON c.GroupID = DeviceGroups.ID
)
SELECT 
  ID, 
  DeviceName, 
  GroupID, 
  DeviceIP, 
  Status, 
  DateTime LastUpdate,
  DeviceGroups.GroupName
FROM CTE
WHERE rn = 1

嗨,谢谢你的帮助,排号()版本使查询在不到1的时间内运行second@joshkirkpatrick…在这种情况下,我很惊讶你没有接受这个答案。另一个人也给出了一个即时的答案,他首先回答,如果可以的话,我会同时接受这两个答案。@Josh Kirkpatrick…那很好。你有一个很好的理由,
交叉应用
解决方案(显然)。嗨,谢谢你的帮助,排号()版本使查询在不到1的时间内运行second@joshkirkpatrick…在这种情况下,我很惊讶你没有接受这个答案。另一个人也给出了一个即时的答案,他首先回答,如果可以的话,我会同时接受这两个答案。@Josh Kirkpatrick…那很好。你有一个很好的理由,
交叉应用
解决方案(显然).您好,谢谢您的帮助!您所做的第一次更改使其在大约4秒的执行时间内完成,第二次更改使其几乎瞬间完成。不要忘记为每个表创建聚集索引。您好,谢谢您的帮助!您所做的第一次更改使其在大约4秒的执行时间内完成,第二次更改使其几乎瞬间完成。不要忘记创建聚集索引每一张桌子。