| | |
| | | 主查询已去掉 GROUP BY:pat_med_inhosp.inhospid 唯一、pat_archive.id 是主键, |
| | | a.id = b.patid 一对一,因此 join 结果行数就是业务记录数。 |
| | | 这里直接对相同的 FROM/WHERE 做 COUNT,不再包一层分组子查询。 |
| | | |
| | | ⚠️⚠️ 【本接口真正的热点是这条 count,不是上面的列表 SQL】 |
| | | |
| | | 2026-11-13 实测(smartor_sltd:pat_med_inhosp 50,175 行、pat_archive 249,045 行、 |
| | | MySQL 5.7.34、innodb_buffer_pool_size=128MB): |
| | | |
| | | 列表 ORDER BY b.endtime DESC LIMIT 10 → 25 ~ 35 ms |
| | | count 本条语句 → 10467 ms(冷)/ 350 ms(热) |
| | | |
| | | 用户看到的「全时间段 20s / 3 个月 8s」就是它:一次翻页 = count + 列表。 |
| | | 原因有三条,改 SQL 解决不了,必须在库上做: |
| | | |
| | | 1) b 侧必须有 (del_flag, orgid, inhospstate, endtime) 复合索引, |
| | | 否则 MySQL 要按 orgid 回表逐行过滤 del_flag / inhospstate; |
| | | 有了它,count(1) 是 Using index(不回表),列表也能按索引顺序提前 LIMIT。 |
| | | 2) MySQL 5.7 对「不在索引里的等值条件」按 10% 估(EXPLAIN 的 filtered=10.0), |
| | | 会把「没有 inhospstate 的索引」算得更便宜,从而选错。 |
| | | sltd 上实测:强行走 idx_pmi_del_org_state_end = 349ms, |
| | | 默认执行计划 = 2085ms。所以光建索引不够,还要清掉竞争索引或强制索引。 |
| | | 3) pat_archive 是 60+ 列的大表,count 每命中一行都要按主键回表一次 |
| | | (sltd 是 38,881 次随机读)。缓存命中 0.35s、冷缓存 10s, |
| | | 差距全在磁盘随机读 → 见第 5 步 innodb_buffer_pool_size。 |
| | | |
| | | 配套脚本:sql/optimize_pat_med_inhosp_list_count_20261113.sql |
| | | |
| | | ⚠️ 【不要给 pat_med_inhosp 加 idx_pmi_patid (patid)】 |
| | | 实测加上后优化器会改成「先扫 pat_archive 再回查 pat_med_inhosp」, |
| | | 列表查询 25 ms → 481 ms(慢 20 倍),ORDER BY 也用不上索引。 |
| | | 本查询的驱动表必须固定是 b。 |
| | | |
| | | ⚠️ 【不要为了快把 a 的 join 去掉】 |
| | | a.id = b.patid 不是恒等的:pat_med_inhosp.patid 可能为空或在 pat_archive 里 |
| | | 找不到。2026-11-13 实测 smartor_shiyi 有 5,713 行 patid 为空、 |
| | | smartor_lishui 有 9,270 行孤儿行 —— 去掉 join 会把这些行算进 total, |
| | | 只会在「孤儿行恰好为 0」的库上看起来正确。 |
| | | --> |
| | | <select id="selectPatMedInhospList_COUNT" parameterType="com.ruoyi.inhospvisit.domain.PatMedInhosp" |
| | | resultType="java.lang.Long"> |