在一列中拆分一个字符串并在sql中进行透视

时间:2017-01-13 12:04:12

标签: sql sql-server pivot sql-server-2014

我正在使用sql server 2014,我有一个这样的表(## tt_allresults)

ID      |       Area        |       Event                                                                           |
1       |       FB1         |   Dev_Chg, Old Value: 0, New Value: 50, Workstation: Blah1, Function: Blah1 func      |
1       |       FB2         |   Dev_Chg, Old Value: 99, New Value: 5, Workstation: Blah2, Function: Blah2 func      |       
1       |       FB1         |   Dev_Chg, Old Value: 50, New Value: 55, Workstation: Blah1, Function: Blah1 func     |

我想从这样的表格(预期输出)

Area    |   Old Value   |       New Value   |       Function        |
FB1     |   0           |       50          |   Blah1 func          |   
FB2     |   99          |       5           |   Blah2 func          |   
FB1     |   50          |       55          |   Blah1 func          |   

这是我到目前为止所尝试的

Declare @id int
WHILE EXISTS(SELECT * FROM ##tt_allresults)
BEGIN
Select Top 1 @id = Id from ##tt_allresults

-- Do the work --
Declare @area nvarchar(100)
set @area = (Select Area from ##tt_allresults where id = @id)

Insert into ##tt_changedetails
select @area, * from fnsplit((Select [event] from ##tt_allresults where id = @id),',')

-- Scrap the ID and Move On --
Delete ##tt_allresults where ID = @id
END


select * from ##tt_changedetails

我得到以下结果

Area    |       ChangeDetails       |
FB1     |   Dev_Chg                 |
FB1     |   Old value :0            |
FB1     |   New Value :50           |
FB1     |   Workstation :blah1      |
FB1     |   Function :blah1 func    |
FB2     |   Dev_Chg                 |
FB2     |   Old value :99           |
FB2     |   New Value :5            |
FB2     |   Workstation :blah2      |
FB2     |   Function :blah2 func    |
FB1     |   Dev_Chg                 |
FB1     |   Old value :50           |
FB1     |   New Value :55           |
FB1     |   Workstation :blah1      |
FB1     |   Function :blah1 func    |

如何拆分初始表并根据拆分对其进行转移。我想看到以下结果

FB1     |   0           |       50          |   Blah1 func          |   
FB2     |   99          |       5           |   Blah2 func          |   
FB1     |   50          |       55          |   Blah1 func          |   

2 个答案:

答案 0 :(得分:2)

不需要UDF,可以在CROSS APPLY和一点点XML的帮助下完成

您可以根据需要进行扩展或收缩。我留下9个位置来说明

1)没有功能

Declare @YourTable table (ID int,Area varchar(25),Event varchar(500))
Insert Into @YourTable values
(1,'FB1','Dev_Chg, Old Value: 0, New Value: 50, Workstation: Blah1, Function: Blah1 func'),
(1,'FB2','Dev_Chg, Old Value: 99, New Value: 5, Workstation: Blah2, Function: Blah2 func'),
(1,'FB1','Dev_Chg, Old Value: 50, New Value: 55, Workstation: Blah1, Function: Blah1 func')

Select A.Area
      ,[Old Value] = Substring(Pos2,CharIndex(':',Pos2)+1,Len(Pos2))
      ,[New Value] = Substring(Pos3,CharIndex(':',Pos3)+1,Len(Pos3))
      ,[Function]  = Substring(Pos5,CharIndex(':',Pos5)+1,Len(Pos5))
 From  @YourTable A
 Cross Apply (
                Select Pos1 = ltrim(rtrim(xDim.value('/x[1]','varchar(max)')))
                      ,Pos2 = ltrim(rtrim(xDim.value('/x[2]','varchar(max)')))
                      ,Pos3 = ltrim(rtrim(xDim.value('/x[3]','varchar(max)')))
                      ,Pos4 = ltrim(rtrim(xDim.value('/x[4]','varchar(max)')))
                      ,Pos5 = ltrim(rtrim(xDim.value('/x[5]','varchar(max)')))
                      ,Pos6 = ltrim(rtrim(xDim.value('/x[6]','varchar(max)')))
                      ,Pos7 = ltrim(rtrim(xDim.value('/x[7]','varchar(max)')))
                      ,Pos8 = ltrim(rtrim(xDim.value('/x[8]','varchar(max)')))
                      ,Pos9 = ltrim(rtrim(xDim.value('/x[9]','varchar(max)')))
                 From  (Select Cast('<x>' + replace((Select A.Event as [*] For XML Path('')),',','</x><x>')+'</x>' as xml) as xDim) as A 
       ) B

<强>返回

Area    Old Value   New Value   Function
FB1     0           50          Blah1 func
FB2     99          5           Blah2 func
FB1     50          55          Blah1 func

或2使用功能

Select A.Area
      ,[Old Value] = Substring(Pos2,CharIndex(':',Pos2)+1,Len(Pos2))
      ,[New Value] = Substring(Pos3,CharIndex(':',Pos3)+1,Len(Pos3))
      ,[Function]  = Substring(Pos5,CharIndex(':',Pos5)+1,Len(Pos5))
 From  @YourTable A
 Cross Apply [dbo].[udf-Str-Parse-Row](A.Event,',') B

UDF(如果需要)

ALTER FUNCTION [dbo].[udf-Str-Parse-Row] (@String varchar(max),@Delimiter varchar(10))
Returns Table 
As
Return (
    Select Pos1 = ltrim(rtrim(xDim.value('/x[1]','varchar(max)')))
            ,Pos2 = ltrim(rtrim(xDim.value('/x[2]','varchar(max)')))
            ,Pos3 = ltrim(rtrim(xDim.value('/x[3]','varchar(max)')))
            ,Pos4 = ltrim(rtrim(xDim.value('/x[4]','varchar(max)')))
            ,Pos5 = ltrim(rtrim(xDim.value('/x[5]','varchar(max)')))
            ,Pos6 = ltrim(rtrim(xDim.value('/x[6]','varchar(max)')))
            ,Pos7 = ltrim(rtrim(xDim.value('/x[7]','varchar(max)')))
            ,Pos8 = ltrim(rtrim(xDim.value('/x[8]','varchar(max)')))
            ,Pos9 = ltrim(rtrim(xDim.value('/x[9]','varchar(max)')))
     From  (Select Cast('<x>' + replace((Select @String as [*] For XML Path('')),@Delimiter,'</x><x>')+'</x>' as xml) as xDim) as A 
)
--Select * from [dbo].[udf-Str-Parse-Row]('Dog,Cat,House,Car',',')
--Select * from [dbo].[udf-Str-Parse-Row]('John <test> Cappelletti',' ')

如果它有助于可视化,CROSS APPLY(可以很容易地成为TVF)产生以下内容

enter image description here

答案 1 :(得分:0)

这是正确的查询,我希望它有所帮助。

          declare @str varchar(1000) 

        declare @temp as table (id int , area varchar(10) , [event] varchar(100))

        insert into @temp (id,area,[event]) values (1,'FB1','Dev_Chg, Old Value: 0, New Value: 50, Workstation: Blah1, Function: Blah1 func')
        insert into @temp (id,area,[event]) values (1,'FB2','Dev_Chg, Old Value: 99, New Value: 5, Workstation: Blah2, Function: Blah2 func')
        insert into @temp (id,area,[event]) values (1,'FB1','Dev_Chg, Old Value: 50, New Value: 55, Workstation: Blah1, Function: Blah1 func')

        set @str ='Dev_Chg, Old Value: 0, New Value: 50, Workstation: Blah1, Function: Blah1 func'



        select  * from (
        select area,  RTRIM( LTRIM(  SUBSTRING( String,0 , CHARINDEX (':',String ))) )as theader  , SUBSTRING( String,  CHARINDEX (':',String )+1,15) as tvalue from (
        select * from 
        @temp cross apply 
        dbo.ufn_CSVToTable ([event])
        ) b
        where b.String!='Dev_Chg'

        ) 
         as final 
         pivot (  max ( tvalue ) for theader in ([Old Value] , [New Value],[Workstation],[Function])
         ) as pvt

- 我使用了一个tabled值函数来完成它,源是这个

        ALTER FUNCTION [dbo].[ufn_CSVToTable] ( @StringInput VARCHAR(8000) )
        RETURNS @OutputTable TABLE ( [String] nVARCHAR(1000) )
        AS
        BEGIN

            DECLARE @String    nVARCHAR(1000)

            WHILE LEN(@StringInput) > 0
            BEGIN
                SET @String      = LEFT(@StringInput, 
                                        ISNULL(NULLIF(CHARINDEX(',', @StringInput) - 1, -1),
                                        LEN(@StringInput)))
                SET @StringInput = SUBSTRING(@StringInput,
                                             ISNULL(NULLIF(CHARINDEX(',', @StringInput), 0),
                                             LEN(@StringInput)) + 1, LEN(@StringInput))

                INSERT INTO @OutputTable ( [String] )
                VALUES ( @String )
            END

            RETURN
        END