上一篇
"小王啊,客户要求我们找出所有订单备注里包含'紧急'但不在前20个字符的记录,这个SQL该怎么写?" 上周五临下班时,产品经理老张突然抛来这个问题。
我挠了挠头,在SQL Server里可没有Oracle那样的INSTR函数直接能用,折腾了半小时才搞定,差点误了和朋友的饭局,今天咱们就来好好聊聊,在SQL Server里如何高效实现字符串定位功能。
SELECT CHARINDEX('找', '我要找字符串中的某个字') AS 位置;
CHARINDEX是SQL Server自带的字符串查找函数,语法简单:
-- 从第5个字符开始查找 SELECT CHARINDEX('sql', '学习mssql从基础开始', 5) AS 位置;
当需要模糊查找时,PATINDEX就派上用场了:
SELECT PATINDEX('%[0-9]%', '订单编号ABC123') AS 首个数字位置;
这个函数支持使用通配符,
虽然LIKE通常用于WHERE条件,但结合其他函数也能实现位置查找:
SELECT 备注内容, CASE WHEN 备注内容 LIKE '%紧急%' THEN CHARINDEX('紧急', 备注内容) ELSE 0 END AS 紧急位置 FROM 订单表;
Oracle的INSTR函数有四个参数:
在SQL Server中我们可以这样模拟:
CREATE FUNCTION dbo.INSTR ( @原字符串 NVARCHAR(MAX), @查找字符串 NVARCHAR(MAX), @开始位置 INT = 1, @第几次出现 INT = 1 ) RETURNS INT AS BEGIN DECLARE @位置 INT = 0; DECLARE @计数器 INT = 0; DECLARE @当前查找位置 INT = @开始位置; -- 处理异常情况 IF @开始位置 < 1 OR @第几次出现 < 1 RETURN 0; WHILE @计数器 < @第几次出现 BEGIN SET @位置 = CHARINDEX(@查找字符串, @原字符串, @当前查找位置); IF @位置 = 0 RETURN 0; SET @计数器 = @计数器 + 1; SET @当前查找位置 = @位置 + 1; END RETURN @位置; END
对于大文本字段,我们可以优化:
CREATE FUNCTION dbo.INSTR_OPTIMIZED ( @原字符串 NVARCHAR(MAX), @查找字符串 NVARCHAR(MAX), @开始位置 INT = 1, @第几次出现 INT = 1 ) RETURNS INT AS BEGIN -- 快速失败逻辑 IF LEN(@查找字符串) > LEN(@原字符串) OR @开始位置 > LEN(@原字符串) OR @开始位置 < 1 OR @第几次出现 < 1 RETURN 0; -- 单次查找直接使用CHARINDEX IF @第几次出现 = 1 RETURN CHARINDEX(@查找字符串, @原字符串, @开始位置); -- 多次查找使用递归CTE DECLARE @结果 INT = 0; WITH 查找CTE AS ( SELECT CHARINDEX(@查找字符串, @原字符串, @开始位置) AS 位置, 1 AS 出现次数 UNION ALL SELECT CHARINDEX(@查找字符串, @原字符串, 位置 + 1), 出现次数 + 1 FROM 查找CTE WHERE 位置 > 0 AND 出现次数 < @第几次出现 ) SELECT @结果 = 位置 FROM 查找CTE WHERE 出现次数 = @第几次出现 OPTION (MAXRECURSION 100); RETURN ISNULL(@结果, 0); END
SQL Server没有内置的反向查找函数,但可以这样实现:
CREATE FUNCTION dbo.INSTR_REVERSE ( @原字符串 NVARCHAR(MAX), @查找字符串 NVARCHAR(MAX) ) RETURNS INT AS BEGIN DECLARE @反转字符串 NVARCHAR(MAX) = REVERSE(@原字符串); DECLARE @反转查找 NVARCHAR(MAX) = REVERSE(@查找字符串); DECLARE @位置 INT = CHARINDEX(@反转查找, @反转字符串); IF @位置 = 0 RETURN 0; RETURN LEN(@原字符串) - @位置 - LEN(@查找字符串) + 2; END
-- 查找"ab"在字符串中第二次出现的位置 SELECT dbo.INSTR('abcabdabeabf', 'ab', 1, 2) AS 第二次出现位置;
当处理大文本字段(VARCHAR(MAX), NVARCHAR(MAX))时:
-- 先截取前1000字符查找 SELECT CHARINDEX('关键词', SUBSTRING(大文本字段, 1, 1000)) FROM 大表;
SQL Server的CHARINDEX对中文支持良好,但要注意:
-- 正确查找中文 SELECT CHARINDEX('数据', 'SQL Server数据库') AS 位置; -- 返回11 -- 注意NVARCHAR和VARCHAR的区别 SELECT CHARINDEX('数据', CAST('SQL Server数据库' AS VARCHAR(50))); -- 可能返回错误结果
最佳实践:始终使用NVARCHAR类型处理可能包含中文的文本。
-- 糟糕的写法(全表扫描) SELECT * FROM 订单表 WHERE CHARINDEX('紧急', 备注) > 0; -- 稍好的写法(可能使用索引) SELECT * FROM 订单表 WHERE 备注 LIKE '%紧急%';
更优解:考虑使用全文索引(FULLTEXT INDEX)或者计算列+索引。
查找包含通配符的内容时:
-- 查找包含百分号的内容 SELECT CHARINDEX('%', '折扣20% off') AS 百分号位置; -- 查找包含下划线的内容 SELECT CHARINDEX('_', 'user_name') AS 下划线位置;
经过这些探索,我们总结出几个原则:
回到开头的需求,现在我们可以优雅地解决了:
SELECT * FROM 订单表 WHERE dbo.INSTR(备注, '紧急', 1, 1) > 20;
字符串处理往往是性能瓶颈,在真实业务中要结合索引、缓存等多种手段优化,希望这篇指南能让你下次处理字符串定位问题时更加得心应手!
本文由 尹文思 于2025-08-01发表在【云服务器提供商】,文中图片由(尹文思)上传,本平台仅提供信息存储服务;作者观点、意见不代表本站立场,如有侵权,请联系我们删除;若有图片侵权,请您准备原始证明材料和公证书后联系我方删除!
本文链接:https://vps.7tqx.com/wenda/501006.html
发表评论