MySQL 怎么处理 JSON 数据?

文章导读
MySQL 5.7.8 版本引入了 JSON 数据类型,允许您访问 JSON 文档中的数据。
📋 目录
  1. A 介绍
  2. B 关键要点
  3. C 前提条件
  4. D 步骤 1:定义 Schema
  5. E 步骤 2:在 JSON 字段中创建数据
  6. F 步骤 3:从 JSON 字段读取数据
  7. G 步骤 4:更新 JSON 字段中的数据
  8. H 步骤 5:从 JSON 字段删除数据
  9. I 步骤 6:创建 Migration
  10. J 步骤 7:创建模型
A A

介绍

MySQL 5.7.8 版本引入了 JSON 数据类型,允许您访问 JSON 文档中的数据。

SQL 数据库在设计上往往较为刚性。本质上,结构化查询语言强制执行数据类型和大小约束。

相比之下,NoSQL 数据库鼓励设计上的灵活性。在这些无 schema 的数据库中,没有强加的结构限制,只有需要保存的数据。

MySQL 中的 JSON 数据类型赋予您两种系统的优势。它允许您对数据库的某些部分进行结构化,而让其他部分保持灵活。

本文的前半部分将设计一个带有 JSON 字段的数据库。它将逐步介绍使用 MySQL 内置函数来创建、读取、更新和删除行。

本文的后半部分将利用 Laravel 的 Eloquent ORM 与数据库通信。您将构建一个管理面板,支持显示产品、添加新产品、修改现有产品以及删除产品。

关键要点

  • MySQL 5.7.8 及更高版本支持原生 JSON 数据类型,因此您可以在关系型数据库中存储和查询半结构化数据,而无需离开它。
  • 使用 JSON_OBJECTJSON_ARRAYJSON_MERGE_PRESERVE 在 SQL 中构建 JSON;使用 JSON_EXTRACT(或 -> 操作符)读取和过滤 JSON 路径。
  • 使用 JSON_INSERTJSON_REPLACEJSON_SET 更新 JSON;使用 JSON_REMOVE 删除键。
  • 为了更快地查询 JSON 字段,请考虑在生成列上创建索引,或采用其他查询和表优化策略。
  • MySQL 中的 JSON 适用于灵活属性(例如,产品规格),同时将核心数据保存在普通列中;关于何时选择关系型存储与文档式存储,请参阅数据库类型和 SQL 与 NoSQL 的比较。

前提条件

如果您想跟随本文学习,您需要:

  • MySQL 5.7.8 或更高版本,以及 PHP 7.3.24 或更高版本。您可以参考我们的 Linux、Apache、MySQL 和 PHP 安装教程
  • 对 SQL 查询有一定熟悉度。
  • 对编写 PHP 有一定熟悉度。
  • 对 Laravel 有一定熟悉度。
  • 本教程以通过 Composer 安装 Laravel 为前提。您可以参考我们的 Composer 安装教程。

注意: Laravel 现在提供了一个名为 Sail 的工具,用于与 Docker 配合工作,它将配置一个包含 MySQL、PHP 和 Composer 的环境。

如果您在设置本地环境时遇到困难,这可能是一个替代选项。

本教程已在 MySQL v8.0.23、PHP v7.3.24、Composer v2.0.9 和 Laravel v8.26.1 上验证。

步骤 1:定义 Schema

在本教程中,您将基于一个定义在线商店电子产品库存的 schema 进行构建,该商店销售各种电子产品。

传统上,会使用 Entity–attribute–value model (EAV) 模式来允许客户比较产品的特性。

然而,使用 JSON 数据类型,可以以不同的方式处理此用例。

数据库将命名为 e_store,包含三个表:brandscategoriesproducts

创建 e_store 数据库:

CREATE DATABASE IF NOT EXISTS `e_store`
DEFAULT CHARACTER SET utf8
DEFAULT COLLATE utf8_general_ci;

SET default_storage_engine = INNODB;

brandscategories 表各包含一个 id 和一个 name 字段。

创建 brands 表:

CREATE TABLE `e_store`.`brands`(
 `id` INT UNSIGNED NOT NULL auto_increment ,
 `name` VARCHAR(250) NOT NULL ,
 PRIMARY KEY(`id`)
);

创建 categories 表:

CREATE TABLE `e_store`.`categories`(
 `id` INT UNSIGNED NOT NULL auto_increment ,
 `name` VARCHAR(250) NOT NULL ,
 PRIMARY KEY(`id`)
);

接下来,添加一些示例 brands

INSERT INTO `e_store`.`brands`(`name`)
VALUES
 ('Samsung');

INSERT INTO `e_store`.`brands`(`name`)
VALUES
 ('Nokia');

INSERT INTO `e_store`.`brands`(`name`)
VALUES
 ('Canon');

然后,添加一些 categories

INSERT INTO `e_store`.`categories`(`name`)
VALUES
 ('Television');

INSERT INTO `e_store`.`categories`(`name`)
VALUES
 ('Mobile Phone');

INSERT INTO `e_store`.`categories`(`name`)
VALUES
 ('Camera');

接下来,创建一个包含 idnamebrand_idcategory_idattributes 字段的 products 表:

CREATE TABLE `e_store`.`products`(
 `id` INT UNSIGNED NOT NULL AUTO_INCREMENT ,
 `name` VARCHAR(250) NOT NULL ,
 `brand_id` INT UNSIGNED NOT NULL ,
 `category_id` INT UNSIGNED NOT NULL ,
 `attributes` JSON NOT NULL ,
 PRIMARY KEY(`id`) ,
 INDEX `CATEGORY_ID`(`category_id` ASC) ,
 INDEX `BRAND_ID`(`brand_id` ASC) ,
 CONSTRAINT `brand_id` FOREIGN KEY(`brand_id`) REFERENCES `e_store`.`brands`(`id`) ON DELETE RESTRICT ON UPDATE CASCADE ,
 CONSTRAINT `category_id` FOREIGN KEY(`category_id`) REFERENCES `e_store`.`categories`(`id`) ON DELETE RESTRICT ON UPDATE CASCADE
);

此表定义为 brand_idcategory_id 字段指定了外键约束,规定它们分别引用 brandscategories 表。此表定义还规定不允许删除引用的行,如果更新,则更改应反映到引用中。

attributes 字段的列类型声明为 JSON,这是 MySQL 中现在可用的原生数据类型。这允许您在 attributes 字段上使用 MySQL 中的各种 JSON 相关构造。

以下是创建的数据库的实体关系图:

此数据库设计在效率和准确性方面并非最佳。有一些常见现实世界用例未被考虑。例如,products 表中没有价格列,也没有支持产品属于多个类别。然而,本教程的目的不是教授数据库设计,而是展示如何使用 MySQL 的 JSON 功能在一个表中建模不同性质的对象。

步骤 2:在 JSON 字段中创建数据

现在,你将使用 INSERT INTOVALUES 创建产品并添加到数据库中。

以下是一些示例电视机,包含屏幕尺寸、分辨率、端口和扬声器的数据,使用字符串化的 JSON 对象:

INSERT INTO `e_store`.`products`(
 `name` ,
 `brand_id` ,
 `category_id` ,
 `attributes`
)
VALUES(
 'Prime' ,
 '1' ,
 '1' ,
 '{"screen": "50 inch", "resolution": "2048 x 1152 pixels", "ports": {"hdmi": 1, "usb": 3}, "speakers": {"left": "10 watt", "right": "10 watt"}}'
);

INSERT INTO `e_store`.`products`(
 `name` ,
 `brand_id` ,
 `category_id` ,
 `attributes`
)
VALUES(
 'Octoview' ,
 '1' ,
 '1' ,
 '{"screen": "40 inch", "resolution": "1920 x 1080 pixels", "ports": {"hdmi": 1, "usb": 2}, "speakers": {"left": "10 watt", "right":

步骤 3:从 JSON 字段读取数据

现在数据库中已有一些产品数据,您可以尝试读取这些数据。

对于典型的非 JSON 类型的 MySQL 值,通常会依赖 WHERE 子句。但在使用 JSON 列时,需要使用基于路径的提取方式。如果需要复习基本的查询语法,请参阅 MySQL 查询简介。

当您希望使用 JSON 字段选择行时,应熟悉路径表达式的概念。路径表达式使用美元符号 ($) 和目标对象键。

结合 JSON_EXTRACT 函数使用时,可以检索指定列的值。

考虑这样一个场景:您对所有至少有一个 USB 接口和一个 HDMI 接口的电视感兴趣:

SELECT
 *
FROM
 `e_store`.`products`
WHERE
 `category_id` = 1
AND JSON_EXTRACT(`attributes` , '$.ports.usb') > 0
AND JSON_EXTRACT(`attributes` , '$.ports.hdmi') > 0;

JSON_EXTRACT 函数的第一个参数是要应用路径表达式的 JSON,即 attributes 列。$ 符号用于标记要操作的对象。$.ports.usb$.ports.hdmi 路径表达式分别表示“获取 ports 下 usb 键的值”和“获取 ports 下 hdmi 键的值”。

提取出感兴趣的键后,您可以使用 MySQL 操作符,例如大于符号 (>) 来操作它们。

此查询将返回三个结果:

这三款电视至少各有一个 USB 接口和一个 HDMI 接口。“Bravia”和“Proton”型号不符合这些条件。

另外,JSON_EXTRACT 函数有一个别名 ->,您可以使用它使查询更易读。

修改前面的查询以使用 -> 别名:

SELECT
 *
FROM
 `e_store`.`products`
WHERE
 `category_id` = 1
AND `attributes` -> '$.ports.usb' > 0
AND `attributes` -> '$.ports.hdmi' > 0;

现在,您可以从 JSON 字段读取数据了。

步骤 4:更新 JSON 字段中的数据

您可以使用 JSON_INSERTJSON_REPLACEJSON_SET 函数来更新 JSON 字段中的数据。这些函数还需要路径表达式来指定要修改 JSON 对象的哪些部分。这些函数的输出是一个应用了更改的有效 JSON 对象。

首先,使用 JSON_INSERT 更新 JSON 字段,为所有手机添加一个新的 chipset 键,其值为 “Qualcomm”:

UPDATE `e_store`.`products`
SET `attributes` = JSON_INSERT(
 `attributes` ,
 '$.chipset' ,
 'Qualcomm'
)
WHERE
 `category_id` = 2;

$.chipset 路径表达式标识了 chipset 属性位于对象根部的位置。

使用以下查询检查更新后的手机类别:

SELECT
 *
FROM
 `e_store`.`products`
WHERE
 `category_id` = 2

现在所有手机都包含 “Qualcomm”:

现在,使用 JSON_REPLACE 更新 JSON 字段,将所有手机的现有 chipset 键的值修改为 “Qualcomm Snapdragon”:

UPDATE `e_store`.`products`
SET `attributes` = JSON_REPLACE(
 `attributes` ,
 '$.chipset' ,
 'Qualcomm Snapdragon'
)
WHERE
 `category_id` = 2;

现在所有手机的 “Qualcomm” 已被替换为 “Qualcomm Snapdragon”:

最后,使用 JSON_SET 更新 JSON 字段,为所有电视添加一个新的 body_color 键,其值为 “red”:

UPDATE `e_store`.`products`
SET `attributes` = JSON_SET(
 `attributes` ,
 '$.body_color' ,
 'red'
)
WHERE
 `category_id` = 1;

现在所有电视都应用了 “red” 颜色:

这些函数看起来相同,但它们的行为方式有所不同。

JSON_INSERT 函数仅在属性不存在时才会将其添加到对象中。

JSON_REPLACE 函数仅在找到属性时才会替换它。

JSON_SET 函数如果未找到属性则添加它,否则替换它。

现在,您可以从 JSON 字段更新数据了。

步骤 5:从 JSON 字段删除数据

您可以使用 JSON_REMOVE 函数和 DELETE 从 JSON 字段中删除数据。

JSON_REMOVE 允许您从 JSON 列中删除特定的键/值。

使用 JSON_REMOVE 函数,可以从所有相机中移除 mount_type 键/值对:

UPDATE `e_store`.`products`
SET `attributes` = JSON_REMOVE(`attributes` , '$.mount_type')
WHERE
 `category_id` = 3;

JSON_REMOVE 函数根据路径表达式移除指定的键后,返回更新后的 JSON。

或者,您可以使用 JSON 列来 DELETE 整个行。

使用 DELETEJSON_EXTRACT 以及 LIKE,可以移除所有具有 “Jellybean” 版本 Android 操作系统的手机:

DELETE FROM `e_store`.`products`
WHERE `category_id` = 2
AND JSON_EXTRACT(`attributes` , '$.os') LIKE '%Jellybean%';

此查询将移除 “Desire” 和 “Passion” 型号的手机。

处理特定属性需要使用 JSON_EXTRACT 函数。首先提取手机的 os 属性。然后对包含字符串 Jellybean 的所有记录应用 LIKE 操作符进行 DELETE

现在,您可以从 JSON 字段删除数据了。

步骤 6:创建 Migration

现在,创建一个新的 Laravel 项目。

警告: 此 Web 应用程序仅用于教程目的,不应用于生产环境。

打开终端窗口并运行以下命令:

  1. composer create-project laravel/laravel estore-example

进入新创建的项目目录:

  1. cd estore-example

配置你的 Laravel 应用程序使用 MySQL 数据库。

你可能需要修改 .env 文件来设置 DB_DATABASEDB_USERNAMEDB_PASSWORD

你将分别为 brandscategoriesproducts 创建三个 migration。

创建 create_brands migration:

  1. php artisan make:migration create_brands

使用以下代码修改 create_brands.php migration:

database/migrations/(...)create_brands.php
<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

class CreateBrands extends Migration
{
    /**
     * 运行 migration。
     *
     * @return void
     */
    public function up()
    {
        Schema::create('brands', function(Blueprint $table){
            $table->engine = 'InnoDB';
            $table->increments('id');
            $table->string('name');
            $table->timestamps();
        });
    }

    /**
     * 回滚 migration。
     *
     * @return void
     */
    public function down()
    {
        Schema::dropIfExists('brands');
    }
}

创建 create_categories migration:

  1. php artisan make:migration create_categories

使用以下代码修改 create_categories.php migration:

database/migrations/(...)create_categories.php
<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

class CreateCategories extends Migration
{
    /**
     * 运行 migration。
     *
     * @return void
     */
    public function up()
    {
        Schema::create('categories', function(Blueprint $table){
            $table->engine = 'InnoDB';
            $table->increments('id');
            $table->string('name');
            $table->timestamps();
        });
    }

    /**
     * 回滚 migration。
     *
     * @return void
     */
    public function down()
    {
        Schema::dropIfExists('categories');
    }
}

create_products migration 还将包含索引和外键的指令:

  1. php artisan make:migration create_products

使用以下代码修改 create_products.php migration:

database/migrations/(...)create_products.php
<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

class CreateProducts extends Migration
{
    /**
     * 运行 migration。
     *
     * @return void
     */
    public function up()
    {
        Schema::create('products', function(Blueprint $table){
            $table->engine = 'InnoDB';
            $table->increments('id');
            $table->string('name');
            $table->unsignedInteger('brand_id');
            $table->unsignedInteger('category_id');
            $table->json('attributes');
            $table->timestamps();
            // 外键约束
            $table->foreign('brand_id')->references('id')->on('brands')->onDelete('restrict')->onUpdate('cascade');
            $table->foreign('category_id')->references('id')->on('categories')->onDelete('restrict')->onUpdate('cascade');
            // 索引
            $table->index('brand_id');
            $table->index('category_id');
        });
    }

    /**
     * 回滚 migration。
     *
     * @return void
     */
    public function down()
    {
        Schema::dropIfExists('products');
    }
}

注意 migration 中的 $table->json('attributes'); 语句。

注意: 这仅适用于支持 JSON 数据类型的数据库引擎。

像较旧版本的 MySQL 这样的引擎将无法执行这些 migration。

类似于使用适当的数据类型命名方法创建其他类型的表字段,你使用 json 方法创建了一个名为 attributes 的 JSON 列。

步骤 7:创建模型

你将分别为 brandscategoriesproducts 创建三个模型。

创建 Brand 模型:

  1. php artisan make:model Brand

使用以下代码修改 Brand.php 文件:

app/Models/Brand.php
<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;

class Brand extends Model
{
    use HasFactory;

    // 一个品牌对应多个产品
    public function products(){
        return $this->hasMany('Product')
    }
}

创建 Category 模型:

  1. php artisan make:model Category

使用以下代码修改 Category.php 文件:

app/Models/Category.php
<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;

class Category extends Model
{
    // 一个分类对应多个产品
    public function products(){
        return $this->hasMany('Product')
    }
}

创建 Product 模型:

  1. php artisan make:model Product

使用以下代码修改 Product.php 文件:

app/Models/Product.php
<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;

class Product extends Model
{
    use HasFactory;

    public $timestamps = false;

    // 将 attributes JSON 转换为数组
    protected $casts = [
        'attributes' => 'array'
    ];

    // 每个产品属于一个品牌
    public function brand(){
        return $this->belongsTo('Brand');
    }

    // 每个产品属于一个分类
    public function category(){
        return $this->belongsTo('Category');
    }
}

$casts 数组中将键 attributes 设置为 array,确保每次从数据库获取产品时,其 attributes JSON 都会被转换为关联数组。这允许你在 controller 操作中更新记录。

步骤 8:创建产品

本教程剩余部分的重点将是相机产品类别。

你将构建一个带有相机专用字段的表单视图。为了简洁起见,不会涵盖电视和手机产品类别——但设计会非常相似。

为相机产品类别创建 controller:

  1. php artisan make:controller CameraController

使用以下代码修改 CameraController.php

app/Http/Controller/CameraController.php
<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;

class CameraController extends Controller
{
    // 在数据库中创建产品
    // 使用表单字段
    public function store(Request $request){
        // 创建对象并设置属性
        $camera = new \App\Models\Product();
        $camera->name = $request->name;
        $camera->brand_id = $request->brand_id;
        $camera->category_id = $request->category_id;
        $camera->attributes = [
            'processor' => $request->processor,
            'sensor_type' => $request->sensor_type,
            'monitor_type' => $request->monitor_type,
            'scanning_system' => $request->scanning_system,
        ];
        // 保存到数据库
        $camera->save();
        // 显示创建的相机
        return view('product.camera.show', ['camera' => $camera]);
    }
}

这完成了相机的 store function。

resources/views/product/camera 目录树中创建 new.blade.php 文件来创建视图:

resources/views/product/camera/new.blade.php
<form method="POST" action="/product/camera/store">
    @csrf
    <table>
        <tr>
            <td><label for="name">名称</label></td>
            <td><input id="name" name="name" type="text"></td>
        </tr>
        <tr>
            <td><label for="brand-id">品牌 ID</label></td>
            <td>
                <select id="brand-id" name="brand_id">
                    <option value="1">Samsung</option>
                    <option value="2">Nokia</option>
                    <option value="3">Canon</option>
                </select>
            </td>
        </tr>
        <tr>
            <td><label for="attributes-processor">处理器</label></td>
            <td><input id="attributes-processor" name="processor" type="text"></td>
        </tr>
        <tr>
            <td><label for="attributes-sensor-type">传感器类型</label></td>
            <td><input id="attributes-sensor-type" name="sensor_type" type="text"></td>
        </tr>
        <tr>
            <td><label for="attributes-monitor-type">显示器类型</label></td>
            <td><input id="attributes-monitor-type" name="monitor_type" type="text"></td>
        </tr>
        <tr>
            <td><label for="attributes-scanning-system">扫描系统</label></td>
            <td><input id="attributes-scanning-system" name="scanning_system" type="text"></td>
        </tr>
    </table>
    <input name="category_id" value="3" type="hidden">
    <button type="submit">提交</button>
</form>

brand_id 以硬编码的 select 元素呈现,包含先前创建的三个品牌作为 optioncategory_id 以硬编码的隐藏输入值呈现,设置为相机的 id

修改 routes/web.php 中的路由以显示相机:

routes/web.php
// ...

use App\Http\Controllers\CameraController;

Route::get('/product/camera/new', function() {
    return view('product/camera/new');
});

Route::post(
    '/product/camera/store',
    [CameraController::class, 'store']
);

使用以下命令启动应用:

  1. php artisan serve

然后,在浏览器中访问 localhost:8000/product/camera/new。它将显示一个用于添加新相机的表单。

步骤 9:获取产品

先前在 Product 模型中声明的 $casts 数组将帮助您将属性视为关联数组来读取和编辑产品。

使用以下代码修改 CamerasController

app/Http/Controller/CameraController.php
<?php

// ...

class CameraController extends Controller
{
    // ... store ...

    // 从数据库获取单个产品
    public function show($id){
        $camera = \App\Models\Product::find($id);
        return view('product.camera.show', ['camera' => $camera]);
    }
}

这就完成了相机产品的 show 函数。

resources/views/product/camera 目录树中创建 show.blade.php 视图文件:

resources/views/product/camera/show.blade.php
<table>
    <tr>
        <td>名称</td>
        <td>{{ $camera->name }}</td>
    </tr>
    <tr>
        <td>品牌 ID</td>
        <td>{{ $camera->brand_id }}</td>
    </tr>
    <tr>
        <td>分类 ID</td>
        <td>{{ $camera->category_id }}</td>
    </tr>
    <tr>
        <td>处理器</td>
        <td>{{ $camera->attributes['processor'] }}</td>
    </tr>
    <tr>
        <td>传感器类型</td>
        <td>{{ $camera->attributes['sensor_type'] }}</td>
    </tr>
    <tr>
        <td>显示器类型</td>
        <td>{{ $camera->attributes['monitor_type'] }}</td>
    </tr>
    <tr>
        <td>扫描系统</td>
        <td>{{ $camera->attributes['scanning_system'] }}</td>
    </tr>
</table>

修改 routes/web.php 中的路由以显示相机产品:

routes/web.php
// ...

Route::get(
    '/product/camera/show/{id}',
    [CameraController::class, 'show']
);

使用以下命令启动应用程序:

  1. php artisan serve

然后,在浏览器中访问有效的相机产品 id(例如 localhost:8000/product/camera/show/11)。它将显示 ID 为“11”的产品的相机信息表格。

步骤 10:编辑产品

通过结合 storeshow 的技术,您可以创建一个视图来 edit 现有产品。

您可以创建类似于 new.blade.php 中的表单。然后使用类似于 show.blade.php 中使用的产品变量来预填充它:

<tr>
    <td><label for="attributes-processor">处理器</label></td>
    <td><input id="attributes-processor" name="processor" type="text" value="{{ $camera->attributes['processor'] }}"></td>
</tr>

现在,表单会显示现有值,让用户更容易看到需要更新的内容。

首先,使用 id 检索模型。然后,应用请求中的值。最后,将新值保存到数据库。

第 11 步:基于 JSON 属性的搜索

您还可以使用 Eloquent ORM 查询 JSON 列。

考虑一个搜索页面,允许用户根据他们感兴趣的属性搜索相机。

public function search(Request $request){
    $cameras = \App\Models\Product::where([
        ['attributes->processor', 'like', $request->processor],
        ['attributes->sensor_type', 'like', $request->sensor_type],
        ['attributes->monitor_type', 'like', $request->monitor_type],
        ['attributes->scanning_system', 'like', $request->scanning_system]
    ])->get();
    return view('product.camera.search', ['cameras' => $cameras]);
}

检索到的记录现在将作为 $cameras 集合提供给 product.camera.search 视图。这将允许您循环遍历结果并显示满足用户搜索请求条件的相机。

第 12 步:删除产品

使用非 JSON 列属性,您可以通过指定 where 子句然后调用 delete 方法来删除产品。

例如,在 id 的情况下。

\App\Models\Product::where('id', $id)->delete();

对于 JSON 列,使用单个或多个属性指定 where 子句,然后调用 delete 方法。

\App\Models\Product::where('attributes->sensor_type', 'CMOS')->delete();

在这个例子中,这段代码将删除所有具有设置为“CMOS”的 sensor_type 属性的产品。

常见问题解答

1. 哪些 MySQL 版本支持 JSON?

原生 JSON 数据类型和 JSON 函数在 MySQL 5.7.8 及更高版本中可用,包括 MySQL 8.x。如果您使用的是较旧版本,则无法使用 JSON 列类型或内置 JSON 函数。

2. 如何在 MySQL 中为 JSON 字段创建索引?

您不能直接在 JSON 列上创建索引。为了加速查询,请使用生成的(虚拟或存储)列来提取您过滤的 JSON 路径,然后为该列创建索引。有关 MySQL 中索引的更多信息,请参阅《如何在 MySQL 中使用索引》。

3. 在 MySQL 中存储 JSON 是一种好实践吗?

这取决于使用场景。MySQL 中的 JSON 适用于半结构化或可变属性(例如,产品规格、功能标志),同时将数据的其余部分保留在普通列中。对于高度关系型、严格结构化的数据,传统的列和规范化通常更好。有关何时选择关系型模型与其他模型的更多信息,请参阅数据库类型以及 SQL 与 NoSQL 的比较。

4. JSON 列可以强制执行模式吗?

不能。MySQL 中的 JSON 类型存储有效的 JSON,但不对键或值类型强制执行模式。从 MySQL 8.0.17 开始,您可以使用 JSON_SCHEMA_VALID() 验证文档是否符合 JSON Schema;否则,验证由您的应用程序负责。

5. MySQL 中 JSON 的限制是什么?

JSON 文档以二进制形式存储,并受与其他数据相同的行和数据包大小限制。在实践中,文档大小是有限的(例如,最大行大小和 max_allowed_packet)。JSON 值的理论最大值为 1GB。对于大型或深度嵌套的 JSON,请考虑文档存储或规范化表是否更合适。

结论

在本文中,您设计了一个使用 JSON 数据类型的 MySQL 数据库,并使用 Laravel Web 应用程序连接到它。

每当您需要在单独的表中以键/值对形式保存数据或处理实体的灵活属性时,都应考虑使用 JSON 数据类型字段,因为它可以极大地帮助压缩您的数据库设计。

如果您有兴趣深入了解,MySQL 文档是一个很好的资源,可以进一步探索 JSON 概念。

有关 Laravel 的更多信息,您可以参考我们的技术讲座《Laravel 入门》。

后续步骤和相关资源

  • 在云端运行 MySQL: 使用 Managed Databases for MySQL 来实现自动化备份、高可用性和扩展,这样您就可以专注于构建应用程序,而不必管理数据库。
  • 深入了解 MySQL: 阅读 How To Use Indexes in MySQLHow To Optimize Queries and Tables in MySQL and MariaDB on a VPS,以提升处理 JSON 和其他列时的性能。
  • 选择合适的数据模型: 查看 What Are the Different Types of Databases?A Comparison of NoSQL Database Management Systems and Models,以决定关系型数据库中的 JSON 是否适合,还是文档型或其他数据库类型更合适。