获取每日最新条目/ SQL优化

时间:2016-12-02 11:37:42

标签: sql sql-server tsql optimization outer-apply

给出以下数据库表,该表记录了不同对象(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?

3 个答案:

答案 0 :(得分: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)上建议一个索引。

答案 1 :(得分: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

答案 2 :(得分:-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表执行读取。 这将增加写入的性能损失,因为您需要两次写入而不是一次写入。但是在阅读时会给你更稳定的响应时间。如果您不需要从“运行”表中进行SELECT,则可以避免对其进行索引,因此,通过减少索引维护,可以部分补偿两次写入的性能损失。