SQL 查询与最近(及时)的前一个数据库条目的时间差

SQL query to get time diff from closest (in time) previous db entry

我有 2 个数据库 table 来处理问题跟踪系统(如 Zendesk 或 Jira):一个 tickets(父 table)和一个 messages (子table,存储一张票的每条消息)。像这样:

tickets table:

id
created_at

messages table:

id
created_at
ticket_id
text
source (could be either 'company' or 'client')

我想制作一个 SQL 查询,该查询将生成票证消息,其中每一行都涉及一条 'company' 消息以及自上次 'client' 消息以来的时间延迟。 “时间延迟”是指:

created_at 来自 'company' 消息减去 created_at 来自之前的 'client' 消息

请注意,可以连续发送多个 'company' 或 'client' 条消息。

因此,如果工单(ID 为 4528)有这些消息(按时间顺序):

- 'client' message (id 1)
- 'company' message (id 2)
- 'client' message (id 3)
- 'company' message (id 4)
- 'company' message (id 5)

我希望 SQL 查询产生如下内容:

ticket id message id Time diff since previous 'client' message
4528 2 time diff between 2 and 1
4528 4 time diff between 4 and 3
4528 5 time diff between 5 and 3

感谢任何帮助。

这是一个例子。 Fiddle

只有 join company 行具有适用的 client 行具有先前的 created_at 值。然后按相反的时间顺序为每个 company 的这些 client 行分配一个 row_number。选择 n = 1 以获得每个 company 行的最近前 client 行。

我们可以在 PARTITION BY 子句中添加 ticket_id 来处理多张工单。

SQL:

WITH cte1 (id, ticket_id, text, ts, client_id, ts2, n, dt) AS (
        SELECT m1.id, m1.ticket_id, m1.text, m1.created_at
             , m2.id AS last_client_id, m2.created_at
             , ROW_NUMBER() OVER (PARTITION BY m1.id, m1.ticket_id ORDER BY m2.id DESC) AS n
             , m1.created_at - m2.created_at AS dt
          FROM      messages AS m1
          LEFT JOIN messages AS m2
            ON m1.ticket_id = m2.ticket_id
           AND m2.source = 'client'
           AND m2.created_at < m1.created_at
         WHERE m1.source = 'company'
     )
SELECT * FROM cte1 WHERE n = 1
;

我没有注意到原始问题上的 MySQL 标签,只注意到 postgresql 标签。 SQL两者的结构基本相同,只是在时差计算上稍作调整。

这也是 MariaDB/MySQL 的最新版本 (Fiddle):

WITH cte1 (id, ticket_id, text, ts, client_id, ts2, n, dt) AS (
        SELECT m1.id, m1.ticket_id, m1.text, m1.created_at
             , m2.id AS last_client_id, m2.created_at
             , ROW_NUMBER() OVER (PARTITION BY m1.id, m1.ticket_id ORDER BY m2.id DESC) AS n
             , ABS(timestampdiff(minute, m1.created_at, m2.created_at)) AS dt
          FROM      messages AS m1
          LEFT JOIN messages AS m2
            ON m1.ticket_id = m2.ticket_id
           AND m2.source = 'client'
           AND m2.id < m1.id
         WHERE m1.source = 'company'
     )
SELECT * FROM cte1 WHERE n = 1
;

旧版本的 MySQL 或 Maria 将不支持 WITH clauseWindow Function。还是可以的。

设置:

CREATE TABLE messages (
  id          int
, created_at  timestamp
, ticket_id   int
, text        varchar(30)
, source      varchar(20)
);

INSERT INTO messages (id, ticket_id, source, text, created_at) VALUES
  (1, 1234, 'client' , 'message 01', current_timestamp + INTERVAL '+1 HOUR')
, (2, 1234, 'company', 'message 02', current_timestamp + INTERVAL '+2 HOUR')
, (3, 1234, 'client' , 'message 03', current_timestamp + INTERVAL '+3 HOUR')
, (4, 1234, 'company', 'message 04', current_timestamp + INTERVAL '+4 HOUR')
, (5, 1234, 'company', 'message 05', current_timestamp + INTERVAL '+5 HOUR')
;

MySQL/MariaDB 的 INTERVAL 语法略有不同:

INSERT INTO messages (id, ticket_id, source, text, created_at) VALUES
  (1, 1234, 'client' , 'message 01', current_timestamp + INTERVAL 1 HOUR)
, (2, 1234, 'company', 'message 02', current_timestamp + INTERVAL 2 HOUR)
, (3, 1234, 'client' , 'message 03', current_timestamp + INTERVAL 3 HOUR)
, (4, 1234, 'company', 'message 04', current_timestamp + INTERVAL 4 HOUR)
, (5, 1234, 'company', 'message 05', current_timestamp + INTERVAL 5 HOUR)
;