2017-09-13 48 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好奇,想知道它如何執行你的真實世界的數據 –