MySQLのJSONデータ型

coldplay.xixi
リリース: 2020-10-30 17:23:00
転載
5569 人が閲覧しました

mysql ビデオ チュートリアル 列では、JSON データ型を紹介します。

MySQLのJSONデータ型

MySQL 5.7 では、JSON データ型のサポートが追加されました。以前は、JSON 型データを保存したい場合は、自分で行うしかありませんでした。JSON。 stringify( )JSON.parse() 操作があり、JSON 内のデータに対してクエリ操作を実行する方法がありません。すべての操作を読み取って解析する必要があり、非常に面倒です。ネイティブ JSON データ型がサポートされると、JSON に対してデータのクエリと変更操作を直接実行できるようになり、以前よりもはるかに便利になりました。

デモンストレーションの便宜上、最初に user テーブルを作成します。このテーブルでは、info フィールドがユーザーの基本情報を保存するために使用されます。フィールドを JSON タイプのデータとして定義するのは非常に簡単で、フィールド名の後に直接 JSON を追加するだけです。

CREATE TABLE user (
  id INT(11) UNSIGNED AUTO_INCREMENT PRIMARY KEY,
  name VARCHAR(30) NOT NULL,
  info JSON
);复制代码
ログイン後にコピー

テーブルが正常に作成されたら、従来の CRUD データ操作に従って JSON データ型を操作する方法について説明します。

データの追加

データの追加は比較的簡単ですが、MySQL の JSON ストレージは本質的に文字列ストレージ操作であることを理解する必要があります。ただ、JSON 型として定義すると、後続の操作を容易にするために内部でいくつかのインデックスが作成されます。したがって、JSON データを追加するときは文字列パッケージを使用する必要があります。

mysql> INSERT INTO user (`name`, `info`) VALUES('lilei', '{"sex": "male", "age": 18, "hobby": ["basketball", "football"], "score": [85, 90, 100]}');
Query OK, 1 row affected (0.00 sec)复制代码
ログイン後にコピー

JSON を自分で入力するだけでなく、MySQL の JSON 作成関数を呼び出して作成することもできます。

  • JSON_OBJECT: 奇数列をキー、偶数列を値として使用して、JSON オブジェクトをすばやく作成します。使用方法は JSON_OBJECT(key,value,key1,value1) )
  • JSON_ARRAY: メソッド JSON_ARRAY(item0, item1, item2)
mysql> INSERT INTO user (`name`, `info`) VALUES('hanmeimei', JSON_OBJECT(
    ->   'sex', 'female', 
    ->   'age', 18, 
    ->   'hobby', JSON_ARRAY('badminton', 'sing'), 
    ->   'score', JSON_ARRAY(90, 95, 100)
    -> ));
Query OK, 1 row affected (0.00 sec)复制代码
ログイン後にコピー
## を使用して、JSON 配列をすばやく作成します。 #でもJavaScriptエンジニアにとって JSONを作るのに文字列を使うにせよ、組み込み関数を使うにせよ、非常に面倒で、JSのネイティブオブジェクトに比べると格段に使いやすさが劣ると言われています。したがって、

think-model モジュールでは、JSON データ型データ JSON.stringify() の自動処理のサポートを追加し、JS オブジェクト データを直接渡すことができるようにしました。

データの自動シリアル化と解析はフィールド タイプに基づいて行われるため、実行中のプロジェクトに影響を与えないように、これを有効にするためにモジュールで

jsonFormat: true を構成する必要があります。関数。

//adapter.jsconst MySQL = require('think-model-mysql');exports.model = {  type: 'mysql',  mysql: {    handle: MySQL,
    ...    jsonFormat: true
  }
};复制代码
ログイン後にコピー
//user.jsmodule.exports = class extends think.Controller {  async indexAction() {    const userId = await this.model('user').add({      name: 'lilei',      info: {        sex: 'male',        age: 16,        hobby: ['basketball', 'football'],        score: [85, 90, 100]
      }
    });    return this.success(userId);
  }
}复制代码
ログイン後にコピー
データベースに最終的に保存されるデータがどのようなものかを見てみましょう

mysql> SELECT * FROM `user`;
+----+-----------+-----------------------------------------------------------------------------------------+
| id | name      | info                                                                                    |
+----+-----------+-----------------------------------------------------------------------------------------+
|  1 | lilei     | {"age": 18, "sex": "male", "hobby": ["basketball", "football"], "score": [85, 90, 100]} |
|  2 | hanmeimei | {"age": 18, "sex": "female", "hobby": ["badminton", "sing"], "score": [90, 95, 100]}    |
+----+-----------+-----------------------------------------------------------------------------------------+
2 rows in set (0.00 sec)复制代码
ログイン後にコピー
クエリ データ

JSON データの操作をより適切にサポートするために、MySQL は次のサービスを提供します。いくつかの JSON データ操作クラスのメソッド。クエリ操作に関連するメソッドは主に次のとおりです。

  • JSON_EXTRACT(): Path に基づいて JSON データの一部を取得するには、メソッド JSON_EXTRACT(json_doc, path[, path]. ..)
  • ##->
  • : JSON_EXTRACT()
  • -> と同等;>
  • : JSON_EXTRACT() および JSON_UNQUOTE()
  • JSON_CONTAINS()
  • : JSON データが存在するかどうかをクエリします。指定されたPathに含まれる 指定されたデータが含まれる場合は1を返し、含まれない場合は0を返します。使用方法 JSON_CONTAINS(json_doc, val[, path])
  • JSON_CONTAINS_PATH()
  • : 指定されたパスが存在するかどうかを問い合わせ、存在する場合は 1 を返し、存在しない場合は 0 を返します。 。 one_or_all 値は「one」または「all」のみを取ることができます。one は 1 つだけ存在することを意味し、all はすべてが存在することを意味します。使用方法 JSON_CONTAINS_PATH(json_doc, one_or_all, path[, path] ...)
  • JSON_KEYS()
  • : 配下のJSONデータのキー値をすべて取得します。指定されたパス。使用方法 JSON_KEYS(json_doc[, path]) は、JavaScript の Object.keys() メソッドに似ています。
  • JSON_SEARCH()
  • : 指定された文字列を含むパスをクエリし、それを JSON 配列として返します。クエリ文字列は、LIKE の「%」または「_」を使用して照合できます。使用方法 JSON_SEARCH(json_doc, one_or_all, search_str[,scape_char[, path] ...]) は、JavaScript の findIndex() 操作に似ています。
  • ここでは、例を挙げて各メソッドを 1 つずつ説明するのではなく、いくつかのシナリオでの操作方法の例のみを示します。

ユーザーの年齢と性別を返す

この例の目的は、JSON データの一部を取得し、通常のテーブル フィールドに従ってそれを返す方法を説明することです。このブロックでは、

JSON_EXTRACT

または同等の -> 操作を使用できます。この例によれば、sex によって返されるデータは引用符で囲まれていることがわかりますが、このとき、JSON_UNQUOTE() を使用するか、直接 -> を使用できます。 >。引用符を削除しました。 <div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:php;toolbar:false">mysql&gt; SELECT `name`, JSON_EXTRACT(`info`, '$.age') as `age`, `info`-&gt;'$.sex' as sex FROM `user`; +-----------+------+----------+ | name      | age  | sex      | +-----------+------+----------+ | lilei     | 18   | &quot;male&quot;   | | hanmeimei | 16   | &quot;female&quot; | +-----------+------+----------+ 2 rows in set (0.00 sec)复制代码</pre><div class="contentsignin">ログイン後にコピー</div></div> <p>这里我们第一次接触到了 Path 的写法,MySQL 通过这种字符串的 Path 描述帮助我们映射到对应的数据。和 JavaScript 中对象的操作比较类似,通过 <code>. 获取下一级的属性,通过 [] 获取数组元素。

不一样的地方在于需要通过 $ 表示本身,这个也比较好理解。另外就是可以使用 *** 两个通配符,比如 .* 表示当前层级的所有成员的值,[*] 则表示当前数组中所有成员值。** 类似 LIKE 一样可以接前缀和后缀,比如 a**b 表示的是以 a 开头,b结尾的路径。

路径的写法非常简单,后面的内容里也会出现。上面的这个查询对应在 think-model 的写法为

//user.jsmodule.exports = class extends think.Controller {  async indexAction() {    const userModel = this.model('user');    const field = "name, JSON_EXTRACT(info, '$.age') AS age, info->'$.sex' as sex";    const users = await userModel.field(field).where('1=1').select();    return this.success(users);
  }
}复制代码
ログイン後にコピー

返回喜欢篮球的男性用户

mysql> SELECT `name` FROM `user` WHERE JSON_CONTAINS(`info`, '"male"', '$.sex') AND JSON_SEARCH(`info`, 'one', 'basketball', null, '$.hobby');
+-------+
| name  |
+-------+
| lilei |
+-------+
1 row in set, 1 warning (0.00 sec)复制代码
ログイン後にコピー

这个例子就是简单的告诉大家怎么对属性和数组进行查询搜索。其中需要注意的是 JSON_CONTAINS() 查询字符串由于不带类型转换的问题字符串需要使用加上 "" 包裹查询,或者使用 JSON_QUOTE('male') 也可以。

如果你使用的是 MySQL 8 的话,也可以使用新增的 JSON_VALUE() 来代替 JSON_CONTAINS(),新方法的好处是会带类型转换,避免刚才双引号的尴尬问题。不需要返回的路径的话,JSON_SEARCH() 在这里也可以使用新增的 MEMBER OF 或者 JSON_OVERLAPS() 方法替换。

mysql> SELECT `name` FROM `user` WHERE JSON_VALUE(`info`, '$.sex') = 'male' AND 'basketball' MEMBER OF(JSON_VALUE(`info`, '$.hobby'));
+-------+
| name  |
+-------+
| lilei |
+-------+
1 row in set (0.00 sec)

mysql> SELECT `name` FROM `user` WHERE JSON_VALUE(`info`, '$.sex') = 'male' AND JSON_OVERLAPS(JSON_VALUE(`info`, '$.hobby'), JSON_QUOTE('basketball'));
+-------+
| name  |
+-------+
| lilei |
+-------+
1 row in set (0.00 sec)复制代码
ログイン後にコピー

上面的这个查询对应在 think-model 的写法为

//user.jsmodule.exports = class extends think.Controller {  async indexAction() {    const userModel = this.model('user');    const where = {      _string: [        "JSON_CONTAINS(info, '\"male\"', '$.sex')",        "JSON_SEARCH(info, 'one', 'basketball', null, '$.hobby')"
      ]
    };    const where1 = {      _string: [        "JSON_VALUE(`info`, '$.sex') = 'male'",        "'basketball' MEMBER OF (JSON_VALUE(`info`, '$.hobby'))"
      ]
    };    const where2 = {      _string: [        "JSON_VALUE(`info`, '$.sex') = 'male'",        "JSON_OVERLAPS(JSON_VALUE(`info`, '$.hobby'), JSON_QUOTE('basketball'))"
      ]
    }    const users = await userModel.field('name').where(where).select();    return this.success(users);
  }
}复制代码
ログイン後にコピー

修改数据

MySQL 提供的 JSON 操作函数中,和修改操作相关的方法主要如下:

  • JSON_APPEND/JSON_ARRAY_APPEND:这两个名字是同一个功能的两种叫法,MySQL 5.7 的时候为 JSON_APPEND,MySQL 8 更新为 JSON_ARRAY_APPEND,并且之前的名字被废弃。该方法如同字面意思,给数组添加值。使用方法 JSON_ARRAY_APPEND(json_doc, path, val[, path, val] ...)
  • JSON_ARRAY_INSERT:给数组添加值,区别于 JSON_ARRAY_APPEND() 它可以在指定位置插值。使用方法 JSON_ARRAY_INSERT(json_doc, path, val[, path, val] ...)
  • JSON_INSERT/JSON_REPLACE/JSON_SET:以上三个方法都是对 JSON 插入数据的,他们的使用方法都为 JSON_[INSERT|REPLACE|SET](json_doc, path, val[, path, val] ...),不过在插入原则上存在一些差别。
    • JSON_INSERT:当路径不存在才插入
    • JSON_REPLACE:当路径存在才替换
    • JSON_SET:不管路径是否存在
  • JSON_REMOVE:移除指定路径的数据。使用方法 JSON_REMOVE(json_doc, path[, path] ...)

由于 JSON_INSERT, JSON_REPLACE, JSON_SETJSON_REMOVE 几个方法支持属性和数组的操作,所以前两个 JSON_ARRAY 方法用的会稍微少一点。下面我们根据之前的数据继续举几个实例看看。

修改用户的年龄

mysql> UPDATE `user` SET `info` = JSON_REPLACE(`info`, '$.age', 20) WHERE `name` = 'lilei';
Query OK, 1 row affected (0.00 sec)
Rows matched: 1  Changed: 1  Warnings: 0

mysql> SELECT JSON_VALUE(`info`, '$.age') as age FROM `user` WHERE `name` = 'lilei';
+------+
| age  |
+------+
| 20   |
+------+
1 row in set (0.00 sec)复制代码
ログイン後にコピー

JSON_INSERTJSON_SET 的例子也是类似,这里就不多做演示了。对应到 think-model 中的话,需要使用 EXP 条件表达式处理,对应的写法为

//user.jsmodule.exports = class extends think.Controller {  async indexAction() {    const userModel = this.model('user');    await userModel.where({name: 'lilei'}).update({      info: ['exp', "JSON_REPLACE(info, '$.age', 20)"]
    });    return this.success();
  }
}复制代码
ログイン後にコピー

修改用户的爱好

mysql> UPDATE `user` SET `info` = JSON_ARRAY_APPEND(`info`, '$.hobby', 'badminton') WHERE `name` = 'lilei';
Query OK, 1 row affected (0.00 sec)
Rows matched: 1  Changed: 1  Warnings: 0

mysql> SELECT JSON_VALUE(`info`, '$.hobby') as hobby FROM `user` WHERE `name` = 'lilei';
+-----------------------------------------+
| hobby                                   |
+-----------------------------------------+
| ["basketball", "football", "badminton"] |
+-----------------------------------------+
1 row in set (0.00 sec)复制代码
ログイン後にコピー

JSON_ARRAY_APPEND 在对数组进行操作的时候还是要比 JSON_INSERT 之类的方便的,起码你不需要知道数组的长度。对应到 think-model 的写法为

//user.jsmodule.exports = class extends think.Controller {  async indexAction() {    const userModel = this.model('user');    await userModel.where({name: 'lilei'}).update({      info: ['exp', "JSON_ARRAY_APPEND(info, '$.hobby', 'badminton')"]
    });    return this.success();
  }
}复制代码
ログイン後にコピー

删除用户的分数

mysql> UPDATE `user` SET `info` = JSON_REMOVE(`info`, '$.score[0]') WHERE `name` = 'lilei';
Query OK, 1 row affected (0.00 sec)
Rows matched: 1  Changed: 1  Warnings: 0

mysql> SELECT `name`, JSON_VALUE(`info`, '$.score') as score FROM `user` WHERE `name` = 'lilei';
+-------+-----------+
| name  | score     |
+-------+-----------+
| lilei | [90, 100] |
+-------+-----------+
1 row in set (0.00 sec)复制代码
ログイン後にコピー

删除这块和之前修改操作类似,没有什么太多需要说的。但是对数组进行操作很多时候我们可能就是想删值,但是却不知道这个值的 Path 是什么。这个时候就需要利用之前讲到的 JSON_SEARCH() 方法,它是根据值去查找路径的。比如说我们要删除 lilei 兴趣中的 badminton 选项可以这么写。

mysql> UPDATE `user` SET `info` = JSON_REMOVE(`info`, JSON_UNQUOTE(JSON_SEARCH(`info`, 'one', 'badminton'))) WHERE `name` = 'lilei';
Query OK, 1 row affected (0.00 sec)
Rows matched: 1  Changed: 1  Warnings: 0

mysql> SELECT JSON_VALUE(`info`, '$.hobby') as hobby FROM `user` WHERE `name` = 'lilei';
+----------------------------+
| hobby                      |
+----------------------------+
| ["basketball", "football"] |
+----------------------------+
1 row in set (0.00 sec)复制代码
ログイン後にコピー

这里需要注意由于 JSON_SEARCH 不会做类型转换,所以匹配出来的路径字符串需要进行 JSON_UNQUOTE() 操作。另外还有非常重要的一点是 JSON_SEARCH 无法对数值类型数据进行查找,也不知道这个是 Bug 还是 Feature。这也是为什么我没有使用 score 来进行举例而是换成了 hobby 的原因。如果数值类型的话目前只能取出来在代码中处理了。

mysql> SELECT JSON_VALUE(`info`, '$.score') FROM `user` WHERE `name` = 'lilei';
+-------------------------------+
| JSON_VALUE(`info`, '$.score') |
+-------------------------------+
| [90, 100]                     |
+-------------------------------+
1 row in set (0.00 sec)

mysql> SELECT JSON_SEARCH(`info`, 'one', 90, null, '$.score') FROM `user` WHERE `name` = 'lilei';
+-------------------------------------------------+
| JSON_SEARCH(`info`, 'one', 90, null, '$.score') |
+-------------------------------------------------+
| NULL                                            |
+-------------------------------------------------+
1 row in set (0.00 sec)复制代码
ログイン後にコピー

以上对应到 think-model 的写法为

//user.jsmodule.exports = class extends think.Controller {  async indexAction() {    const userModel = this.model('user');    // 删除分数
    await userModel.where({name: 'lilei'}).update({      info: ['exp', "JSON_REMOVE(info, '$.score[0]')"]
    });    // 删除兴趣
    await userModel.where({name: 'lilei'}).update({      info: ['exp', "JSON_REMOVE(`info`, JSON_UNQUOTE(JSON_SEARCH(`info`, 'one', 'badminton')))"]
    }); 
    return this.success();
  }
}复制代码
ログイン後にコピー

后记

由于最近有一个需求,有一堆数据,要记录这堆数据的排序情况,方便根据排序进行输出。一般情况下肯定是给每条数据增加一个 order 字段来记录该条数据的排序情况。但是由于有着批量操作,在这种时候使用单字段去存储会显得特别麻烦。在服务端同事的建议下,我采取了使用 JSON 字段存储数组的情况来解决这个问题。

也因为这样了解了一下 MySQL 对 JSON 的支持情况,同时将 think-model 做了一些优化,对 JSON 数据类型增加了支持。由于大部分 JSON 操作需要通过内置的函数来操作,这个本身是可以通过 EXP 条件表达式来完成的。所以只需要对 JSON 数据的添加和查询做好优化就可以了。

整体来看,配合提供的 JSON 操作函数,MySQL 对 JSON 的支持完成一些日常的需求还是没有问题的。除了作为 WHERE 条件以及查询字段之外,其它的 ORDER, GROUP, JOIN 等操作也都是支持 JSON 数据的。

不过对比 MongoDB 这种天生支持 JSON 的话,在操作性上还是要麻烦许多。特别是在类型转换这块,使用一段时间后发现非常容易掉坑。什么时候会带引号,什么时候会不带引号,什么时候需要引号,什么时候不需要引号,这些都容易让新手发憷。另外 JSON_SEARCH() 不支持数字查找这个也是一个不小的坑了。

相关免费学习推荐:mysql视频教程

以上がMySQLのJSONデータ型の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

関連ラベル:
ソース:juejin.im
このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。
最新の問題
人気のチュートリアル
詳細>
最新のダウンロード
詳細>
ウェブエフェクト
公式サイト
サイト素材
フロントエンドテンプレート