У меня 2 стола. Первая таблица - товары, вторая - ревизии.
Присоединение производится по product.id = ревизии.revisionable_id.
Мне нужно получить продукт как 1 запись и соединить как первую ревизию (поле created_at (2017)) и последнюю (created_at (2020)) и получить что-то вроде этого:
Product_name, Revision_2017, Revision_2020
Пожалуйста, помогите.
Продукты
+----+-------+
| id | name |
+----+-------+
| 1 | Tesla |
+----+-------+
Редакции
+----+-----------------+------------+----------+---------+
| id | revisionable_id | created_at | metadata | user_id |
+----+-----------------+------------+----------+---------+
| 1 | 1 | 2017-01-01 | {} | 5 |
| 2 | 1 | 2018-01-01 | {} | 5 |
| 3 | 1 | 2019-01-01 | {} | 5 |
| 4 | 1 | 2020-01-01 | {} | 5 |
+----+-----------------+------------+----------+---------+
Желаемые результаты:
+-------------+---------------+---------------------+-------------------+--------------------+------------------+
| Products.id | Products.name | revFirst.created_at | revFirst.metadata | revLast.created_at | revLast.metadata |
+-------------+---------------+---------------------+-------------------+--------------------+------------------+
| 1 | Tesla | 2017-01-01 | {} | 2020-01-01 | {} |
+-------------+---------------+---------------------+-------------------+--------------------+------------------+
добавлены желаемые результаты






Один вариант использует оконные функции, доступные в MySQL 8.0:
select p.*,
max(case when rn_asc = 1 then created_at end) as first_created_at,
max(case when rn_asc = 1 then metadata end) as first_metadata,
max(case when rn_desc = 1 then created_at end) as last_created_at,
max(case when rn_desc = 1 then metadata end) as last_metadata
from product p
left join (
select r.*,
row_number() over(partition by product_id order by created_at) rn_asc,
row_number() over(partition by product_id order by created_at desc) rn_desc
from revisions r
) r on r.product_id = p.id and 1 in (r.rn_asc, r.rn_desc)
group by p.id
Я не мог точно сказать, какой столбец в revisions относится к продукту id (id? revisionable_id?), поэтому вместо этого я назвал этот столбец product_id.
В самых последних версиях MySQL (8.0.13) два боковых соединения могут быть хорошим подходом:
select p.*, r1.*, r2.*
from product p
left join lateral (
select created_at as first_created_at, metadata as first_metadata
from revisions r
where r.product_id = p.id
order by created_at limit 1
) r1 on true
left join lateral (
select created_at as last_created_at, metadata as last_metadata
from revisions r
where r.product_id = p.id
order by created_at desc limit 1
) r2 on true
В более ранних версиях я бы использовал пару подзапросов:
select p.*,
(
select r.created_at
from revisions r
where r.product_id = p.id
order by r.created_at limit 1
) as first_created_at,
(
select r.metadata
from revisions r
where r.product_id = p.id
order by r.created_at limit 1
) as first_metadata
(
select r.created_at
from revisions r
where r.product_id = p.id
order by r.created_at desc limit 1
) as last_created_at,
(
select r.metadata
from revisions r
where r.product_id = p.id
order by r.created_at desc limit 1
) as last_metadata
from product p
Для производительности вам нужны следующие индексы:
revisions(product_id, created_at, metadata);
revisions(product_id, created_at desc, metadata);
Присоединение производится по product.id = ревизии.revisionable_id. Спасибо за ответ, стараюсь
Я использую версию 5.7(( Ваш подход не поможет, все равно спасибо
@АнатолийКостяков: ок, тогда смотри мое обновление.
Не знал, что MySQL реализует боковые соединения. +1 только за это.
@TheImpaler: на мой взгляд, это большое улучшение в MySQL!
Предоставьте образцы данных и желаемые результаты в виде табличного текста.