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

PostgreSQL高效检索同列前缀根值及对应衍生值方法

高效查找数字根值及对应衍生值的SQL方案

场景说明

现有存储数字字符串的表numbers,样例数据如下:

| number |
----------
|123     |
|1234    |
|12345   |
|123456  |
|111     |
|1111    |
|2       |
|700     |

需求是找出所有根值:即存在更长前缀匹配衍生值的最短数字,衍生值定义为前缀完全匹配根值、且长度大于根值的数字。无衍生值的数字(如样例中的2、700)直接排除。
支持两种输出格式:

  • 理想聚合格式:
| root   | derivatives         |
--------------------------------
| 123    | 1234, 12345, 123456 |
| 111    | 1111                |
  • 可接受逐行映射格式(支持后续自行聚合):
| root   | derivative |
-----------------------
| 123    | 1234       |
| 123    | 12345      |
| 123    | 123456     |
| 111    | 1111       |

原有朴素实现性能极差,50万条数据运行4小时未出结果,无法支撑百万级数据集,原SQL如下:

select number
from numbers n1
where exists(
              select number
              from numbers n2
              where n2.number <> n1.number
                and n2.number like n1.number || '_%'
          );

性能问题核心原因

原写法时间复杂度为O(n²),本质是近似笛卡尔积的全表关联,且like模糊匹配无法有效利用索引,数据量上涨后性能会指数级下降。

前置优化准备

首先给number字段建B树索引,这是性能提升的核心,B树索引天然支持等值匹配、前缀匹配的快速定位:

-- 通用建索引语法,不同数据库可根据特性调整,如MySQL可指定前缀长度
CREATE INDEX idx_numbers_number ON numbers(number);

注意:number字段需为字符串类型(VARCHAR/CHAR/TEXT),如果是数值类型请先转为字符串再做前缀匹配,否则逻辑会出错。

实现代码

1. 逐行映射格式(性能最优,百万级数据秒级返回)

核心逻辑:放弃like模糊匹配,用字符串截断函数取前缀做等值匹配,可100%命中索引;通过窗口函数自动筛选每个衍生值对应的最短根值,避免根嵌套问题。
适配MySQL 8.0+、PostgreSQL、Spark SQL等支持窗口函数的数据库:

WITH num_with_len AS (
    -- 预计算每个数字的长度,避免重复计算
    SELECT 
        number,
        CHAR_LENGTH(number) AS num_len
    FROM numbers
),
derivative_mapping AS (
    SELECT
        n1.number AS derivative,
        n2.number AS root,
        -- 同一衍生值匹配到多个前缀时,取长度最短的作为真正根值
        ROW_NUMBER() OVER (PARTITION BY n1.number ORDER BY n2.num_len ASC) AS rn
    FROM num_with_len n1
    INNER JOIN num_with_len n2
        ON n2.num_len < n1.num_len
        -- 直接截断衍生值的前缀做等值匹配,比like性能高一个数量级
        AND LEFT(n1.number, n2.num_len) = n2.number
)
SELECT root, derivative
FROM derivative_mapping
WHERE rn = 1;

2. 聚合格式输出

在上述逐行结果基础上,直接用数据库内置的字符串聚合函数即可得到理想格式,不同数据库函数略有区别:

-- 替换上述代码最后一段SELECT逻辑即可
SELECT 
    root,
    -- MySQL用GROUP_CONCAT,PostgreSQL/SQL Server用STRING_AGG,Oracle用LISTAGG
    GROUP_CONCAT(derivative ORDER BY derivative SEPARATOR ', ') AS derivatives
FROM derivative_mapping
WHERE rn = 1
GROUP BY root;

额外优化技巧

  • 如果数字的最大长度很小(比如不超过10位),可以去掉自关联逻辑,直接枚举每个数字的所有可能长度的前缀做存在性判断,性能还能再提升30%以上。
  • 可以提前过滤掉长度等于表内全局最小长度的数字:这类数字不可能是任何根的衍生值,不需要参与关联计算。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.29 04:16:21