每个字符出现按字符拆分

时间:2017-09-13 09:06:11

标签: sql sql-server string tsql split

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。

我使用CHARINDEXSUBSTRING尝试了解决方案,但到目前为止我无法使其正常工作。

谢谢你们!

3 个答案:

答案 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

<强>返回

enter image description here

答案 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

这里的结果图片(如果你想知道的话,那就是第27列): enter image description here