与 TEXT 的对比优势
| 特性 | JSON 类型 | TEXT/VARCHAR |
| ----- | -------- | ------------ |
| 格式校验 | 自动 | 无 |
| 二进制存储 | 是 | 否 |
| 字段提取 | 支持路径访问 | 需全量解析 |
| 部分更新 | 支持(8.0+) | 整列替换 |
1. 建表语句
CREATE TABLE `user_order` (
`id` int NOT NULL AUTO_INCREMENT,
`user_info` json DEFAULT NULL,
`userName` varchar(64) AS (user_info->>'$.name') STORED,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
2. 操作函数
2.1 JSON_VALID() json格式校验
基础用法
-- 验证通过返回 1
SELECT JSON_VALID('{"name": "张三"}'); -- 结果: 1
-- 验证失败返回 0(键未加双引号)
SELECT JSON_VALID('{name: "张三"}'); -- 结果: 0
-- 输入 NULL 返回 NULL
SELECT JSON_VALID(NULL); -- 结果: NULL
实际应用场景
场景 A:CHECK 约束(插入前强制校验)
CREATE TABLE config (
id INT PRIMARY KEY,
settings VARCHAR(2000),
-- 确保settings必须是合法JSON或NULL
CONSTRAINT chk_json CHECK (JSON_VALID(settings) OR settings IS NULL)
);
-- 成功
INSERT INTO config VALUES (1, '{"debug": true}');
-- 失败(违反约束)
INSERT INTO config VALUES (2, 'invalid json'); -- ERROR 3819: check constraint violation
场景 B:插入/更新前过滤(避免报错)
-- 只插入合法JSON,非法的转为NULL或跳过
INSERT INTO logs (id, data)
SELECT id, CASE
WHEN JSON_VALID(raw_str) = 1 THEN raw_str
ELSE NULL -- 或抛出错误/记录日志
END
FROM temp_import_table;
-- 更新时校验
UPDATE user_ext
SET ext_json = new_value
WHERE id = 1 AND JSON_VALID(new_value) = 1; -- 非法JSON不执行更新
场景 C:查询时过滤脏数据
-- 清理表中非法JSON数据
SELECT * FROM legacy_table WHERE JSON_VALID(json_col) = 0;
-- 统计合法/非法数量
SELECT
SUM(JSON_VALID(data)) as valid_count,
SUM(NOT JSON_VALID(data)) as invalid_count
FROM logs;
与直接插入的区别
| 方式 | 特点 | 适用场景 |
| -------------------- | --------------- | --------------- |
| **直接 INSERT** | 非法JSON直接报错,事务回滚 | 应用层已保证格式正确,追求性能 |
| **JSON\_VALID() 预检** | 先验证再操作,可控错误处理 | 接收外部/用户输入,需容错处理 |
结合 Java/MyBatis 使用
// 插入前在代码层预检(避免SQL报错)
public void insertConfig(String jsonStr) {
// 先校验,不过关直接抛业务异常
if (!isValidJson(jsonStr)) {
throw new IllegalArgumentException("JSON格式错误");
}
mapper.insertConfig(jsonStr);
}
// 或在SQL层过滤(批量插入时跳过脏数据)
// Mapper XML
<insert id="batchInsertSafe">
INSERT INTO logs (id, data) VALUES
<foreach collection="list" item="item" separator=",">
(#{item.id},
CASE WHEN JSON_VALID(#{item.jsonStr}) = 1
THEN #{item.jsonStr} ELSE NULL END)
</foreach>
</insert>
常见非法 JSON 示例
-- 这些都会返回 0
SELECT JSON_VALID('{name: "test"}'); -- 键必须用双引号
SELECT JSON_VALID("{'key': 'value'}"); -- 单引号包裹整体,内部也用单引号
SELECT JSON_VALID('{key: "value",}'); -- 尾随逗号
SELECT JSON_VALID(''); -- 空字符串
SELECT JSON_VALID('null'); -- 注意:这是合法的(JSON表示空值)
2.2 提取函数 JSON_EXTRACT()(或 -> / ->> 快捷操作符)
操作符语法(最常用)
| 操作符 | 返回类型 | 特点 | 示例结果 |
| ----- | ------- | --------- | ------ |
| `->` | JSON 格式 | 带引号包裹 | `"张三"` |
| `->>` | 普通文本 | 去引号,可直接比较 | `张三` |
SELECT user_info->>'$.name' FROM user_order;
JSON_EXTRACT() 函数(多路径/动态路径)
-- 基础提取
JSON_EXTRACT(json_column, '$.path')
-- 提取多个路径(返回数组)
JSON_EXTRACT(json_column, '$.name', '$.age', '$.tags[0]')
路径语法详解
-- 单层键
data->>'$.name'
-- 嵌套对象(用点号)
data->>'$.user.profile.email'
-- 数组索引(从 0 开始)
data->>'$.tags[0]' -- 第一个元素
data->>'$.tags[-1]' -- 最后一个元素(MySQL 8.0 支持)
-- 数组范围(返回 JSON 数组)
JSON_EXTRACT(data, '$.tags[0 to 1]') -- ["java", "mysql"]
-- 通配符(8.0+)
data->>'$[*]' -- 提取根下所有值(如果根是数组)
data->>'$.user.*' -- 提取 user 下所有键的值
实战示例
示例数据准备
CREATE TABLE products (
id INT PRIMARY KEY,
info JSON
);
INSERT INTO products VALUES (1, '{
"name": "MacBook Pro",
"price": 14999,
"specs": {"cpu": "M3", "ram": "16G", "ssd": "512G"},
"colors": ["深空灰", "银色"],
"tags": ["笔记本", "苹果", "办公"]
}');
提取不同数据类型
-- 1. 提取字符串(必须用 ->> 否则带引号)
SELECT info->>'$.name' as name, -- MacBook Pro
info->'$.name' as name_json -- "MacBook Pro"
FROM products;
-- 2. 提取数字(可直接计算)
SELECT info->>'$.price' * 0.8 as discount_price -- 11999.2
FROM products;
-- 3. 提取嵌套对象
SELECT info->>'$.specs.cpu' as cpu, -- M3
info->>'$.specs.ram' as ram -- 16G
FROM products;
-- 4. 提取数组元素
SELECT info->>'$.colors[0]' as first_color, -- 深空灰
info->>'$.colors[1]' as second_color -- 银色
FROM products;
-- 5. 提取整个子对象(返回 JSON 字符串)
SELECT info->'$.specs' as specs_json -- {"cpu": "M3", "ram": "16G", ...}
FROM products;
-- 6. 提取多个值(JSON_EXTRACT 特有)
SELECT JSON_EXTRACT(info, '$.name', '$.price', '$.specs.cpu') as multi
-- 结果: ["MacBook Pro", 14999, "M3"]
FROM products;
条件查询中的使用
-- 精确匹配(->> 去引号后比较)
SELECT * FROM products
WHERE info->>'$.specs.cpu' = 'M3';
-- 数值范围(->> 返回字符串,需类型转换或隐式转换)
SELECT * FROM products
WHERE info->>'$.price' > 10000; -- MySQL 自动转数值比较
-- 数组包含(JSON_CONTAINS 更高效,但也可用路径)
SELECT * FROM products
WHERE info->>'$.tags[0]' = '笔记本'
OR info->>'$.tags[1]' = '笔记本'; -- 笨办法
-- 推荐:检查数组中是否包含元素(任意位置)
SELECT * FROM products
WHERE JSON_CONTAINS(info->>'$.tags', '"笔记本"'); -- 注意内部引号
-- 模糊查询(->> 提取后 LIKE)
SELECT * FROM products
WHERE info->>'$.name' LIKE '%Mac%';
与生成列结合(优化查询)
-- 添加虚拟列存储提取值(用于索引)
ALTER TABLE products
ADD COLUMN cpu_model VARCHAR(10) AS (info->>'$.specs.cpu') STORED,
ADD COLUMN price_val DECIMAL(10,2) AS (info->>'$.price') STORED;
-- 建立索引(必须基于 STORED 生成列)
CREATE INDEX idx_cpu ON products(cpu_model);
CREATE INDEX idx_price ON products(price_val);
-- 现在可以直接用生成列查询(走索引)
SELECT * FROM products WHERE cpu_model = 'M3';
常见错误与解决方案
-- 错误 1:路径不存在返回 NULL(不会报错)
SELECT info->>'$.nonexistent'; -- NULL
-- 错误 2:数组越界返回 NULL
SELECT info->>'$.colors[99]'; -- NULL
-- 错误 3:混淆 -> 和 ->>
SELECT * FROM products WHERE info->'$.name' = 'MacBook Pro'; -- 无结果(比较 "MacBook Pro")
-- 错误 4:JSON 中的 null vs SQL NULL
SELECT info->>'$.optional_field' IS NULL; -- 判断 JSON 中是否存在该键
性能提示
-
大范围扫描慢:
WHERE json_col->>'$.field' = 'value'会全表扫描(无法使用索引) -
解决方案:提取到生成列并建索引,或用 MySQL 8.0 的多值索引
-
类型注意:
->>返回的是字符串,数值比较时 MySQL 会隐式转换,但范围查询建议显式转换或直接用生成列
2.3 修改:JSON_SET()(新增/更新)、JSON_REPLACE()(仅更新)、JSON_REMOVE()(删除)
以下是这三个修改函数的核心区别与实战案例,关键差异在于对"不存在路径"的处理方式:
| 函数 | 路径存在 | 路径不存在 | 适用场景 |
| ---------------- | ------ | --------- | ---------------- |
| `JSON_SET()` | **更新** | **新增** | 动态配置(有则改,无则加) |
| `JSON_REPLACE()` | **更新** | **忽略** | 安全更新(只改现有字段,不新增) |
| `JSON_REMOVE()` | **删除** | **忽略/报错** | 清理字段 |
1. JSON_SET() - 有则改,无则加(Upsert)
-- 基础用法:更新 price,新增 discount(原 JSON 中没有 discount)
UPDATE products
SET info = JSON_SET(info,
'$.price', 12999, -- 已存在,更新
'$.discount', 0.15, -- 不存在,新增
'$.stock', 100 -- 不存在,新增
)
WHERE id = 1;
-- 嵌套对象操作(自动创建层级)
UPDATE users
SET profile = JSON_SET(profile,
'$.contact.phone', '13800138000', -- 如果 contact 不存在,会自动创建对象
'$.contact.email', 'test@qq.com',
'$.settings.theme', 'dark' -- 深层嵌套也支持
)
WHERE id = 100;
2. JSON_REPLACE() - 只改现有,不新增(安全更新)
-- 场景:只允许更新已有配置,禁止新增字段(防止脏数据)
UPDATE system_config
SET config = JSON_REPLACE(config,
'$.api_key', 'new_secret_key', -- 已存在,更新成功
'$.timeout', 30, -- 已存在,更新成功
'$.hacker_field', 'injected' -- 不存在,**自动忽略,不会新增**
)
WHERE app_id = 'order_service';
-- 实际应用:部分字段更新(避免误创建错别字路径)
UPDATE users
SET prefs = JSON_REPLACE(prefs,
'$.lang', 'en', -- 正确更新
'$.lage', 'en' -- 拼写错误路径,被忽略(如果用 JSON_SET 就悲剧了)
)
WHERE id = 1;
3. JSON_REMOVE() - 删除字段/数组元素
-- 删除单个字段(对象键)
UPDATE logs
SET data = JSON_REMOVE(data, '$.password', '$.token') -- 可同时删多个路径
WHERE id < 100;
-- 删除数组元素(按索引,注意:删除后数组长度收缩,索引会变化)
UPDATE tasks
SET tags = JSON_REMOVE(tags, '$[1]') -- 删除数组第 2 个元素(索引 1)
WHERE task_id = 5;
-- 删除嵌套路径
UPDATE orders
SET meta = JSON_REMOVE(meta, '$.temp_info.cache_key')
WHERE create_time < DATE_SUB(NOW(), INTERVAL 7 DAY);
4. 三函数对比实战(同一数据看效果)
-- 初始数据
SET @json = '{"name": "张三", "age": 25, "role": "user"}';
-- JSON_SET:新增 + 更新
SELECT JSON_SET(@json, '$.age', 26, '$.email', 'zhangsan@qq.com');
-- 结果: {"name": "张三", "age": 26, "role": "user", "email": "zhangsan@qq.com"}
-- JSON_REPLACE:仅更新(email 被忽略)
SELECT JSON_REPLACE(@json, '$.age', 26, '$.email', 'zhangsan@qq.com');
-- 结果: {"name": "张三", "age": 26, "role": "user"} ← email 没加进来
-- JSON_REMOVE:删除 age
SELECT JSON_REMOVE(@json, '$.age');
-- 结果: {"name": "张三", "role": "user"}
5. 复杂场景:数组与嵌套操作
-- 场景:更新用户权限列表(数组),并修改最后登录时间
UPDATE admin_users
SET permissions = JSON_SET(permissions,
-- 更新数组第 0 个元素
'$[0].access', 'full',
-- 追加到数组(先获取长度再插入,或直接指定大索引)
'$[999]', JSON_OBJECT('module', 'finance', 'read', true)
),
profile = JSON_REMOVE(profile, '$.temp_session') -- 清理临时字段
WHERE user_id = 1;
-- 删除数组中满足条件的元素(8.0+ 特色,结合 JSON_SEARCH)
UPDATE items
SET tags = JSON_REMOVE(tags,
-- JSON_SEARCH 返回路径如 "$[2]",可直接用于 REMOVE
JSON_UNQUOTE(JSON_SEARCH(tags, 'one', '废弃标签'))
)
WHERE item_id = 100;
6. 高级技巧:条件更新与链式操作
-- 结合 IF 做条件判断(如果库存为 0,增加 sold_out 标记)
UPDATE products
SET info = JSON_SET(info,
'$.status', IF(JSON_EXTRACT(info, '$.stock') = 0, 'sold_out', 'on_sale'),
'$.last_update', NOW()
)
WHERE id = 1;
-- 链式删除多个层级(清理敏感字段)
UPDATE audit_logs
SET payload = JSON_REMOVE(payload,
'$.user.password',
'$.user.id_card',
'$.internal.debug_info'
)
WHERE created_at < '2024-01-01';
常见坑点提醒
-
数组越界:
JSON_SET(arr, '$[999]', 'val')会给数组填充null直到第 999 位(稀疏数组),不是追加 -
REPLACE 的静默失败:路径不存在时不报错,只是不生效,容易误以为更新成功
-
REMOVE 不存在的路径:忽略操作,不报错,但数据不变
-
返回值必须回写:
SET col = JSON_XXX(col, ...),不会自动保存
2.4 聚合:JSON_ARRAYAGG()、JSON_OBJECTAGG()
1. 基础语法对比
| 函数 | 输出格式 | 适用场景 | 键值要求 |
| -------------------------- | ------------------------ | --------------------- | --------- |
| `JSON_ARRAYAGG(col)` | `[val1, val2, ...]` | 构造列表(如商品列表、标签数组) | 无需键,只取列值 |
| `JSON_OBJECTAGG(key, val)` | `{"k1":"v1", "k2":"v2"}` | 构造映射(如配置项、ID-Name 对照) | 必须指定键列和值列 |
2. 实战数据准备
CREATE TABLE orders (
order_id INT PRIMARY KEY,
user_name VARCHAR(50),
create_date DATE
);
CREATE TABLE order_items (
id INT PRIMARY KEY,
order_id INT,
product_name VARCHAR(100),
price DECIMAL(10,2),
qty INT,
category VARCHAR(50)
);
-- 插入测试数据
INSERT INTO orders VALUES
(1, '张三', '2026-01-20'),
(2, '李四', '2026-01-21');
INSERT INTO order_items VALUES
(101, 1, 'iPhone 16', 5999.00, 1, '电子产品'),
(102, 1, 'AirPods', 1299.00, 2, '电子产品'),
(103, 1, '手机壳', 99.00, 1, '配件'),
(201, 2, 'MacBook', 12999.00, 1, '电子产品'),
(202, 2, '鼠标', 299.00, 1, '配件');
3. JSON_ARRAYAGG() - 构造列表数组
场景 A:订单商品列表(将多行商品名聚合成数组)
SELECT
o.order_id,
o.user_name,
JSON_ARRAYAGG(oi.product_name) AS product_list
FROM orders o
JOIN order_items oi ON o.order_id = oi.order_id
GROUP BY o.order_id;
-- 结果:
-- order_id | user_name | product_list
-- 1 | 张三 | ["iPhone 16", "AirPods", "手机壳"]
-- 2 | 李四 | ["MacBook", "鼠标"]
场景 B:聚合对象数组(每个元素是对象)
SELECT
o.order_id,
JSON_ARRAYAGG(
JSON_OBJECT(
'name', oi.product_name,
'price', oi.price,
'qty', oi.qty
)
) AS items_detail
FROM orders o
JOIN order_items oi ON o.order_id = oi.order_id
GROUP BY o.order_id;
-- 结果:
-- [{"name": "iPhone 16", "price": 5999.00, "qty": 1}, {"name": "AirPods", ...}]
场景 C:去重与排序(8.0.17+)
-- DISTINCT 去重 + ORDER BY 排序(8.0.17 起支持)
SELECT
o.order_id,
JSON_ARRAYAGG(DISTINCT oi.category ORDER BY oi.category ASC) AS categories
FROM orders o
JOIN order_items oi ON o.order_id = oi.order_id
GROUP BY o.order_id;
-- 结果:["电子产品", "配件"](自动去重且排序)
4. JSON_OBJECTAGG() - 构造键值对映射
场景 A:ID-Name 映射表
SELECT
JSON_OBJECTAGG(order_id, user_name) AS user_map
FROM orders;
-- 结果:{"1": "张三", "2": "李四"}
场景 B:商品名称与价格映射
SELECT
order_id,
JSON_OBJECTAGG(product_name, price) AS price_map
FROM order_items
GROUP BY order_id;
-- 结果:
-- order_id | price_map
-- 1 | {"iPhone 16": 5999.00, "AirPods": 1299.00, "手机壳": 99.00}
-- 2 | {"MacBook": 12999.00, "鼠标": 299.00}
场景 C:处理键冲突(取最后一个值)
-- 注意:如果键重复,后面的值会覆盖前面的!
INSERT INTO order_items VALUES (104, 1, '手机壳', 199.00, 1, '配件'); -- 手机壳重复
SELECT
order_id,
JSON_OBJECTAGG(product_name, price) AS price_map
FROM order_items
WHERE order_id = 1
GROUP BY order_id;
-- 结果:{"iPhone 16": 5999.00, "AirPods": 1299.00, "手机壳": 199.00}
-- 注意:第一个手机壳(99元)被第二个(199元)覆盖了!
5. 复杂组合:构造标准 API 响应结构
-- 目标格式:{"order_id": 1, "user": "张三", "items": [...], "categories": {...}}
SELECT
JSON_OBJECT(
'order_id', o.order_id,
'user', o.user_name,
'total_items', COUNT(oi.id),
'items', JSON_ARRAYAGG(
JSON_OBJECT(
'product', oi.product_name,
'price', oi.price * oi.qty
)
),
'category_summary', JSON_OBJECTAGG(
oi.category,
SUM(oi.price * oi.qty) -- 聚合函数嵌套
)
) AS api_response
FROM orders o
JOIN order_items oi ON o.order_id = oi.order_id
GROUP BY o.order_id;
-- 单个结果示例:
-- {
-- "order_id": 1,
-- "user": "张三",
-- "total_items": 3,
-- "items": [{"product": "iPhone 16", "price": 5999.00}, {"product": "AirPods", ...}],
-- "category_summary": {"电子产品": 8597.00, "配件": 99.00}
-- }
6. 分组统计与数组聚合结合(8.0+ 窗口函数)
-- 每个类别中的商品列表(使用窗口函数避免子查询)
SELECT DISTINCT
category,
JSON_ARRAYAGG(product_name) OVER (PARTITION BY category) AS products_in_category
FROM order_items;
-- 结果:
-- category | products_in_category
-- 电子产品 | ["iPhone 16", "AirPods", "MacBook"]
-- 配件 | ["手机壳", "鼠标"]
7. 注意事项与陷阱
A. NULL 值处理
-- NULL 值会被忽略(不会加入数组/对象)
-- 如果想保留 NULL,需用 IFNULL 转换
JSON_ARRAYAGG(IFNULL(product_name, '未知商品'))
B. 结果长度限制
-- 受 group_concat_max_len 限制(默认 1024 字节)
-- 大数据量前需调整:SET SESSION group_concat_max_len = 1000000;
C. 键冲突警告(JSON_OBJECTAGG)
-- 重复的键会静默覆盖,且 8.0 前无警告!
-- 建议先子查询去重,或用 JSON_ARRAYAGG 替代
D. 与 GROUP_CONCAT 对比
-- 旧方案(字符串拼接,需手动处理引号):
GROUP_CONCAT(CONCAT('"', product_name, '"')) -- 繁琐且易错
-- 新方案(原生 JSON):
JSON_ARRAYAGG(product_name) -- 自动转义,格式标准
8. MyBatis 集成示例(Java)
<!-- 查询订单详情(自动映射为 JSON 字符串) -->
<select id="getOrderDetail" resultType="String">
SELECT JSON_OBJECT(
'orderId', o.order_id,
'items', JSON_ARRAYAGG(
JSON_OBJECT('name', oi.product_name, 'price', oi.price)
)
) as json_result
FROM orders o
JOIN order_items oi ON o.order_id = oi.order_id
WHERE o.order_id = #{orderId}
GROUP BY o.order_id
</select>
建议:在 5.7+ 中优先使用 JSON 聚合函数替代 GROUP_CONCAT + 字符串拼接,避免手动处理引号和转义,且便于前端直接 JSON.parse() 使用。
3. 生成列
场景 1:可以基于JSON字段新增生成列
假设已有 data 列为 JSON 类型,现在想从其中提取 name 字段作为生成列
-- 添加虚拟生成列(不占用存储空间,查询时实时计算)
ALTER TABLE users
ADD COLUMN name VARCHAR(64) AS (data->>'$.name') VIRTUAL;
-- 或添加存储生成列(占用磁盘空间,写入时计算,查询更快)
ALTER TABLE users
ADD COLUMN name VARCHAR(64) AS (data->>'$.name') STORED;
场景 2:将已有普通列强制改为生成列
MODIFY 会报错(生成列的值必须由表达式计算,不允许手动插入)。方案 A:先清空数据再修改(谨慎操作)
-- 1. 备份数据
CREATE TABLE users_backup AS SELECT * FROM users;
-- 2. 清空表(或删除该列数据)
UPDATE users SET price = NULL; -- 或根据业务处理
-- 3. 改为生成列(示例:price 改为 base_price * 0.8)
ALTER TABLE users
MODIFY COLUMN price DECIMAL(10,2)
AS (base_price * 0.8) STORED;
方案 B:删旧列建新列(推荐)
-- 1. 先备份原列数据(如果需要)
ALTER TABLE users ADD COLUMN price_backup DECIMAL(10,2);
UPDATE users SET price_backup = price;
-- 2. 删除原列
ALTER TABLE users DROP COLUMN price;
-- 3. 添加同名生成列
ALTER TABLE users
ADD COLUMN price DECIMAL(10,2)
AS (base_price * 0.8) STORED;
关键语法参数
| 参数 | 说明 | 适用场景 |
| --------- | --------------- | ---------------- |
| `VIRTUAL` | 虚拟列,不存储,查询时实时计算 | 数据一致性要求高,磁盘敏感 |
| `STORED` | 存储列,写入时计算并持久化 | 查询性能优先,减少 CPU 开销 |
完整示例:JSON 提取优化
-- 原表
CREATE TABLE logs (
id INT PRIMARY KEY,
raw_data JSON
);
-- 插入测试数据
INSERT INTO logs VALUES (1, '{"user": "张三", "action": "login", "ip": "192.168.1.1"}');
-- 添加多个生成列(用于索引和快速查询)
ALTER TABLE logs
ADD COLUMN user_name VARCHAR(32) AS (raw_data->>'$.user') STORED,
ADD COLUMN action_type VARCHAR(32) AS (raw_data->>'$.action') VIRTUAL,
ADD COLUMN user_ip VARCHAR(16) AS (raw_data->>'$.ip') STORED;
-- 现在可以直接查询生成列,且可建索引
SELECT user_name, action_type FROM logs WHERE user_name = '张三';
注意事项
-
表达式限制:不能包含子查询、参数、变量、某些不确定函数(如
NOW()只能用于STORED列) -
索引支持:生成列可建索引,这是优化 JSON 查询的主要手段
-
8.0.13+ 多值索引:针对 JSON 数组可建特殊索引
ALTER TABLE items
ADD COLUMN tags_array VARCHAR(64) ARRAY AS (data->'$.tags') STORED;
注意:本文归作者所有,未经作者允许,不得转载