2

假设我有一些服务器,它们不断更新数据库的状态。

我需要运行一些关于这些服务器状态的报告。在桌子上做一些清理工作真的会有所帮助。

我为每条状态消息(开始时间和结束时间)获得 2 个时间戳。我想做的是采取具有相同状态的后续更新,并将其删除。我想更新结束时间以反映正确的间隔。

让我举例说明...

server_status 表:

server   |    status    |     start_time      |       end_time
---------+------------+---------------------+---------------------
 web1    |  running     | 2013-06-04 00:00:00 | 2013-06-04 00:05:00
 web2    |  down        | 2013-06-04 00:01:00 | 2013-06-04 00:03:00
 web1    |  running     | 2013-06-04 00:05:00 | 2013-06-04 01:00:00
 msdb    |  idle        | 2013-06-04 00:02:00 | 2013-06-04 02:00:00
 web1    |  running     | 2013-06-04 01:00:00 | 2013-06-04 02:00:00
 web2    |  down        | 2013-06-04 00:03:00 | 2013-06-04 03:00:00
 web2    |  running     | 2013-06-04 03:00:00 | 2013-06-04 05:00:00
 web1    |  maintenance | 2013-06-04 02:00:00 | 2013-06-04 05:00:00
 web1    |  running     | 2013-06-04 05:00:00 | 2013-06-04 07:00:00

我希望我的桌子最终看起来像这样(按 start_time 排序):

server   |    status    |     start_time      |       end_time
---------+------------+---------------------+---------------------
 web1    |  running     | 2013-06-04 00:00:00 | 2013-06-04 02:00:00
 web2    |  down        | 2013-06-04 00:01:00 | 2013-06-04 03:00:00
 msdb    |  idle        | 2013-06-04 00:02:00 | 2013-06-04 02:00:00
 web1    |  maintenance | 2013-06-04 02:00:00 | 2013-06-04 05:00:00
 web2    |  running     | 2013-06-04 03:00:00 | 2013-06-04 05:00:00
 web1    |  running     | 2013-06-04 05:00:00 | 2013-06-05 07:00:00

这让我确切地知道我的盒子何时改变状态,然后当我在这些表上运行一些报告时,我可以在 SQL 中查询 start_time 和 end_time。

任何线索如何做到这一点?我假设我需要一个更新语句,然后是一个删除。如果需要,我可以添加行号,尽管它们目前不存在。这可能是必要的,所以我们可以排序然后检查 X 行的服务器和状态是否与 X + 1 行相同。

运行 postgres 8.1(我知道,我知道。很快就会到 8.4)。

4

1 回答 1

1

这是一个棘手的问题,因为您有多个相同的值组(server, status),所以简单GROUP BYDISTINCT (ON)不会削减它。

但是,窗口函数lag()(从 PostgreSQL 8.4开始可用)非常适合您的问题,使解决方案非常简单。

要在 a 中获取您要查找的值SELECT

SELECT server, status, start_time, end_time
FROM  (
   SELECT *, status IS DISTINCT FROM 
             lag(status) OVER (PARTITION BY server ORDER BY start_time) AS step
   FROM   server_status
   ) sub
WHERE  step
ORDER  BY start_time;

旧版本:这也应该适用于8.1。仅使用 8.4 进行测试。
相关子查询可能比窗口函数慢很多。

SELECT server, status, start_time, end_time
FROM   server_status s
WHERE ( 
   SELECT s1.status
   FROM   server_status s1
   WHERE  s1.server = s.server
   AND    s1.start_time < s.start_time
   ORDER  BY s1.start_time DESC
   LIMIT  1
   ) IS DISTINCT FROM s.status
ORDER  BY start_time;

-> SQLfiddle 根据需要用于两个
ToDELETE行:

DELETE FROM server_status s
USING (
   SELECT server, status, start_time
         ,status IS DISTINCT FROM
          lag(status) OVER (PARTITION BY server ORDER BY start_time) AS step
   FROM   server_status
   ) d
WHERE  s.server = d.server
AND    s.status = d.status
AND    s.start_time = d.start_time
AND    NOT d.step;

对于8.1. 仅使用 8.4 进行测试。

DELETE FROM server_status s
WHERE (   
   SELECT s1.status = s.status
   FROM   server_status s1
   WHERE  s1.server = s.server
   AND    s1.start_time < s.start_time
   ORDER  BY s1.start_time DESC
   LIMIT  1
   );

对于任何这些查询,任何索引(server, start_time)将大大提高更大表的性能。

仅出于安全原因,您需要升级。(但为什么要停在 8.4?直接进入当前版本。

于 2013-06-05T02:33:11.510 回答