有时候我们需要将如下结果集中的ids进行分割
ids
--------
1,12,123
(1 行受影响)
希望得到这样的结果:
--------
1
12
123
(3 行受影响)
之前一直用表值函数来截取,这里借助master.dbo.spt_values表来实现!
准备数据:
IF OBJECT_ID('tempdb..#a') IS NOT NULL DROP TABLE #a
SELECT '1,12,123' AS ids INTO #a
当我们在使用SUBSTRING()截取时,需要提供三个参数:
expression 是字符串、二进制字符串、文本、图像、列或包含列的表达式。不要使用包含聚合函数的表达式。
start 指定子字符串开始位置的整数。start 可以为 bigint 类型。
length 一个正整数,指定要返回的 expression 的字符数或字节数。如果 length 为负,则会返回错误。length 可以是 bigint 类型。
因此需要找到“指定子字符串开始位置”和“截取的长度”
1.寻找截取的开始位置
很明显对于这个字符串的开始截取位置是1(从第一个字符开始截取),那么尝试从位置1开始截取,每次截取1个字符
使用SUBSTRING(a.ids,b.number,1)后可以看到字符串的每个字符都被截取成单个的字符(一共8个字符)
但是要截取两位,三位的字符就得计算截取长度了
2.计算截取的长度
分别截取1,12,123这三个数字
1,12,123
SUBSTRING(ids,1,1)----1
SUBSTRING(ids,3,2)----12
SUBSTRING(ids,6,3)----123
两个问题:1.怎么获取开始截取位置? 2.怎么计算截取长度?
解决第一个问题: 可以通过加一个条件来筛选b.number,我们只需要1,3,6的值
再看看这个字符串'1,12,123' 逗号所在的位置为:2,5
试试在字符串'1,12,123' 前面加个逗号 ',1,12,123' 这时逗号的位置是:1,3,6
ok开始位置就拿到了,加个条件实现:AND SUBSTRING(','+a.ids,b.number,1)=','
看看效果:
ids number
-------- -----------
1,12,123 1
1,12,123 3
1,12,123 6
解决第二个问题:我们知道截取长度分别是1,2,3 怎么计算?
我们再来看看每个逗号的位置,以及number的数值
ids number 逗号位置
-------- ----------- -----------
1,12,123 1 2
1,12,123 3 5
1,12,123 6 0
发现逗号位置减去number将得到结果:1,2,-6
还差一点,如果逗号位置的最后一个数值是9就好了,怎么办?
试试往后再加一个逗号看看:'1,12,123,'现在最后一个逗号的位置就是9了吧!
逗号位置:CHARINDEX(',',a.ids+',',b.number)
截取长度:CHARINDEX(',',a.ids+',',b.number)-b.number
这样我们就可以计算截取长度了:
ids number 逗号位置 截取长度
-------- ----------- ----------- -----------
1,12,123 1 2 1
1,12,123 3 5 2
1,12,123 6 9 3
截取的时候,从number开始,按截取长度来截取将是:
ids number
-------- ----------- --------
1,12,123 1 1
1,12,123 3 12
1,12,123 6 123
完整语句:
1 GO
2 SELECT a.*,b.number,
3 SUBSTRING(a.ids,b.number,
4 CHARINDEX(',',a.ids+',',b.number)-b.number
5 )
6 FROM #a a,spt_values b
7 WHERE b.type='P'
8 AND b.number BETWEEN 1 and LEN(a.ids)
9 AND SUBSTRING(','+a.ids,b.number,1)=','