跳转至

3126. 服务器利用时间 🔒

难度中等

题目描述

表:Servers

+----------------+----------+
| Column Name    | Type     |
+----------------+----------+
| server_id      | int      |
| status_time    | datetime |
| session_status | enum     |
+----------------+----------+
(server_id, status_time, session_status) 是这张表的主键(有不同值的列的组合)。
session_status 是 ('start', 'stop') 的 ENUM (category)。
这张表的每一行包含 server_id, status_time 和 session_status。

编写一个解决方案来查找服务器 运行 的 总时间。输出应向下舍入为最接近的 整天数。

以 任意 顺序返回结果表。

结果格式如下所示。

 

示例:

输入:

Servers 表:

+-----------+---------------------+----------------+
| server_id | status_time         | session_status |
+-----------+---------------------+----------------+
| 3         | 2023-11-04 16:29:47 | start          |
| 3         | 2023-11-05 01:49:47 | stop           |
| 3         | 2023-11-25 01:37:08 | start          |
| 3         | 2023-11-25 03:50:08 | stop           |
| 1         | 2023-11-13 03:05:31 | start          |
| 1         | 2023-11-13 11:10:31 | stop           |
| 4         | 2023-11-29 15:11:17 | start          |
| 4         | 2023-11-29 15:42:17 | stop           |
| 4         | 2023-11-20 00:31:44 | start          |
| 4         | 2023-11-20 07:03:44 | stop           |
| 1         | 2023-11-20 00:27:11 | start          |
| 1         | 2023-11-20 01:41:11 | stop           |
| 3         | 2023-11-04 23:16:48 | start          |
| 3         | 2023-11-05 01:15:48 | stop           |
| 4         | 2023-11-30 15:09:18 | start          |
| 4         | 2023-11-30 20:48:18 | stop           |
| 4         | 2023-11-25 21:09:06 | start          |
| 4         | 2023-11-26 04:58:06 | stop           |
| 5         | 2023-11-16 19:42:22 | start          |
| 5         | 2023-11-16 21:08:22 | stop           |
+-----------+---------------------+----------------+

输出:

+-------------------+
| total_uptime_days |
+-------------------+
| 1                 |
+-------------------+

解释:

  • 对于 server ID 3:
    • 从 2023-11-04 16:29:47 到 2023-11-05 01:49:47: ~9.3 小时
    • 从 2023-11-25 01:37:08 到 2023-11-25 03:50:08: ~2.2 小时
    • 从 2023-11-04 23:16:48 到 2023-11-05 01:15:48: ~1.98 小时
    server 3 共计:~13.48 小时
  • 对于 server ID 1:
    • 从 2023-11-13 03:05:31 到 2023-11-13 11:10:31: ~8 小时
    • 从 2023-11-20 00:27:11 到 2023-11-20 01:41:11: ~1.23 小时
    server 1 共计:~9.23 小时
  • 对于 server ID 4:
    • 从 2023-11-29 15:11:17 到 2023-11-29 15:42:17: ~0.52 小时
    • 从 2023-11-20 00:31:44 到 2023-11-20 07:03:44: ~6.53 小时
    • 从 2023-11-30 15:09:18 到 2023-11-30 20:48:18: ~5.65 小时
    • 从 2023-11-25 21:09:06 到 2023-11-26 04:58:06: ~7.82 小时
    server 4 共计:~20.52 小时
  • 对于 server ID 5:
    • 从 2023-11-16 19:42:22 到 2023-11-16 21:08:22: ~1.43 小时
    server 5 共计:~1.43 小时
所有服务器的累积运行时间总计约为 44.46 小时,相当于一整天加上一些额外的小时。然而,由于我们只考虑整天,因此最终输出四舍五入为 1 整天。

解法

方法一:运行深度配对

思考

样例中同一台服务器会连续写入两次 \(\texttt{start}\),再写入两次 \(\texttt{stop}\)。把每条 \(\texttt{start}\) 配到下一行,会得到 \(\texttt{start}\) 到下一次 \(\texttt{start}\) 的间隔,外层 \(\texttt{stop}\) 则不会进入任何一段。

题解给出的区间是后出现的 \(\texttt{start}\) 先被 \(\texttt{stop}\) 关闭: \(23{:}16{:}48\) 配到 \(01{:}15{:}48\), \(16{:}29{:}47\) 配到 \(01{:}49{:}47\)。这与栈的出入顺序一致。

按时间维护深度, \(\texttt{start}\) 为 \(+1\)、 \(\texttt{stop}\) 为 \(-1\)。每条 \(\texttt{start}\) 匹配其后第一条深度恰好比它小 \(1\) 的 \(\texttt{stop}\)。各段秒数求和后整除 \(86400\)。

同一台服务器上的 \(\texttt{start}\) 与 \(\texttt{stop}\) 可能嵌套。按时间计算深度后,每条 \(\texttt{start}\) 与其后第一条深度少 \(1\) 的 \(\texttt{stop}\) 组成一段运行时间。把全部段的秒数相加,再向下取整到整天。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
# Write your MySQL query statement below
WITH
    T AS (
        SELECT
            server_id,
            status_time,
            session_status,
            SUM(IF(session_status = 'start', 1, -1)) OVER (
                PARTITION BY server_id
                ORDER BY status_time, session_status
                ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
            ) AS depth
        FROM Servers
    )
SELECT FLOOR(SUM(TIMESTAMPDIFF(SECOND, start_time, stop_time)) / 86400) AS total_uptime_days
FROM (
    SELECT
        s.status_time AS start_time,
        MIN(t.status_time) AS stop_time
    FROM T AS s
    JOIN T AS t
        ON s.server_id = t.server_id
        AND t.session_status = 'stop'
        AND t.depth = s.depth - 1
        AND t.status_time > s.status_time
    WHERE s.session_status = 'start'
    GROUP BY s.server_id, s.status_time
) AS p;

评论