如何消除MySQL的UNION或优化多条件查询以提升性能?
优化多UNION查询以提升性能的方案
我来帮你分析下这个问题——你的查询逻辑是按优先级从高到低获取符合条件的服务商记录:先找城市+指定业务类型+指定成本区间的,不够5条就补同城市同业务类型的,再不够补同城市的,最后补同州的,最终取前5条。当前用四次UNION子查询的方式确实会带来不小的性能开销,因为每个子查询都得单独执行一遍全关联查询,最后还要做UNION的去重合并,这就是耗时超15秒的核心原因。
下面给你两个核心优化方向:
1. 合并查询逻辑,用优先级排序替代UNION
我们可以把四个子查询的条件合并成一次主查询,给每条符合条件的记录标记优先级权重,然后按权重+评分排序,最后直接取前5条。这样只需要执行一次关联查询,避免了多次扫描和UNION的额外开销。
优化后的SQL如下:
SELECT p.professional_id, p.company_name, pbt.name AS professional_business_type_name, pbtm.kukun_url, p.kukun_score, cc.year_founded, p.contractor_category, p.permit_data_count, p.cost_range_code, fpcr.cost_min_value, fpcr.cost_max_value FROM professional p INNER JOIN company_contact cc ON cc.company_contact_id = p.company_contact_id INNER JOIN professional_business_type_map AS pbtm ON pbtm.professional_id = p.professional_id INNER JOIN of_professional_business_type_organization AS pbt ON pbt.professional_business_type_organization_id = pbtm.professional_business_type_organization_id INNER JOIN f_professional_cost_range fpcr ON fpcr.cost_range_code = p.cost_range_code WHERE p.professional_id != 262100 AND ( -- 最高优先级:城市+业务类型+成本区间 (cc.company_city_id = 5229 AND pbt.professional_business_type_organization_id = 2 AND p.cost_range_code = 4) -- 次高优先级:城市+业务类型 OR (cc.company_city_id = 5229 AND pbt.professional_business_type_organization_id = 2 AND NOT (p.cost_range_code = 4)) -- 第三优先级:仅城市匹配 OR (cc.company_city_id = 5229 AND NOT (pbt.professional_business_type_organization_id = 2)) -- 最低优先级:仅州匹配(且不满足前面所有城市条件) OR (cc.company_state_id = 5 AND NOT (cc.company_city_id = 5229)) ) -- 按优先级权重降序,再按kukun_score降序排序 ORDER BY CASE WHEN cc.company_city_id = 5229 AND pbt.professional_business_type_organization_id = 2 AND p.cost_range_code = 4 THEN 4 WHEN cc.company_city_id = 5229 AND pbt.professional_business_type_organization_id = 2 THEN 3 WHEN cc.company_city_id = 5229 THEN 2 WHEN cc.company_state_id = 5 THEN 1 ELSE 0 END DESC, p.kukun_score DESC LIMIT 5;
逻辑说明:
- 用
CASE语句给不同条件的记录标记权重,优先级越高权重值越大 - 排序时先按权重降序,确保高优先级的记录排在前面,再按你的原有规则
kukun_score降序 - 最后
LIMIT 5直接取最符合要求的前5条,完全替代了原来四次UNION的逻辑
2. 增加覆盖索引,加速关联查询
查询慢的另一个常见原因是缺少合适的索引,导致数据库做全表扫描。建议给以下表添加覆盖索引(包含查询需要的所有字段,避免回表):
- professional表:
CREATE INDEX idx_prof_priority ON professional (professional_id, cost_range_code, kukun_score, contractor_category, permit_data_count, company_contact_id); - company_contact表:
CREATE INDEX idx_cc_city_state ON company_contact (company_contact_id, company_city_id, company_state_id, year_founded); - professional_business_type_map表:
CREATE INDEX idx_pbtm_prof ON professional_business_type_map (professional_id, professional_business_type_organization_id); - of_professional_business_type_organization表:
CREATE INDEX idx_pbt_id_name ON of_professional_business_type_organization (professional_business_type_organization_id, name); - f_professional_cost_range表:
CREATE INDEX idx_fpcr_code ON f_professional_cost_range (cost_range_code, cost_min_value, cost_max_value);
索引作用:
这些覆盖索引可以让数据库直接从索引中获取所有需要的字段,不需要回表查询原数据,大幅减少IO开销,提升查询速度。
额外提示
如果你的场景中不需要去重(即同一个professional_id不会同时满足多个优先级条件),原来的UNION可以换成UNION ALL(因为UNION会默认去重,带来额外开销),但上面的合并查询方案已经完全避免了这个问题,优先级高的记录会被优先选中。
内容的提问来源于stack exchange,提问作者naveen chennu
相关产品推荐
相关产品推荐

