在SQL Server的数据库业务中,我们经常会遇到需要从完整URL中拆分出域名的需求,比如统计不同域名的访问量、对来源网站做分类汇总等。SUBSTRING函数用于截取字符串的指定部分,CHARINDEX函数用于查找指定字符在字符串中的位置,两者结合就能精准定位域名的起始和结束位置,完成提取操作。

核心函数基础用法
CHARINDEX函数
CHARINDEX的作用是在一个字符串中查找另一个字符串第一次出现的位置,返回的是字符的索引值,索引从1开始计数。如果未找到目标字符串,返回0。基础语法如下:
-- 语法格式
CHARINDEX(要查找的字符串, 被查找的字符串, [开始查找的位置])
-- 示例:查找example在字符串中的位置
SELECT CHARINDEX('example', 'https://www.ippipp.com/path') AS position;
-- 返回结果:12
SUBSTRING函数
SUBSTRING用于从字符串的指定位置开始,截取指定长度的字符。基础语法如下:
-- 语法格式
SUBSTRING(原始字符串, 起始位置, 截取长度)
-- 示例:从第12位开始截取11个字符
SELECT SUBSTRING('https://www.ippipp.com/path', 12, 11) AS result;
-- 返回结果:ippipp.com
完整域名提取逻辑
提取域名的核心思路是先找到协议部分结束的位置,再找到域名后面第一个斜杠或者字符串结束的位置,最后截取中间的部分。以常见的https://www.ippipp.com/path?param=1这类URL为例,步骤如下:
- 第一步:找到
://的位置,协议部分结束的下一个位置就是域名的起始位置 - 第二步:从域名起始位置开始,查找第一个
/的位置,这个位置之前的部分就是域名,如果找不到/说明URL没有路径部分,直接取到字符串末尾 - 第三步:用SUBSTRING截取起始位置到结束位置之间的字符串,得到域名
对应的完整SQL代码如下:
-- 创建测试表
CREATE TABLE #url_test (
id INT IDENTITY(1,1),
full_url VARCHAR(500)
);
-- 插入测试数据
INSERT INTO #url_test (full_url) VALUES
('https://www.ippipp.com/path'),
('http://ipipp.com/article/123'),
('https://blog.test.org'),
('ftp://file.example.cn/download/file.zip');
-- 提取域名的查询语句
SELECT
full_url,
-- 计算域名起始位置:://的位置加3
CHARINDEX('://', full_url) + 3 AS domain_start,
-- 计算域名结束位置:从起始位置找第一个/的位置,如果找不到返回字符串长度+1
CASE
WHEN CHARINDEX('/', full_url, CHARINDEX('://', full_url) + 3) = 0
THEN LEN(full_url) + 1
ELSE CHARINDEX('/', full_url, CHARINDEX('://', full_url) + 3)
END AS domain_end,
-- 截取域名
SUBSTRING(
full_url,
CHARINDEX('://', full_url) + 3,
CASE
WHEN CHARINDEX('/', full_url, CHARINDEX('://', full_url) + 3) = 0
THEN LEN(full_url) + 1
ELSE CHARINDEX('/', full_url, CHARINDEX('://', full_url) + 3)
END - (CHARINDEX('://', full_url) + 3)
) AS domain
FROM #url_test;
-- 清理测试表
DROP TABLE #url_test;
特殊场景处理
实际业务中可能会遇到一些特殊的URL格式,需要做对应的适配:
没有协议头的URL
如果URL是www.ippipp.com/path这种没有://的格式,需要先判断是否存在协议头,再做处理:
SELECT
full_url,
CASE
WHEN CHARINDEX('://', full_url) = 0
THEN SUBSTRING(
full_url,
1,
CASE WHEN CHARINDEX('/', full_url) = 0 THEN LEN(full_url) ELSE CHARINDEX('/', full_url) - 1 END
)
ELSE SUBSTRING(
full_url,
CHARINDEX('://', full_url) + 3,
CASE
WHEN CHARINDEX('/', full_url, CHARINDEX('://', full_url) + 3) = 0
THEN LEN(full_url) + 1
ELSE CHARINDEX('/', full_url, CHARINDEX('://', full_url) + 3)
END - (CHARINDEX('://', full_url) + 3)
)
END AS domain
FROM (SELECT 'www.ippipp.com/path' AS full_url) t;
带端口号的URL
如果URL包含端口号比如https://www.ippipp.com:8080/path,上述逻辑会把端口号也包含在域名里,如果需要去掉端口号,可以再查找冒号的位置做二次截取:
SELECT
full_url,
-- 先提取包含端口的部分
SUBSTRING(
full_url,
CHARINDEX('://', full_url) + 3,
CASE
WHEN CHARINDEX('/', full_url, CHARINDEX('://', full_url) + 3) = 0
THEN LEN(full_url) + 1
ELSE CHARINDEX('/', full_url, CHARINDEX('://', full_url) + 3)
END - (CHARINDEX('://', full_url) + 3)
) AS domain_with_port,
-- 去掉端口号
CASE
WHEN CHARINDEX(':', SUBSTRING(
full_url,
CHARINDEX('://', full_url) + 3,
CASE
WHEN CHARINDEX('/', full_url, CHARINDEX('://', full_url) + 3) = 0
THEN LEN(full_url) + 1
ELSE CHARINDEX('/', full_url, CHARINDEX('://', full_url) + 3)
END - (CHARINDEX('://', full_url) + 3)
)) = 0
THEN SUBSTRING(
full_url,
CHARINDEX('://', full_url) + 3,
CASE
WHEN CHARINDEX('/', full_url, CHARINDEX('://', full_url) + 3) = 0
THEN LEN(full_url) + 1
ELSE CHARINDEX('/', full_url, CHARINDEX('://', full_url) + 3)
END - (CHARINDEX('://', full_url) + 3)
)
ELSE SUBSTRING(
SUBSTRING(
full_url,
CHARINDEX('://', full_url) + 3,
CASE
WHEN CHARINDEX('/', full_url, CHARINDEX('://', full_url) + 3) = 0
THEN LEN(full_url) + 1
ELSE CHARINDEX('/', full_url, CHARINDEX('://', full_url) + 3)
END - (CHARINDEX('://', full_url) + 3)
),
1,
CHARINDEX(':', SUBSTRING(
full_url,
CHARINDEX('://', full_url) + 3,
CASE
WHEN CHARINDEX('/', full_url, CHARINDEX('://', full_url) + 3) = 0
THEN LEN(full_url) + 1
ELSE CHARINDEX('/', full_url, CHARINDEX('://', full_url) + 3)
END - (CHARINDEX('://', full_url) + 3)
)) - 1
)
END AS domain
FROM (SELECT 'https://www.ippipp.com:8080/path' AS full_url) t;
注意事项
- CHARINDEX的索引从1开始,和很多编程语言的0起始索引不同,编写逻辑时需要注意偏移量计算
- 如果URL字段可能为空,需要提前做非空判断,避免出现函数参数为空的错误
- 对于超长的URL,需要保证字段类型的长度足够存储完整内容,避免截取时出现截断问题