如何在一次选择中获得当前和下一个更大的值?

Mar*_*ngs 19 mysql innodb select

我有一个带有列的 InnoDB 表“idtimes”(MySQL 5.0.22-log)

`id` int(11) NOT NULL,
`time` int(20) NOT NULL, [...]
Run Code Online (Sandbox Code Playgroud)

使用复合唯一键

UNIQUE KEY `id_time` (`id`,`time`)
Run Code Online (Sandbox Code Playgroud)

所以每个 id 可以有多个时间戳,每个时间戳可以有多个 id。

我正在尝试设置一个查询,在其中获取所有条目以及每个条目的下一个更大时间(如果存在),因此它应该返回,例如:

+-----+------------+------------+
| id  | time       | nexttime   |
+-----+------------+------------+
| 155 | 1300000000 | 1311111111 |
| 155 | 1311111111 | 1322222222 |
| 155 | 1322222222 |       NULL |
| 156 | 1312345678 | 1318765432 |
| 156 | 1318765432 |       NULL |
+-----+------------+------------+
Run Code Online (Sandbox Code Playgroud)

现在我到目前为止:

SELECT l.id, l.time, r.time FROM 
    idtimes AS l LEFT JOIN idtimes AS r ON l.id = r.id
    WHERE l.time < r.time ORDER BY l.id ASC, l.time ASC;
Run Code Online (Sandbox Code Playgroud)

但当然这会返回 r.time > l.time 的所有行,而不仅仅是第一行......

我想我需要一个像

SELECT outer.id, outer.time, 
    (SELECT time FROM idtimes WHERE id = outer.id AND time > outer.time 
        ORDER BY time ASC LIMIT 1)
    FROM idtimes AS outer ORDER BY outer.id ASC, outer.time ASC;
Run Code Online (Sandbox Code Playgroud)

但我不知道如何引用当前时间(我知道以上不是有效的 SQL)。

如何使用单个查询执行此操作(我不希望使用依赖于一次单行遍历表并记住最后一个值的 @variables)?

Erw*_*out 20

执行 JOIN 是您可能需要的一件事。

SELECT l.id, l.time, r.time FROM 
    idtimes AS l LEFT JOIN idtimes AS r ON l.id = r.id
Run Code Online (Sandbox Code Playgroud)

我想外连接是故意的,你想得到空值。稍后再谈。

WHERE l.time < r.time ORDER BY l.id ASC, l.time ASC;
Run Code Online (Sandbox Code Playgroud)

你只想要r。具有高于 l.time 的最低 (MIN) 时间的行。那就是您需要子查询的地方。

WHERE r.time = (SELECT MIN(time) FROM idtimes r2 where r2.id = l.id AND r2.time > l.time)
Run Code Online (Sandbox Code Playgroud)

现在到空值。如果“没有下一个更高的时间”,那么 SELECT MIN() 将评估为 null(或更糟),并且它本身永远不会等于任何东西,因此您的 WHERE 子句将永远不会得到满足,并且“最高时间”对于每个 ID,永远不会出现在结果集中。

您可以通过消除 JOIN 并将标量子查询移动到 SELECT 列表中来解决它:

SELECT id, time, 
    (SELECT MIN(time) FROM idtimes sub 
        WHERE sub.id = main.id AND sub.time > main.time) as nxttime
  FROM idtimes AS main 
Run Code Online (Sandbox Code Playgroud)


小智 7

我总是避免SELECT块中或在FROM,因为它使代码“更脏”,有时效率较低。

\n

我认为更优雅的方法是:

\n

1. 找出大于时间的次数该行

\n

您可以使用idtimesJOIN表与其自身之间来执行此操作,将连接限制为相同的id和大于时间的时间当前行时间的

\n

您应该使用LEFT JOIN以避免排除没有时间的行当前行之一的行。

\n
SELECT\n    i1.id,\n    i1.time AS time,\n    i2.time AS greater_time\nFROM\n    idtimes AS i1\n    LEFT JOIN idtimes AS i2 ON i1.id = i2.id AND i2.time > i1.time\n
Run Code Online (Sandbox Code Playgroud)\n

正如您所提到的,问题是您有多行next_time大于time

\n
+-----+------------+--------------+\n| id  | time       | greater_time |\n+-----+------------+--------------+\n| 155 | 1300000000 | 1311111111   |\n| 155 | 1300000000 | 1322222222   |\n| 155 | 1311111111 | 1322222222   |\n| 155 | 1322222222 |       NULL   |\n| 156 | 1312345678 | 1318765432   |\n| 156 | 1318765432 |       NULL   |\n+-----+------------+--------------+\n
Run Code Online (Sandbox Code Playgroud)\n

2. 找到greater_time不仅更大而且next_time的

\n

过滤所有这些无用行的最佳方法是找出该id的time(大于)和Greater_time(小于)之间是否存在时间

\n
SELECT\n    i1.id,\n    i1.time AS time,\n    i2.time AS next_time,\n    i3.time AS intrudor_time\nFROM\n    idtimes AS i1\n    LEFT JOIN idtimes AS i2 ON i1.id = i2.id AND i2.time > i1.time\n    LEFT JOIN idtimes AS i3 ON i2.id = i3.id AND i3.time > i1.time AND i3.time < i2.time\n
Run Code Online (Sandbox Code Playgroud)\n

ops,我们仍然有一个错误的 next_time

\n
+-----+------------+--------------+---------------+\n| id  | time       | next_time    | intrudor_time |\n+-----+------------+--------------+---------------+\n| 155 | 1300000000 | 1311111111   |         NULL  |\n| 155 | 1300000000 | 1322222222   |    1311111111 |\n| 155 | 1311111111 | 1322222222   |         NULL  |\n| 155 | 1322222222 |       NULL   |         NULL  |\n| 156 | 1312345678 | 1318765432   |         NULL  |\n| 156 | 1318765432 |       NULL   |         NULL  |\n+-----+------------+--------------+---------------+\n
Run Code Online (Sandbox Code Playgroud)\n

只需过滤发生此事件的行,添加WHERE下面的约束

\n
WHERE\n    i3.time IS NULL\n
Run Code Online (Sandbox Code Playgroud)\n

瞧\xc3\xa0,我们有我们需要的了!

\n
+-----+------------+--------------+---------------+\n| id  | time       | next_time    | intrudor_time |\n+-----+------------+--------------+---------------+\n| 155 | 1300000000 | 1311111111   |         NULL  |\n| 155 | 1311111111 | 1322222222   |         NULL  |\n| 155 | 1322222222 |       NULL   |         NULL  |\n| 156 | 1312345678 | 1318765432   |         NULL  |\n| 156 | 1318765432 |       NULL   |         NULL  |\n+-----+------------+--------------+---------------+\n
Run Code Online (Sandbox Code Playgroud)\n

我希望四年后你仍然需要答案!

\n