操作存储在mysql文本列中的Json数据结构

时间:2019-01-10 13:10:12

标签: mysql

我在MySQL表中有一列是文本数据类型的。它包含一些json字符串[{"abc":"1","def":2,"xyz":3}] 我需要将这些json值更新为类似的

[
  {
    "abc": "1",
    "changed": [
      {
        "def": 2,
        "xyz": 3
      }
    ]
  }
]

我们如何使用MySQL过程实现这一目标?我是存储过程的新手。

任何帮助都会非常有帮助。预先感谢

2 个答案:

答案 0 :(得分:0)

我的看法是,您用所需的值更新该列。 因为假设您使用存储过程并检索该列并执行一些字符串操作任务,所以它只会延迟系统的性能。考虑到这一点和简单性,我建议您只引用具有相关ID的那一列,然后更新为所需的值。

答案 1 :(得分:0)

首先可以通过处理给定的JSON结构开始。 假设结构将保持不变,即[ {}, {}, {}, {}, ...]。让我们从第一个对象获取内容开始

SET @a := '[{"abc":"1","def":2,"xyz":3}]';
SET @b := JSON_REMOVE( @a, '$[0].def', '$[0].xyz' );
SET @c := JSON_REMOVE( @a, '$[0].abc' );
SET @d := JSON_EXTRACT( @c, '$[0]');


SET @e := JSON_INSERT( @b, '$[0].change', JSON_QUERY( @d, '$' ) ); -- for maria db
/** SET @e := JSON_INSERT( @b, '$[0].change', CAST( @d AS JSON ) ); -- for mysql **/

必须有一种更有效的方法来进行上述操作

  

我已经在mariadb中完成了所有这些操作,因此您将需要取消注释并可能需要对mysql进行一些修正

我们现在需要确定每次修改所使用的部分。

  • 数组索引
  • 保留钥匙
  • 移动键

假设包含数据的表就是这样

DROP TABLE IF EXISTS `json_test`;

/**
-- mysql
CREATE TABLE `json_test` (
  `id` SMALLINT(4) UNSIGNED NOT NULL AUTO_INCREMENT,
  `json` JSON NOT NULL,
  PRIMARY KEY(`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
*/

-- maria db
CREATE TABLE `json_test` (
  `id` SMALLINT(4) UNSIGNED NOT NULL AUTO_INCREMENT,
  `json` LONGTEXT NOT NULL,
  PRIMARY KEY(`id`),
  CHECK( JSON_VALID(`json`) )
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;

-- add some values to test
INSERT INTO `json_test`(`json`) VALUES
    ( '[{"abc":"1","def":2,"xyz":3}]' ),
    ( '[{"abc":"1","def":2,"xyz":3},{"abc":"1","def":2,"xyz":3}]' );

所以要操纵内容并可以说通过程序对其进行更新

DELIMITER //

/**
 * This proc uses prepared statements to be able to reproduce the aforementioned example
 *
 * @note:   I am not capturing cases of warnings/errors those you will have to add them by yourself.
 * 
 * @param: `INindex` - INT - the position in the json array
 * @param: `INkeep` - VARCHAR(32) - the value to keep, format /\.[a-z]/
 * @param: `INmove` - VARCHAR(128) - the values to move, format /^(\.[a-z], )*\.[a-z]$/
 * @param: x any other parameter that you would like to add
 */
DROP PROCEDURE IF EXISTS `proc_update_json_test` //
CREATE PROCEDURE `proc_update_json_test`( IN `INid` SMALLINT(4), IN `INindex` INT, IN `INkeep` VARCHAR(32), IN `INmove` VARCHAR(128) )
`bgn_lbl`:BEGIN -- or just begin, use labels to escape some conditions
  DECLARE `arrayPos` VARCHAR(32) DEFAULT CONCAT( '$[',`INindex`,']');
  DECLARE `encodedJSON` LONGTEXT; -- for mariadb
  /** DECLARE `encodedJSON` JSON; -- for mysql **/

  IF ( SELECT COUNT(*) FROM `json_test` ) = 0 THEN
    SELECT 'no such id. Exiting';
    LEAVE `bgn_lbl`;
  END IF;

  SELECT `json` INTO `encodedJSON` FROM `json_test` WHERE `id` = `INid`;

  SET `INkeep` = REPLACE( `INkeep`, '.', CONCAT( `arrayPos`, '.' ) );
  SET `INmove` = REPLACE( `INmove`, '.', CONCAT( `arrayPos`, '.' ) );
  SET `INmove` = REPLACE( `INmove`, ',', "', '" );

  PREPARE `stmt` FROM CONCAT( "SET @b := JSON_REMOVE( '",`encodedJSON`,"', '",`INmove`,"' )" );
  EXECUTE `stmt`;
  DEALLOCATE PREPARE `stmt`;

  -- since this is not feasible to add more than a single key unless if we declare it as a
  SET @c := JSON_REMOVE( `encodedJSON`, `INkeep` );
  SET @d := JSON_EXTRACT( @c, `arrayPos` );

  UPDATE `json_test`
    SET `json` = JSON_INSERT( @b, CONCAT( `arrayPos`, '.change' ), JSON_QUERY( @d, '$' ) ) -- for maria db
    /** `json` = JSON_INSERT( @b, CONCAT( `arrayPos`, '.change' ), CAST( @d AS JSON ) ) -- for mysql **/
    WHERE `id` = `INid`;
END //

DELIMITER ;

要运行该过程并检查结果

CALL `proc_update_json_test`( 2, 1, '.abc', '.def, .xyz' );
SELECT * FROM `json_test`;

您需要进行大量尝试才能使此结构以您希望的任何结构运行。只是为了了解如何访问数据

资源MySQL JSONJSON with MariaDBJSON Functions MariadDB

进一步阅读Create Procedure MySQLPrepared Statements