【问题标题】:Getting the latest entry per day / SQL Optimizing每天获取最新条目/SQL 优化
【发布时间】:2016-12-02 22:47:14
【问题描述】:

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

【问题讨论】:

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


    【解决方案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) 上建立索引。

    【讨论】:

    • 太棒了,这个小小的改变——我太盲目了,看不到——使代码运行速度提高了 8 倍。非常感谢!
    【解决方案2】:

    使用 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
    

    【讨论】:

      【解决方案3】:

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

      【讨论】:

      • 感谢您的建议,但这是一个商业应用程序,我不允许更改。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-07-22
      • 2020-07-18
      • 1970-01-01
      • 2015-06-04
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多