如何在 MySQL 中为每个唯一列值选择两条记录作为一行?
Posted
技术标签:
【中文标题】如何在 MySQL 中为每个唯一列值选择两条记录作为一行?【英文标题】:How to SELECT two records for each unique column value as one row in MySQL? 【发布时间】:2020-10-24 22:26:00 【问题描述】:我有一个这样的 mysql 表:
+----+-----+-------+------+------+-------+---------------------+
| ID | GID | Name | p1 | p10 | p100 | createdAt |
+----+-----+-------+------+------+-------+---------------------+
| 1 | 100 | Item1 | 150 | 1499 | 10245 | 2020-07-04 12:00:00 |
| 2 | 857 | Item2 | 1047 | 9875 | 90000 | 2020-07-04 12:00:10 |
| 3 | 100 | Item1 | 149 | 1495 | 10245 | 2020-07-04 12:15:00 |
| 4 | 857 | Item2 | 1099 | 9875 | 89999 | 2020-07-04 12:15:10 |
| 5 | 100 | Item1 | 149 | 1495 | 10247 | 2020-07-04 12:30:00 |
| 6 | 857 | Item2 | 970 | 9879 | 89998 | 2020-07-04 12:30:10 |
+----+-----+-------+------+------+-------+---------------------+
我尝试为每个唯一的GID
输出最近的两个createdAt
中的p1, p10, p100
输出示例:
+-----+-------+------+------+-------+---------+----------+-----------+
| GID | Name | p1 | p10 | p100 | p1-last | p10-last | p100-last |
+-----+-------+------+------+-------+---------+----------+-----------+
| 100 | Item1 | 149 | 1495 | 10245 | 149 | 1495 | 10247 |
| 857 | Item2 | 1099 | 9875 | 89999 | 970 | 9879 | 89998 |
+-----+-------+------+------+-------+---------+----------+-----------+
我尝试使用子查询来实现我的目标,但我对此并不满意。
感谢任何可以为我提供信息和帮助的人。
【问题讨论】:
你的 MySql 版本是多少? 您好,我使用的是mysql Ver 15.1 Distrib 5.5.65-MariaDB 【参考方案1】:您可以为此使用lag()
:
select gid, name, p1, p10, p100, prev_p1, prev_p10, prev_p100
from (select t.*,
lag(p1) over (partition by gid order by createdAt) as prev_p1,
lag(p10) over (partition by gid order by createdAt) as prev_p10,
lag(p100) over (partition by gid order by createdAt) as prev_p100,
row_number() over (partition by gid order by createdAt desc) as seqnum
from t
) t
where seqnum = 1;
Here 是一个 dbfiddle。
子查询返回每列的先前值。外部查询只是过滤到每个 gid
/name
组合的最新行。
【讨论】:
您好,感谢您的回答。但是,MariaDB 中的 LAG() 函数出现在 10.2.2 版本中。我的 MariaDB 版本是 5.5.65-MariaDB MariaDB Server。 我将 MariaDB 服务器更新到 10.4,查询返回 prev_p1、prev_p10、prev_p100 的“NULL”结果。 需要将WHERE seqnum = 1
更改为2。(感谢@forpas)
@Dampen59 。 . . .应该是where seqnum = 1
。问题是desc
在lag()
中的顺序。哎呀。我删除了。【参考方案2】:
如果没有窗口函数,这并不容易和优雅。 这是一种方法,它涉及自连接、2 级聚合和条件聚合:
select t.gid, t.name,
max(case when c.counter = 1 then t.p1 end) p1,
max(case when c.counter = 1 then t.p10 end) p10,
max(case when c.counter = 1 then t.p100 end) p100,
max(case when c.counter = 0 then t.p1 end) p1_last,
max(case when c.counter = 0 then t.p10 end) p10_last,
max(case when c.counter = 0 then t.p100 end) p100_last
from tablename t inner join (
select t1.gid, t1.createdat, count(t2.createdat) counter
from tablename t1 left join tablename t2
on t2.gid = t1.gid and t1.createdat < t2.createdat
group by t1.gid, t1.createdat
having count(t2.createdat) <= 1
) c on c.gid = t.gid and c.createdat = t.createdat
group by t.gid, t.name
请参阅demo。 结果:
| gid | name | p1 | p10 | p100 | p1_last | p10_last | p100_last |
| --- | ----- | ---- | ---- | ----- | ------- | -------- | --------- |
| 100 | Item1 | 149 | 1495 | 10245 | 149 | 1495 | 10247 |
| 857 | Item2 | 1099 | 9875 | 89999 | 970 | 9879 | 89998 |
【讨论】:
您好,感谢您的回答。我尝试了您的查询,但似乎在填充表上需要很长时间。我让查询运行了 5 分钟以上,它仍在运行。无论如何感谢您的贡献!我已经更新了 MariaDB 的服务器版本以检查 Gordon 的答案,但它似乎无法正常工作。我正在尝试修复它。 这就是你需要窗口函数的原因。 是的,我已经编辑了我的评论。我的 MariaDB 版本已更新到 10.4 将 Gordon 的回答改为:where seqnum = 2
以上是关于如何在 MySQL 中为每个唯一列值选择两条记录作为一行?的主要内容,如果未能解决你的问题,请参考以下文章
如何在 BigQuery 中取消嵌套重复记录,一个数组给出列名,另一个给出列值?
Pyspark - 如何检查两条记录中哪一条具有最新日期及其列值?