数据准备
测试数据5000条,Mysql 8.0
DROP TABLE IF EXISTS `reconc_billing_proposal_request_cancle_info`;
CREATE TABLE `reconc_billing_proposal_request_cancle_info` (
`id` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
`send_company_id` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '发送公司ID',
`accept_company_id` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '接受公司ID',
`send_tenant_id` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '发送租户ID',
`accept_tenant_id` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '接受租户ID',
`request_status` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '申请动作',
`request_reason` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '申请理由',
`buyer_response_status` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '买方回应动作',
`buyer_response_reason` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '买方回应原因',
`buyer_response_remark` varchar(200) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '买方回应备注',
`request_at` datetime NULL DEFAULT NULL COMMENT '申请时间',
`response_at` datetime NULL DEFAULT NULL COMMENT '回应时间',
`updated_by` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '更新人',
`updated_at` datetime NULL DEFAULT NULL COMMENT '更新时间',
`created_by` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '创建人',
`created_at` datetime NULL DEFAULT NULL COMMENT '创建时间',
`is_deleted` bigint NULL DEFAULT 0 COMMENT '删除标记',
`version` bigint NULL DEFAULT 0 COMMENT '版本号',
`void_bp_line_id` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '申请作废结算单id',
`company_id` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '申请作废公司(卖方公司)id',
`tenant_id` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '申请作废租户(卖方租户)id',
`operator_id` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '操作人id',
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '结算单申请作废记录表' ROW_FORMAT = Dynamic;
具体分析
In
EXPLAIN SELECT
id,
void_bp_line_id,
created_at
FROM
reconc_billing_proposal_request_cancle_info
WHERE
id IN (
SELECT
SUBSTRING_INDEX ( GROUP_CONCAT( a.id ORDER BY a.created_at DESC ), ',', 1 ) kngId
FROM
reconc_billing_proposal_request_cancle_info a
GROUP BY
a.void_bp_line_id
);
explain执行结果:
查询结果分析:
查询时间接近57s,是所有查询方式里最慢的,通过explain分析,它都是全表扫描。
exists
EXPLAIN SELECT
b.*
FROM
reconc_billing_proposal_request_cancle_info b
WHERE
exists (
SELECT
*
FROM
(
SELECT
SUBSTRING_INDEX ( GROUP_CONCAT( a.id ORDER BY a.created_at DESC ), ',', 1 ) kngId
FROM
reconc_billing_proposal_request_cancle_info a
GROUP BY
a.void_bp_line_id
) temp
WHERE
temp.kngId = b.id
);
explain执行结果:
查询结果分析:
查询时间很短,0.057s,通过explain分析,它是会走部分primary key,同时exists适合用于驱动表小,被驱动表较大时,因为查询被驱动表可以走索引,比全表扫描快。
join
EXPLAIN SELECT
b.*
FROM
reconc_billing_proposal_request_cancle_info b
JOIN (
SELECT
SUBSTRING_INDEX ( GROUP_CONCAT( a.id ORDER BY a.created_at DESC ), ',', 1 ) id
FROM
reconc_billing_proposal_request_cancle_info a
GROUP BY
a.void_bp_line_id
) temp ON b.id = temp.id;
explain执行结果:
查询结果分析:
查询时间很短,0.058s,通过explain分析,它也是会走部分primary key,如果把join 换成left join,就变成全表扫描,Extra中会出现Using join buffer (hash join),在连接查询执行过程中,当被驱动表不能有效的利用索引加快访问速度,MySQL一般会为其分配一块名叫join buffer的内存块来加快查询速度,也就是我们所讲的基于块的嵌套循环算法。
内连接(隐式)
EXPLAIN SELECT
b.*
FROM
reconc_billing_proposal_request_cancle_info b,
(
SELECT
SUBSTRING_INDEX ( GROUP_CONCAT( a.id ORDER BY a.created_at DESC ), ',', 1 ) id
FROM
reconc_billing_proposal_request_cancle_info a
GROUP BY
a.void_bp_line_id
) temp
WHERE
b.id = temp.id;
explain执行结果:
查询结果分析:
查询时间很短,0.052s,通过explain分析,它也是会走部分primary key。
总结:
编写sql语句时尽量使用exists,少用in,使用not exists 代替not in ,join本身底层用的是简单嵌套查询算法,尽量能使用基于索引的嵌套查询算法减少查询被驱动表次数,mysql8.0后,默认开启了基于块的嵌套查询算法减少查询驱动表次数,查询执行分析器默认会先使用基于索引方式查询,如果没有索引,则会使用基于块方式。