MySQL:将单行结果放在单独的列中
MySQL: Putting single row results in seperate columns
我正在使用 MySQL 版本 5.7.31,但刚刚安装了一个 MySQL 服务器版本 8 以备不时之需
我有一个 table 包含关于实体的一系列信息,如下所示:
+------+-----+
| Name | Age |
+------+-----+
| Bob | 22 |
| Jack | 15 |
| Jane | 25 |
+------+-----+
在另一个 table 中,我有关于哪些技能属于哪个姓名和年龄组合的信息,如下所示:
+-------------+------+-----+
| Skill | Name | Age |
+-------------+------+-----+
| programming | Bob | 22 |
| programming | Jane | 25 |
| marketing | Jane | 25 |
+-------------+------+-----+
这让我能够 select 25 岁的简的所有技能 - 返回 2 行。但是,我需要在如下所示的最终视图中将这些放入每个实体的单独列中,我正在努力弄清楚如何做到这一点。当我在上面显示的 tables
中获得额外的行时,视图应该继续工作
+------+-----+-------------+-----------+--------+
| Name | Age | skill1 | skill2 | skill3 |
+------+-----+-------------+-----------+--------+
| Bob | 22 | programming | Na | Na |
| Jack | 15 | Na | Na | Na |
| Jane | 25 | programming | marketing | Na |
+------+-----+-------------+-----------+--------+
您可以使用 row_number()
和条件聚合。假设这些表称为 persons
和 person_skills
:
select p.name, p.age,
max(case when ps.rn = 1 then ps.skill end) skill1,
max(case when ps.rn = 2 then ps.skill end) skill2,
max(case when ps.rn = 3 then ps.skill end) skill3
from persons p
left join (
select ps.*, row_number() over(partition by name, age order by id) rn
from person_skills ps
) ps on ps.name = p.name and ps.age = p.age
group by p.name, p.age
不清楚您希望将哪些逻辑分布到列中。这假设您有一个名为 id
的排序列,可用于此。
您可以使用 GROUP_CONCAT 聚合函数来获取逗号分隔的技能列表:
select
Users.Name,
Users.Age,
GROUP_CONCAT(Skill) AS Skills
from Users
left join Skills on Skills.Name = Users.Name
group by Users.Name, Users.Age
;
这里是fiddleSQLize.online
结果
Name Age Skills
Bob 22 programming
Jack 15 NULL
Jane 25 programming,marketing
如果技能计数有限,可以使用下一个查询:
select
Users.Name,
Users.Age,
GROUP_CONCAT(if(Skill='programming', 'programming', NULL)) AS Skill1,
GROUP_CONCAT(if(Skill='marketing', 'marketing', NULL)) AS Skill2
from Users
left join Skills on Skills.Name = Users.Name
group by Users.Name, Users.Age
;
Fiddle here
首先以可以使用确定条件的方式创建 table 结构...在 Pivot 选项
中搜索更多内容
下面是这种情况的一个例子
Create table Person (FName varchar(20), LName varchar(20), Age Int);
Insert into Person values
('Jacke','W',29),
('Prince','P',30),
('Rush','B',29);
Create table Speciality (FName varchar(20), Skill varchar(20),Technology varchar(30));
Insert into Speciality values
('Jacke','Skill1','Java'),
('Jacke','Skill2','Unix'),
('Jacke','Skill3','PHP'),
('Prince','Skill1','.Net'),
('Prince','Skill2','Python'),
('Prince','Skill3','Notepad++'),
('Rush','Skill1','Office 365'),
('Rush','Skill2','.Net');
select P.FName, p.LName, p.Age,
MAX(IF(Skill = 'Skill1', Technology, NULL)) AS Skill1,
MAX(IF(Skill = 'Skill2', Technology, NULL)) AS Skill2,
MAX(IF(Skill = 'Skill3', Technology, NULL)) AS Skill3
from Person P, Speciality S
where P.FName=S.Fname
group by P.FName, p.LName, p.Age;
+--------+-------+------+------------+--------+-----------+
| FName | LName | Age | Skill1 | Skill2 | Skill3 |
+--------+-------+------+------------+--------+-----------+
| Jacke | W | 29 | Java | Unix | PHP |
| Prince | P | 30 | .Net | Python | Notepad++ |
| Rush | B | 29 | Office 365 | .Net | NULL |
+--------+-------+------+------------+--------+-----------+
如果你想在 MySQL 5.7 中将它们放在单独的列中,可能最简单的方法是相关子查询:
select p.*,
(select ps.skill
from person_skills ps
where ps.name = p.name and ps.age = p.age
order by ps.skill
limit 1 offset 0
) as skill_1,
(select ps.skill
from person_skills ps
where ps.name = p.name and ps.age = p.age
order by ps.skill
limit 1 offset 1
) as skill_2,
(select ps.skill
from person_skills ps
where ps.name = p.name and ps.age = p.age
order by ps.skill
limit 1 offset 2
) as skill_3
from persons p;
这看起来很复杂,但是三个子查询除了offset
.
外都是一样的
注意:当没有可用技能时,此 returns NULL
而不是 'NA'
。这是表示不可用值的典型方式。
如果你确实需要'NA'
,你可以使用COALESCE()
:
coalesce( (select ps.skill
from person_skills ps
where ps.name = p.name and ps.age = p.age
order by ps.skill
limit 1 offset 0
), 'NA') as skill_1,
旧版本中的替代方法是使用 group_concat()
:
select p.name, p.age,
substring_index(group_concat(ps.skill order by ps.skill), ',', 1) as skill_1,
substring_index(substring_index(group_concat(ps.skill order by ps.skill), ',', 2), ',', -1) as skill_2,
substring_index(substring_index(group_concat(ps.skill order by ps.skill), ',', 3), ',', -1) as skill_3
from persons p left join
person_skills ps
on ps.name = p.name and ps.age = p.age
group by p.name, p.age;
这会将所有技能聚合到一个字符串中,然后提取第 nth 个元素。
对于 'NA'
,您可以像上面那样使用 COALESCE()
。
好吧,你在这里做的根本是错误的,这不是你应该如何对待关系数据库,这是近乎滥用的。
关系数据库第 101 课,始终使用索引自增主键。总是! (很抱歉,我太苛刻了,但是在许多由不了解这些基础知识的人设计的系统中工作,这使得性能变得非常糟糕)。
这就是您的用户 table 的样子。
+----+------+-----+
| id | Name | Age |
+----+------+-----+
| 1 | Bob | 22 |
| 2 | Jack | 15 |
| 3 | Jane | 25 |
+----+------+-----+
技能table
+----+--------------+
| id | skill |
+----+--------------+
| 1 | programming |
| 2 | Marketing |
+----+--------------+
Link table
+----+---------+--------+
| id | skillId | userId |
+----+---------+--------+
| 1 | 1 | 3 |
| 2 | 2 | 3 |
| 3 | 1 | 1 |
+----+---------+--------+
好的,现在 SQL 本身,从你的例子来看,你似乎总是希望在 skill1 和 marketing 作为 skill2 上进行编程,在这种情况下(我们使用 max 和 group by 来获得一个每个用户的行数)。
SELECT
u.name Name, u.age Age,
MAX(CASE WHEN s.id = 1 THEN s.skill ELSE NULL END) Skill1, -- Programmer
MAX(CASE WHEN s.id = 2 THEN s.skill ELSE NULL END) Skill2 -- Marketing
FROM
users u
LEFT JOIN
linktable l ON l.userId = u.id
LEFT JOIN
skills s ON s.id = l.skillsId
GROUP BY
u.name, u.age
最好的办法显然是像其他人建议的那样使用 group_concat
但是你必须在一列中处理所有结果,但可以处理无限数量的技能。
SELECT
u.name Name, u.age Age,
GROUP_CONCAT(s.skill) Skills
FROM
users u
LEFT JOIN
linktable l ON l.userId = u.id
LEFT JOIN
skills s ON s.id = l.skillsId
GROUP BY
u.name, u.age
我正在使用 MySQL 版本 5.7.31,但刚刚安装了一个 MySQL 服务器版本 8 以备不时之需
我有一个 table 包含关于实体的一系列信息,如下所示:
+------+-----+
| Name | Age |
+------+-----+
| Bob | 22 |
| Jack | 15 |
| Jane | 25 |
+------+-----+
在另一个 table 中,我有关于哪些技能属于哪个姓名和年龄组合的信息,如下所示:
+-------------+------+-----+
| Skill | Name | Age |
+-------------+------+-----+
| programming | Bob | 22 |
| programming | Jane | 25 |
| marketing | Jane | 25 |
+-------------+------+-----+
这让我能够 select 25 岁的简的所有技能 - 返回 2 行。但是,我需要在如下所示的最终视图中将这些放入每个实体的单独列中,我正在努力弄清楚如何做到这一点。当我在上面显示的 tables
中获得额外的行时,视图应该继续工作+------+-----+-------------+-----------+--------+
| Name | Age | skill1 | skill2 | skill3 |
+------+-----+-------------+-----------+--------+
| Bob | 22 | programming | Na | Na |
| Jack | 15 | Na | Na | Na |
| Jane | 25 | programming | marketing | Na |
+------+-----+-------------+-----------+--------+
您可以使用 row_number()
和条件聚合。假设这些表称为 persons
和 person_skills
:
select p.name, p.age,
max(case when ps.rn = 1 then ps.skill end) skill1,
max(case when ps.rn = 2 then ps.skill end) skill2,
max(case when ps.rn = 3 then ps.skill end) skill3
from persons p
left join (
select ps.*, row_number() over(partition by name, age order by id) rn
from person_skills ps
) ps on ps.name = p.name and ps.age = p.age
group by p.name, p.age
不清楚您希望将哪些逻辑分布到列中。这假设您有一个名为 id
的排序列,可用于此。
您可以使用 GROUP_CONCAT 聚合函数来获取逗号分隔的技能列表:
select
Users.Name,
Users.Age,
GROUP_CONCAT(Skill) AS Skills
from Users
left join Skills on Skills.Name = Users.Name
group by Users.Name, Users.Age
;
这里是fiddleSQLize.online
结果
Name Age Skills
Bob 22 programming
Jack 15 NULL
Jane 25 programming,marketing
如果技能计数有限,可以使用下一个查询:
select
Users.Name,
Users.Age,
GROUP_CONCAT(if(Skill='programming', 'programming', NULL)) AS Skill1,
GROUP_CONCAT(if(Skill='marketing', 'marketing', NULL)) AS Skill2
from Users
left join Skills on Skills.Name = Users.Name
group by Users.Name, Users.Age
;
Fiddle here
首先以可以使用确定条件的方式创建 table 结构...在 Pivot 选项
中搜索更多内容下面是这种情况的一个例子
Create table Person (FName varchar(20), LName varchar(20), Age Int);
Insert into Person values
('Jacke','W',29),
('Prince','P',30),
('Rush','B',29);
Create table Speciality (FName varchar(20), Skill varchar(20),Technology varchar(30));
Insert into Speciality values
('Jacke','Skill1','Java'),
('Jacke','Skill2','Unix'),
('Jacke','Skill3','PHP'),
('Prince','Skill1','.Net'),
('Prince','Skill2','Python'),
('Prince','Skill3','Notepad++'),
('Rush','Skill1','Office 365'),
('Rush','Skill2','.Net');
select P.FName, p.LName, p.Age,
MAX(IF(Skill = 'Skill1', Technology, NULL)) AS Skill1,
MAX(IF(Skill = 'Skill2', Technology, NULL)) AS Skill2,
MAX(IF(Skill = 'Skill3', Technology, NULL)) AS Skill3
from Person P, Speciality S
where P.FName=S.Fname
group by P.FName, p.LName, p.Age;
+--------+-------+------+------------+--------+-----------+
| FName | LName | Age | Skill1 | Skill2 | Skill3 |
+--------+-------+------+------------+--------+-----------+
| Jacke | W | 29 | Java | Unix | PHP |
| Prince | P | 30 | .Net | Python | Notepad++ |
| Rush | B | 29 | Office 365 | .Net | NULL |
+--------+-------+------+------------+--------+-----------+
如果你想在 MySQL 5.7 中将它们放在单独的列中,可能最简单的方法是相关子查询:
select p.*,
(select ps.skill
from person_skills ps
where ps.name = p.name and ps.age = p.age
order by ps.skill
limit 1 offset 0
) as skill_1,
(select ps.skill
from person_skills ps
where ps.name = p.name and ps.age = p.age
order by ps.skill
limit 1 offset 1
) as skill_2,
(select ps.skill
from person_skills ps
where ps.name = p.name and ps.age = p.age
order by ps.skill
limit 1 offset 2
) as skill_3
from persons p;
这看起来很复杂,但是三个子查询除了offset
.
注意:当没有可用技能时,此 returns NULL
而不是 'NA'
。这是表示不可用值的典型方式。
如果你确实需要'NA'
,你可以使用COALESCE()
:
coalesce( (select ps.skill
from person_skills ps
where ps.name = p.name and ps.age = p.age
order by ps.skill
limit 1 offset 0
), 'NA') as skill_1,
旧版本中的替代方法是使用 group_concat()
:
select p.name, p.age,
substring_index(group_concat(ps.skill order by ps.skill), ',', 1) as skill_1,
substring_index(substring_index(group_concat(ps.skill order by ps.skill), ',', 2), ',', -1) as skill_2,
substring_index(substring_index(group_concat(ps.skill order by ps.skill), ',', 3), ',', -1) as skill_3
from persons p left join
person_skills ps
on ps.name = p.name and ps.age = p.age
group by p.name, p.age;
这会将所有技能聚合到一个字符串中,然后提取第 nth 个元素。
对于 'NA'
,您可以像上面那样使用 COALESCE()
。
好吧,你在这里做的根本是错误的,这不是你应该如何对待关系数据库,这是近乎滥用的。
关系数据库第 101 课,始终使用索引自增主键。总是! (很抱歉,我太苛刻了,但是在许多由不了解这些基础知识的人设计的系统中工作,这使得性能变得非常糟糕)。
这就是您的用户 table 的样子。
+----+------+-----+
| id | Name | Age |
+----+------+-----+
| 1 | Bob | 22 |
| 2 | Jack | 15 |
| 3 | Jane | 25 |
+----+------+-----+
技能table
+----+--------------+
| id | skill |
+----+--------------+
| 1 | programming |
| 2 | Marketing |
+----+--------------+
Link table
+----+---------+--------+
| id | skillId | userId |
+----+---------+--------+
| 1 | 1 | 3 |
| 2 | 2 | 3 |
| 3 | 1 | 1 |
+----+---------+--------+
好的,现在 SQL 本身,从你的例子来看,你似乎总是希望在 skill1 和 marketing 作为 skill2 上进行编程,在这种情况下(我们使用 max 和 group by 来获得一个每个用户的行数)。
SELECT
u.name Name, u.age Age,
MAX(CASE WHEN s.id = 1 THEN s.skill ELSE NULL END) Skill1, -- Programmer
MAX(CASE WHEN s.id = 2 THEN s.skill ELSE NULL END) Skill2 -- Marketing
FROM
users u
LEFT JOIN
linktable l ON l.userId = u.id
LEFT JOIN
skills s ON s.id = l.skillsId
GROUP BY
u.name, u.age
最好的办法显然是像其他人建议的那样使用 group_concat
但是你必须在一列中处理所有结果,但可以处理无限数量的技能。
SELECT
u.name Name, u.age Age,
GROUP_CONCAT(s.skill) Skills
FROM
users u
LEFT JOIN
linktable l ON l.userId = u.id
LEFT JOIN
skills s ON s.id = l.skillsId
GROUP BY
u.name, u.age