如何使用 MySQL 连接 table 中的第一条和最后一条记录?
How to join the first and the last record from table using MySQL?
我有 2 个 table。第一个 table 是 - 产品,第二个是修订。
加入是由 product.id = revisions.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 | {} |
+-------------+---------------+---------------------+-------------------+--------------------+------------------+
一个选项使用 window 函数,在 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);
我有 2 个 table。第一个 table 是 - 产品,第二个是修订。
加入是由 product.id = revisions.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 | {} |
+-------------+---------------+---------------------+-------------------+--------------------+------------------+
一个选项使用 window 函数,在 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);