SQL脚本
DROP TABLE IF EXISTS `entrust`;
CREATE TABLE `entrust` (
`id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键ID',
`contract_id` varchar(50) NOT NULL COMMENT '合同id',
`entrust_code` varchar(50) NOT NULL COMMENT '外协编号',
`entrust_name` varchar(50) NOT NULL COMMENT '外协名称',
`entrust_info` json NOT NULL COMMENT '外协扩展信息',
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 69 COMMENT = '外协' ROW_FORMAT = Dynamic;
INSERT INTO `entrust` VALUES (11, '1', 'WX_001', 'HT001-设计外协', '{\"sign_status\": 1, \"entrust_type\": 1}');
INSERT INTO `entrust` VALUES (12, '2', 'WX_002', 'HT002-设计外协', '{\"sign_status\": 1, \"entrust_type\": 1}');
INSERT INTO `entrust` VALUES (13, '3', 'WX_003', 'HT003-设计外协', '{\"sign_status\": 1, \"entrust_type\": 1}');
INSERT INTO `entrust` VALUES (14, '4', 'WX_004', 'HT004-设计外协', '{\"sign_status\": 1, \"entrust_type\": 1}');
INSERT INTO `entrust` VALUES (16, '1', 'WX_001', 'HT001-勘察外协', '{\"sign_status\": 1, \"entrust_type\": 1}');
INSERT INTO `entrust` VALUES (17, '2', 'WX_002', 'HT002-勘察外协', '{\"sign_status\": 1, \"entrust_type\": 1}');
INSERT INTO `entrust` VALUES (18, '3', 'WX_003', 'HT003-勘察外协', '{\"sign_status\": 1, \"entrust_type\": 1}');
INSERT INTO `entrust` VALUES (19, '4', 'WX_004', 'HT004-勘察外协', '{\"sign_status\": 1, \"entrust_type\": 1}');
INSERT INTO `entrust` VALUES (23, '1', 'WX_001', 'HT001-物探外协', '{\"sign_status\": 1, \"entrust_type\": 1}');
INSERT INTO `entrust` VALUES (24, '2', 'WX_002', 'HT002-物探外协', '{\"sign_status\": 1, \"entrust_type\": 1}');
INSERT INTO `entrust` VALUES (25, '3', 'WX_003', 'HT003-物探外协', '{\"sign_status\": 1, \"entrust_type\": 1}');
INSERT INTO `entrust` VALUES (26, '4', 'WX_004', 'HT004-物探外协', '{\"sign_status\": 1, \"entrust_type\": 1}');
INSERT INTO `entrust` VALUES (27, '1', 'WX_001', 'HT001-测绘外协', '{\"sign_status\": 2, \"entrust_type\": 1}');
INSERT INTO `entrust` VALUES (28, '2', 'WX_002', 'HT002-测绘外协', '{\"sign_status\": 1, \"entrust_type\": 1}');
INSERT INTO `entrust` VALUES (29, '3', 'WX_003', 'HT003-测绘外协', '{\"sign_status\": 1, \"entrust_type\": 1}');
INSERT INTO `entrust` VALUES (30, '4', 'WX_004', 'HT004-测绘外协', '{\"sign_status\": 1, \"entrust_type\": 1}');
INSERT INTO `entrust` VALUES (38, '1', 'WX_001', 'HT001-劳务外协', '{\"sign_status\": 2, \"entrust_type\": 1}');
INSERT INTO `entrust` VALUES (39, '2', 'WX_002', 'HT002-劳务外协', '{\"sign_status\": 1, \"entrust_type\": 1}');
INSERT INTO `entrust` VALUES (40, '3', 'WX_003', 'HT003-劳务外协', '{\"sign_status\": 2, \"entrust_type\": 1}');
INSERT INTO `entrust` VALUES (41, '4', 'WX_004', 'HT004-劳务外协', '{\"sign_status\": 1, \"entrust_type\": 1}');
DROP TABLE IF EXISTS `contract`;
CREATE TABLE `contract` (
`id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键ID',
`contract_code` varchar(50) NOT NULL COMMENT '合同编号',
`contract_name` varchar(50) NOT NULL COMMENT '合同名称',
`create_time` datetime NOT NULL COMMENT '创建时间',
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 11112 COMMENT = '合同' ROW_FORMAT = Dynamic;
INSERT INTO `contract` VALUES (1, 'HT001', 'HT001-1', '2024-05-15 00:00:00');
INSERT INTO `contract` VALUES (2, 'HT002', 'HT002-1', '2024-05-15 00:00:00');
INSERT INTO `contract` VALUES (3, 'HT003', 'HT003-1', '2024-05-15 00:00:00');
INSERT INTO `contract` VALUES (4, 'HT004', 'HT004-1', '2024-05-15 08:55:49');
要求优化下方的MySQL语句
SELECT
e.*,
c.contract_name
FROM
contract c
JOIN entrust e ON c.id = e.contract_id AND e.entrust_info LIKE '%"sign_status": 2%'
WHERE
c.id IN ( 1, 3 );
思路分析
- 避免使用 LIKE,LIKE 操作符在 JSON 字段上通常不会利用索引,因此我们可以使用 JSON 函数来直接访问 entrust_info 中的 sign_status 字段
- 使用 JSON 函数, MySQL 提供了 JSON 函数,比如
JSON_EXTRACT()
可以直接提取 JSON 数据中的字段,这样可以提高查询效率 - 使用 JOIN 代替子查询:通过将子查询转换为 JOIN,可以提高性能,因为这样可以减少查询的复杂性
- 为 签订状态(sign_status)添加索引
参考答案
1. 使用 JSON 函数代替子查询
SELECT
e.*,
c.contract_name
FROM
contract c
JOIN
entrust e ON c.id = e.contract_id
WHERE
c.id IN (1, 3)
AND JSON_UNQUOTE(JSON_EXTRACT(e.entrust_info, '$.sign_status')) = '2';
JSON_EXTRACT()
使用该函数提取 entrust_info 中的 sign_status 字段的值JSON_UNQUOTE()
将提取的 JSON 值转换为普通字符串,以便进行比较JOIN
结构 保持原有的 JOIN 结构,确保能够获取到合同名称
2. 索引优化
ALTER TABLE entrust ADD COLUMN sign_status INT GENERATED ALWAYS AS (JSON_UNQUOTE(JSON_EXTRACT(entrust_info, '$.sign_status'))) VIRTUAL;
CREATE INDEX idx_sign_status ON entrust (sign_status);
3. 最终SQL结果
SELECT
e.*,
c.contract_name
FROM
contract c
JOIN
entrust e ON c.id = e.contract_id
WHERE
c.id IN (1, 3)
AND e.sign_status = 2;