2017-09-13 27 views
0

SSMS字符2016企业分割字符串每个字符发生

如果我有以下弦一柱:

$/Harmony/Maintenance/6.0/Dev/ 

如何创建以下结果

Column1 | Column 2 | Column 3 | Column 4 | Column 5 

    $  Harmony Maintenance 6.0   Dev 

字符串中的最大数字/将为27.因此,在每一行中,/的数目可以它们之间的字符串也不尽相同。

我需要创建27列并填充它们,如果字符串只有5,那么下面的表将是空的。

我尝试了使用CHARINDEXSUBSTRING的解决方案,但我无法按照我目前的要求使其工作。

谢谢你们!

+0

只是SO中的其中一个存在:https://stackoverflow.com/questions/10914576/t-sql-split-string – etsa

+0

你可以显示MSSQL版本吗? – etsa

+0

它的SQL Server 2016 ETSA上运行 –

回答

0

它requiers一些编码,但我的总体思路是:

创建动态地查询,然后执行它。

我提供了可以应用的解决方案。我基于固定字符串(您提供的),将它分配给@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

+0

我只是不知道如何将这种适应对我有 ID表的情况下| ColumnWithStringToSplit –

+0

请检查编辑 –

+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 
+0

这看起来太神奇了! 但是我怎样才能使用这个函数来引用一个列,并让它在我在函数内部手动输入字符串时的功能? –

+0

我会对脚本进行一些更改。挂上:) – plaidDK

+0

再次看看我的脚本,我编辑了一下。我希望你在你的专栏上有某种ID,因为我用它来订购专栏。 – plaidDK

1

然而,在演唱会使用一些XML的另一种选择带有CROSS APPLY。

易于扩展到27 ...该模式是很简单

EDIT - 增加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

+0

XML路径唯一的问题是它使用你的CPU到最大,如果有很多行,你会遇到问题。 – plaidDK

+0

漂亮整洁的这一个。简单而做的工作,只需要检查的性能,但是这是真棒 –

+0

@LuisDoCantoBrum好奇,想知道它如何执行你的真实世界的数据 –