MySQL查询语句优化

SQL脚本

sql 复制代码
-- ----------------------------
-- Table structure for entrust
-- ----------------------------
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;

-- ----------------------------
-- Records of entrust
-- ----------------------------
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}');

-- ----------------------------
-- Table structure for contract
-- ----------------------------
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;

-- ----------------------------
-- Records of contract
-- ----------------------------
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语句

sql 复制代码
-- 优化下面的SQL
-- Sql说明:查询合同 id (1,3)下签订状态(sign_status)等于 2 的外协(entrust)信息
-- 要求: 所有字段都走索引
-- 提示: 可以新增字段,但不能把 contract_name 冗余到 entrust 表直接改成单表查询

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 );

思路分析

  1. 避免使用 LIKE,LIKE 操作符在 JSON 字段上通常不会利用索引,因此我们可以使用 JSON 函数来直接访问 entrust_info 中的 sign_status 字段
  2. 使用 JSON 函数, MySQL 提供了 JSON 函数,比如 JSON_EXTRACT() 可以直接提取 JSON 数据中的字段,这样可以提高查询效率
  3. 使用 JOIN 代替子查询:通过将子查询转换为 JOIN,可以提高性能,因为这样可以减少查询的复杂性
  4. 为 签订状态(sign_status)添加索引

参考答案

1. 使用 JSON 函数代替子查询
sql 复制代码
--  1. 避免使用 LIKE, LIKE 操作符在 JSON 字段上通常不会利用索引,因此我们可以使用 JSON 函数来直接访问 entrust_info 中的 sign_status 字段 
--  2. 使用 JSON 函数, MySQL 提供了 JSON 函数,比如 JSON_EXTRACT() 可以直接提取 JSON 数据中的字段,这样可以提高查询效率 
--  3. 使用 JOIN 代替子查询:通过将子查询转换为 JOIN,可以提高性能,因为这样可以减少查询的复杂性。
--  基于以上优化思路,以下是重写后的 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 JSON_UNQUOTE(JSON_EXTRACT(e.entrust_info, '$.sign_status')) = '2';	
  1. JSON_EXTRACT() 使用该函数提取 entrust_info 中的 sign_status 字段的值
  2. JSON_UNQUOTE() 将提取的 JSON 值转换为普通字符串,以便进行比较
  3. JOIN 结构 保持原有的 JOIN 结构,确保能够获取到合同名称
2. 索引优化
sql 复制代码
-- 索引建议: 
-- 为了确保所有字段都走索引,建议在 entrust 表的 entrust_info 字段上创建一个虚拟列,并为该虚拟列创建索引。可以按照以下步骤进行:
 
-- 1. 添加虚拟列
ALTER TABLE entrust ADD COLUMN sign_status INT GENERATED ALWAYS AS (JSON_UNQUOTE(JSON_EXTRACT(entrust_info, '$.sign_status'))) VIRTUAL;

-- 2. 创建索引
CREATE INDEX idx_sign_status ON entrust (sign_status);
3. 最终SQL结果
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;
相关推荐
张某布响丸辣37 分钟前
SQL关键字的优先级执行顺序:深入理解SQL查询的构造
java·sql·mysql·面试
万笑佛1 小时前
mysql后台导入sql文件-设定字符集
mysql
沐风ya2 小时前
mysql索引 -- 全文索引介绍(如何创建,使用),explain关键字
数据库·mysql
帆船3 小时前
SQL注入
mysql
Passion不晚3 小时前
【面试题】mysql中怎么保持主从数据库一致
数据库·mysql·面试
Python私教4 小时前
macOS安装MySQL以后如何配置环境变量
数据库·mysql·macos
wrx繁星点点4 小时前
Java 事务隔离级别及相关概念
java·开发语言·数据库·mysql
百成Java4 小时前
基于springboot的旅游网站
java·spring boot·后端·mysql·spring·智能家居·旅游
为java添砖加瓦5 小时前
【读写分离?聊聊Mysql多数据源实现读写分离的几种方案】
java·数据库·spring boot·后端·mysql·spring·mybatis
木鬼与槐8 小时前
MySQL高阶1988-找出没所学校的最低分数要求
android·前端·mysql