题目
一所美国大学有来自亚洲、欧洲和美洲的学生,他们的地理信息存放在如下 student 表中。
name | continent |
---|---|
Jack | America |
Pascal | Europe |
Xi | Asia |
Jane | America |
写一个查询语句实现对大洲(continent)列的 透视表 操作,使得每个学生按照姓名的字母顺序依次排列在对应的大洲下面。输出的标题应依次为美洲(America)、亚洲(Asia)和欧洲(Europe)。数据保证来自美洲的学生不少于来自亚洲或者欧洲的学生。
对于样例输入,它的对应输出是:
America | Asia | Europe |
---|---|---|
Jack | Xi | Pascal |
Jane |
进阶:如果不能确定哪个大洲的学生数最多,你可以写出一个查询去生成上述学生报告吗?
生成数据
CREATE TABLE student2 (NAME VARCHAR(25),continent VARCHAR(25));
INSERT INTO student2 VALUES('Jack','America');
INSERT INTO student2 VALUES('Pascal','Europe');
INSERT INTO student2 VALUES('Xi','Asia');
INSERT INTO student2 VALUES('Jane','America');
审题
这特喵不就是长数据转宽数据么 py、R一下就可以实现的 这里也可能就是了解长转宽的底层逻辑吧。。
解答
重点是两步:
第一,确定每个属性中各个数据所在的数据行。 其实是求每个数据的排名。
第二,确定每个数据行都有哪些数据。按照排名放置数据。
定义变量对每个周进行排名 这个之前做过比较多次了 直接写
SELECT S.`continent`, S.`name`,
IF(S.`continent`=@pre_con, @rank:= @rank+1, @rank:=1) AS rank,
@pre_con:= S.`continent` AS pre_con
FROM student2 AS S, (SELECT @rank:=0, @pre_con:= NULL) AS init
ORDER BY S.`continent`, S.`name`;
对rank进行分组如果continent是亚洲 把他放在亚洲列 别的同理
SELECT tmp.rank, IF(tmp.continent = 'America',tmp.NAME,NULL) AS `America`,
IF(tmp.continent = 'Asia', tmp.NAME,NULL) AS `Asia`,
IF(tmp.continent = 'Europe',tmp.NAME,NULL) AS `Europe`
FROM (SELECT S.`continent`, S.`name`,
IF(S.`continent`=@pre_con, @rank:= @rank+1, @rank:=1) AS rank,
@pre_con:= S.`continent` AS pre_con
FROM student2 AS S, (SELECT @rank:=0, @pre_con:= NULL) AS init
ORDER BY S.`continent`, S.`name`) AS tmp
GROUP BY tmp.rank;
这样的结果 看下边这个就明白了
SELECT tmp.rank, IF(tmp.continent = 'America',tmp.NAME,NULL) AS `America`,
IF(tmp.continent = 'Asia', tmp.NAME,NULL) AS `Asia`,
IF(tmp.continent = 'Europe',tmp.NAME,NULL) AS `Europe`
FROM (SELECT S.`continent`, S.`name`,
IF(S.`continent`=@pre_con, @rank:= @rank+1, @rank:=1) AS rank,
@pre_con:= S.`continent` AS pre_con
FROM student2 AS S, (SELECT @rank:=0, @pre_con:= NULL) AS init
ORDER BY S.`continent`, S.`name`) AS tmp;
分组取得是每一组的第一行 所以造成了如上的结果
还是那个老问题 分组后最好与聚合函数连用 max min都行
SELECT tmp.rank, MAX(IF(tmp.continent = 'America',tmp.NAME,NULL)) AS `America`,
MAX(IF(tmp.continent = 'Asia', tmp.NAME,NULL)) AS `Asia`,
MAX(IF(tmp.continent = 'Europe',tmp.NAME,NULL)) AS `Europe`
FROM (SELECT S.`continent`, S.`name`,
IF(S.`continent`=@pre_con, @rank:= @rank+1, @rank:=1) AS rank,
@pre_con:= S.`continent` AS pre_con
FROM student2 AS S, (SELECT @rank:=0, @pre_con:= NULL) AS init
ORDER BY S.`continent`, S.`name`) AS tmp
GROUP BY tmp.rank;
分组排序还有自连接的方法
SELECT
MAX(if(A.continent = 'America',A.NAME,NULL)) AS `America`,
MAX(if(A.continent = 'Asia',A.NAME,NULL)) AS `Asia`,
MAX(if(A.continent = 'Europe',A.NAME,NULL)) AS `Europe`
FROM
(
SELECT S1.continent,S1.NAME,S1.row_id,COUNT(*) AS `trank`
FROM
(
SELECT S.*,@row_id:=(@row_id + 1) AS `row_id`
FROM student AS S,(SELECT @row_id:=0) AS T
) AS S1
JOIN
(
SELECT S.*,@n_row_id:=(@n_row_id + 1) AS `n_row_id`
FROM student AS S,(SELECT @n_row_id:=0) AS T
) AS S2
ON (S1.continent = S2.continent AND (S1.NAME > S2.NAME OR (S1.NAME = S2.NAME AND S1.row_id >= S2.n_row_id)))
group BY S1.continent,S1.NAME,S1.row_id
order BY S1.continent,S1.NAME
) AS A
GROUP BY A.trank