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;
相关推荐
北漂老男孩1 分钟前
MySQL、Oracle 和 PostgreSQL 是三种主流的关系型数据库的主要原理性差异分析
数据库·mysql·postgresql·oracle
小小鸭程序员25 分钟前
零基础教程:Windows电脑安装Linux系统(双系统/虚拟机)全攻略
linux·运维·服务器·mysql·spring
GalaxyPokemon2 小时前
MySQL基础 [六] - 内置函数+复合查询+表的内连和外连
linux·运维·数据库·mysql·ubuntu
Linux运维老纪2 小时前
Linux 命令清单(Linux Command List)
linux·运维·服务器·数据库·mysql·云计算·运维开发
biubiubiu07064 小时前
Mysql
数据库·mysql
RainbowSea5 小时前
7. MySQL 当中的 InnoDB 数据存储结构(详解)
java·sql·mysql
关山月6 小时前
设计 MySQL 表的 14 条原则
mysql
firepation7 小时前
基于 springboot 的在线考试系统
java·spring boot·mysql·源码·课程设计
程序猿阿伟7 小时前
《 Scikit-learn与MySQL的深度协同:构建智能数据生态系统的架构哲学》
mysql·架构·scikit-learn
梦三辰8 小时前
超详细解读:数据库MVCC机制
数据库·mysql·mvcc·快照