concat函数

说实话,MySQL 的 concat 函数用起来还是很酷的。
我在做项目之前也经历过陷阱,所以印象特别深刻。
以数字处理为例。
我曾经编写过一个查询,想要将表中的数字列连接到字符串。
结果直接使用concat的时候出现了问题:数字自动转换成二进制格式,整个SQL以一堆乱码结束。

我记得是2 02 1 年的一个电商后端项目,专门链接订单号和状态描述。
我写的是concat(order_id, '-', status),但是我发现像order_id这样的数字列被直接当作二进制字符串处理。
经过大量调试,我终于发现我需要使用 concat(cast(order_id as char), '-', status) 显式转换类型。
说实话,这个细节挺烦人的,但是你得注意一下。

将NULL放入参数中更简单、更粗暴。
我有一个老客户数据库。
在一次数据迁移过程中,有人将某个字段修改为NULL。
结果整个concat函数的返回值直接变成了NULL。
当时运维小哥满头大汗,因为依赖这个字段的业务逻辑有一半被堵住了。
之后我只能手动填写数据并再次运行SQL。
这个教训太深刻了,现在在写SQL之前,我会检查是否有地方可以在参数中传递NULL。

有一个关于将数字转换为字符串的小技巧可以分享。
如果您不希望数字前面有额外的 0,例如订单号 1 00 显示为 '1 00' 而不是 '000001 00',则需要使用 concat(cast(order_id as char(6 )), '-') 并且可以通过指定长度来控制格式。
上次我在开发报告系统时就这样做了。
我和产品经理进行了长时间的讨论。
他坚持要以统一的格式显示六位数字,否则报告页面就会完全对齐。

二进制处理也相当复杂。
虽然很少用,但是遇到了就有问题了。
例如,当您链接 BLOB 类型的字段时,结果将成为二进制字符串,您可以使用 concat(char_col) 将其转换为正常显示。
我记得有一次我正在调试一个存储过程。
参数与普通的blob和字符串混合在一起,返回值直接显示一系列十六进制代码。
当时确实很迷茫。

最后说一个具体的场景。
有义务报告。
用户名、注册时间和订单金额必须一起出现,但金额前面不能有货币符号。
我使用 concat(username, 'registered at',cast(registration_date as char), 'order amount',cast(amount asdecimal(1 0,2 ))) 编写它。
当时测试妹子抱怨太长了,我就说没办法,金额精度要保持在两位数。
该查询已经运行了几个月,没有出现任何问题。
只是最近的系统更新发现了一个小bug,但与concat无关。
这纯粹是巧合。

我自己没有跑过,但我记得有数据表明大约有5 %的人使用concat,但实际应用场景可能会更高。
我已经遇到过3 次以上的数字转换问题了好几次,而且每次都需要很长时间。
所以建议初学者记住:数字参数前加cast,NULL参数需要处理。

group_concat的用法

哎,这个GROUP_CONCAT,我刚接手电商公司的数据库的时候,确实头疼了一段时间。
2 01 8 年,该公司正在举办一场活动,列出消费者购买的所有产品的名称。
一个用户对应多个产品。
您无法直接使用 SELECT 删除列表。
您需要使用 GROUP_CONCAT。

我当时就想,这个功能听起来真不错,可以对一列数据进行排序。
一旦结果被应用,亲爱的,默认是 1 02 4 个字符。
当很多用户购买太多产品时,结果直接被切断,所有后续购买都会丢失。
当顾客抱怨时,他们会来找我,说为什么我买的产品不够多?我当时脸就绿了,赶紧改配置,加大group_concat_max_len。
我记得是换算成十万字符的,我都勉强应付了。

下次,在 2 02 0 年,我们将创建一个报告来列出该部门所有员工的电子邮件地址并发送通知。
结果,当我使用 GROUP_CONCAT 时,我发现很多员工都有相同的电子邮件地址,而且重复很烦人。
幸运的是,我的大脑发生了变化,我添加了 DISTINCT,删除了重复,然后连接起来,就完成了。

哦,对了,还有排序和分隔符。
之前有一个需求,按照订单金额从大到小列出用户购买的所有产品。
我使用了 GROUP_CONCAT 加上 ORDER BY 子句,以逗号分隔。
该功能非常易于使用,尤其是在处理聚合数据时。
但是,您需要注意性能。
我发现过一次。
数据量太大,一直用GROUP_CONCAT烧服务器CPU。
那次我差点就被解雇了。
然后我意识到我需要优化SQL,不能仅仅依靠GROUP_CONCAT来反击。