记录一次在Mysql中行转列的经历

最近工作上遇到一个需求,类似将每位客户到访记录的访问打卡记录统计出来。a表关键字段如下:

id

customer_id

customer_name

morning

afternoon

123456

123456

测试

83514,111547

134512,170230

后面两个字段分别记录客户上午下午的到访时间,现要通过到访时间去查询到访时的照片取证。熟悉的小伙伴看到可能就想到,咱们把morning和afternoon两个字段合并起来in一次不就可以了么。所以就有了如下的SQL:(假设b表为照片信息表)

SELECT t1.id,t1.customer_id,t1.customer_name,t2.picture_url FROM a t1, b t2 WHERE FIND_IN_SET(t2.time, CONCAT(IF(t2.morning='','',IF(t2.afternoon='',t2.morning, CONCAT(t2.morning,','))),IF(t2.afternoon='','',t2.afternoon)));

诚然用上面的SQL对付小数据量的表完全有效,但是数据量上万就完犊子了,哪怕a表针对morning、afternoon制定索引也于事无补。毕竟where条件中的函数会导致索引失效嘛,FIND_IN_SET函数会无法命中索引~ 现实这么残酷,我当时直接就

那是不可能的,于是有了这么一段SQL:

SELECT 到访ID, 客户ID, 客户name, t2.picture_url
FROM (SELECT tmp.*,
      SUBSTRING_INDEX(SUBSTRING_INDEX(tmp.ma, ',', b.help_topic_id ), ',',-1) `time` 
     FROM (SELECT t.id `到访ID`, t.dm_id `客户ID`, t.dm_name `客户name`, 
            concat( t.morning, ',', t.afternoon ) `ma` FROM a t) tmp
     JOIN help_topic b ON b.help_topic_id <= (LENGTH(tmp.time)-LENGTH(REPLACE(tmp.time, ',', '' )) + 1 )) t1
INNER JOIN b t2 ON t1.time = t2.time 

通过上述SQL可以将a表数据结构转换成:

id

customer_id

customer_name

time

123456

123456

测试

83514

123456

123456

测试

111547

123456

123456

测试

134512

123456

123456

测试

170230

这样将行转为列是不是就可以一一对应了呢~

急于使用的小伙伴拿熟肉去改改就行,接下来笔者对于SQL中部分知识点进行梳理:

图示中很清晰的告知使用者FIND_IN_SET函数可以将字段中类似于set且分隔符只能为逗号(半角)的值进行分割,然后对目标字符串进行查找返回目标字符串所在位置。

PS:如果字段是其他分隔符,请使用replace函数将分隔符改为逗号。

  • TableA,TableB

如上所示的表连接方式是交叉连接,它类似于:

select * from TableA cross join TableB ~ select * from TableA,TableB

结果:TableA的行数*TableB的行数的结果集。

可以将SUBSTRING_INDEX函数理解为两个函数,一个是查找delim定界符,一个是截取字符串。从上图中count为2时可以知道,SUBSTRING_INDEX先查找从左往右数的第二个定界符,找到之后从左往右截取字符串直到搜寻到的定界符(不包含定界符)。

此方法在SQL中的作用请查看第五点

  • help_topic表

Mysql中有四大help table:help_topic、help_relation、help_category、help_keyword。

翻了翻Mysql的官方文档没看到详尽解释,下面的help语法概述摘抄自

(作者:沃趣科技 链接:https://zhuanlan.zhihu.com/p/42076758)

help 语法支持3种模式的匹配查询:查看所有主题顶层类别或子类别、查看帮助主题下的关键字、使用给定主题下的唯一关键字查看帮助信息,这些信息分表保存在 help_category、help_topic、help_keyword表,help_relation表存放help_topic与help_keyword表中信息的映射信息。下面将针对这几张表的基础知识进行简单的科普。

(1)help_category

该表提供查询帮助主题的类别信息,每一个类别分别对应着N个帮助主题名或者主题子类别名,通过查询表中的信息我们也可以看出来,如下:

root@localhost : mysql 01:10:59> select * from help_category;
+------------------+-----------------------------------------------+--------------------+-----+
| help_category_id | name                                          | parent_category_id | url |
+------------------+-----------------------------------------------+--------------------+-----+
|                1 | Geographic                                    |                  0 |     |
|                2 | Polygon properties                            |                 35 |     |
......
|               39 | Functions                                     |                 36 |     |
|               40 | Data Definition                               |                 36 |     |
+------------------+-----------------------------------------------+--------------------+-----+
40 rows in set (0.00 sec)

表字段含义

  • help_category_id:帮助主题名称或子类别名称在表中的记录ID

  • name:帮助主题类别名称或字类别名称

  • parent_category_id:父主题类别名称在表中的记录ID,一些主题类别具有子主题类别,例如:绝大多数的主题类别其实是Contents类别的子类别(且是顶层类别,也是一级父类别),还有一部分是Geographic Features 类别的子类别(二级父类别),一部分是Functions的子类别(二级父类别)

  • url :对应在MySQL 官方手册中的链接地址

(2)help_keyword

该表提供查询与帮助主题相关的关键字字符串信息,如下:

root@localhost : mysql 01:12:07> select * from help_keyword limit 5;
+-----------------+---------+
| help_keyword_id | name    |
+-----------------+---------+
|             681 | (JSON   |
|             486 | ->      |
|             205 | ->>     |
|             669 | <>      |
|             521 | ACCOUNT |
+-----------------+---------+
5 rows in set (0.00 sec)

表字段含义

  • help_keyword_id:帮助关键字名称在表中记录对应的ID

  • name:帮助关键字字符串

(3)help_relation

该表提供查询帮助关键字信息和主题详细信息之间的映射,用于关联查询help_keyword与help_topic表,如下:

root@localhost : mysql 01:13:09> select * from help_relation limit 5;
+---------------+-----------------+
| help_topic_id | help_keyword_id |
+---------------+-----------------+
|             0 |               0 |
|           535 |               0 |
|           294 |               1 |
|           277 |               2 |
|             2 |               3 |
+---------------+-----------------+
5 rows in set (0.00 sec)

表字段含义

  • help_topic_id:帮助主题详细信息ID,该ID值与help_topic表中的help_topic_id相等

  • help_keyword_id:帮助主题关键字信息ID,该ID值与help_keyword表中的help_keyword_id相等

(4)help_topic

该表提供查询帮助主题给定关键字的详细内容(详细帮助信息),如下:

root@localhost : mysql 01:13:31> select * from help_topic limit 1\G;
*************************** 1. row ***************************
help_topic_id: 0
        name: JOIN
help_category_id: 28
 description: MySQL supports the following JOIN syntaxes for the table_references
part of SELECT statements and multiple-table DELETE and UPDATE
statements:
table_references:
escaped_table_reference [, escaped_table_reference] ...
escaped_table_reference:
table_reference
| { OJ table_reference }
......
         url: http://dev.mysql.com/doc/refman/5.7/en/join.html
1 row in set (0.00 sec)

表字段含义

  • help_topic_id:帮助主题详细信息在表记录中对应的ID

  • name:帮助主题给定的关键字名称,与help_keyword表中的name字段值相等

  • help_category_id:帮助主题类别ID,与help_category表中的help_category_id字段值相等

  • description:帮助主题的详细信息(这里就是我们通常查询帮助信息真正想看的内容,例如:告诉我们某某语句如何使用的语法与注意事项等)

  • example:帮助主题的示例信息(这里告诉我们某某语句如何使用的示例)

  • url:该帮助主题对应在MySQL官方在线手册中的URL链接地址

  • LENGTH(tmp.time)-LENGTH(REPLACE(tmp.time, ',', '' )) + 1

意为获取字段长度减去去除定界符长度后加一的长度,这其实是为了与help_topic_id对比获取字段

原始SQL:

JOIN help_topic b ON b.help_topic_id <= (LENGTH(tmp.time)-LENGTH(REPLACE(tmp.time, ',', '' )) + 1 )) t1

首先加一是为了抵消咱们通过concat拼接逗号导致定界符加一的问题

举一个例子:

假设morning和afternoon都为空的话,concat函数将字段组合之后值变为( , )。原始SQL执行之后会得到:

id

customer_id

customer_name

ma

time

help_topic_id

123456

123456

测试

,

1

123456

123456

测试

,

2

假设morning为空,afternoon不为空,concat函数将字段组合之后值变为(83514, )。原始SQL执行之后会得到:

id

customer_id

customer_name

ma

time

help_topic_id

123456

123456

测试

83514,

83514

1

123456

123456

测试

83514,

2

假设morning不为空为空,afternoon为空,concat函数将字段组合之后值变为(,111547)。原始SQL执行之后会得到:

id

customer_id

customer_name

ma

time

help_topic_id

123456

123456

测试

,111547

1

123456

123456

测试

,111547

111547

2

假设morning不为空,afternoon不为空,concat函数将字段组合之后值变为(83514,111547)。原始SQL执行之后会得到:

id

customer_id

customer_name

ma

time

help_topic_id

123456

123456

测试

83514,111547

83514

1

123456

123456

测试

83514,111547

111547

2

由上可知SQL通过使用help_topic表存储由特定定界符分割的字段,如果没有值的话就会返回空字符串。这里也就告诉我们下面的SQL其实获取了定界符分割出来原始字段个数(空字符串也算一个)

LENGTH(tmp.time)-LENGTH(REPLACE(tmp.time,  ',', '' )) + 1 

经过上面我们知道help_topic_id其实就是代表分割出来的字段标识,再通过第三点了解SUBSTRING_INDEX函数的用法就可以知道下面SQL其实就是为了得到具体分割字段的值:

SUBSTRING_INDEX(SUBSTRING_INDEX(tmp.ma, ',', b.help_topic_id ), ',',-1)

假设例子为下面的表格大家可以试试看是否可以得出time的值

id

customer_id

customer_name

ma

time

help_topic_id

123456

123456

测试

83514,111547

83514

1

123456

123456

测试

83514,111547

111547

2

end~

猜你喜欢

转载自blog.csdn.net/weixin_42505381/article/details/128562919
今日推荐