能提取,但必须处理好协议头、端口、路径三类干扰,否则结果错得离谱;需用instr精确定位起始(跳过://和user:pass@)与结束位置(取:、/、?、#中最早出现者),再用substring截取。

如何用 SUBSTRING 和 INSTR 提取 URL 中的域名
直接说结论:能提取,但必须处理好协议头、端口、路径三类干扰,否则结果错得离谱。MySQL 没有原生的 PARSE_URL(8.0.29+ 才有 JSON_EXTRACT 配合 URL_ENCODE 的变通方案),所以靠字符串函数硬解是常见做法,关键在定位逻辑是否鲁棒。
INSTR 定位时为什么不能只找第一个 //
因为有些 URL 是相对路径(如 example.com/path),没有协议;有些带认证信息(如 https://user:pass@example.com:8080);还有些是 ftp:// 或 file:// 协议。只依赖 INSTR(url, '//') 会漏掉无协议 URL,或把认证部分误判为域名起始点。
- 先判断是否存在
://:用IF(INSTR(url, '://') > 0, ...)分支处理 - 若存在,起始位置 =
INSTR(url, '://') + 3;若不存在,起始位置 = 1 - 紧接着要跳过可能存在的用户信息(
user:pass@):再查@是否在起始位置之后,是则从@后一位开始
域名结束位置怎么确定才不截断端口或路径
域名结尾不是固定字符,而是遇到 :(端口)、/(路径)、?(查询参数)、#(锚点)中的任一个就停。MySQL 不支持正则提取(5.7 及以前),所以得用嵌套 LEAST 找最早出现的位置:
LEAST( NULLIF(INSTR(SUBSTRING(url, start_pos), ':'), 0), NULLIF(INSTR(SUBSTRING(url, start_pos), '/'), 0), NULLIF(INSTR(SUBSTRING(url, start_pos), '?'), 0), NULLIF(INSTR(SUBSTRING(url, start_pos), '#'), 0) )
注意:所有 INSTR 返回 0 表示未找到,NULLIF(x, 0) 把 0 转成 NULL,这样 LEAST 才能忽略它——否则 0 会压倒一切变成最小值,导致长度算错。
完整可执行的提取逻辑长什么样
下面这段 SQL 覆盖了 https://user:pass@example.com:8080/path?k=v#frag、example.com/path、ftp://host.net 三种典型情况:
SELECT url,
SUBSTRING(
url,
IF(INSTR(url, '://') > 0,
IF(INSTR(url, '@', INSTR(url, '://') + 3) > 0,
INSTR(url, '@', INSTR(url, '://') + 3) + 1,
INSTR(url, '://') + 3
),
1
),
LEAST(
NULLIF(INSTR(SUBSTRING(url,
IF(INSTR(url, '://') > 0,
IF(INSTR(url, '@', INSTR(url, '://') + 3) > 0,
INSTR(url, '@', INSTR(url, '://') + 3) + 1,
INSTR(url, '://') + 3
),
1
)
), ':'), 0),
NULLIF(INSTR(SUBSTRING(url,
IF(INSTR(url, '://') > 0,
IF(INSTR(url, '@', INSTR(url, '://') + 3) > 0,
INSTR(url, '@', INSTR(url, '://') + 3) + 1,
INSTR(url, '://') + 3
),
1
)
), '/'), 0),
NULLIF(INSTR(SUBSTRING(url,
IF(INSTR(url, '://') > 0,
IF(INSTR(url, '@', INSTR(url, '://') + 3) > 0,
INSTR(url, '@', INSTR(url, '://') + 3) + 1,
INSTR(url, '://') + 3
),
1
)
), '?'), 0),
NULLIF(INSTR(SUBSTRING(url,
IF(INSTR(url, '://') > 0,
IF(INSTR(url, '@', INSTR(url, '://') + 3) > 0,
INSTR(url, '@', INSTR(url, '://') + 3) + 1,
INSTR(url, '://') + 3
),
1
)
), '#'), 0)
) - 1
) AS domain
FROM (SELECT 'https://user:pass@example.com:8080/path?k=v#frag' AS url) t;
真正上线时建议封装成函数,否则每次写这么一长串既难读又难维护。另外,如果数据里有大量非法 URL(比如缺失 .、全是数字),这个逻辑不会报错但可能返回空或意外子串——校验得靠应用层或额外加 REGEXP(MySQL 8.0+)兜底。











