三个很有用的sqlserver中的处理多个以分隔符分隔的参数的函数

 1,获取字符串中分隔符分隔的长度

alter FUNCTION GetLenBySelChar
(
 -- Add the parameters for the function here

 @StrParams nvarchar(4000),
 @char char(1)=N'1'
)
RETURNS int
AS
BEGIN
 declare   @m  int,

 @length int
 set @length=0
 
 set  @m=CHARINDEX(@char,@strparams) 

 WHILE  @m>0 
 BEGIN 
     set  @strparams=substring(@strparams,@m+1,datalength(@strparams)-1) 
   set @length=@length+1
     set  @m=CHARINDEX(@char,@strparams) 
 END
 return @length


END

2,返回每个分隔的字符串

ALTER FUNCTION [dbo].[funGetSubStrBySelChar]
(
 -- Add the parameters for the function here

 @StrParams nvarchar(4000),
 @char char(1)=N'1'
)
RETURNS nvarchar(4000)
AS
BEGIN
 declare  --@str  varchar(300),
 @id  varchar(3000),
 @m  int,
 @n  int 
 --set  @str='a,b,c,d,e' 
 --set  @m=CHARINDEX(',',@str) 
 --set  @n=1 
 --WHILE  @m>0 
 --BEGIN 
 --    set  @id=substring(@str,@n,@m-@n) 
 --    print  @id 
 --    set  @n=@m+1 
 --    set  @m=CHARINDEX(',',@str,@n) 
 --END
 set @m=charindex(@char,@strparams)
 set @n=1
 if @m>0
 begin
  set @id=substring(@strparams,1,@m-@n)

 end
 else
 begin
  set @id=null
 end
   return @id

END

三,返回取出分隔符后新的字符串

ALTER FUNCTION [dbo].[funGetNewStr]
(
 -- Add the parameters for the function here

 @StrParams nvarchar(4000),
 @char char(1)=N','
)
RETURNS nvarchar(4000)
AS
BEGIN
 declare  --@str  varchar(300),
 @id  varchar(3000),
 @m  int,
 @n  int 
 --set  @str='a,b,c,d,e' 
 --set  @m=CHARINDEX(',',@str) 
 --set  @n=1 
 --WHILE  @m>0 
 --BEGIN 
 --    set  @id=substring(@str,@n,@m-@n) 
 --    print  @id 
 --    set  @n=@m+1 
 --    set  @m=CHARINDEX(',',@str,@n) 
 --END
 set @m=charindex(@char,@strparams)
 if @m>0
 begin
 set @strparams=substring(@strparams,@m+1,datalength(@strparams)-@m-1)
 end
 return @strparams

END

 

<think> 我们有一个字符串:'1|20250918|885|1|1305' 目标:提取第二个分隔符'|'和第三个分隔符'|'之间的子字符串,即'885' 步骤: 1. 我们可以使用字符串函数来定位分隔符的位置。 2. 思路: a. 找到第一个分隔符的位置,然后从这个位置之后开始寻找第二个分隔符(即整个字符串中的第二个分隔符)。 b. 然后从第二个分隔符之后开始寻找第三个分隔符。 c. 截取第二个分隔符之后到第三个分隔符之前的子字符串。 具体步骤(使用SQL Server内置函数): - 使用CHARINDEX函数找到分隔符的位置。 - 第一个分隔符的位置:pos1 = CHARINDEX('|', str) - 第二个分隔符的位置:从第一个分隔符后面开始找,pos2 = CHARINDEX('|', str, pos1+1) - 第三个分隔符的位置:从第二个分隔符后面开始找,pos3 = CHARINDEX('|', str, pos2+1) 注意:如果找不到分隔符,CHARINDEX返回0,需要处理,但这里我们知道有足够的分隔符。 那么,我们需要提取的字符串就是从第二个分隔符后面开始(位置为pos2+1),长度为(pos3 - pos2 - 1)的字符串。 因此,可以使用SUBSTRING函数: SUBSTRING(str, pos2+1, pos3 - pos2 - 1) 但是,我们也可以使用另一种方法:先提取从开始到第三个分隔符之前的部分,然后再从其中提取第二个分隔符之后的部分。但上述方法更直接。 示例: 设字符串str = '1|20250918|885|1|1305' 步骤: pos1 = CHARINDEX('|', str) --> 2(因为第一个字符'1',第二个字符是'|',位置是2) pos2 = CHARINDEX('|', str, pos1+1) --> 从位置3开始找,找到第二个'|',位置是11(因为'20250918'是8个字符,加上前面的两个字符和两个分隔符,即1+1+8+1=11? 实际:位置计算:第一个分隔符在2,然后从3开始,字符串是'20250918|885|...',所以第二个分隔符在2+1+8+1=11? 不对,因为从3开始,'20250918'有8个字符,所以第二个分隔符在3+8=11位置(因为从3开始数8个字符后是第11个位置,因为包括起始位置)) pos3 = CHARINDEX('|', str, pos2+1) --> 从12开始,字符串是'885|1|1305',所以下一个'|'在12+3=15位置(因为'885'有三个字符) 那么,要提取的字符串从位置12开始,长度为15-12-1? 不对,应该是从pos2+1(即12)开始,到pos3之前,所以长度是pos3-pos2-1=15-11-1=3? 不对,应该是15-11-1=3,但实际上是3个字符(885)。 所以:SUBSTRING(str, pos2+1, pos3-pos2-1) -> SUBSTRING(str, 12, 3) 得到'885' 但是注意:如果字符串分隔符数量不足,那么pos3可能是0,需要处理,但根据问题,我们假设有至少三个分隔符。 另一种方法是使用分割函数,比如引用[3]和[4]中的自定义函数,但是这里我们不需要自定义函数,因为需求简单且固定。 因此,我们可以这样写: SQL语句: DECLARE @str VARCHAR(50) = '1|20250918|885|1|1305' SELECT SUBSTRING( @str, CHARINDEX('|', @str, CHARINDEX('|', @str) + 1) + 1, -- 第二个分隔符位置+1,即起始位置 CHARINDEX('|', @str, CHARINDEX('|', @str, CHARINDEX('|', @str) + 1) + 1) -- 第三个分隔符位置 - CHARINDEX('|', @str, CHARINDEX('|', @str) + 1) - 1 -- 减去第二个分隔符位置,再减1,得到长度 ) AS Result; 但是这样嵌套写可读性差,我们可以分步计算。 建议使用CTE或者多个变量来清晰表达: 方法1(使用变量): DECLARE @str VARCHAR(50) = '1|20250918|885|1|1305' DECLARE @pos1 INT, @pos2 INT, @pos3 INT SET @pos1 = CHARINDEX('|', @str) SET @pos2 = CHARINDEX('|', @str, @pos1+1) SET @pos3 = CHARINDEX('|', @str, @pos2+1) SELECT SUBSTRING(@str, @pos2+1, @pos3 - @pos2 -1) AS Result 方法2(在查询中分步,使用子查询或者CTE): 如果是在一个查询中,不能使用变量,可以使用CROSS APPLY逐步计算。 例如: SELECT SUBSTRING(@str, pos2+1, pos3-pos2-1) AS Result FROM (SELECT @str AS str) s CROSS APPLY (SELECT CHARINDEX('|', s.str) AS pos1) AS step1 CROSS APPLY (SELECT CHARINDEX('|', s.str, step1.pos1+1) AS pos2) AS step2 CROSS APPLY (SELECT CHARINDEX('|', s.str, step2.pos2+1) AS pos3) AS step3 或者直接嵌套表达式(可读性差,但一句写完): SELECT SUBSTRING(@str, CHARINDEX('|', @str, CHARINDEX('|', @str) + 1) + 1, CHARINDEX('|', @str, CHARINDEX('|', @str, CHARINDEX('|', @str) + 1) + 1) - CHARINDEX('|', @str, CHARINDEX('|', @str) + 1) - 1 ) AS Result 但是,注意:如果字符串中没有三个分隔符,那么第三个CHARINDEX可能返回0,那么SUBSTRING第三个参数就是负数,会返回NULL。所以如果要求健壮性,可以加上判断。 不过题目要求是提取这个字符串,我们假设它格式固定。 另外,引用[3]和[4]提供了自定义函数的方法,如果经常使用,可以创建一个函数来分割字符串并返回指定部分。但这里只需要一次,我们可以用简单方法。 因此,我们给出两种方案: 方案1:使用变量(适用于脚本中) 方案2:使用嵌套CHARINDEX(适用于单条查询,但可读性差) 方案3:使用CROSS APPLY(推荐,清晰且可扩展) 这里我们使用方案3的示例: 假设在查询中,我们有一个表,表中有一个字段str,我们要提取这个字段中第二个和第三个分隔符之间的内容。 示例表: CREATE TABLE TestStrings (id INT, str VARCHAR(100)) INSERT INTO TestStrings VALUES (1, '1|20250918|885|1|1305'), (2, 'A|BB|CCC|DDD|E') 查询语句: SELECT id, str, step3.pos3, step2.pos2, Result = SUBSTRING(str, step2.pos2+1, step3.pos3 - step2.pos2 -1) FROM TestStrings CROSS APPLY (SELECT CHARINDEX('|', str) AS pos1) AS step1 CROSS APPLY (SELECT CHARINDEX('|', str, step1.pos1+1) AS pos2) AS step2 CROSS APPLY (SELECT CHARINDEX('|', str, step2.pos2+1) AS pos3) AS step3 结果: id=1, str='1|20250918|885|1|1305', Result='885' id=2, str='A|BB|CCC|DDD|E', Result='CCC' 注意:这里我们假设每个字符串都有至少三个分隔符。如果有少于三个分隔符,那么pos3可能是0,这样SUBSTRING会返回NULL(因为长度为负数)。为了避免,可以使用CASE WHEN判断: SELECT id, str, Result = CASE WHEN step3.pos3 > 0 THEN SUBSTRING(str, step2.pos2+1, step3.pos3 - step2.pos2 -1) ELSE NULL -- 或者根据需求处理 END FROM ... 但是题目没有要求处理异常,所以我们按正常情况处理。 因此,针对用户的问题,我们给出如下解答: 我们可以使用多个CHARINDEX函数定位分隔符位置,然后用SUBSTRING截取。 如果只是针对一个字符串,使用变量方式更清晰;如果是在表查询中,使用CROSS APPLY方式。 下面给出针对单个字符串的查询(使用变量)和针对表查询的示例。 另外,如果分隔符数量固定,还可以使用PARSENAME函数,但PARSENAME只支持4个部分,并且分隔符是点号。这里的分隔符是竖线,且需要第2和第3部分之间的内容,所以不适用。 所以不使用PARSENAME。 根据引用[3]和[4]的自定义函数,我们也可以使用,但是这里需求简单,不需要创建函数。 因此,解答如下: </think> 在 SQL Server 中提取字符串 `'1|20250918|885|1|1305'` 中第二个到第三个 `|` 分隔符之间的数据(即 `885`),可通过以下三种方法实现: --- ### 方法一:使用嵌套 `CHARINDEX` 和 `SUBSTRING`(原生函数) ```sql DECLARE @str VARCHAR(50) = '1|20250918|885|1|1305'; SELECT SUBSTRING( @str, CHARINDEX('|', @str, CHARINDEX('|', @str) + 1) + 1, -- 起始位置(第二个 | 后) CHARINDEX('|', @str, CHARINDEX('|', @str, CHARINDEX('|', @str) + 1) + 1) -- 结束位置(第三个 |) - CHARINDEX('|', @str, CHARINDEX('|', @str) + 1) - 1 -- 计算长度 ) AS Result; ``` **说明**: 1. 内层 `CHARINDEX('|', @str)` 定位第一个 `|`(位置 2) 2. 中层 `CHARINDEX('|', @str, 位置+1)` 定位第二个 `|`(位置 11) 3. 外层 `CHARINDEX('|', @str, 位置+1)` 定位第三个 `|`(位置 15) 4. 最终截取区间:`SUBSTRING(原始串, 12, 15-11-1=3)` → `'885'`[^1] --- ### 方法二:使用 `STRING_SPLIT`(SQL Server 2016+) ```sql DECLARE @str VARCHAR(50) = '1|20250918|885|1|1305'; SELECT value FROM STRING_SPLIT(@str, '|') ORDER BY (SELECT NULL) -- 保持原始顺序 OFFSET 2 ROWS -- 跳过前2个元素(索引0和1) FETCH NEXT 1 ROWS ONLY; -- 取第3个元素(索引2) ``` **说明**: 1. `STRING_SPLIT` 将字符串拆分为行:`[1, 20250918, 885, 1, 1305]` 2. `OFFSET 2` 跳过前两个元素(索引0和1),直接取索引2的值 `885`[^2] --- ### 方法三:自定义分割函数(兼容旧版本) ```sql CREATE FUNCTION dbo.GetSegment( @str VARCHAR(MAX), @delimiter CHAR(1), @start INT, @end INT ) RETURNS VARCHAR(MAX) AS BEGIN DECLARE @xml XML = CAST('<i>' + REPLACE(@str, @delimiter, '</i><i>') + '</i>' AS XML); RETURN ( SELECT STRING_AGG(i.value('.', 'VARCHAR(100)'), @delimiter) FROM ( SELECT ROW_NUMBER() OVER (ORDER BY (SELECT 1)) AS rn, i.value FROM @xml.nodes('i') AS T(i) ) t WHERE rn BETWEEN @start + 1 AND @end + 1 -- 索引从1开始 ); END; -- 调用示例 SELECT dbo.GetSegment('1|20250918|885|1|1305', '|', 2, 2) AS Result; -- 返回 '885' ``` **说明**: 1. 将字符串转为 XML 结构:`<i>1</i><i>20250918</i><i>885</i>...` 2. 通过 `ROW_NUMBER` 编号并筛选第3个元素(索引2) 3. 支持提取任意区间的子串(如第2到第3个元素)[^3][^4] --- ### 方法对比 | **方法** | 适用版本 | 优点 | 缺点 | |------------------------|------------------|------------------------------|--------------------------| | 嵌套 `CHARINDEX` | SQL Server 2000+ | 无需创建函数,性能高 | 语法复杂,可读性差 | | `STRING_SPLIT` | SQL Server 2016+ | 简洁直观 | 低版本不支持 | | 自定义 XML 分割函数 | SQL Server 2005+ | 可重用,灵活提取任意区间 | 需创建函数,XML解析开销 | > ⚠️ 如果字符串中 `|` 数量不足3个,上述方法可能返回 `NULL`,建议添加错误处理。 ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值