Как соединить первую и последнюю запись из таблицы с помощью MySQL?

У меня 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         | {}               |
+-------------+---------------+---------------------+-------------------+--------------------+------------------+

Предоставьте образцы данных и желаемые результаты в виде табличного текста.

GMB 24.12.2020 15:08

добавлены желаемые результаты

Анатолий Костяков 24.12.2020 15:29
Освоение архитектуры микросервисов с Laravel: Лучшие практики, преимущества и советы для разработчиков
Освоение архитектуры микросервисов с Laravel: Лучшие практики, преимущества и советы для разработчиков
В последние годы архитектура микросервисов приобрела популярность как способ построения масштабируемых и гибких приложений. Laravel , популярный PHP...
Как построить CRUD-приложение в Laravel
Как построить CRUD-приложение в Laravel
Laravel - это популярный PHP-фреймворк, который позволяет быстро и легко создавать веб-приложения. Одной из наиболее распространенных задач в...
Освоение PHP и управление базами данных: Создание собственной СУБД - часть II
Освоение PHP и управление базами данных: Создание собственной СУБД - часть II
В предыдущем посте мы создали функциональность вставки и чтения для нашей динамической СУБД. В этом посте мы собираемся реализовать функции обновления...
Документирование API с помощью Swagger на Springboot
Документирование API с помощью Swagger на Springboot
В предыдущей статье мы уже узнали, как создать Rest API с помощью Springboot и MySql .
Роли и разрешения пользователей без пакета Laravel 9
Роли и разрешения пользователей без пакета Laravel 9
Этот пост изначально был опубликован на techsolutionstuff.com .
Как установить LAMP Stack - Security 5/5 на виртуальную машину Azure Linux VM
Как установить LAMP Stack - Security 5/5 на виртуальную машину Azure Linux VM
В предыдущей статье мы завершили установку базы данных, для тех, кто не знает.
0
2
50
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Один вариант использует оконные функции, доступные в 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. Спасибо за ответ, стараюсь

Анатолий Костяков 24.12.2020 15:40

Я использую версию 5.7(( Ваш подход не поможет, все равно спасибо

Анатолий Костяков 24.12.2020 15:43

@АнатолийКостяков: ок, тогда смотри мое обновление.

GMB 24.12.2020 15:55

Не знал, что MySQL реализует боковые соединения. +1 только за это.

The Impaler 24.12.2020 16:22

@TheImpaler: на мой взгляд, это большое улучшение в MySQL!

GMB 24.12.2020 16:28

Другие вопросы по теме