Record an experience of row-to-column conversion in Mysql

Recently, I encountered a need at work, which is similar to counting the visit and check-in records of each customer's visit record. The key fields of table a are as follows:

id

customer_id

customer_name

morning

afternoon

123456

123456

test

83514,111547

134512,170230

The latter two fields respectively record the customer's visit time in the morning and afternoon. Now we need to use the visit time to query the photo evidence during the visit. Familiar friends may think of it when they see it, can't we just combine the two fields of morning and afternoon in one time? So there is the following SQL: (assuming table b is a photo information table)

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)));

It is true that using the above SQL to deal with tables with a small amount of data is completely effective, but the amount of data is tens of thousands, and even if table a is indexed for morning and afternoon, it will not help. After all, the function in the where condition will cause the index to fail, and the FIND_IN_SET function will fail to hit the index~ The reality is so cruel, I was directly

That is impossible, so there is such a piece of 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 

Through the above SQL, the data structure of table a can be converted into:

id

customer_id

customer_name

time

123456

123456

test

83514

123456

123456

test

111547

123456

123456

test

134512

123456

123456

test

170230

Is it possible to convert rows into columns in this way?

Friends who are eager to use can use cooked meat to modify it. Next, the author sorts out some knowledge points in SQL:

The figure clearly informs the user that the FIND_IN_SET function can divide the values ​​in the field that are similar to set and whose delimiter can only be a comma (half-width), and then search the target string and return the location of the target string.

PS: If the field is other delimiter, please use the replace function to change the delimiter to comma.

  • Table A, Table B

The way tables are joined as shown above is a cross join, which is something like:

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

Result: the result set of the number of rows of TableA * the number of rows of 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~

Guess you like

Origin blog.csdn.net/weixin_42505381/article/details/128562919