Sql самостоятельное соединение с несоответствующими строками - PullRequest
0 голосов
/ 01 февраля 2020

У меня есть такая таблица

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

Мне нужно получить эту таблицу:

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

Вот таблица, которую я получаю:

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

Как я могу получить таблицу с совпадающим открытием / закрытием в том же столбце вместе со строками, у которых нет соответствующего закрывающего столбца?

Ответы [ 2 ]

2 голосов
/ 01 февраля 2020

Вам нужно использовать 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)

Демонстрация по SQLFiddle

0 голосов
/ 01 февраля 2020

Другой подход состоит в том, чтобы разделить процесс на 3 этапа:

  • Соберите все строки open и отсортируйте их по created_at в порядке возрастания. Во время этого процесса создайте переменную, скажем @rank, чтобы присвоить ранг каждой строке в порядке возрастания, например 1,2,3 и т. Д.

  • Соберите все строки, которые close и отсортируйте их по created_at в порядке возрастания. Во время этого процесса создайте переменную, скажем @rank2, чтобы присвоить ранг каждой строке в порядке возрастания, например 1,2,3 и т. Д.

  • Теперь выполните left join для обеих таблиц на основе rank.

Фрагмент запроса:

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

...