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;
相关推荐
zmgst1 小时前
canal1.1.7使用canal-adapter进行mysql同步数据
java·数据库·mysql
令狐少侠20111 小时前
explain执行计划分析 ref_
mysql
随心............1 小时前
python操作MySQL以及SQL综合案例
数据库·mysql
xjjeffery1 小时前
MySQL 基础
数据库·mysql
恒辉信达1 小时前
hhdb数据库介绍(8-4)
服务器·数据库·mysql
小兜全糖(xdqt)4 小时前
mysql数据同步到sql server
mysql·adb
Karoku0664 小时前
【企业级分布式系统】Zabbix监控系统与部署安装
运维·服务器·数据库·redis·mysql·zabbix
周全全4 小时前
MySQL报错解决:The user specified as a definer (‘root‘@‘%‘) does not exist
android·数据库·mysql
白云如幻4 小时前
MySQL的分组函数
数据库·mysql
秋意钟6 小时前
MySQL日期类型选择建议
数据库·mysql