作者:畢濤濤,致力于Java學(xué)習(xí)的踐行者笆呆。原創(chuàng)文章衷戈,轉(zhuǎn)載請(qǐng)注明出處。
參考鏈接:https://blog.csdn.net/wqc19920906/article/details/79791322
一稠通、創(chuàng)建表結(jié)構(gòu)衬衬、插入數(shù)據(jù)
1、三種表:學(xué)生表改橘、課程表滋尉、分?jǐn)?shù)表
-- 學(xué)生表
CREATE TABLE `student` (
`stuid` VARCHAR(16) NOT NULL COMMENT '學(xué)號(hào)',
`stunm` VARCHAR(20) NOT NULL COMMENT '學(xué)生姓名',
PRIMARY KEY (`stuid`)
) COMMENT '學(xué)生表';
-- 課程表
CREATE TABLE `courses` (
`courseno` VARCHAR(20) NOT NULL,
`coursenm` VARCHAR(100) NOT NULL,
PRIMARY KEY (`courseno`)
) COMMENT '課程表';
-- 成績(jī)表
CREATE TABLE `score` (
`stuid` VARCHAR(16) NOT NULL,
`courseno` VARCHAR(20) NOT NULL,
`scores` FLOAT NULL DEFAULT NULL,
PRIMARY KEY (`stuid`, `courseno`)
) COMMENT '成績(jī)表';
2、插入數(shù)據(jù)
/*學(xué)生表數(shù)據(jù)*/
Insert Into student (stuid, stunm) Values('1001', '張三');
Insert Into student (stuid, stunm) Values('1002', '李四');
Insert Into student (stuid, stunm) Values('1003', '趙二');
Insert Into student (stuid, stunm) Values('1004', '王五');
Insert Into student (stuid, stunm) Values('1005', '劉青');
Insert Into student (stuid, stunm) Values('1006', '周明');
/*課程表數(shù)據(jù)*/
Insert Into courses (courseno, coursenm) Values('C001', '大學(xué)語文');
Insert Into courses (courseno, coursenm) Values('C002', '新視野英語');
Insert Into courses (courseno, coursenm) Values('C003', '離散數(shù)學(xué)');
Insert Into courses (courseno, coursenm) Values('C004', '概率論與數(shù)理統(tǒng)計(jì)');
Insert Into courses (courseno, coursenm) Values('C005', '線性代數(shù)');
Insert Into courses (courseno, coursenm) Values('C006', '高等數(shù)學(xué)(一)');
/*成績(jī)表數(shù)據(jù)*/
Insert Into score(stuid, courseno, scores) Values('1001', 'C001', 67);
Insert Into score(stuid, courseno, scores) Values('1002', 'C001', 68);
Insert Into score(stuid, courseno, scores) Values('1003', 'C001', 69);
Insert Into score(stuid, courseno, scores) Values('1004', 'C001', 70);
Insert Into score(stuid, courseno, scores) Values('1005', 'C001', 71);
Insert Into score(stuid, courseno, scores) Values('1006', 'C001', 72);
Insert Into score(stuid, courseno, scores) Values('1001', 'C002', 87);
Insert Into score(stuid, courseno, scores) Values('1002', 'C002', 88);
Insert Into score(stuid, courseno, scores) Values('1003', 'C002', 89);
Insert Into score(stuid, courseno, scores) Values('1004', 'C002', 90);
Insert Into score(stuid, courseno, scores) Values('1005', 'C002', 91);
Insert Into score(stuid, courseno, scores) Values('1006', 'C002', 92);
Insert Into score(stuid, courseno, scores) Values('1001', 'C003', 83);
Insert Into score(stuid, courseno, scores) Values('1002', 'C003', 84);
Insert Into score(stuid, courseno, scores) Values('1003', 'C003', 85);
Insert Into score(stuid, courseno, scores) Values('1004', 'C003', 86);
Insert Into score(stuid, courseno, scores) Values('1005', 'C003', 87);
Insert Into score(stuid, courseno, scores) Values('1006', 'C003', 88);
Insert Into score(stuid, courseno, scores) Values('1001', 'C004', 88);
Insert Into score(stuid, courseno, scores) Values('1002', 'C004', 89);
Insert Into score(stuid, courseno, scores) Values('1003', 'C004', 90);
Insert Into score(stuid, courseno, scores) Values('1004', 'C004', 91);
Insert Into score(stuid, courseno, scores) Values('1005', 'C004', 92);
Insert Into score(stuid, courseno, scores) Values('1006', 'C004', 93);
Insert Into score(stuid, courseno, scores) Values('1001', 'C005', 77);
Insert Into score(stuid, courseno, scores) Values('1002', 'C005', 78);
Insert Into score(stuid, courseno, scores) Values('1003', 'C005', 79);
Insert Into score(stuid, courseno, scores) Values('1004', 'C005', 80);
Insert Into score(stuid, courseno, scores) Values('1005', 'C005', 81);
Insert Into score(stuid, courseno, scores) Values('1006', 'C005', 82);
Insert Into score(stuid, courseno, scores) Values('1001', 'C006', 77);
Insert Into score(stuid, courseno, scores) Values('1002', 'C006', 78);
Insert Into score(stuid, courseno, scores) Values('1003', 'C006', 79);
Insert Into score(stuid, courseno, scores) Values('1004', 'C006', 80);
Insert Into score(stuid, courseno, scores) Values('1005', 'C006', 81);
Insert Into score(stuid, courseno, scores) Values('1006', 'C006', 82);
二飞主、為什么要行轉(zhuǎn)列
1狮惜、查詢學(xué)生姓名、對(duì)應(yīng)課程以及相應(yīng)的分?jǐn)?shù)
SELECT
A.stuid,
A.stunm,
B.coursenm,
C.scores
FROM
student A
INNER JOIN score C ON A.stuid = C.stuid
INNER JOIN courses B ON C.courseno = B.courseno;
2既棺、行轉(zhuǎn)列后的結(jié)果
上面是進(jìn)行成績(jī)查詢的時(shí)候看到的縱列的結(jié)果讽挟,但是我們想要的是下面的結(jié)果,那么需要這樣的結(jié)果就要進(jìn)行 行轉(zhuǎn)列 來操作了丸冕。
三耽梅、實(shí)現(xiàn)過程
1、靜態(tài)行轉(zhuǎn)列
SELECT st.stuid, st.stunm,
SUM(CASE c.coursenm WHEN '大學(xué)語文' THEN ifnull(s.scores,0) ELSE 0 END ) '大學(xué)語文',
SUM(CASE c.coursenm WHEN '新視野英語' THEN ifnull(s.scores,0) ELSE 0 END ) '新視野英語',
SUM(CASE c.coursenm WHEN '離散數(shù)學(xué)' THEN ifnull(s.scores,0) ELSE 0 END ) '離散數(shù)學(xué)',
SUM(CASE c.coursenm WHEN '概率論與數(shù)理統(tǒng)計(jì)' THEN ifnull(s.scores,0) ELSE 0 END ) '概率論與數(shù)理統(tǒng)計(jì)',
SUM(CASE c.coursenm WHEN '線性代數(shù)' THEN ifnull(s.scores,0) ELSE 0 END ) '線性代數(shù)',
SUM(CASE c.coursenm WHEN '高等數(shù)學(xué)(一)' THEN ifnull(s.scores,0) ELSE 0 END ) '高等數(shù)學(xué)(一)'
FROM student st
LEFT JOIN score s ON st.stuid = s.stuid
LEFT JOIN courses c ON c.courseno = s.courseno
GROUP BY st.stuid;
看上面的語句可以看出胖烛,我們是在知道固定的幾門課程之后眼姐,可以使用這樣的語句來實(shí)現(xiàn)行轉(zhuǎn)列。
SUM(CASE c.coursenm WHEN '大學(xué)語文' THEN ifnull(s.scores,0) ELSE 0 END ) '大學(xué)語文',
與后面的GROUP BY 語句結(jié)合使用佩番,主要目的是為了消除階梯狀數(shù)據(jù)众旗。(關(guān)聯(lián)學(xué)生表,分?jǐn)?shù)表后趟畏,你的結(jié)果集會(huì)和分?jǐn)?shù)表?xiàng)l數(shù)一致)
但我們都知道贡歧,課程不僅僅這幾門,如果用上面的語句去寫赋秀,第一要確定有多少課程利朵,這么多課程的課程名要再拿出來,那樣的話寫一個(gè)查詢語句下來猎莲,可是要寫很多了绍弟。那么就想能不能動(dòng)態(tài)進(jìn)行行轉(zhuǎn)列的操作?答案當(dāng)然是肯定的了著洼!
2樟遣、動(dòng)態(tài)行轉(zhuǎn)列
那么如何進(jìn)行動(dòng)態(tài)行轉(zhuǎn)列呢而叼?首先我們要?jiǎng)討B(tài)獲取這樣的語句
MAX(CASE c.coursenm WHEN '大學(xué)語文' THEN ifnull(s.scores,0) ELSE 0 END ) '大學(xué)語文',
MAX(CASE c.coursenm WHEN '新視野英語' THEN ifnull(s.scores,0) ELSE 0 END ) '新視野英語',
MAX(CASE c.coursenm WHEN '離散數(shù)學(xué)' THEN ifnull(s.scores,0) ELSE 0 END ) '離散數(shù)學(xué)',
而不是像上面那樣一句句寫出來,那如何得到這樣的語句呢豹悬?這里就要用到SQL語句拼接了葵陵。具體就是下面的語句:
SELECT
GROUP_CONCAT(DISTINCT
CONCAT(
'SUM(IF(c.coursenm = ''',
c.coursenm,
''', s.scores, 0)) AS ''',
c.coursenm, ''''
)
)
FROM courses c;
得到的結(jié)果就是對(duì),沒錯(cuò)屿衅,就是我們上面進(jìn)行 行轉(zhuǎn)列 查詢要用的語句埃难,那樣就不用知道多少課程和這些課程的名字,只要這樣幾行代碼便可以得到動(dòng)態(tài)的列了涤久。
動(dòng)態(tài)的列是拿到了涡尘,那如何再結(jié)合SQL語句進(jìn)行查詢得到結(jié)果呢?
這里要說明一點(diǎn)响迂,因?yàn)橛玫搅似唇雍瘮?shù)考抄,如果像上面的查詢語句,只是把那幾行語句替換掉蔗彤,也就是下面這樣:
SELECT st.stuid, st.stunm,
(
SELECT
GROUP_CONCAT(DISTINCT
CONCAT(
'MAX(IF(c.coursenm = ''',
c.coursenm,
''', s.scores, NULL)) AS ',
c.coursenm
)
)
FROM courses c
)
FROM student st
LEFT JOIN score s ON st.stuid = s.stuid
LEFT JOIN courses c ON c.courseno = s.courseno
GROUP BY st.stuid;
然而事與愿違川梅,得到的結(jié)果卻是這樣的這里我就不多做贅述了,想必大家也明白然遏。那么既然這樣不行贫途,那該怎么做呢?
沒錯(cuò)待侵,這里就要像普通的那些語句那樣丢早,進(jìn)行聲明,將語句拼接完整之后秧倾,再執(zhí)行怨酝,也就是下面這樣:
SET @sql = NULL;
SELECT
GROUP_CONCAT(DISTINCT
CONCAT(
'SUM(IF(c.coursenm = ''',
c.coursenm,
''', s.scores, 0)) AS ''',
c.coursenm, ''''
)
) INTO @sql
FROM courses c;
SET @sql = CONCAT('SELECT st.stuid, st.stunm, ', @sql,
' FROM Student st
LEFT JOIN score s ON st.stuid = s.stuid
LEFT JOIN courses c ON c.courseno = s.courseno
GROUP BY st.stuid');
PREPARE stmt FROM @sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
直接執(zhí)行這些語句,得到如下結(jié)果:當(dāng)然這個(gè)語句拼接中的查詢可以加入條件查詢那先,比如我們要查詢學(xué)號(hào)是1003的成績(jī)
也就是下面這樣:
語句則如下:
SET @sql = NULL;
SET @stuid = '1003';
SELECT
GROUP_CONCAT(DISTINCT
CONCAT(
'SUM(IF(c.coursenm = ''',
c.coursenm,
''', s.scores, 0)) AS ''',
c.coursenm, ''''
)
) INTO @sql
FROM courses c;
SET @sql = CONCAT('SELECT st.stuid, st.stunm, ', @sql,
' FROM Student st
LEFT JOIN score s ON st.stuid = s.stuid
LEFT JOIN courses c ON c.courseno = s.courseno
WHERE st.stuid = ''', @stuid, '''
GROUP BY st.stuid');
PREPARE stmt FROM @sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
對(duì)比前面的語句农猬,我們可以看到在第二行的LEFT JOIN 后面我改了一些,還有就是前面的變量加了一個(gè)@stuid [ 注:這里的 @ 符號(hào)是在SQL語句定義變量習(xí)慣用法售淡,我個(gè)人理解應(yīng)該是用來區(qū)分吧斤葱!]
像上面的語句,我們?nèi)绻苯釉贛ySQL中操作是沒問題的揖闸,但如果用到項(xiàng)目中苦掘,那么這個(gè)語句顯然我們沒法用,而且我這次做的項(xiàng)目是結(jié)合使用MyBatis楔壤,大家都知道在MyBatis中的XML文件中可以自己寫SQL語句,但是這樣的很顯然我們沒法放到XML文件中惯驼。
而且最關(guān)鍵的是蹲嚣,這里不能用 If 條件递瑰,好比我們要判斷學(xué)號(hào)是否為空或者等于0再加上條件進(jìn)行查詢,可是這里不支持隙畜。
沒錯(cuò)就是下面這樣
SET @sql = NULL;
SET @stuid = '1003';
SET @courseno = 'C002';
SELECT
GROUP_CONCAT(DISTINCT
CONCAT(
'SUM(IF(c.coursenm = ''',
c.coursenm,
''', s.scores, 0)) AS ''',
c.coursenm, ''''
)
) INTO @sql
FROM courses c;
SET @sql = CONCAT('SELECT st.stuid, st.stunm, ', @sql,
' From Student st
LEFT JOIN score s ON st.stuid = s.stuid
LEFT JOIN courses c ON c.courseno = s.courseno');
IF @stuid IS NOT NULL AND @stuid != 0 THEN
SET @sql = CONCAT(@sql, ' WHERE st.stuid = ''', @stuid, '''');
END IF;
SET @sql = CONCAT(@sql, ' GROUP BY st.stuid');
PREPARE stmt FROM @sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
對(duì)抖部,我就是加上 if 之后人家就是不支持,就是這么任性议惰。
所以就要用到存儲(chǔ)過程啦慎颗,而且用存儲(chǔ)過程的好處是,方便我們調(diào)用言询,相當(dāng)于一個(gè)函數(shù)俯萎,其他學(xué)生也是類似的查詢不需再重復(fù)寫代碼,直接調(diào)存儲(chǔ)過程就好运杭,還能隨心所欲的加上if條件判斷夫啊,多么美好的事情,哈哈~辆憔。
那么說到存儲(chǔ)過程撇眯,這里該如何寫呢?
創(chuàng)建存儲(chǔ)過程的語句我就不多寫了虱咧,這里呢把上面的查詢語句直接放到創(chuàng)建存儲(chǔ)過程的begin和end直接就可以了熊榛,如下:
DELIMITER &&
drop procedure if exists SP_QueryData;
Create Procedure SP_QueryData(IN stuid varchar(16))
READS SQL DATA
BEGIN
SET @sql = NULL;
SET @stuid = NULL;
SELECT
GROUP_CONCAT(DISTINCT
CONCAT(
'SUM(IF(c.coursenm = ''',
c.coursenm,
''', s.scores, 0)) AS ''',
c.coursenm, ''''
)
) INTO @sql
FROM courses c;
SET @sql = CONCAT('SELECT st.stuid, st.stunm, ', @sql,
' From Student st
LEFT JOIN score s ON st.stuid = s.stuid
LEFT JOIN courses c ON c.courseno = s.courseno');
IF @stuid IS NOT NULL AND @stuid != 0 THEN
SET @sql = CONCAT(@sql, ' WHERE st.stuid = ''', @stuid, '''');
END IF;
SET @sql = CONCAT(@sql, ' GROUP BY st.stuid');
PREPARE stmt FROM @sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
END &&
DELIMITER ;
嗯,對(duì)比上面簡(jiǎn)單的SQL語句可以看出腕巡,這里使用了 if 語句玄坦,對(duì)學(xué)號(hào)進(jìn)行了判斷
不過這里要注意一點(diǎn),這里的if語句不像我們平時(shí)Java那種寫法逸雹,也就是下面
if(條件)
{
要執(zhí)行的語句塊
}
對(duì)营搅,在SQL里面的if語句不一樣,不需要括號(hào)啊什么的梆砸,就像直接說英文一樣
IF @stuid is not null and @stuid != 0 then
SET @sql = CONCAT(@sql, ' WHERE st.stuid = ''', @stuid, '''');
END IF;
嗯转质,就是這么簡(jiǎn)單明了,如果條件滿足帖世,那么就去調(diào)用休蟹,然后結(jié)束。
然后我們就可以傳參數(shù)調(diào)用這個(gè)SP了日矫。
CALL `SP_QueryData`('1001');
同樣得到我們想要的結(jié)果:好了赂弓,以上就是這次我在mysql進(jìn)行動(dòng)態(tài)行轉(zhuǎn)列的實(shí)現(xiàn)過程。