在 ',' 和 '|' 上拆分 sql 字符串
split sql string on ',' and '|'
我的目标是转换 sql 字符串
'element1|value,element2|value2,element3|value3'
进入
'<element1>value</element1>
<element2>value2</element1>
<element3>value3</element1>'
我的想法是
declare @test as varchar(max) = 'element1|value1,element2|value2,element3|value3'
SELECT CHARINDEX(',',@test)
SELECT SUBSTRING(@test,0,CHARINDEX(',',@test))
我遇到的问题是我对 sql 不是很熟悉,是否有列表功能或我可以用来将其分成 3 个块,然后剖析每个块的东西?
您可以使用 XML
功能解析字符串:
;WITH cte AS (SELECT testString = 'element1|value,element2|value2,element3|value3'
UNION SELECT 'test,1,2,3'
)
,SplitString AS (SELECT testString,
CONVERT(XML,'<String><Section>'+ REPLACE(REPLACE(testString ,'|',','),',', '</Section><Section>') + '</Section></String>') AS xmlString
FROM cte
)
SELECT xmlString.value('/String[1]/Section[1]','varchar(100)') AS Col1
,xmlString.value('/String[1]/Section[2]','varchar(100)') AS Col2
,xmlString.value('/String[1]/Section[3]','varchar(100)') AS Col3
,xmlString.value('/String[1]/Section[4]','varchar(100)') AS Col4
FROM SplitString
这里我刚刚把你的|
改成了,
,一口气完成了所有的分裂,但如果不是所有偶数对,你可以分两步完成,首先分裂在 |
然后在 ,
.
您也可以查看 PARSENAME()
,但它仅限于 4 个部分,或者您可以创建一个 PARSE
函数,例如:
/********************************************************************************************
Create Parse Function
********************************************************************************************/
CREATE FUNCTION dbo.FN_PARSE(@chunk VARCHAR(4000), @delimiter CHAR(1), @index INT )
RETURNS VARCHAR(1000)
AS
BEGIN
DECLARE
@curIndex INT = 0,
@pos INT = 1,
@prevPos INT = 0,
@result VARCHAR(1000)
WHILE @pos > 0
BEGIN
SET @pos = CHARINDEX(@delimiter, @chunk, @prevPos);
IF(@pos > 0)
BEGIN -- Characters between position and previous position
SET @result = SUBSTRING(@chunk, @prevPos, @pos-@prevPos)
END
ELSE
BEGIN -- Last Delim
SET @result = SUBSTRING(@chunk, @prevPos, LEN(@chunk))
END
IF(@index = @curIndex)
BEGIN
RETURN @result
END
SET @prevPos = @pos + 1
SET @curIndex = @curIndex + 1;
END
RETURN '' -- Else Empty
END
然后简称为:
;WITH cte AS (SELECT testString = 'element1|value,element2|value2,element3|value3'
UNION SELECT 'test,1,2,3'
)
SELECT dbo.FN_PARSE(testString ,'|', 0) AS Col1
,dbo.FN_PARSE(testString ,'|', 1) AS Col2
...
FROM cte
请注意,该部分的索引在上述函数中从 0
开始。
我目前比较喜欢XML
版本,但是没有做过太多对比测试。
只是有选项:-),如果最终目标是将输入字符串转换为 XML,您可以使用正则表达式替换来执行此操作,它不会被限制为任何数字元素数:
DECLARE @Sample NVARCHAR(500),
@ElementBasedXML NVARCHAR(500),
@AttributeBasedXML NVARCHAR(500);
SELECT @Sample = N'element1|value,element2|value2,element3|value3',
@ElementBasedXML = N'<></>' + NCHAR(10),
@AttributeBasedXML = N'<row ="" />' + NCHAR(10);
SELECT SQL#.RegEx_Replace4k(@Sample, N'(([^|,]+)\|([^|,]+)),?', @ElementBasedXML,
-1, 1, Null) AS [ElementBased],
SQL#.RegEx_Replace4k(@Sample, N'(([^|,]+)\|([^|,]+)),?', @AttributeBasedXML,
-1, 1, Null) AS [AttributeBased];
Returns:
ElementBased
------------
<element1>value</element1>
<element2>value2</element2>
<element3>value3</element3>
AttributeBased
--------------
<row element1="value" />
<row element2="value2" />
<row element3="value3" />
或者,您可以在 CTE 内用逗号拆分字符串(提供无限数量的由管道符号分隔的键值对),然后在它们的单个分隔符上拆分这些对:
DECLARE @Sample2 NVARCHAR(500);
SELECT @Sample = N'element1|value,element2|value2,element3|value3';
;WITH kvpairs AS
(
SELECT split.SplitVal,
CHARINDEX(N'|', split.SplitVal) AS [PipeLocation],
LEN(split.SplitVal) AS [PairLength]
FROM SQL#.String_Split4k(@Sample2, N',', 1) split
), pieces AS
(
SELECT LEFT(kvpairs.SplitVal, (kvpairs.PipeLocation - 1)) AS [Key],
RIGHT(kvpairs.SplitVal, (kvpairs.PairLength - kvpairs.PipeLocation))
AS [Value]
FROM kvpairs
)
SELECT CONVERT(XML, N'<' + pieces.[Key] + N'>'
+ pieces.[Value]
+ N'</' + pieces.[Key] + N'>')
FROM pieces
FOR XML PATH('');
请注意:
这两个示例都使用了 SQL# 库,它是 SQLCLR 函数和过程的集合(我写的,但这里显示的函数在免费版).
RegEx 方法很好用在针对 table 的 SELECT 语句中,传入一个列作为要计算的表达式。 Split/CTE 方法需要放入函数(内联 TVF)中才能通过 CROSS APPLY
.
在查询中使用
第二个例子,进行拆分,不必使用SQLCLR;它可以使用纯 T-SQL 分离器,使用内联计数 table 或 XML.
我的目标是转换 sql 字符串
'element1|value,element2|value2,element3|value3'
进入
'<element1>value</element1>
<element2>value2</element1>
<element3>value3</element1>'
我的想法是
declare @test as varchar(max) = 'element1|value1,element2|value2,element3|value3'
SELECT CHARINDEX(',',@test)
SELECT SUBSTRING(@test,0,CHARINDEX(',',@test))
我遇到的问题是我对 sql 不是很熟悉,是否有列表功能或我可以用来将其分成 3 个块,然后剖析每个块的东西?
您可以使用 XML
功能解析字符串:
;WITH cte AS (SELECT testString = 'element1|value,element2|value2,element3|value3'
UNION SELECT 'test,1,2,3'
)
,SplitString AS (SELECT testString,
CONVERT(XML,'<String><Section>'+ REPLACE(REPLACE(testString ,'|',','),',', '</Section><Section>') + '</Section></String>') AS xmlString
FROM cte
)
SELECT xmlString.value('/String[1]/Section[1]','varchar(100)') AS Col1
,xmlString.value('/String[1]/Section[2]','varchar(100)') AS Col2
,xmlString.value('/String[1]/Section[3]','varchar(100)') AS Col3
,xmlString.value('/String[1]/Section[4]','varchar(100)') AS Col4
FROM SplitString
这里我刚刚把你的|
改成了,
,一口气完成了所有的分裂,但如果不是所有偶数对,你可以分两步完成,首先分裂在 |
然后在 ,
.
您也可以查看 PARSENAME()
,但它仅限于 4 个部分,或者您可以创建一个 PARSE
函数,例如:
/********************************************************************************************
Create Parse Function
********************************************************************************************/
CREATE FUNCTION dbo.FN_PARSE(@chunk VARCHAR(4000), @delimiter CHAR(1), @index INT )
RETURNS VARCHAR(1000)
AS
BEGIN
DECLARE
@curIndex INT = 0,
@pos INT = 1,
@prevPos INT = 0,
@result VARCHAR(1000)
WHILE @pos > 0
BEGIN
SET @pos = CHARINDEX(@delimiter, @chunk, @prevPos);
IF(@pos > 0)
BEGIN -- Characters between position and previous position
SET @result = SUBSTRING(@chunk, @prevPos, @pos-@prevPos)
END
ELSE
BEGIN -- Last Delim
SET @result = SUBSTRING(@chunk, @prevPos, LEN(@chunk))
END
IF(@index = @curIndex)
BEGIN
RETURN @result
END
SET @prevPos = @pos + 1
SET @curIndex = @curIndex + 1;
END
RETURN '' -- Else Empty
END
然后简称为:
;WITH cte AS (SELECT testString = 'element1|value,element2|value2,element3|value3'
UNION SELECT 'test,1,2,3'
)
SELECT dbo.FN_PARSE(testString ,'|', 0) AS Col1
,dbo.FN_PARSE(testString ,'|', 1) AS Col2
...
FROM cte
请注意,该部分的索引在上述函数中从 0
开始。
我目前比较喜欢XML
版本,但是没有做过太多对比测试。
只是有选项:-),如果最终目标是将输入字符串转换为 XML,您可以使用正则表达式替换来执行此操作,它不会被限制为任何数字元素数:
DECLARE @Sample NVARCHAR(500),
@ElementBasedXML NVARCHAR(500),
@AttributeBasedXML NVARCHAR(500);
SELECT @Sample = N'element1|value,element2|value2,element3|value3',
@ElementBasedXML = N'<></>' + NCHAR(10),
@AttributeBasedXML = N'<row ="" />' + NCHAR(10);
SELECT SQL#.RegEx_Replace4k(@Sample, N'(([^|,]+)\|([^|,]+)),?', @ElementBasedXML,
-1, 1, Null) AS [ElementBased],
SQL#.RegEx_Replace4k(@Sample, N'(([^|,]+)\|([^|,]+)),?', @AttributeBasedXML,
-1, 1, Null) AS [AttributeBased];
Returns:
ElementBased
------------
<element1>value</element1>
<element2>value2</element2>
<element3>value3</element3>
AttributeBased
--------------
<row element1="value" />
<row element2="value2" />
<row element3="value3" />
或者,您可以在 CTE 内用逗号拆分字符串(提供无限数量的由管道符号分隔的键值对),然后在它们的单个分隔符上拆分这些对:
DECLARE @Sample2 NVARCHAR(500);
SELECT @Sample = N'element1|value,element2|value2,element3|value3';
;WITH kvpairs AS
(
SELECT split.SplitVal,
CHARINDEX(N'|', split.SplitVal) AS [PipeLocation],
LEN(split.SplitVal) AS [PairLength]
FROM SQL#.String_Split4k(@Sample2, N',', 1) split
), pieces AS
(
SELECT LEFT(kvpairs.SplitVal, (kvpairs.PipeLocation - 1)) AS [Key],
RIGHT(kvpairs.SplitVal, (kvpairs.PairLength - kvpairs.PipeLocation))
AS [Value]
FROM kvpairs
)
SELECT CONVERT(XML, N'<' + pieces.[Key] + N'>'
+ pieces.[Value]
+ N'</' + pieces.[Key] + N'>')
FROM pieces
FOR XML PATH('');
请注意:
这两个示例都使用了 SQL# 库,它是 SQLCLR 函数和过程的集合(我写的,但这里显示的函数在免费版).
RegEx 方法很好用在针对 table 的 SELECT 语句中,传入一个列作为要计算的表达式。 Split/CTE 方法需要放入函数(内联 TVF)中才能通过
CROSS APPLY
. 在查询中使用
第二个例子,进行拆分,不必使用SQLCLR;它可以使用纯 T-SQL 分离器,使用内联计数 table 或 XML.