SSMS 2016 Enterprise
如果我有一个包含以下字符串的列:
$/Harmony/Maintenance/6.0/Dev/
如何创建以下结果
Column1 | Column 2 | Column 3 | Column 4 | Column 5
$ Harmony Maintenance 6.0 Dev
字符串中/
的最大数量为27.因此,在每一行中,/
的数量以及它们之间的字符串都会有所不同。
我需要创建27列并填充它们,如果字符串只有5,那么下面的表将为null。
我使用CHARINDEX
和SUBSTRING
尝试了解决方案,但到目前为止我无法使其正常工作。
谢谢你们!
答案 0 :(得分:1)
我首先要创建一个函数来分割你的字符串,然后再将它转动。
功能:
CREATE FUNCTION [dbo].[dba_parseString_udf] (
@stringToParse VARCHAR(8000)
, @delimiter CHAR(1)
)
RETURNS @parsedString TABLE (stringValue VARCHAR(128)) AS
BEGIN
/* Declare variables */
DECLARE @trimmedString VARCHAR(8000);
/* We need to trim our string input in case the user entered extra spaces */
SET @trimmedString = LTRIM(RTRIM(@stringToParse));
/* Let's create a recursive CTE to break down our string for us */
WITH parseCTE (StartPos, EndPos)
AS
(
SELECT 1 AS StartPos
, CHARINDEX(@delimiter, @trimmedString + @delimiter) AS EndPos
UNION ALL
SELECT EndPos + 1 AS StartPos
, CHARINDEX(@delimiter, @trimmedString + @delimiter , EndPos + 1) AS EndPos
FROM parseCTE
WHERE CHARINDEX(@delimiter, @trimmedString + @delimiter, EndPos + 1) <> 0
)
/* Let's take the results and stick it in a table */
INSERT INTO @parsedString
SELECT SUBSTRING(@trimmedString, StartPos, EndPos - StartPos)
FROM parseCTE
WHERE LEN(LTRIM(RTRIM(SUBSTRING(@trimmedString, StartPos, EndPos - StartPos)))) > 0
OPTION (MaxRecursion 8000);
RETURN;
END
在此之后我会转动你的结果:
----For test purpose-----
DECLARE @Table TABLE (
ID int null,
string nvarchar(500) null
)
INSERT into @Table (ID,String)
VALUES(1,'$/Harmony/Maintenance/6.0/Dev/'),
(2,'$/Harmony/Maintenance/6.0/Dev/Test/')
--------------------------
select [Column1],[Column2],[Column3],[Column4],[Column5],[Column6],
[Column7],[Column8],[Column9],[Column10],[Column11],[Column12],[Column13]
,[Column14],[Column15],[Column16],[Column17],[Column18],[Column19],
[Column20],[Column21],[Column22],[Column23],[Column24],[Column25],
[Column26],[Column27]
FROM
(
SELECT 'Column' + cast(Row_number() over(Partition BY ID order by ID) as
varchar(50)) as ColumnRow,
* FROM @Table
CROSS APPLY dbo.[dba_parseString_udf](string,'/')
) AS SOURCETABLE
PIVOT
(
min(stringValue)
FOR ColumnRow IN ([Column1],[Column2],[Column3],[Column4],[Column5],
[Column6],[Column7],[Column8],[Column9],[Column10],[Column11],[Column12],
[Column13]
,[Column14],[Column15],[Column16],[Column17],[Column18],[Column19],
[Column20],[Column21],[Column22],[Column23],[Column24],[Column25],
[Column26],[Column27])
)
AS
PivotTable
答案 1 :(得分:1)
另一个选项是使用一个小的XML与CROSS APPLY一起使用。
易于扩展到27 ......模式非常简单
编辑 - 添加了2ndToLast - 还添加了NullIf(),这是可选的
示例强>
Select B.*
,[2ndToLast] = reverse(Cast('<x>' + replace((Select replace(reverse(A.SomeCol),'/','§§Split§§') as [*] For XML Path('')),'§§Split§§','</x><x>')+'</x>' as xml).value('/x[3]','varchar(max)'))
From @YourTable A
Cross Apply (
Select Pos1 = nullif(xDim.value('/x[1]','varchar(max)'),'')
,Pos2 = nullif(xDim.value('/x[2]','varchar(max)'),'')
,Pos3 = nullif(xDim.value('/x[3]','varchar(max)'),'')
,Pos4 = nullif(xDim.value('/x[4]','varchar(max)'),'')
,Pos5 = nullif(xDim.value('/x[5]','varchar(max)'),'')
,Pos6 = nullif(xDim.value('/x[6]','varchar(max)'),'')
,Pos7 = nullif(xDim.value('/x[7]','varchar(max)'),'')
,Pos8 = nullif(xDim.value('/x[8]','varchar(max)'),'')
,Pos9 = nullif(xDim.value('/x[9]','varchar(max)'),'')
From (Select Cast('<x>' + replace((Select replace(A.SomeCol,'/','§§Split§§') as [*] For XML Path('')),'§§Split§§','</x><x>')+'</x>' as xml) as xDim) as A
) B
<强>返回强>
答案 2 :(得分:0)
它需要一些编码,但我的一般想法是:
创建动态查询,然后执行它。
我提供了解决方案,您可以申请。我将它基于固定字符串(您提供),将其分配给@str
变量并对其进行修改。但是你可以将它全部放在一个循环中,在循环的每次运行中你可以为它分配新的字符串,它将以给定的方式进行转换。每次循环执行后,将运行插入查询。
create table #resultTable(
col1 varchar(50), col2 varchar(50), col3 varchar(50), col4 varchar(50), col5 varchar(50), col6 varchar(50), col7 varchar(50),
col8 varchar(50), col9 varchar(50), col10 varchar(50), col11 varchar(50), col12 varchar(50), col13 varchar(50), col14 varchar(50),
col15 varchar(50), col16 varchar(50), col17 varchar(50), col18 varchar(50), col19 varchar(50), col20 varchar(50), col21 varchar(50),
col22 varchar(50), col23 varchar(50), col24 varchar(50), col25 varchar(50), col26 varchar(50), col27 varchar(50)
)
declare @tableToSplit table(id int identity(1,1), ColumnWithStringToSplit varchar(2000))
insert into @tableToSplit values ('$/Harmony/Maintenance/6.0/Dev/'),
('$/Harmony/Maintenance/6.0/Dev/123/'),('$/Harmony/Maintenance/6.0/Dev/123/456/')
select * from @tableToSplit
declare @howManyRows int, @id int, @iterator int = 1
select @howManyRows = count(*) from @tableToSplit
declare @i int, @counter int, @values varchar(1000), @query nvarchar(2000)
while @iterator <= @howManyRows
begin
set @query = 'insert into #resultTable ('
--get values from table
select top 1 @id = id, @values = ColumnWithStringToSplit from
@tableToSplit
--count how many strings we will have, which is equal to number of '/' in string
set @i = len(@values) - len(replace(@values,'/',''))
--get rid of last forward-slash
set @values = left(@values, len(@values) - 1)
--replace all slashes, so it can be used as part of insert statement
set @values = '(''' + REPLACE(@values, '/', ''',''') + ''')'
set @counter = 1
while @counter <= @i
begin
set @query = @query + 'col' + cast(@counter as varchar(2)) + ','
set @counter = @counter + 1
end
--get rid of last comma and append rest of the query
set @query = left(@query, len(@query) - 1) + ') values ' + @values
exec sp_executesql @query
--we processed that row, so we need to delete it
delete from @tableToSplit where id = @id
set @iterator = @iterator + 1
end
select * from #resultTable