MySQL中concat()、concat_ws()、group_concat()函數

首先我們來建立一個測試的表和數據,代碼如下

CREATE TABLE `per` (
   `id` int(11) NOT NULL AUTO_INCREMENT,
   `pname` varchar(50) DEFAULT NULL,
   `page` int(11) DEFAULT NULL,
   `psex` varchar(50) DEFAULT NULL,
   `paddr` varchar(50) DEFAULT NULL,
   PRIMARY KEY (`id`)
 ) ENGINE=InnoDB AUTO_INCREMENT=13 DEFAULT CHARSET=utf8mb4;
 
 
 INSERT INTO `per` VALUES ('1', '王小華', '30', '男', '北京');
 INSERT INTO `per` VALUES ('2', '張文軍', '24', '男', '上海');
 INSERT INTO `per` VALUES ('3', '羅敏', '19', '女', '重慶');
 INSERT INTO `per` VALUES ('4', '張建新', '32', '男', '重慶');
 INSERT INTO `per` VALUES ('5', '劉婷', '26', '女', '成都');
 INSERT INTO `per` VALUES ('6', '劉小亞', '22', '女', '重慶');
 INSERT INTO `per` VALUES ('7', '王建軍', '22', '男', '貴州');
 INSERT INTO `per` VALUES ('8', '謝濤', '28', '男', '海南');
 INSERT INTO `per` VALUES ('9', '張良', '26', '男', '上海');
 INSERT INTO `per` VALUES ('10', '黎記', '17', '男', '貴陽');
 INSERT INTO `per` VALUES ('11', '趙小麗', '26', '女', '上海');
 INSERT INTO `per` VALUES ('12', '張三', null, '女', '北京');

concat()函數

首先我們先學一個函數叫concat()函數, 這個函數非常簡單

功能:就是將多個字符串連接成一個字符串

語法:concat(字符串1, 字符串2,...) 字符串參數用逗號隔開!

返回值: 結果為連接參數產生的字符串,如果有任何一個參數為null,則返回值為null。

案例1

select concat('重慶','北京','上海');

效果如下圖: 是不是覺得很簡單 很直觀呢!


image

案例2

這有一張表

+----+-----------+------+------+--------+
 | id | pname     | page | psex | paddr  |
 +----+-----------+------+------+--------+
 |  1 | 王小華    |   30 | 男   | 北京   |
 |  2 | 張文軍    |   24 | 男   | 上海   |
 |  3 | 羅敏      |   19 | 女   | 重慶   |
 |  4 | 張建新    |   32 | 男   | 重慶   |
 |  5 | 劉婷      |   26 | 女   | 成都   |
 |  6 | 劉小亞    |   22 | 女   | 重慶   |
 |  7 | 王建軍    |   22 | 男   | 貴州   |
 |  8 | 謝濤      |   28 | 男   | 海南   |
 |  9 | 張良      |   26 | 男   | 上海   |
 | 10 | 黎記      |   17 | 男   | 貴陽   |
 | 11 | 趙小麗    |   26 | 女   | 上海   |
 | 12 | 張三      | NULL | 女   | 北京   |
 +----+-----------+------+------+--------+
 
 #-- 執(zhí)行如下語句
 select concat(pname,page,psex) from per;
 
 #--結果
 +-------------------------+
 | concat(pname,page,psex) |
 +-------------------------+
 | 王小華30男              |
 | 張文軍24男              |
 | 羅敏19女                |
 | 張建新32男              |
 | 劉婷26女                |
 | 劉小亞22女              |
 | 王建軍22男              |
 | 謝濤28男                |
 | 張良26男                |
 | 黎記17男                |
 | 趙小麗26女              |
 | NULL                    |
 +-------------------------+
 
 #--為什么會有一條是NULL呢?  
 #--那是因為第12條數據中的page字段為空,根據有一個字段為空結果就為NULL的理論推導出 查詢出的最后一條記錄為NULL!

但是大家一定會發(fā)現雖然連在一起顯示了 但是彼此沒有分隔符啊 看起來好難受 對不對? 所以接下來我們就來講講衍生出來的 concat_ws()函數

concat_ws()函數

功能:concat_ws()函數 和 concat()函數一樣,也是將多個字符串連接成一個字符串,但是可以指定分隔符!

語法:concat_ws(separator, str1, str2, ...) 第一個參數指定分隔符, 后面依舊是字符串

separator就是分隔符字符!

需要注意的是分隔符不能為null,如果為null,則返回結果為null。

案例代碼:

select concat_ws(',',pname,page,psex) from per;  
 #--以逗號分割 結果如下
 +--------------------------------+
 | concat_ws(',',pname,page,psex) |
 +--------------------------------+
 | 王小華,30,男                   |
 | 張文軍,24,男                   |
 | 羅敏,19,女                     |
 | 張建新,32,男                   |
 | 劉婷,26,女                     |
 | 劉小亞,22,女                   |
 | 王建軍,22,男                   |
 | 謝濤,28,男                     |
 | 張良,26,男                     |
 | 黎記,17,男                     |
 | 趙小麗,26,女                   |
 | 張三,女                        |
 +--------------------------------+
 
 #--把分隔符指定為null,結果全部變成了null
 
 select concat_ws(null,pname,page,psex) from per;   #--錯誤的
 +---------------------------------+
 | concat_ws(null,pname,page,psex) |
 +---------------------------------+
 | NULL                            |
 | NULL                            |
 | NULL                            |
 | NULL                            |
 | NULL                            |
 | NULL                            |
 | NULL                            |
 | NULL                            |
 | NULL                            |
 | NULL                            |
 | NULL                            |
 | NULL                            |
 +---------------------------------+

group_concat()函數

接下來就要進入我們本文的主題了,group_concat()函數, 理解了上面兩個函數的作用和用法 就對理解group_concat()函數有很大幫助了!

功能:將group by產生的同一個分組中的值連接起來,返回一個字符串結果。

語法:group_concat( [distinct] 要連接的字段 [order by 排序字段 asc/desc ] [separator '分隔符'] )

注意: 中括號是可選的

分析: 通過使用distinct可以排除重復值;如果希望對結果中的值進行排序,可以使用order by子句;separator是一個字符串值,缺省為一個逗號。

以下我準備了幾個案例 小伙伴們可以選擇性的去閱讀 并且把代碼復制到MySQL中執(zhí)行以下就可以知道用法了!

重點注意

1.group_concat只有與group by語句同時使用才能產生效果 所以使用 GROUP_CONCAT()函數必須對源數據進行分組,否則所有數據會被合并成一行
2.需要將拼接的結果去重的話,可與DISTINCT結合使用即可

案例1

需求: 比如我們要查在重慶的有哪些人? 并且把這些人的名字用 '-' 字符分隔開 然后顯示出來, SQL語句如下

#--這里就用到了 : 取出重復、顯示排序、 定義分隔字符
 select 
     paddr,
     group_concat(distinct pname order by pname desc separator '-') as '人' 
 from per 
 group by paddr;
 
 #--結果為:
 +--------+----------------------------+
 | paddr  | 人                         |
 +--------+----------------------------+
 | 上海   | 趙小麗-張良-張文軍         |
 | 北京   | 王小華-張三                |
 | 成都   | 劉婷                       |
 | 海南   | 謝濤                       |
 | 貴州   | 王建軍                     |
 | 貴陽   | 黎記                       |
 | 重慶   | 羅敏-張建新-劉小亞         |
 +--------+----------------------------+
 
 #--有多個的自然會被用字符分隔連接起來,只有一個人的就沒有什么變化!直接顯示

案例2

需求: 比如我們要查在重慶的有哪些人? 并且把這些人的名字用逗號隔開,

以上需求跟上面的案例1 差不多 我們就加一個效果, 也就是顯示出來的名字前面把id號 也加上

#--顯示出來每一個名字所對應的id號  這里我們結合了group_concat()函數 和 concat_ws()函數, 
 
 select 
     paddr,
     group_concat(concat_ws('-',id,pname) order by id asc) as '人' 
 from per 
 group by paddr;
 
 #--顯示結果
 +--------+-----------------------------------+
 | paddr  | 人                                |
 +--------+-----------------------------------+
 | 上海   | 2-張文軍,9-張良,11-趙小麗         |
 | 北京   | 1-王小華,12-張三                  |
 | 成都   | 5-劉婷                            |
 | 海南   | 8-謝濤                            |
 | 貴州   | 7-王建軍                          |
 | 貴陽   | 10-黎記                           |
 | 重慶   | 3-羅敏,4-張建新,6-劉小亞          |
 +--------+-----------------------------------+

注意:

1.MySQL中函數是可以嵌套使用的
2.一般使用group_concat()函數,必須是存在group by 分組的情況下 才能使用這個函數

案例3

我們再來看一個案例, 首先我們準備以下測試數據

準備一個student學生表、MySQL代碼如下

#-- student
 CREATE TABLE `student` (
   `id` int(11) NOT NULL AUTO_INCREMENT,   #--id
   `stuName` varchar(22) DEFAULT NULL,     #--學生姓名
   `course` varchar(22) DEFAULT NULL,      #--學習科目
   `score` int(11) DEFAULT NULL,           #--學分
   PRIMARY KEY (`id`)                      #--設置主鍵
 ) ENGINE=InnoDB AUTO_INCREMENT=10 DEFAULT CHARSET=utf8;  #--設置表引擎  自動遞增起始值 默認編碼格式
 
 -- ----------------------------
 -- 插入以下數據
 -- ----------------------------
 INSERT INTO `student`(stuName,course,score) VALUES ('張三', '語文', '91');
 INSERT INTO `student`(stuName,course,score) VALUES ('張三', '數學', '90');
 INSERT INTO `student`(stuName,course,score) VALUES ('張三', '英語', '87');
 INSERT INTO `student`(stuName,course,score) VALUES ('李四', '語文', '79');
 INSERT INTO `student`(stuName,course,score) VALUES ('李四', '數學', '95');
 INSERT INTO `student`(stuName,course,score) VALUES ('李四', '英語', '80');
 INSERT INTO `student`(stuName,course,score) VALUES ('王五', '語文', '77');
 INSERT INTO `student`(stuName,course,score) VALUES ('王五', '數學', '81');
 INSERT INTO `student`(stuName,course,score) VALUES ('王五', '英語', '89');
 
 
 #--建立好之后 數據如下顯示:
 mysql> select * from student;
 +----+---------+--------+-------+
 | id | stuName | course | score |
 +----+---------+--------+-------+
 | 10 | 張三    | 語文   |    91 |
 | 11 | 張三    | 數學   |    90 |
 | 12 | 張三    | 英語   |    87 |
 | 13 | 李四    | 語文   |    79 |
 | 14 | 李四    | 數學   |    95 |
 | 15 | 李四    | 英語   |    80 |
 | 16 | 王五    | 語文   |    77 |
 | 17 | 王五    | 數學   |    81 |
 | 18 | 王五    | 英語   |    89 |
 +----+---------+--------+-------+

建立好表和數據之后 我們就來繼續(xù)使用group_concat()函數 加深以下印象!

需求1: 以stuName學生名稱分組,把得分數score字段的值打印在一行,逗號分隔(默認) SQL如下

 select stuName, GROUP_CONCAT(score) as '當前這個學生的得分數' from student GROUP BY stuName;
 
 #--運行結果如下
 mysql> select stuName, GROUP_CONCAT(score) as '當前這個學生的得分數' from student GROUP BY stuName;
 +---------+--------------------------------+
 | stuName | 當前這個學生的得分數           |
 +---------+--------------------------------+
 | 張三    | 91,90,87                       |
 | 李四    | 79,95,80                       |
 | 王五    | 77,81,89                       |
 +---------+--------------------------------+

需求2: 那么根據上面的結果 我們看到分數是出來了 但是不知道是什么科目分數 那么我們還要把科目也連起來顯示,并且分數還是從小到大,我們應該怎么做呢 ? 其實很簡單的啦 SQL如下

select stuName, GROUP_CONCAT(concat_ws('=',course,score) order by score asc) as '當前這個學生的得分數' from student GROUP BY stuName;

#--執(zhí)行結果如下
+---------+--------------------------------+
| stuName | 當前這個學生的得分數           |
+---------+--------------------------------+
| 張三    | 英語=87,數學=90,語文=91        |
| 李四    | 語文=79,英語=80,數學=95        |
| 王五    | 語文=77,數學=81,英語=89        |
+---------+--------------------------------+
#-- 這樣顯示是不是覺得更加清楚了呢! 

需求3: 這里再給小伙伴深入一個問題 那么我們現在要查詢出 語文、數學、外語 三門課的最低分,還有哪個學生考的? 現在應該怎么寫呢?

方法1

#--首先我們可以把這個問題拆分成兩個部分
#--1.就是找出語文、數學、外語 三門課的最低分 這一步還是比較簡單的我們可以使用分組查詢就可以解決

#--分析問題后得出SQL方案 按照科目進行分組查詢  然后使用聚合函數篩選出最小的得分數, 顯示對應科目字段 這樣就得出了三門課的最低分

SELECT min(score),course FROM student GROUP BY course;
#--那么查詢出的結果如下
+------------+--------+
| min(score) | course |
+------------+--------+
|         81 | 數學   |
|         80 | 英語   |
|         77 | 語文   |
+------------+--------+

#--那么接下來我們要考慮的是如何找到是哪個學生考的!?
#--這里我們可以使用in() 的包含+ 子查詢的方式來 根據上面SQL的結果 來進行匹配包含查詢 學生名 
SELECT stuName,score,course from student where (score,course) in(SELECT min(score),course FROM student GROUP BY course);

#--結果如下
+---------+-------+--------+
| stuName | score | course |
+---------+-------+--------+
| 李四    |    80 | 英語   |
| 王五    |    77 | 語文   |
| 王五    |    81 | 數學   |
+---------+-------+--------+


問題分析

1.這里的重點就在于子查詢的使用 上面已經用一句SQL查詢出了 三門課的最低分和科目 那么我們就可以列用這個結果集來 當做另外一句SQL所要查詢條件 !
2.where 后面跟的是一個圓括號 里面寫的是 分數和科目兩個字段,用來匹配in() 里面的子查詢結果 可能這里有些新手小伙伴并沒有見過這樣寫 現在應該清楚了

方法2

#--我們也可以用以下SQL語句來實現 ,性能上比上面好一點點!
SELECT  g.`id`,g.`course`,g.`score`,g.`stuName`FROM (SELECT course, SUBSTRING_INDEX(GROUP_CONCAT(score ORDER BY score ASC),   ',',1) AS score FROM student GROUP BY course) AS t LEFT JOIN student AS g ON (t.course = g.`course` AND t.score = g.`score`)
    



#--小提示:SUBSTRING_INDEX() 函數是提取的連接字符中的第一個


#--簡單的說 先連接好分數字段中的得分默認用逗號 再從分數連接字符中提取第一個出來, 
#--首先我們要得到每一個科目中最小的分數 我們可以分析出如下SQL,  
#--這里的分組條件還是以科目進行分組, 分組之后還是GROUP_CONCAT()函數用逗號連接起相對應的所有分數,然后用SUBSTRING_INDEX()函數提取連接字符中的第一個字符作為結果
SELECT course,SUBSTRING_INDEX(GROUP_CONCAT(score ORDER BY score ASC),',',1) AS score FROM student GROUP BY course;
#--結果如下
+--------+-------+
| course | score |
+--------+-------+
| 數學   | 81    |
| 英語   | 80    |
| 語文   | 77    |
+--------+-------+

#--我們可以把這個結果 想象成一張?zhí)摂M表取一個別名 t, 現在t這個是一個臨時的表,我們要查詢id,科目,分數,姓名, 就在前面加上需要的字段,注意別名
#--然后再使用左連接篩選出 對應的結果
SELECT  g.`id`,g.`course`,g.`score`,g.`stuName`FROM
(SELECT course,SUBSTRING_INDEX(GROUP_CONCAT(score ORDER BY score ASC),',',1) AS score FROM student GROUP BY course) as t
LEFT JOIN student AS g ON (t.course = g.`course` AND t.score = g.`score`)  #--left join 來顯示出符合條件的結果 也就是用上面查詢出來的結果來對應條件

#--結果如下
+------+--------+-------+---------+
| id   | course | score | stuName |
+------+--------+-------+---------+
|   15 | 英語   |    80 | 李四    |
|   16 | 語文   |    77 | 王五    |
|   17 | 數學   |    81 | 王五    |
+------+--------+-------+---------+



    

案例4

我們來簡單的準備一個商品表吧 , 代碼如下

#-- goods
CREATE TABLE `goods` (
  `id` int(11) NOT NULL AUTO_INCREMENT,   #--id
  `price` varchar(22) DEFAULT NULL,       #--商品價格
  `goods_name` varchar(22) DEFAULT NULL,  #--商品名稱
  PRIMARY KEY (`id`)                      
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;  #--設置表引擎  自動遞增起始值 默認編碼格式

-- ----------------------------
-- 插入以下數據
-- ----------------------------
INSERT INTO `goods`(price,goods_name) VALUES (10.00, '皮包');
INSERT INTO `goods`(price,goods_name) VALUES (20.00, '圍巾');
INSERT INTO `goods`(price,goods_name) VALUES (30.00, '圍巾');
INSERT INTO `goods`(price,goods_name) VALUES (40.00, '游戲機');
INSERT INTO `goods`(price,goods_name) VALUES (60.00, '皮包');
INSERT INTO `goods`(price,goods_name) VALUES (80.00, '游戲機');
INSERT INTO `goods`(price,goods_name) VALUES (220.00, '游戲機');
INSERT INTO `goods`(price,goods_name) VALUES (780.00, '圍巾');
INSERT INTO `goods`(price,goods_name) VALUES (560.00, '游戲機');
INSERT INTO `goods`(price,goods_name) VALUES (30.00, '皮包');


需求1: 以 商品名稱分組,把price字段的值在一行打印出來,分號分隔

select goods_name,group_concat(price) from goods group by goods_name;

需求2: 以 商品名稱分組,把price字段的值在一行打印出來,分號分隔 并且去除重復冗余的價格字段的值

select goods_name,group_concat(distinct price) from goods group by goods_name;


需求3: 以 商品名稱分組,把price字段的值在一行打印出來,分號分隔 去除重復冗余的價格字段的值 并且排序 從小到大

select goods_name,group_concat(distinct price order by price desc) from goods group by goods_name;  #--錯誤的

select goods_name,group_concat(distinct price order by price+1 desc) from goods group by goods_name; #--正確的


#--注意以上存在隱式數據類型轉換 如果不這樣轉換排序出來的結果是錯誤的   , 因為我保存price價格的字段是varchar類型的

案例5

我們再結合group_concat()函數來做一個多表查詢的案例

準備 三張 測試數據表: 用戶表[user]、水果表[fruit]、用戶喜歡哪些水果的表[user_like]

首先 建立用戶表[user] SQL語句代碼如下

#-- user
CREATE TABLE `user` (
  `id` int(11) NOT NULL AUTO_INCREMENT,   #--id
  `username` varchar(22) DEFAULT NULL,    #--用戶名
  PRIMARY KEY (`id`)                      
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;  #--設置表引擎  自動遞增起始值 默認編碼格式

#--插入測試數據
INSERT INTO `user`(username) VALUES ('張三');
INSERT INTO `user`(username) VALUES ('李四');
INSERT INTO `user`(username) VALUES ('王文玉');


建立水果表[fruit] SQL語句代碼如下

#-- fruit
CREATE TABLE `fruit` (
  `id` int(11) NOT NULL AUTO_INCREMENT,   #--id
  `fruitname` varchar(22) DEFAULT NULL,   #--水果名稱
  PRIMARY KEY (`id`)                      
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;  #--設置表引擎  自動遞增起始值 默認編碼格式


#--插入測試數據
INSERT INTO `fruit`(fruitname) VALUES ('西瓜');
INSERT INTO `fruit`(fruitname) VALUES ('蘋果');
INSERT INTO `fruit`(fruitname) VALUES ('芒果');
INSERT INTO `fruit`(fruitname) VALUES ('梨');
INSERT INTO `fruit`(fruitname) VALUES ('葡萄');

建立 用戶喜愛表 [user_like]
但是建立這個表的時候 跟前面兩個表有所不同,小伙們們首先要搞清楚 這個表是一個什么用來干啥的表

分析清楚這個表的關系, 因為是用戶喜歡哪些水果的表 那么 一個水果可以被多個用戶所喜歡對吧? 反過來說一個用戶也可以喜歡多個水果吧 對吧 那么這里是一個什么關系呢 ?? 很明顯是一個 多對多的關系!

所以建立這個表 我們就可以使用 用戶的id 來對應 水果的id 就可以實現一個中間連接多對多的表了

SQL語句代碼如下:

#-- fruit
CREATE TABLE `user_like` (
  `id` int(11) NOT NULL AUTO_INCREMENT,   #--id
  `user_id` int,                          #--用戶的id號
  `fruit_id` int,                         #--水果的id號
  CONSTRAINT user_like PRIMARY KEY (id,user_id,fruit_id)  #--定義聯(lián)合主鍵  讓每一條記錄唯一
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;  #--設置表引擎  自動遞增起始值 默認編碼格式


#--測試數據
INSERT INTO `user_like`(user_id,fruit_id) VALUES (1,1);  #--這就代表用戶表中id號為1的用戶  喜歡fruit表中id號為1的水果
INSERT INTO `user_like`(user_id,fruit_id) VALUES (1,2);  #--這就代表用戶表中id號為1的用戶  喜歡fruit表中id號為2的水果
INSERT INTO `user_like`(user_id,fruit_id) VALUES (1,3);  #--這就代表用戶表中id號為1的用戶  喜歡fruit表中id號為3的水果
INSERT INTO `user_like`(user_id,fruit_id) VALUES (2,3);  #--這就代表用戶表中id號為2的用戶  喜歡fruit表中id號為3的水果
INSERT INTO `user_like`(user_id,fruit_id) VALUES (2,4);  #--這就代表用戶表中id號為2的用戶  喜歡fruit表中id號為4的水果
INSERT INTO `user_like`(user_id,fruit_id) VALUES (2,5);  #--這就代表用戶表中id號為2的用戶  喜歡fruit表中id號為5的水果
INSERT INTO `user_like`(user_id,fruit_id) VALUES (3,5);  #--這就代表用戶表中id號為3的用戶  喜歡fruit表中id號為5的水果
INSERT INTO `user_like`(user_id,fruit_id) VALUES (3,1);  #--這就代表用戶表中id號為3的用戶  喜歡fruit表中id號為1的水果
INSERT INTO `user_like`(user_id,fruit_id) VALUES (3,2);  #--這就代表用戶表中id號為3的用戶  喜歡fruit表中id號為2的水果
#--  以此類推...

好了 現在數據 和 表我們都已經準備好了 , 那么 接下來 我們就要開始進行 GROUP_CONCAT()函數的使用了

需求: 查出每個用戶喜歡的水果都有哪些!

#--查詢SQL如下
select u.username,group_concat(f.fruitname) from user_like as c inner join user as u on c.user_id=u.id inner join  fruit as f on c.fruit_id=f.id group by c.user_id; 

#--結果如下
+-----------+---------------------------+
| username  | group_concat(f.fruitname) |
+-----------+---------------------------+
| 張三      | 芒果,蘋果                 |
| 李四      | 梨,芒果,葡萄              |
| 王文玉    | 西瓜,葡萄,蘋果            |
+-----------+---------------------------+

博客園:https://www.cnblogs.com/leepandar/
CSDN: https://blog.csdn.net/Devilli0310
騰訊云:https://cloud.tencent.com/developer/column/78320
掘金:https://juejin.cn/user/1433418894949143

?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

友情鏈接更多精彩內容