You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

MySQL查询优化求助:PHP应用中高资源占用查询的优化方案

MySQL查询优化请求

我有一段PHP应用中的MySQL查询语句,根据Cpanel资源使用监控数据,该查询占用了大量系统资源,恳请提供可行的优化方案。以下是查询语句及相关数据表结构:

$q = "SELECT businesses.id 
    FROM businesses 
        JOIN businesses_business_types ON businesses_business_types.business_id = businesses.id 
        JOIN business_counties ON business_counties.business_id = businesses.id 
        JOIN business_details ON business_details.business_id = businesses.id 
    WHERE (
            businesses_business_types.business_type_id = $cat 
            OR businesses.primary_business_type_id = $cat
        ) 
    AND 
        (
            business_counties.county_id = $county 
            OR businesses.primary_city = $county
        ) 
    AND business_details.status = 2 
    AND businesses.status = 13 
    LIMIT 1";

数据表结构

business_counties表

字段名类型是否允许为空默认值主键
idint(11)否-是
business_idint(11)否-否
county_idint(11)否-否

business_details表

字段名类型是否允许为空默认值主键
idint(11)否-是
business_idint(11)否-否
imagevarchar(255)是NULL否
logovarchar(255)是NULL否
image_draftvarchar(255)是NULL否
logo_draftvarchar(255)是NULL否
descriptiontext是NULL否
description_drafttext否-否
statusint(11)否0否
modifieddatetime否-否
createddatetime否-否
uk_descriptiontext是NULL否
uk_description_drafttext是NULL否

business_types表

字段名类型是否允许为空默认值主键
idint(11)否-是
namevarchar(255)否-否
parent_idint(11)否0否
descriptiontext是NULL否
display_orderint(11)是0否
levelint(2)否1否
display_in_navint(2)是0否

businesses表

字段名类型是否允许为空默认值主键
idint(11)否-是
namevarchar(255)否-否
postcodevarchar(50)否-否
primary_cityint(11)否-否
actual_locationint(11)是NULL否
primary_business_type_idint(11)是NULL否
notestext是NULL否
statusint(2)是0否
createddatetime是NULL否
updateddatetime是NULL否
business_address_1varchar(255)是NULL否
business_address_2varchar(255)是NULL否
business_address_3varchar(255)是NULL否
business_cityvarchar(255)是NULL否
business_countyvarchar(255)是NULL否
business_postcodevarchar(50)是NULL否
billing_address_1varchar(255)是NULL否
billing_address_2varchar(255)是NULL否
billing_address_3varchar(255)是NULL否
billing_cityvarchar(255)是NULL否
billing_countyvarchar(255)是NULL否
billing_postcodevarchar(50)是NULL否
salesman_idint(11)否0否
next_callbackdatetime是NULL否
business_functionvarchar(255)是NULL否
bunsiness_typevarchar(255)是NULL否
ratingint(11)是0否
business_type_tagstext是NULL否
show_ondate是NULL否
trial_startdate是NULL否
trial_enddate是NULL否
added_byint(11)是0否
latitudedouble是NULL否
longitudedouble是NULL否
multiple_emailtinyint(1)否0否
websitevarchar(255)是NULL否

businesses_business_types表

字段名类型是否允许为空默认值主键
idint(11)否-是
business_idint(11)否-否
business_type_idint(11)否-否
levelint(2)否2否

优化方案

1. 添加针对性索引

  • businesses表创建组合索引:(status, primary_business_type_id, primary_city, id),覆盖WHERE过滤条件和返回字段,避免回表查询。
  • businesses_business_types表创建组合索引:(business_type_id, business_id),快速过滤类型匹配的记录并关联商家表。
  • business_counties表创建组合索引:(county_id, business_id),高效定位区县匹配的商家记录。
  • business_details表创建组合索引:(business_id, status),快速验证商家状态是否符合要求。

2. 重构查询逻辑,规避OR的性能损耗

OR条件会导致MySQL无法有效利用索引,可将原查询拆分为多个子查询用UNION ALL合并(外层加LIMIT 1,找到第一条符合条件的记录就停止):

SELECT businesses.id 
FROM businesses 
JOIN business_details ON business_details.business_id = businesses.id 
WHERE businesses.primary_business_type_id = $cat 
  AND businesses.primary_city = $county 
  AND business_details.status = 2 
  AND businesses.status = 13 
LIMIT 1
UNION ALL
SELECT businesses.id 
FROM businesses 
JOIN businesses_business_types ON businesses_business_types.business_id = businesses.id 
JOIN business_details ON business_details.business_id = businesses.id 
WHERE businesses_business_types.business_type_id = $cat 
  AND businesses.primary_city = $county 
  AND business_details.status = 2 
  AND businesses.status = 13 
LIMIT 1
UNION ALL
SELECT businesses.id 
FROM businesses 
JOIN business_counties ON business_counties.business_id = businesses.id 
JOIN business_details ON business_details.business_id = businesses.id 
WHERE businesses.primary_business_type_id = $cat 
  AND business_counties.county_id = $county 
  AND business_details.status = 2 
  AND businesses.status = 13 
LIMIT 1
UNION ALL
SELECT businesses.id 
FROM businesses 
JOIN businesses_business_types ON businesses_business_types.business_id = businesses.id 
JOIN business_counties ON business_counties.business_id = businesses.id 
JOIN business_details ON business_details.business_id = businesses.id 
WHERE businesses_business_types.business_type_id = $cat 
  AND business_counties.county_id = $county 
  AND business_details.status = 2 
  AND businesses.status = 13 
LIMIT 1
LIMIT 1;

每个子查询都能利用对应索引快速定位数据,大幅提升查询效率。

3. 前置过滤条件减少关联数据量

将过滤条件直接写在JOIN语句中,在关联阶段就过滤掉不符合的记录:
比如JOIN businesses_business_types ON businesses_business_types.business_id = businesses.id AND businesses_business_types.business_type_id = $cat,避免后续处理无效数据。

4. 校验JOIN的必要性

原查询用JOIN business_counties会过滤掉没有关联区县记录的商家,若业务允许商家仅通过primary_city=$county匹配,需将JOIN改为LEFT JOIN,并调整WHERE条件为(business_counties.county_id = $county OR businesses.primary_city = $county OR business_counties.id IS NULL),避免误过滤有效数据的同时优化性能。

5. 修复SQL注入风险

原查询直接拼接变量到SQL中存在严重安全隐患,改用PHP预处理语句:

$stmt = $pdo->prepare("SELECT businesses.id FROM businesses 
    JOIN businesses_business_types ON businesses_business_types.business_id = businesses.id 
    JOIN business_counties ON business_counties.business_id = businesses.id 
    JOIN business_details ON business_details.business_id = businesses.id 
    WHERE (businesses_business_types.business_type_id = ? OR businesses.primary_business_type_id = ?) 
    AND (business_counties.county_id = ? OR businesses.primary_city = ?) 
    AND business_details.status = 2 
    AND businesses.status = 13 
    LIMIT 1");
$stmt->execute([$cat, $cat, $county, $county]);

预处理不仅提升安全性,还能让MySQL缓存查询计划,重复执行时性能更优。

内容的提问来源于stack exchange,提问作者jdk

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.15 23:55:25