使用場景
爲什麼要用到字符串分割,行轉列
在我們開發的過程中,有時我們會遇到這樣的數據結構:
主鍵id | 子集主鍵id |
---|---|
id | children_id |
122 |
4795/4796/4797/ |
我們可以看到,在我們根據主表查詢子表中的信息時,我們從主表中獲取的子表id
是以字符串的形式進行拼接的,當我們要想根據主表關聯子表信息時,我們首先將主表的子表id
字段進行字符串拆分,然後將拆分後的字段行轉列,以此來關聯子表,實現子表信息的查詢操作。
字符串分割行轉列的思路
我們在進行字符串分割行轉列的時候,我們需要用到如下兩個主要步驟:
- 使用分隔符,實現字符串拆分操作
- 使用循環,讓拆分後的字段實現行轉列效果
字符串分割函數
在MySQL
中,其實現字符串拆分的函數爲SUBSTRING_INDEX(str,delim,count)
關鍵字 | 含義 |
---|---|
str | 被拆分的字符串 |
delim | 分隔符 |
count | 在定界符 delim 以及count 出現前,從字符串str返回自字符串。若count爲正值,則返回最終定界符(從左邊開始)左邊的一切內容。若count爲負值,則返回定界符(從右邊開始)右邊的一切內容。 |
它的用法如下:
比如說我們想要獲取4795/4796/4797/
中的4795
,我們的做法是:
SELECT SUBSTRING_INDEX('4795/4796/4797/','/',1)
循環思路
在MySQL
中,一共有三種循環,分別爲while
、 loop
、repeat
,而這三種循環的使用,一般是在存儲過程之中,而這裏,由於我們所採取的並非存儲過程方法,因而在此處我們不做過多討論。
如果我們既想要實現循環操作效果,又不想使用存儲過程,那該怎麼實現呢?
當我們在進行字符串分割時,我們想實現的效果是,對於一個字符串,比如說4795/4796/4797/
,第一次分割,我們想獲取的字符串爲4795
,我們可以將其下標定義爲0,然後第二次分割,我們想獲取的字符串爲4796
,我們將其下標定義爲1,以此類推,我們可以發現,通過下標的0、1、2、3…我們就可以獲取我們所要分割後的字符串數值。
在想明白了這點之後,我們就可以發現,通過藉助第三張表,無論該表爲什麼表名,只要是其主鍵id值爲0、1、2、3…,那麼該表就可以被我們拿來做循環操作使用。
在MySQL
中,還恰恰就存在這麼一張表,就是mysql.help_topic
,該表的主鍵字段help_topic_id
從0開始,依次遞增,最多有634條數據,換言之,如果使用該表做循環操作,那麼理論上,其可以滿足我們絕大多數的循環操作使用場景。
字符串分割,行轉列
方法一
在進行了上面的敘述之後,我們就可以直接上代碼了,如下:
SELECT
SUBSTRING_INDEX(SUBSTRING_INDEX('4795/4796/4797/','/',ht.help_topic_id+1),'/',-1) AS children_id
FROM
mysql.help_topic ht
WHERE
ht.help_topic_id < LENGTH('4795/4796/4797/')-LENGTH(REPLACE('4795/4796/4797/','/',''))
其展示的結果爲:
children_id |
---|
4795 |
4796 |
4797 |
方法二
如果我們的數據庫做了權限限制,當我們只能訪問當前數據庫時(不含mysql.help_topic
),此時我們就只能自己創建一個新表,以此來替代mysql.help_topic
,這裏我們創建的表爲table_for
,建表語句如下:
CREATE TABLE `table_for` (
`id` INT(11) DEFAULT NULL COMMENT 'for循環主鍵id'
) ENGINE=INNODB DEFAULT CHARSET=utf8
在表中插入數據:
INSERT INTO table_for(id)
SELECT help_topic_id FROM mysql.help_topic ORDER BY help_topic_id ASC
上面的插入數據源,仍然選擇的是mysql.help_topic
,我們可以在本地MySQL
上執行上述操作,然後將其上傳到項目所在的數據庫中,此時方法一中的sql
將變成如下:
SELECT
SUBSTRING_INDEX(SUBSTRING_INDEX('4795/4796/4797/','/',tf.id+1),'/',-1) AS children_id
FROM
`table_for` tf
WHERE
tf.id < LENGTH('4795/4796/4797/')-LENGTH(REPLACE('4795/4796/4797/','/',''))