美文网首页数据蛙数据分析每周作业
在MySQL中提取字段名称以及更新前后数据

在MySQL中提取字段名称以及更新前后数据

作者: bf3780a4db09 | 来源:发表于2019-08-01 14:34 被阅读6次

    这两天在工作中碰到一个需求,开发那边希望在做增量数据的时候,能够得到被更新的字段名以及该字段被更新前后的数据,然后在实现的过程中,用到了两个之前没有看到过的函数:FIND_IN_SET()和SUBSTRING_INDEX()。
    一、函数的用法
    1、FIND_IN_SET(str,strlist),其中,str是要查找的字符串(可以是字符串也可以是字段),strlist是用逗号分隔的字符串(可以是字符串也可以是字段);假设str包含在strlist中,则返回str在strlist中第一次出现的位置(位置从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

    注:,参数str中不能带逗号,如果带逗号,函数会不起作用,
    比如

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

    返回0,函数认为'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为关键字,count为关键字出现的次数(count可以是负数,此时从后往前数)。用来在str中截取第count个关键字之前的所有字符。
    比如:
    -- count是正数时

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

    返回


    image.png

    上面的查询用来截取strlist中第二个逗号之前的所有字符,当字符串中的逗号个数小于2时,返回原数据(比如最后一条记录e,返回的还是e)。
    -- count是负数时

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

    返回


    image.png

    上面的查询用来截取strlist倒数第三个逗号之后的所有字符,当字符串中的逗号个数小于3时,返回原数据。
    二、实现流程
    假设现在的目标表test_instr如下(主键是id)


    image.png

    要更新到目标表中的数据test_instr_2如下(主键是id)


    image.png

    大体的思路如下:
    1、当主键匹配上时才做更新操作,需要收集更新信息(匹配不上的做新增);
    2、将目标表test_instr除主键id外,进行列转行操作,列名所在列为UPDATE_FIELD,值所在列为OLD_VALUE;
    3、将待更新数据test_instr_2中除主键id外,剩余字段值用逗号分隔,合并成一列CONCAT_VALUE,同时将所有列名也用逗号分隔,合并成一列CONCAT_COLUMN;
    4、利用函数find_in_set,OLD_VALUE和CONCAT_VALUE分别为参数str和strlist,当返回值为0时,表示发生了更新,提取CONCAT_VALUE中相应的值(根据更新的字段名称)作为NEW_VALUE。
    具体实现如下:
    MySQL中的列转行

    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()函数得到更新的数据,通过

    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
    -- 列转行
    (
    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中的两个参数不同时为空(至少有一个不为空),如果两个参数都为空的话,FIND_IN_SET也会返回0,但实际上空到空不算做更新。
    但是,现在有个问题:
    列转行和合并字段值,需要把除主键以外的所有字段都列出来,实际业务中有的表字段非常多,全部列出来的话工作量很大,我考虑利用kettle将列转行和合并字段值的数据分别存到新表,用新表操作,但是这样的话每张表就要对应一个流程,有多少张表就要建立多少个流程,很繁琐,如果有好的方法希望大家多多指教!

    相关文章

      网友评论

        本文标题:在MySQL中提取字段名称以及更新前后数据

        本文链接:https://www.haomeiwen.com/subject/cppfdctx.html