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

寻求替代DISTINCT与REGEXP的更快MySQL查询方案

优化MySQL组合位置查询的高效方案

原查询的性能瓶颈在于REGEXP或LIKE这类子字符串匹配操作——它们无法利用索引,再加上两次子查询的重复计算,直接导致速度极慢。下面提供两种优化思路,分别适用于无法修改表结构和可以调整表结构的场景:

方案一:不修改表结构,用CTE拆分组合位置(MySQL 8.0+)

利用数字辅助表拆分combined_locations为单独位置条目,结合用户的偏好集合做匹配,替代低效的正则匹配:

WITH user_prefs AS (
    -- 一次性获取用户1的Yes/No位置集合
    SELECT
        GROUP_CONCAT(CASE WHEN user_selection = 'Yes' THEN user_locations END) AS yes_locs,
        GROUP_CONCAT(CASE WHEN user_selection = 'No' THEN user_locations END) AS no_locs
    FROM users
    WHERE user_id = 1
),
split_combined AS (
    -- 拆分每个组合位置为单独条目
    SELECT
        l.combined_locations,
        TRIM(SUBSTRING_INDEX(SUBSTRING_INDEX(l.combined_locations, ',', n.num), ',', -1)) AS single_loc
    FROM locations l
    -- 数字辅助表,根据实际组合位置的最大数量调整(比如最多5个就加到5)
    CROSS JOIN (SELECT 1 num UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4 UNION ALL SELECT 5) n
    WHERE n.num <= 1 + LENGTH(l.combined_locations) - LENGTH(REPLACE(l.combined_locations, ',', ''))
)
SELECT DISTINCT sc.combined_locations
FROM split_combined sc
JOIN user_prefs up ON 1=1
WHERE
    -- 确保组合中所有位置都在用户的Yes列表里
    NOT EXISTS (
        SELECT 1
        FROM split_combined sc2
        WHERE sc2.combined_locations = sc.combined_locations
        AND FIND_IN_SET(sc2.single_loc, up.yes_locs) = 0
    )
    -- 确保组合中没有位置在用户的No列表里
    AND NOT EXISTS (
        SELECT 1
        FROM split_combined sc2
        WHERE sc2.combined_locations = sc.combined_locations
        AND FIND_IN_SET(sc2.single_loc, up.no_locs) > 0
    );

优势:

  • 仅查询一次users表获取偏好,避免重复计算
  • 用FIND_IN_SET替代REGEXP,匹配效率显著提升
  • 通过拆分后检查所有子位置,逻辑更清晰,避免原查询中两次子查询的冗余

方案二:调整表结构(最优性能方案)

原表将多个位置存储在单个字符串中,是性能问题的根源。建议拆分为关联表,彻底解决字符串匹配的性能瓶颈:

1. 创建拆分后的表结构

-- 存储组合位置的主表
CREATE TABLE location_groups (
    group_id INT PRIMARY KEY AUTO_INCREMENT,
    group_name VARCHAR(255) NOT NULL UNIQUE COMMENT '比如"Arizona, California"'
);

-- 存储组合与单个位置的关联关系
CREATE TABLE group_locations (
    group_id INT NOT NULL,
    location_name VARCHAR(255) NOT NULL,
    PRIMARY KEY (group_id, location_name),
    FOREIGN KEY (group_id) REFERENCES location_groups(group_id),
    INDEX idx_location (location_name) -- 给单个位置加索引,加速匹配
);

2. 导入数据(一次性操作)

用类似方案一的CTE拆分原locations表的数据,插入到新表中。

3. 高效查询

SELECT lg.group_name
FROM location_groups lg
JOIN group_locations gl ON lg.group_id = gl.group_id
-- 关联用户标记为Yes的位置
JOIN users u_yes ON gl.location_name = u_yes.user_locations
    AND u_yes.user_id = 1
    AND u_yes.user_selection = 'Yes'
-- 左关联用户标记为No的位置,排除存在No的组合
LEFT JOIN users u_no ON gl.location_name = u_no.user_locations
    AND u_no.user_id = 1
    AND u_no.user_selection = 'No'
WHERE u_no.user_id IS NULL
-- 分组确保组合的所有位置都匹配到Yes(避免部分匹配)
GROUP BY lg.group_id, lg.group_name
HAVING COUNT(DISTINCT gl.location_name) = (
    SELECT COUNT(DISTINCT location_name)
    FROM group_locations
    WHERE group_id = lg.group_id
);

优势:

  • 所有匹配都是等值匹配,完全利用索引,性能比字符串匹配提升几个数量级
  • 数据结构更规范,后续扩展或维护更方便

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.08 18:22:06