给出以下数据库表,该表记录了不同对象(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?
答案 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,则可以避免对其进行索引,因此,通过减少索引维护,可以部分补偿两次写入的性能损失。