使用CHARINDEX拆分字符串至多列遇问题:结果行重复且列值错误
解决SQL自定义拆分函数的多行与列值错误问题
我仔细看了你的函数代码和遇到的问题,这两个问题其实是因为函数里的逻辑没处理好拆分段的分配和插入方式导致的,咱们一步步来修正:
原函数的核心问题
- 多行记录问题:你在函数里多次执行
INSERT INTO @returnList,每次只插一列,自然会生成多条记录,而不是一条记录包含所有列。 - 列值重复错误:循环里每次都把
@cassette_1到@cassette_4全赋值成当前拆分的第一段,完全没区分不同的拆分段,最后所有变量都会被覆盖成最后一次循环的内容。 - 另外,原逻辑没处理分隔符连续出现的情况(比如末尾的
aaaad),会导致空值混入。
修正后的自定义函数
CREATE FUNCTION dbo.splitstring ( @stringToSplit VARCHAR(MAX) ) RETURNS @returnList TABLE ( [first_segment] [nvarchar] (500), [remaining_note] [nvarchar] (500), [cassette_1] [nvarchar] (500), [cassette_2] [nvarchar] (500), [cassette_3] [nvarchar] (500), [cassette_4] [nvarchar] (500), [total] [nvarchar] (500) ) AS BEGIN DECLARE @first_segment NVARCHAR(255) DECLARE @remaining_note NVARCHAR(255) DECLARE @cassette_1 NVARCHAR(255) DECLARE @cassette_2 NVARCHAR(255) DECLARE @cassette_3 NVARCHAR(255) DECLARE @cassette_4 NVARCHAR(255) DECLARE @total NVARCHAR(255) DECLARE @pos INT DECLARE @segment_count INT = 1 -- 跟踪当前是第几个拆分段 DECLARE @current_segment NVARCHAR(255) -- 先处理掉末尾的无效字符,避免空段干扰 SET @stringToSplit = RTRIM(LTRIM(REPLACE(@stringToSplit, 'aaaad', ''))) WHILE CHARINDEX('a ', @stringToSplit) > 0 BEGIN SELECT @pos = CHARINDEX('a ', @stringToSplit) SELECT @current_segment = LTRIM(SUBSTRING(@stringToSplit, 1, @pos - 1)) -- 去掉段前的空格 -- 根据当前段的序号赋值给对应的变量 IF @segment_count = 1 SET @first_segment = @current_segment ELSE IF @segment_count = 2 SET @remaining_note = @current_segment ELSE IF @segment_count = 3 SET @cassette_1 = @current_segment ELSE IF @segment_count = 4 SET @cassette_2 = @current_segment ELSE IF @segment_count = 5 SET @cassette_3 = @current_segment ELSE IF @segment_count = 6 SET @cassette_4 = @current_segment ELSE IF @segment_count = 7 SET @total = @current_segment -- 截取剩余字符串继续处理 SELECT @stringToSplit = SUBSTRING(@stringToSplit, @pos + 2, LEN(@stringToSplit) - @pos - 1) SET @segment_count = @segment_count + 1 END -- 处理最后一段(如果循环结束后还有剩余) IF LTRIM(@stringToSplit) <> '' SET @total = LTRIM(@stringToSplit) -- 只插入一条记录,包含所有列 INSERT INTO @returnList (first_segment, remaining_note, cassette_1, cassette_2, cassette_3, cassette_4, total) VALUES (@first_segment, @remaining_note, @cassette_1, @cassette_2, @cassette_3, @cassette_4, @total) RETURN END
关键点说明
- 添加段计数器:用
@segment_count跟踪当前拆分到第几个段,对应赋值给不同的变量,不会再全部覆盖成同一个值。 - 单次插入记录:所有变量赋值完成后,只执行一次
INSERT,确保生成单条记录。 - 清理无效字符:提前处理末尾的
aaaad,避免空段干扰拆分逻辑。 - 去除段前空格:用
LTRIM处理每个拆分段,避免开头的多余空格。
调用测试
执行你的调用代码后,就能得到单条记录,且每个列对应正确的内容:
SELECT * FROM dbo.splitstring('005031812180200000100aRemaining Inside Terminala Cassette 1(BIN1)/c:HTG 21,000a Cassette 2(BIN2)/c:HTG 23,000a Cassette 3(BIN3)/d:HTG 47,000a Cassette 4(BIN4)/d:HTG 48,000a Total : HTG 139,000aaaad')
结果示例:
| first_segment | remaining_note | cassette_1 | cassette_2 | cassette_3 | cassette_4 | total |
|---|---|---|---|---|---|---|
| 005031812180200000100 | Remaining Inside Terminal | Cassette 1(BIN1)/c:HTG 21,000 | Cassette 2(BIN2)/c:HTG 23,000 | Cassette 3(BIN3)/d:HTG 47,000 | Cassette 4(BIN4)/d:HTG 48,000 | Total : HTG 139,000 |
内容的提问来源于stack exchange,提问作者Manuel Yumar
相关产品推荐
相关产品推荐

