MySQL 根据某些条件对一列 Select 第二列值进行排序
MySQL Sort One Column Select 2nd Column Values Based on some Criteria
我有一个 table 与下面的非常相似。我需要将 end_time
排序为升序,然后 select start_time
表示在 selected end_time
的正负 2 秒内剩余的行。从本质上讲,我想做的是创建一个端到端的链 end_times 匹配开始时间的端到端。
因此,在第一次匹配后,我将获得匹配的 start_time
行的 main_id
并重复其 end_time
。使用这个你可以看到从下面的 table 可以找到以下链接或聚合。
1->2
3->4
5->6->7->8
10->11
现在我可以很容易地通过读取行以编程方式完成此操作,例如 PHP,但想知道在 SQL 中是否有更有效的方法来执行此操作,其中一些tables 可能很大。
+-------------------+------------+
|main_id|start_time | end_time |
|-------------------+------------+
| 1 1467695616 1467695676 |
| 2 1467695677 1467695683 |
| 3 1467782122 1467782182 |
| 4 1467782181 1467782238 |
| 5 1472329347 1472329374 |
| 6 1472329375 1472329553 |
| 7 1472329554 1472329733 |
| 8 1472329734 1472329764 |
| 9 1472329949 1472330078 |
|10 1472330275 1472330453 |
|11 1472330454 1472330479 |
+----+--------------+------------+
在第一个示例中,我们为每一行分配了一个组。如果前一个 end_time 和当前行 start_time 之间的差异在 +/- 2 秒内,我们将保持相同的组号,否则我们增加组号。这应该适用于 MySQL.
的所有版本
在第二个示例中,我们使用 windows 函数 lag() over (order by ~)
、sum(~) over(order by ~)
和 CTE,因此它在 MySQL 低于 8.
的版本中不起作用
create table t (
main_id int,
start_time int,
end_time int);
insert into t values
(1,1467695616,1467695676),
(2,1467695677,1467695683),
(3,1467782122,1467782182),
(4,1467782181,1467782238),
(5,1472329347,1472329374),
(6,1472329375,1472329553),
(7,1472329554,1472329733),
(8,1472329734,1472329764),
(9,1472329949,1472330078),
(10,1472330275,1472330453),
(11,1472330454,1472330479);
set @latest = 0;
set @group = 0;
select
@group := case when abs(start_time - @latest) <= 2
then @group
else @group + 1
end "group",
main_id,
start_time,
@latest := end_time "end_time"
from t
order by end_time,start_time
> group | main_id | start_time | end_time
> ----- | --------| -----------| -----------|
> 1 | 1 | 1467695616 | 1467695676
> 1 | 2 | 1467695677 | 1467695683
> 2 | 3 | 1467782122 | 1467782182
> 2 | 4 | 1467782181 | 1467782238
> 3 | 5 | 1472329347 | 1472329374
> 3 | 6 | 1472329375 | 1472329553
> 3 | 7 | 1472329554 | 1472329733
> 3 | 8 | 1472329734 | 1472329764
> 4 | 9 | 1472329949 | 1472330078
> 5 | 10 | 1472330275 | 1472330453
> 5 | 11 | 1472330454 | 1472330479
>
with CTE as
(select
case when abs(start_time - lag(end_time) over(order by end_time, start_time)) > 2 then 1 else 0 end diff_previous,
main_id,
start_time,
end_time
from t
order by end_time,start_time
)
select
1 + sum(diff_previous) over(order by end_time) as "group",
main_id,
start_time,
end_time
from CTE
order by end_time, start_time
group | main_id | start_time | end_time
----: | ------: | ---------: | ---------:
1 | 1 | 1467695616 | 1467695676
1 | 2 | 1467695677 | 1467695683
2 | 3 | 1467782122 | 1467782182
2 | 4 | 1467782181 | 1467782238
3 | 5 | 1472329347 | 1472329374
3 | 6 | 1472329375 | 1472329553
3 | 7 | 1472329554 | 1472329733
3 | 8 | 1472329734 | 1472329764
4 | 9 | 1472329949 | 1472330078
5 | 10 | 1472330275 | 1472330453
5 | 11 | 1472330454 | 1472330479
db<>fiddle here
我有一个 table 与下面的非常相似。我需要将 end_time
排序为升序,然后 select start_time
表示在 selected end_time
的正负 2 秒内剩余的行。从本质上讲,我想做的是创建一个端到端的链 end_times 匹配开始时间的端到端。
因此,在第一次匹配后,我将获得匹配的 start_time
行的 main_id
并重复其 end_time
。使用这个你可以看到从下面的 table 可以找到以下链接或聚合。
1->2
3->4
5->6->7->8
10->11
现在我可以很容易地通过读取行以编程方式完成此操作,例如 PHP,但想知道在 SQL 中是否有更有效的方法来执行此操作,其中一些tables 可能很大。
+-------------------+------------+
|main_id|start_time | end_time |
|-------------------+------------+
| 1 1467695616 1467695676 |
| 2 1467695677 1467695683 |
| 3 1467782122 1467782182 |
| 4 1467782181 1467782238 |
| 5 1472329347 1472329374 |
| 6 1472329375 1472329553 |
| 7 1472329554 1472329733 |
| 8 1472329734 1472329764 |
| 9 1472329949 1472330078 |
|10 1472330275 1472330453 |
|11 1472330454 1472330479 |
+----+--------------+------------+
在第一个示例中,我们为每一行分配了一个组。如果前一个 end_time 和当前行 start_time 之间的差异在 +/- 2 秒内,我们将保持相同的组号,否则我们增加组号。这应该适用于 MySQL.
的所有版本
在第二个示例中,我们使用 windows 函数 lag() over (order by ~)
、sum(~) over(order by ~)
和 CTE,因此它在 MySQL 低于 8.
create table t ( main_id int, start_time int, end_time int); insert into t values (1,1467695616,1467695676), (2,1467695677,1467695683), (3,1467782122,1467782182), (4,1467782181,1467782238), (5,1472329347,1472329374), (6,1472329375,1472329553), (7,1472329554,1472329733), (8,1472329734,1472329764), (9,1472329949,1472330078), (10,1472330275,1472330453), (11,1472330454,1472330479);
set @latest = 0; set @group = 0; select @group := case when abs(start_time - @latest) <= 2 then @group else @group + 1 end "group", main_id, start_time, @latest := end_time "end_time" from t order by end_time,start_time
> group | main_id | start_time | end_time > ----- | --------| -----------| -----------| > 1 | 1 | 1467695616 | 1467695676 > 1 | 2 | 1467695677 | 1467695683 > 2 | 3 | 1467782122 | 1467782182 > 2 | 4 | 1467782181 | 1467782238 > 3 | 5 | 1472329347 | 1472329374 > 3 | 6 | 1472329375 | 1472329553 > 3 | 7 | 1472329554 | 1472329733 > 3 | 8 | 1472329734 | 1472329764 > 4 | 9 | 1472329949 | 1472330078 > 5 | 10 | 1472330275 | 1472330453 > 5 | 11 | 1472330454 | 1472330479 >
with CTE as (select case when abs(start_time - lag(end_time) over(order by end_time, start_time)) > 2 then 1 else 0 end diff_previous, main_id, start_time, end_time from t order by end_time,start_time ) select 1 + sum(diff_previous) over(order by end_time) as "group", main_id, start_time, end_time from CTE order by end_time, start_time
group | main_id | start_time | end_time ----: | ------: | ---------: | ---------: 1 | 1 | 1467695616 | 1467695676 1 | 2 | 1467695677 | 1467695683 2 | 3 | 1467782122 | 1467782182 2 | 4 | 1467782181 | 1467782238 3 | 5 | 1472329347 | 1472329374 3 | 6 | 1472329375 | 1472329553 3 | 7 | 1472329554 | 1472329733 3 | 8 | 1472329734 | 1472329764 4 | 9 | 1472329949 | 1472330078 5 | 10 | 1472330275 | 1472330453 5 | 11 | 1472330454 | 1472330479
db<>fiddle here