Mysql 基于第一个表中的时差的SQL联接第二个表
我有两个表,一个表记录事件的开始时间,另一个表记录事件的结束时间,我想加入这两个表 然而,挑战在于并非每个开始事件都有对应的结束事件,如果是这样,我希望输出中有一个NULL。这可能吗 编辑:每个ID代表一个人,每天可以有多个事件开始和停止。对于每个事件,我只希望将单个“正确”的结束时间与开始时间(如果存在)合并。当前没有单独的事件级别标识符 例如:Mysql 基于第一个表中的时差的SQL联接第二个表,mysql,sql,postgresql,join,Mysql,Sql,Postgresql,Join,我有两个表,一个表记录事件的开始时间,另一个表记录事件的结束时间,我想加入这两个表 然而,挑战在于并非每个开始事件都有对应的结束事件,如果是这样,我希望输出中有一个NULL。这可能吗 编辑:每个ID代表一个人,每天可以有多个事件开始和停止。对于每个事件,我只希望将单个“正确”的结束时间与开始时间(如果存在)合并。当前没有单独的事件级别标识符 例如: Select st.id, st.ts_start,et.ts_end from startTime st left join endTime et
Select st.id, st.ts_start,et.ts_end from startTime st left join endTime et on st.id=et.id;
表1:开始时间
id ts_start
123 01:00
123 03:00
123 05:00
123 09:00
表2:结束时间
id ts_end
123 02:00
123 07:00
输出:
id ts_start ts_end
123 01:00 02:00
123 03:00 NULL
123 05:00 07:00
123 09:00 NULL
我使用的是MySQL 5.7,所以还不能访问窗口/分析功能,尽管如果这是最好的解决方案的一部分,那么我很乐意迁移(但必须是开源的,所以需要新版本的MySQL或Postgres)
谢谢在这种情况下,您可以尝试外部联接。例如:
Select st.id, st.ts_start,et.ts_end from startTime st left join endTime et on st.id=et.id;
这样,无论结束时间是否可用,您都将获得开始时间的所有记录
注意:只需在查询中输入正确的表名。在这种情况下,您可以尝试外部联接。例如:
Select st.id, st.ts_start,et.ts_end from startTime st left join endTime et on st.id=et.id;
这样,无论结束时间是否可用,您都将获得开始时间的所有记录
注意:只需在查询中输入正确的表名。您必须查看下一个开始是否晚于下一个结束。一种方法使用两个相关子查询:
select id, ts_start,
(case when next_start > next_end then next_end
end) as ts_end
from (select s.*,
(select max(s2.ts_start)
from starts s2
where s2.id = s.id and s2.ts_start > s.ts_start
) as next_start,
(select min(e2.ts_end)
from ends e2
where e2.id = s.id and e2.ts_end > s.ts_end
) as next_end
from starts s
) s;
使用窗口函数,我会将所有时间组合在一起,并查看下一个值:
with t as (
select id, ts_start as time, 'start' as which
from starts
union all
select id, ts_end, 'end'
from ends
)
select t.id, t.time as ts_start,
(case when next_which = 'end' then next_time
end) as ts_end
from (select t.*,
lead(time) over (partition by id order by time) as next_time,
lead(which) over (partition by id order by time) as next_which
from t
) t
where which = 'start';
你必须看看下一个起点是否比下一个终点晚。一种方法使用两个相关子查询:
select id, ts_start,
(case when next_start > next_end then next_end
end) as ts_end
from (select s.*,
(select max(s2.ts_start)
from starts s2
where s2.id = s.id and s2.ts_start > s.ts_start
) as next_start,
(select min(e2.ts_end)
from ends e2
where e2.id = s.id and e2.ts_end > s.ts_end
) as next_end
from starts s
) s;
使用窗口函数,我会将所有时间组合在一起,并查看下一个值:
with t as (
select id, ts_start as time, 'start' as which
from starts
union all
select id, ts_end, 'end'
from ends
)
select t.id, t.time as ts_start,
(case when next_which = 'end' then next_time
end) as ts_end
from (select t.*,
lead(time) over (partition by id order by time) as next_time,
lead(which) over (partition by id order by time) as next_which
from t
) t
where which = 'start';
首先,您需要为ts_end
获取一个“候选”,它是大于开始时间的最小结束时间。这两种方法都可以
select s.id, s.ts_start, (
select min(e.ts_end)
from end_time e
where e.id = s.id
and e.ts_end > s.ts_start
) as ts_end
from start_time s;
或与
select s.id, s.ts_start, min(e.ts_end) as ts_end
from start_time s
left join end_time e
on e.id = s.id
and e.ts_end > s.ts_start
group by s.id, s.ts_start
两个查询都将返回
| id | ts_start | ts_end |
|-----|----------|----------|
| 123 | 01:00 | 02:00 |
| 123 | 03:00 | 07:00 |
| 123 | 05:00 | 07:00 |
| 123 | 09:00 | null |
现在,当ts\u start
和ts\u end
之间有任何开始时间(表start\u time
)时,我们需要ts\u end
为null
(第二行)。对于第二行ts\u end
必须为NULL
,因为有一个开始时间5:00
,它介于3:00
和7:00
之间
对于第一个查询,我们可以使用带有不存在
条件的HAVING
子句:
select s.id, s.ts_start, (
select min(e.ts_end)
from end_time e
where e.id = s.id
and e.ts_end > s.ts_start
having not exists (
select *
from start_time s2
where s2.id = s.id
and s2.ts_start > s.ts_start
and s2.ts_start < min(e.ts_end)
)
) as ts_end
from start_time s
select s.id, s.ts_start,
case when exists (
select *
from start_time s2
where s2.id = s.id
and s2.ts_start > s.ts_start
and s2.ts_start < min(e.ts_end)
)
then null
else min(e.ts_end)
end as ts_end
from start_time s
left join end_time e
on e.id = s.id
and e.ts_end > s.ts_start
group by s.id, s.ts_start
在MySQL 8.x中,您可以使用LEAD
窗口函数:
select s.id, s.ts_start,
case when min(e.ts_end) > lead(s.ts_start) over (partition by s.id order by s.ts_start)
then null
else min(e.ts_end)
end as ts_end
from start_time s
left join end_time e
on e.id = s.id
and e.ts_end > s.ts_start
group by s.id, s.ts_start
所有三个查询都将返回:
| id | ts_start | ts_end |
|-----|----------|----------|
| 123 | 01:00 | 02:00 |
| 123 | 03:00 | null |
| 123 | 05:00 | 07:00 |
| 123 | 09:00 | null |
演示:首先,您需要为ts\u end
获取一个“候选”,它是大于开始时间的最小结束时间。这两种方法都可以
select s.id, s.ts_start, (
select min(e.ts_end)
from end_time e
where e.id = s.id
and e.ts_end > s.ts_start
) as ts_end
from start_time s;
或与
select s.id, s.ts_start, min(e.ts_end) as ts_end
from start_time s
left join end_time e
on e.id = s.id
and e.ts_end > s.ts_start
group by s.id, s.ts_start
两个查询都将返回
| id | ts_start | ts_end |
|-----|----------|----------|
| 123 | 01:00 | 02:00 |
| 123 | 03:00 | 07:00 |
| 123 | 05:00 | 07:00 |
| 123 | 09:00 | null |
现在,当ts\u start
和ts\u end
之间有任何开始时间(表start\u time
)时,我们需要ts\u end
为null
(第二行)。对于第二行ts\u end
必须为NULL
,因为有一个开始时间5:00
,它介于3:00
和7:00
之间
对于第一个查询,我们可以使用带有不存在
条件的HAVING
子句:
select s.id, s.ts_start, (
select min(e.ts_end)
from end_time e
where e.id = s.id
and e.ts_end > s.ts_start
having not exists (
select *
from start_time s2
where s2.id = s.id
and s2.ts_start > s.ts_start
and s2.ts_start < min(e.ts_end)
)
) as ts_end
from start_time s
select s.id, s.ts_start,
case when exists (
select *
from start_time s2
where s2.id = s.id
and s2.ts_start > s.ts_start
and s2.ts_start < min(e.ts_end)
)
then null
else min(e.ts_end)
end as ts_end
from start_time s
left join end_time e
on e.id = s.id
and e.ts_end > s.ts_start
group by s.id, s.ts_start
在MySQL 8.x中,您可以使用LEAD
窗口函数:
select s.id, s.ts_start,
case when min(e.ts_end) > lead(s.ts_start) over (partition by s.id order by s.ts_start)
then null
else min(e.ts_end)
end as ts_end
from start_time s
left join end_time e
on e.id = s.id
and e.ts_end > s.ts_start
group by s.id, s.ts_start
所有三个查询都将返回:
| id | ts_start | ts_end |
|-----|----------|----------|
| 123 | 01:00 | 02:00 |
| 123 | 03:00 | null |
| 123 | 05:00 | 07:00 |
| 123 | 09:00 | null |
演示:1)将当前和下一个ts\u start
合并到一个查询中:
select
*,
(select min(ts_start) from table1 as tt1 where t1.id = tt1.id and t1.ts_start < tt1.ts_star) as next_start
from table1 as t1;
应该能够在大多数基于SQL的DBMS上工作
使用简化的数据类型和对象名称演示:
with
t1(x,y) as (values(123,1),(123,3),(123,5),(123,9)),
t2(x,z) as (values(123,2),(123,7))
select *
from (
select
*,
(select min(y) from t1 as tt1 where t1.x = tt1.x and t1.y < tt1.y) as next
from t1) as t1 left join
t2 on (t1.x = t2.x and t2.z between t1.y and t1.next);
与
t1(x,y)为(值(123,1)、(123,3)、(123,5)、(123,9)),
t2(x,z)as(值(123,2)、(123,7))
挑选*
从(
挑选
*,
(从t1中选择min(y)作为tt1,其中t1.x=tt1.x和t1.y
1)将当前和下一个ts\u start
合并到一个查询中:
select
*,
(select min(ts_start) from table1 as tt1 where t1.id = tt1.id and t1.ts_start < tt1.ts_star) as next_start
from table1 as t1;
应该能够在大多数基于SQL的DBMS上工作
使用简化的数据类型和对象名称演示:
with
t1(x,y) as (values(123,1),(123,3),(123,5),(123,9)),
t2(x,z) as (values(123,2),(123,7))
select *
from (
select
*,
(select min(y) from t1 as tt1 where t1.x = tt1.x and t1.y < tt1.y) as next
from t1) as t1 left join
t2 on (t1.x = t2.x and t2.z between t1.y and t1.next);
与
t1(x,y)为(值(123,1)、(123,3)、(123,5)、(123,9)),
t2(x,z)as(值(123,2)、(123,7))
挑选*
从(
挑选
*,
(从t1中选择min(y)作为tt1,其中t1.x=tt1.x和t1.y
我只想在每个开始时加入一个正确的结束时间(如果存在)。左连接/外部连接将为每个开始时间提供多个结束时间,这不是我想要的。因此,应该有一些标志来表示哪个开始时间属于哪个结束时间…您如何识别它?@Spcoggthesecond。这些查询中应该有限制1
。相反,我只是添加了min()
/max()
,以获得相同的效果。我只想用一个正确的结束时间(如果存在)来连接每个开始。左连接/外部连接将为每个开始时间提供多个结束时间,这不是我想要的。因此,应该有一些标志来表示哪个开始时间属于哪个结束时间…您如何识别它?@Spcoggthesecond。这些查询中应该有限制1
。相反,我只是添加了min()。子查询返回多行?您好,我正在尝试您对相关子查询的回答,但是您的第一个子查询(选择s.*)在mysql中给了我一个错误:错误代码:1242。子查询返回的行数超过1行?