在MySQL中提取字段名稱以及更新前后數(shù)據(jù)

這兩天在工作中碰到一個需求,開發(fā)那邊希望在做增量數(shù)據(jù)的時候,能夠得到被更新的字段名以及該字段被更新前后的數(shù)據(jù),然后在實現(xiàn)的過程中,用到了兩個之前沒有看到過的函數(shù):FIND_IN_SET()和SUBSTRING_INDEX()。
一、函數(shù)的用法
1、FIND_IN_SET(str,strlist),其中,str是要查找的字符串(可以是字符串也可以是字段),strlist是用逗號分隔的字符串(可以是字符串也可以是字段);假設(shè)str包含在strlist中,則返回str在strlist中第一次出現(xiàn)的位置(位置從1開始),如果不包含,則返回0。
1.1 str和strlist都是字符串
比如:

SELECT 
FIND_IN_SET('a','c,d,a,d,a,v')
FROM db1.`test_instr`

返回3,第一個'a'在字符串'c,d,a,d,a,v'中在第3的位置。

SELECT 
FIND_IN_SET('e','c,d,a,d,a,v')
FROM db1.`test_instr`

返回0,'e'在'c,d,a,d,a,v' 中不存在
特殊情況:
-- 當str或者strlist中任意一個為NULL時,返回NULL

SELECT 
FIND_IN_SET(NULL,'c,d,a,d,a,v')
FROM db1.`test_instr`
-- 
SELECT 
FIND_IN_SET('e',NULL)
FROM db1.`test_instr`
--
SELECT 
FIND_IN_SET(NULL,NULL)
FROM db1.`test_instr`

上面三條查詢均返回NULL
-- 當str或者strlist中任意一個為空字符串時,返回0
比如

SELECT *,
FIND_IN_SET('','') 
FROM instr_test_left
-- 
SELECT *,
FIND_IN_SET('','a,b,c') 
FROM instr_test_left
-- 
SELECT *,
FIND_IN_SET('a','') 
FROM instr_test_left

上面三條查詢均返回0

注:,參數(shù)str中不能帶逗號,如果帶逗號,函數(shù)會不起作用,
比如

SELECT 
FIND_IN_SET('c,d','c,d,a,d,a,v')
FROM db1.`test_instr`

返回0,函數(shù)認為'c,d'在'c,d,a,d,a,v'中不存在,但是看起來又似乎是存在的。
1.2 str和strlist為字段

  • 兩個都是字段:每條記錄的str和strlist進行比較
SELECT *,
FIND_IN_SET(str,strlist)

FROM instr_test_left

返回


image.png

-- str是字段,strlist是字符串:str字段的每個值和strlist作比較

SELECT str,'k,a,d,d,p,b,f',
FIND_IN_SET(str,'k,a,d,d,p,b,f')

FROM instr_test_left

同理,str是字符串,strlist是段:str和strlist的每個值作比較。
2、SUBSTRING_INDEX(str,delim,count),其中,str為被截取字段,delim為關(guān)鍵字,count為關(guān)鍵字出現(xiàn)的次數(shù)(count可以是負數(shù),此時從后往前數(shù))。用來在str中截取第count個關(guān)鍵字之前的所有字符。
比如:
-- count是正數(shù)時

SELECT 
*,
SUBSTRING_INDEX(strlist,',',2)

FROM instr_test_left

返回


image.png

上面的查詢用來截取strlist中第二個逗號之前的所有字符,當字符串中的逗號個數(shù)小于2時,返回原數(shù)據(jù)(比如最后一條記錄e,返回的還是e)。
-- count是負數(shù)時

SELECT 
*,
SUBSTRING_INDEX(strlist,',',-3)

FROM instr_test_left

返回


image.png

上面的查詢用來截取strlist倒數(shù)第三個逗號之后的所有字符,當字符串中的逗號個數(shù)小于3時,返回原數(shù)據(jù)。
二、實現(xiàn)流程
假設(shè)現(xiàn)在的目標表test_instr如下(主鍵是id)


image.png

要更新到目標表中的數(shù)據(jù)test_instr_2如下(主鍵是id)


image.png

大體的思路如下:
1、當主鍵匹配上時才做更新操作,需要收集更新信息(匹配不上的做新增);
2、將目標表test_instr除主鍵id外,進行列轉(zhuǎn)行操作,列名所在列為UPDATE_FIELD,值所在列為OLD_VALUE;
3、將待更新數(shù)據(jù)test_instr_2中除主鍵id外,剩余字段值用逗號分隔,合并成一列CONCAT_VALUE,同時將所有列名也用逗號分隔,合并成一列CONCAT_COLUMN;
4、利用函數(shù)find_in_set,OLD_VALUE和CONCAT_VALUE分別為參數(shù)str和strlist,當返回值為0時,表示發(fā)生了更新,提取CONCAT_VALUE中相應(yīng)的值(根據(jù)更新的字段名稱)作為NEW_VALUE。
具體實現(xiàn)如下:
MySQL中的列轉(zhuǎn)行

SELECT 
a.ID,'cate' UPDATE_FIELD, a.`cate` OLD_VALUE
FROM test_instr a
UNION ALL
SELECT 
a.ID,'uuc' UPDATE_FIELD, a.`uuc` OLD_VALUE
FROM test_instr a
UNION ALL
SELECT 
a.ID,'duc' UPDATE_FIELD, a.`duc` OLD_VALUE
FROM test_instr a

返回


image.png

test_instr_2表合并字段操作

SELECT 
a.`id` ID_B,
-- 將需要比較的字段值用逗號連接
CONCAT(a.`cate`,',',a.`uuc`,',',a.`duc`) CONCAT_VALUE,
-- 將所有列名用逗號連接
(SELECT GROUP_CONCAT(COLUMN_NAME SEPARATOR ",") FROM information_schema.COLUMNS 
WHERE TABLE_SCHEMA = 'db1' AND TABLE_NAME = 'test_instr_2') CONCAT_COLUMN
FROM test_instr_2 a

返回


image.png

利用find_in_set()函數(shù)得到更新的數(shù)據(jù),通過

SELECT 'TEST_INSTR' TABLE_NAME,c.ID ,c.UPDATE_FIELD,c.OLD_VALUE,
SUBSTRING_INDEX( SUBSTRING_INDEX( d.CONCAT_VALUE, ',', FIND_IN_SET(c.UPDATE_FIELD,d.CONCAT_COLUMN)-1 ), ',',- 1 ) NEW_VALUE,
SYSDATE() UPDATE_TIME
FROM
-- 列轉(zhuǎn)行
(
SELECT 
a.ID,'cate' UPDATE_FIELD, a.`cate` OLD_VALUE
FROM test_instr a
UNION ALL
SELECT 
a.ID,'uuc' UPDATE_FIELD, a.`uuc` OLD_VALUE
FROM test_instr a
UNION ALL
SELECT 
a.ID,'duc' UPDATE_FIELD, a.`duc` OLD_VALUE
FROM test_instr a
) c
INNER JOIN
(SELECT 
a.`id` ID_B,
-- 將需要比較的字段值用逗號連接
CONCAT(a.`cate`,',',a.`uuc`,',',a.`duc`) CONCAT_VALUE,
-- 將所有列名用逗號連接
(SELECT GROUP_CONCAT(COLUMN_NAME SEPARATOR ",") FROM information_schema.COLUMNS 
WHERE TABLE_SCHEMA = 'db1' AND TABLE_NAME = 'test_instr_2') CONCAT_COLUMN
FROM test_instr_2 a) d
ON c.ID=d.ID_B
AND FIND_IN_SET(c.OLD_VALUE ,d.CONCAT_VALUE )=0
AND
(c.OLD_VALUE <> ''
or d.CONCAT_VALUE <> '');

返回


image.png

這里加了一個條件FIND_IN_SET中的兩個參數(shù)不同時為空(至少有一個不為空),如果兩個參數(shù)都為空的話,F(xiàn)IND_IN_SET也會返回0,但實際上空到空不算做更新。
但是,現(xiàn)在有個問題:
列轉(zhuǎn)行和合并字段值,需要把除主鍵以外的所有字段都列出來,實際業(yè)務(wù)中有的表字段非常多,全部列出來的話工作量很大,我考慮利用kettle將列轉(zhuǎn)行和合并字段值的數(shù)據(jù)分別存到新表,用新表操作,但是這樣的話每張表就要對應(yīng)一個流程,有多少張表就要建立多少個流程,很繁瑣,如果有好的方法希望大家多多指教!

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容