1

给定以下数据库表,该表记录不同对象(id)的事件(状态)及其时间戳:

ID | Date       | Time | Status
-------------------------------
 7 | 2016-10-10 | 8:23 | Passed
 7 | 2016-10-10 | 8:29 | Failed
 7 | 2016-10-13 | 5:23 | Passed
 8 | 2016-10-09 | 5:43 | Passed

我想使用普通 SQL(MS SQL)得到一个结果表,如下所示:

ID | Date       | Status
------------------------
 7 | 2016-10-10 | Failed
 7 | 2016-10-13 | Passed
 8 | 2016-10-09 | Passed

其中“状态”是一天中的最新条目,因为已记录了该对象的至少一个事件。

我目前的解决方案是像这样使用“外部应用”和“TOP(1)”:

SELECT DISTINCT rn.id,
                tmp.date,
                tmp.status

FROM run rn OUTER apply
  (SELECT rn2.date, tmp2.status AS 'status'
   FROM run rn2 OUTER apply
     (SELECT top(1) rn3.id, rn3.date, rn3.time, rn3.status
      FROM run rn3
      WHERE rn3.id = rn.id
        AND rn3.date = rn2.date
      ORDER BY rn3.id ASC, rn3.date + rn3.time DESC) tmp2
   WHERE tmp2.status <> '' ) tmp

据我了解,这个外部应用命令的工作方式如下:

For every id
  For every recorded day for this id
     Select the newest status for this day and this id

但是我面临性能问题,因此我认为这个解决方案是不够的。任何建议如何解决这个问题或如何优化 sql?

4

3 回答 3

1

您的代码似乎太复杂了。为什么不这样做呢?

SELECT r.id, r.date, r2.status
FROM run r OUTER APPLY
     (SELECT TOP 1 r2.*
      FROM run r2
      WHERE r2.id = r.id AND r2.date = r.date AND r2.status <> ''
      ORDER BY r2.time DESC
     ) r2;

为了性能,我建议在run(id, date, status, time).

于 2016-12-02T11:43:00.227 回答
0

使用 CTE 可能是最快的:

with cte as 
(
    select ID, Date, Status, row_number() over (partition by ID, Date order by Time desc) rn
    from run
)
select ID, Date, Status 
from cte
where rn = 1
于 2016-12-02T14:47:14.683 回答
-1

不要从日志表中选择,而是编写一个更新 latest_run 表的触发器,例如:

CREATE TRIGGER tr_run_insert ON run FOR INSERT AS 
BEGIN
    UPDATE latest_run SET Status=INSERTED.Status WHERE ID=INSERTED.ID AND Date=INSERTED.Date
    IF @@ROWCOUNT = 0
        INSERT INTO latest_run (ID,Date,Status) SELECT (ID,Date,Status) FROM INSERTED
END

然后从更短的 lastest_run 表中执行读取。这将增加写入的性能损失,因为您需要两次写入而不是一次写入。但是会给你更稳定的读取响应时间。如果您不需要从“运行”表中选择,则可以避免对其编制索引,因此两次写入的性能损失可以通过较少的索引维护得到部分补偿。

于 2016-12-02T11:51:42.157 回答