Sql 自连接以及不匹配的行
Sql self join along with non matching rows
我有一个table像这样
id | user | name | action | created_at
--------------------------------------------------
1 | 42 | eve | open | 2020-01-06 06:17:42
2 | 42 | eve | close | 2020-01-06 06:27:42
3 | 42 | eve | open | 2020-01-06 06:37:42
4 | 42 | eve | close | 2020-01-06 06:47:42
5 | 42 | eve | open | 2020-01-06 06:57:42
我需要得到这个 table:
user | name | open | open_created_at | close | close _created_at
-----------------------------------------------------------------------
42 | eve | open | 2020-01-06 06:17:42 | close | 2020-01-06 06:27:42
42 | eve | open | 2020-01-06 06:37:42 | close | 2020-01-06 06:47:42
42 | eve | open | 2020-01-06 06:57:42 | null | null
这是 table 我得到的:
SELECT t1.user, t1.name, t1.action, t1.created_at, t2.action, t2.created_at
FROM tabel t1, table t2
WHERE t1.user = t2.user AND t1.action = 'open' AND t2.action = 'close' AND t1.created_at < t2.created_at
GROUP BY t1.id
user | name | open | open_created_at | close | close _created_at
-----------------------------------------------------------------------
42 | eve | open | 2020-01-06 06:17:42 | close | 2020-01-06 06:27:42
42 | eve | open | 2020-01-06 06:37:42 | close | 2020-01-06 06:47:42
如何在同一列中获得匹配 open/close 的 table 以及没有匹配关闭列的行?
您需要使用 LEFT JOIN
而不是 INNER JOIN
,并且您需要将 WHERE
子句移动到 JOIN
条件中。您还应该在 t2
值周围添加聚合函数以确保结果一致:
SELECT t1.user, t1.name, t1.action AS open, t1.created_at AS open_created_at,
MIN(t2.action) AS close, MIN(t2.created_at) AS close_created_at
FROM log t1
LEFT JOIN log t2 ON t1.user = t2.user AND t2.action = 'close' AND t1.created_at < t2.created_at
WHERE t1.action = 'open'
GROUP BY t1.id, t1.name, t1.action, t1.created_at
输出:
user name open open_created_at close close_created_at
42 eve open 2020-01-06 06:17:42 close 2020-01-06 06:27:42
42 eve open 2020-01-06 06:37:42 close 2020-01-06 06:47:42
42 eve open 2020-01-06 06:57:42 (null) (null)
另一种方法是将过程分为 3 个步骤:
收集所有open
的行,按created_at
升序排列。在此过程中,创建一个变量,比如 @rank
以升序为每一行分配排名,如 1,2,3 等等。
收集所有close
的行,按created_at
升序排列。在此过程中,创建一个变量,比如 @rank2
以升序为每一行分配排名,如 1,2,3 等等。
现在,根据rank
对两个表执行left join
。
查询片段:
select d1.user,d1.name,d1.action as open,d1.open_created_at,d2.action as close,d2.close_created_at
from
(
select @rank := @rank + 1 as rank,user,name,action,created_at as open_created_at
from log,(select @rank := 0) l1
where action = 'open'
order by created_at asc
) d1
left join
(
select @rank2 := @rank2 + 1 as rank,user,name,action,created_at as close_created_at
from log,(select @rank2 := 0) l2
where action = 'close'
order by created_at asc
) d2
on d1.rank = d2.rank
DB Fiddle: https://www.db-fiddle.com/f/hYHyz45rtuiEXGQPbz3m6z/0
我有一个table像这样
id | user | name | action | created_at
--------------------------------------------------
1 | 42 | eve | open | 2020-01-06 06:17:42
2 | 42 | eve | close | 2020-01-06 06:27:42
3 | 42 | eve | open | 2020-01-06 06:37:42
4 | 42 | eve | close | 2020-01-06 06:47:42
5 | 42 | eve | open | 2020-01-06 06:57:42
我需要得到这个 table:
user | name | open | open_created_at | close | close _created_at
-----------------------------------------------------------------------
42 | eve | open | 2020-01-06 06:17:42 | close | 2020-01-06 06:27:42
42 | eve | open | 2020-01-06 06:37:42 | close | 2020-01-06 06:47:42
42 | eve | open | 2020-01-06 06:57:42 | null | null
这是 table 我得到的:
SELECT t1.user, t1.name, t1.action, t1.created_at, t2.action, t2.created_at
FROM tabel t1, table t2
WHERE t1.user = t2.user AND t1.action = 'open' AND t2.action = 'close' AND t1.created_at < t2.created_at
GROUP BY t1.id
user | name | open | open_created_at | close | close _created_at
-----------------------------------------------------------------------
42 | eve | open | 2020-01-06 06:17:42 | close | 2020-01-06 06:27:42
42 | eve | open | 2020-01-06 06:37:42 | close | 2020-01-06 06:47:42
如何在同一列中获得匹配 open/close 的 table 以及没有匹配关闭列的行?
您需要使用 LEFT JOIN
而不是 INNER JOIN
,并且您需要将 WHERE
子句移动到 JOIN
条件中。您还应该在 t2
值周围添加聚合函数以确保结果一致:
SELECT t1.user, t1.name, t1.action AS open, t1.created_at AS open_created_at,
MIN(t2.action) AS close, MIN(t2.created_at) AS close_created_at
FROM log t1
LEFT JOIN log t2 ON t1.user = t2.user AND t2.action = 'close' AND t1.created_at < t2.created_at
WHERE t1.action = 'open'
GROUP BY t1.id, t1.name, t1.action, t1.created_at
输出:
user name open open_created_at close close_created_at
42 eve open 2020-01-06 06:17:42 close 2020-01-06 06:27:42
42 eve open 2020-01-06 06:37:42 close 2020-01-06 06:47:42
42 eve open 2020-01-06 06:57:42 (null) (null)
另一种方法是将过程分为 3 个步骤:
收集所有
open
的行,按created_at
升序排列。在此过程中,创建一个变量,比如@rank
以升序为每一行分配排名,如 1,2,3 等等。收集所有
close
的行,按created_at
升序排列。在此过程中,创建一个变量,比如@rank2
以升序为每一行分配排名,如 1,2,3 等等。现在,根据
rank
对两个表执行left join
。
查询片段:
select d1.user,d1.name,d1.action as open,d1.open_created_at,d2.action as close,d2.close_created_at
from
(
select @rank := @rank + 1 as rank,user,name,action,created_at as open_created_at
from log,(select @rank := 0) l1
where action = 'open'
order by created_at asc
) d1
left join
(
select @rank2 := @rank2 + 1 as rank,user,name,action,created_at as close_created_at
from log,(select @rank2 := 0) l2
where action = 'close'
order by created_at asc
) d2
on d1.rank = d2.rank
DB Fiddle: https://www.db-fiddle.com/f/hYHyz45rtuiEXGQPbz3m6z/0