MySQL使用之三_GROUP_CONCAT、INSTR、JOIN

想要实现的需求:
1.blog、blog_with_tag、tag三张表,blog与tag为多对多关系,blog_with_tag为中间表。
2.blog主键为mid,tag主键为tid,blog_with_tag中进行mid、tid映射。
3.tag中tag字段为具体内容。
需求:在blog中添加tags字段,用于存储blog对应的所有tag具体内容;

一 GROUP_CONCAT

多行合并GROUP_CONCAT:https://www.cnblogs.com/zelzzz/p/7496884.html

GROUP_CONCAT可将多行数据拼接,找到blog对应的所有blog_with_tag中的记录后,可将这些记录值合并拼接。

以将blog对应的blog_with_tag中记录的tid合并为例:

SET group_concat_max_len=102400; 

UPDATE blog
SET blog.tags = (
	SELECT t.tags		// 多余,可精简;详见后文
	FROM (
		SELECT blog_with_tag.mid, GROUP_CONCAT(blog_with_tag.tid SEPARATOR '|') AS tags
		FROM blog_with_tag
		GROUP BY blog_with_tag.mid
	) t // 查询得到单个值(目标值)的table
	WHERE blog.mid = t.mid
); // set的赋值必须是一个值,而不是一个table;当select查询返回的是只有一个值的table,如果用括号框起来就是一个值了,也即:将一个值的table转为单个值

注意: 当GROUP_CONCAT拼接后字段数据超过默认最大长度会报错,可如上手动设置最大长度。

二 基于INSTR实现

注意: 实际上在此使用INSTR在逻辑上就是错误的,以下是为了记录解决过程和熟悉语法。

上述代码只在blog和blog_with_tag两张表之间的操作,只得到了blog对应的所有tid拼接成的tids,需要进一步结合tag表由tids得到具体tag内容。

INSTR使用教程:https://blog.csdn.net/qq_35548288/article/details/81771978

一个可能的思路是,判断tag中记录的tid是否在tids中,筛选出记录的tag值。问题在于:tag.tid是int类型,而tids是string类型,没法用in判断。发现上述教程,说是可以通过INSTR实现。

2.1 INSTR介绍

INSTR(str,substr):用于判断是否为子串

语法:
SELECT XXX
FROM ()t
WHERE INSTR((...),t.XXX)

2.2 INSTR实现

注意: 这里只是为了学习SQL语法;这里使用INSTR本身是错误的且执行效率极低,执行20h未出结果;

 版本一:
 
 UPDATE blog
 SET blog.tags = (
  SELECT t.tags		// 多余,可精简;
  FROM (
    SELECT GROUP_CONCAT(tag.tag SEPARATOR '|') as tags
  	FROM tag
    WHERE INSTR((
    	SELECT t.tags // 多余,可精简;
 	  	FROM (
 	     	SELECT blog_with_tag.mid, GROUP_CONCAT(blog_with_tag.tid) AS tags
 	    	FROM blog_with_tag
 	    	GROUP BY blog_with_tag.mid
	 	)t1
	  	WHERE blog.mid = t.mid),
	  tag.tid)
  )t
);

如注释所说,两步SELECT多余,可以直接获取GROUP_CONCAT的值,不需要先得到表后再获取字段值,即同时包含WHERE、GROUP BY子句。

版本二:

 UPDATE blog
 SET blog.tags = (
    SELECT GROUP_CONCAT(tag.tag SEPARATOR '|') as tags
  	FROM tag
    WHERE INSTR((
 	    SELECT blog_with_tag.mid, GROUP_CONCAT(blog_with_tag.tid) AS tags
 	    FROM blog_with_tag
 	    GROUP BY blog_with_tag.mid
	  	WHERE blog.mid = t.mid),
	  tag.tid)
  )t
);

进一步地,由以下教程可知WHERE在GROUP BY前面执行,在这里使用WHERE筛选后不需要再使用GROUP BY,WHERE的结果作为一整组即可,如下代码所示:

执行顺序: https://blog.csdn.net/menghuanzhiming/article/details/79886083

版本三:执行20h未出结果

UPDATE blog
SET blog.tags = (
 SELECT GROUP_CONCAT(tag.tag SEPARATOR '|')
 FROM tag
 WHERE INSTR((
 	SELECT GROUP_CONCAT(blog_with_tag.tid)
 	FROM blog_with_tag
 	WHERE blog.mid = blog_with_tag.mid
 ), tag.tid)
);

二 INSTR导致的错误

按照上面INSTR的使用方式,存在如下错误:

INSTR(str,substr)

SELECT COUNT(root.mid) FROM root  WHERE INSTR('123,456',123); // 98173

SELECT COUNT(root.mid) FROM root  WHERE INSTR('123,456',12);  // 98173

可见,INSTR是先将Int转为str后再判断,所以认为12'123,456'

其实需求为典型的联表查询,用JOIN即可实现。

三 基于JOIN实现

在一个表中找出字段值等于另一个表中某字段值的记录,该需求是典型的联表查询,使用JOIN可实现。

UPDATE blog
SET blog.tags = (
 SELECT t.tags 
 FROM (
 	SELECT GROUP_CONCAT(t1.tag) AS tags,t1.mid 
 	FROM (
 		SELECT blog_with_tag.mid,blog_with_tag.tid,tag.tag
 		FROM blog_with_tag
 		RIGHT JOIN tag
 		ON blog_with_tag.tid = tag.tid
 	)t1 
 	GROUP BY t1.mid
 )t 
 WHERE blog.mid = t.mid
);

以上代码又是先得到GROUP_CONCAT中间表后再SELECT字段,可优化如下:

blog表70w条记录,更新tags值9w条记录,耗时42s

UPDATE blog
SET blog.tags=(
	SELECT GROUP_CONCAT(t.tag SEPARATOR '|') 
	FROM (
		SELECT blog_with_tag.mid,tag.tag
		FROM blog_with_tag
		RIGHT JOIN tag
		ON blog_with_tag.tid = tag.tid
	)t 
	WHERE blog.mid = t.mid 
	GROUP BY t.mid
);

补充:
JOIN联表查询应用场景:以其他表多行的值为条件
A表、B表中均有name字段,要从A表选出在B表中存在的name值。也即选出A、B表中同时存在的name值,然而MySQL中没有判断存在的函数。可通过联表查询实现。

SELECT A.name AS A_name,B.name AS B_name
FROM A
RIGHT JOIN B
ON A.name = B.name;

若B表为中间表

SELECT A.name AS A_name,B.name AS B_name
FROM A
RIGHT JOIN (SELECT ...)B
ON A.name = B.name;

典型情况是:A表为多对多中间表,B表为含主键的表。此时,将A、B表拼成一个表。

四 收获

解决上述问题虽然绕了不少弯路,但对SQL语法有了更深理解,总结如下:

  1. SQL语句的写法:先按照如下模板开头,然后再详细写中间表的内容。另外,()和别名t之间不需要空格;
  2. SELECT嵌套时,分析清楚后,应从外层往里层写,而且从外往里依次将中间表命名为t、t1…;
  3. SELECT嵌套时,外层表的字段可在内层中直接使用;
模板一:
SELECT XXX
FROM  ()t
WHERE XXX

模板二:
UPDATE XXX
SET XXX.XXX=(

);
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
group_concatMySQL中的一个聚合函数,用于将多行数据按照指定的分隔符连接成一行返回。最简单的用法是将某个字段的值连接在一起,可以通过在group_concat函数中指定要连接的字段来实现。例如,使用group_concat(price)可以将表中price字段的值按照逗号分隔连接成一行返回。 如果想要对连接后的结果进行排序,可以使用Order BY子句,并在group_concat函数中指定排序字段以及排序顺序。例如,使用group_concat(price ORDER BY price DESC)可以将连接后的结果按照price字段降序排列。 另外,还可以使用DISTINCT关键字来去重,使用Separator关键字来指定连接的分隔符。例如,使用group_concat(DISTINCT price SEPARATOR ';')可以将连接后的结果按照分号分隔,并去除重复的值。 总结来说,group_concat函数可以根据指定的字段和条件将多行数据连接成一行返回,并可以通过排序、去重和指定分隔符来满足不同的需求。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* *3* [mysqlgroup_concat函数详解](https://blog.csdn.net/bobozai86/article/details/119858193)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 100%"] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值