用于将字符串拆分为行和列的SQL查询

时间:2013-07-18 11:06:38

标签: sql sql-server tsql

我有以下格式的字符串:

A:B:C;J:K;P:L:J;

我想在冒号(:)之后拆分字符串,并在分号(;)之后开始一个新行。 任何人都可以帮我查询。

输出示例:

A B C

J K

P L J

4 个答案:

答案 0 :(得分:8)

试试这个 -

解决方案#1:

DECLARE @t VARCHAR(100)
SELECT @t = 'A:B:C;J:K;P:L:J;'

SELECT * 
FROM (
     SELECT token = t.c.value('.', 'VARCHAR(100)')
     FROM
     (
          SELECT xmls = CAST('<t>' + 
               REPLACE(
                    REPLACE(@t, ':', ' '), 
                    ';', 
                    '</t><t>') + '</t>' AS XML)
     ) r
     CROSS APPLY xmls.nodes('/t') AS t(c)
) t
WHERE t.token != ''

<强>输出:

----------
A B C
J K
P L J

解决方案#2:

DECLARE @t VARCHAR(100)
SELECT @t = 'A:B:C;J:K;P:L:J;'

PRINT REPLACE(REPLACE(@t, ':', ' '), ';', CHAR(13) + CHAR(13))

<强>输出:

A B C

J K

P L J

解决方案#3:

DECLARE @t VARCHAR(100)
SELECT @t = 'A:B:C;J:K;P:L:J;'

SELECT [1], [2], [3]
FROM (
     SELECT 
            t2.id
          , t2.name
          , rn2 = ROW_NUMBER() OVER (PARTITION BY t2.id ORDER BY 1/0)  
     FROM (
          SELECT 
                id = t.c.value('@n', 'INT')
              , name = t.c.value('@s', 'CHAR(1)')
          FROM (
              SELECT x = CAST('<t s = "' + 
                    REPLACE(token + ':', ':', '" n = "' + CAST(rn AS VARCHAR(10)) 
                    + '" /><t s = "') + '" />' AS XML) 
               FROM (
                    SELECT 
                           token = t.c.value('.', 'VARCHAR(100)')
                         , rn = ROW_NUMBER() OVER (ORDER BY 1/0)
                    FROM (
                         SELECT x = CAST('<t>' + REPLACE(@t, ';', '</t><t>') + '</t>' AS XML)
                    ) r
                    CROSS APPLY x.nodes('/t') t(c)
               ) t
          ) d
          CROSS APPLY x.nodes('/t') t(c)
     ) t2
     WHERE t2.name != ''
) t3
PIVOT (
     MAX(name) FOR rn2 IN ([1], [2], [3])
) p

<强>输出:

1    2    3
---- ---- ----
A    B    C
J    K    NULL
P    L    J

答案 1 :(得分:4)

不确定,我理解正确,但如果你需要数据作为三列rowset:

declare @str nvarchar(max)
set @str = 'A:B:C;J:K;P:L:J;'

select p.[1] as Column1, p.[2] as Column2, p.[3] as Column3
from (
    select T.c.value('.', 'nvarchar(200)') [row], row_number() over (order by @@spid) rn1
    from (select cast('<r>' + replace(@str, ';', '</r><r>') + '</r>' as xml) xmlRows) [rows]
        cross apply xmlRows.nodes('/r') as T(c)
    where T.c.value('.', 'nvarchar(200)') != ''
    ) t1
    cross apply (
         select NullIf(T.c.value('.', 'nvarchar(200)'), '') row2,
            row_number() over (order by @@spid) rn
         from (select cast('<r>' + replace(t1.row, ':', '</r><r>') + '</r>' as xml) xmlRows) [rows]
            cross apply xmlRows.nodes('/r') as T(c)
    ) t2
    pivot (max(t2.row2) for t2.rn in ([1], [2], [3])) p
order by p.rn1

输出

Column1  Column2  Column3
-------- -------- -------
A        B        C
J        K        NULL
P        L        J

答案 2 :(得分:1)

SQL中没有内置的拆分功能,但您只需运行这样的语句就可以自己创建该功能。在您想要执行多部分拆分的其他情况下,此解决方案是可重用的。

CREATE FUNCTION dbo.Split
(
@TextToSplit nvarchar(2000),
@SplitOn nvarchar(5)
)  
RETURNS @RtnValue table 
(
Id int identity(1,1),
Data nvarchar(100)
) 
AS  
BEGIN 
Declare @Cnt int
Set @Cnt = 1

While (Charindex(@SplitOn,@TextToSplit)>0)
Begin
    Insert Into @RtnValue (data)
    Select 
        Data = ltrim(rtrim(Substring(@TextToSplit,1,Charindex(@SplitOn,@TextToSplit)-    1)))

    Set @RowData = Substring(@TextToSplit,Charindex(@SplitOn,@TextToSplit)+1,len(@TextToSplit))
    Set @Cnt = @Cnt + 1
End

Insert Into @RtnValue (data)
Select Data = ltrim(rtrim(@TextToSplit))

Return
END

然后你可以像这样运行它:

Declare @SplitThis as varchar(2000) = 'A:B:C;J:K;P:L:J'
DECLARE @Val as varchar(500)
DECLARE MyCursor CURSOR FAST_FORWARD FOR 
    select data from split(@SplitThis,';')
OPEN MyCursor

FETCH NEXT FROM MyCursor INTO @Val

WHILE @@FETCH_STATUS = 0
    BEGIN
    select(
        select ' ' + data 
        from split(@Val,':') 
        for xml Path(''),type).value('.','varchar(100)') as cat

 FETCH NEXT FROM MyCursor INTO @Val
END

CLOSE MyCursor 
DEALLOCATE MyCursor 

答案 3 :(得分:1)

对于较大的字符串,我发现UDF循环方法比xml方法更有效。

create Function [Split_Text_Into_Table_Multi_Column] (
    @Row_Delimit_Char       Varchar(5)
    ,@Column_Delimit_Char   Varchar(5)
    ,@Text_To_Split         Varchar(Max) )
Returns
    @Output_Table Table (
        row_id      Int             Identity (1, 1)
        ,column_1   Varchar(Max)    Collate Latin1_General_CS_AS
        ,column_2   Varchar(Max)    Collate Latin1_General_CS_AS
        ,column_3   Varchar(Max)    Collate Latin1_General_CS_AS
    )
As
    Begin

        Declare
            @Column_Delimit_Index_1     Int
            ,@Column_Delimit_Index_2    Int
            ,@String                    Varchar(Max)
            ,@String_1                  Varchar(Max)
            ,@String_2                  Varchar(Max)
            ,@String_3                  Varchar(Max)
            ,@String_Len                Int
            ,@Delimit_Len               TinyInt


        Declare
            @Last_Delimit_Index     Int = 0

            ,@Next_Delimit_Index    Int = 1

        --always treat new line as delimiter
        Select
            @Text_To_Split = Replace(Replace(@Text_To_Split, Char(10), @Row_Delimit_Char), Char(13), @Row_Delimit_Char)

        --begin loop while next delimiter found
        While @Next_Delimit_Index > 0
        Begin

            --find next delimiter
            Select
                @Next_Delimit_Index = CharIndex(@Row_Delimit_Char, @Text_To_Split, @Last_Delimit_Index + 1)

            --get value to insert
            Select
                @String = Ltrim(Rtrim(Substring(@Text_To_Split, @Last_Delimit_Index + 1, Case
                    When @Next_Delimit_Index = 0 Then Len(@Text_To_Split) - @Last_Delimit_Index
                    Else @Next_Delimit_Index - @Last_Delimit_Index - 1
                End)))

            --Insert Non blank strings only
            If @String <> ''

            Begin

                Select
                    @Delimit_Len = Len(@Column_Delimit_Char)


                Select

                    @String_Len = DataLength(@String)/*includes trailing spaces*/

                    ,@Column_Delimit_Index_1 = 1
                    ,@Column_Delimit_Index_2 = IsNull(NullIf(CharIndex(@Column_Delimit_Char, @String, @Column_Delimit_Index_1), 0), @String_Len + 1)
                    ,@String_1 =
                                Case
                                    When @Column_Delimit_Index_2 <= @Column_Delimit_Index_1 Or
                                    @Column_Delimit_Index_2 - @Column_Delimit_Index_1 = 0 Then Null
                                    Else Substring(@String, @Column_Delimit_Index_1, @Column_Delimit_Index_2 - @Column_Delimit_Index_1)
                                End

                    ,@Column_Delimit_Index_1 = @Column_Delimit_Index_2 + @Delimit_Len
                    ,@Column_Delimit_Index_2 = IsNull(NullIf(CharIndex(@Column_Delimit_Char, @String, @Column_Delimit_Index_1), 0), @String_Len + 1)
                    ,@String_2 =
                                Case
                                    When @Column_Delimit_Index_2 <= @Column_Delimit_Index_1 Or
                                    @Column_Delimit_Index_2 - @Column_Delimit_Index_1 = 0 Then Null
                                    Else Substring(@String, @Column_Delimit_Index_1, @Column_Delimit_Index_2 - @Column_Delimit_Index_1)
                                End

                    ,@Column_Delimit_Index_1 = @Column_Delimit_Index_2 + @Delimit_Len
                    ,@Column_Delimit_Index_2 = IsNull(NullIf(CharIndex(@Column_Delimit_Char, @String, @Column_Delimit_Index_1), 0), @String_Len + 1)
                    ,@String_3 =
                                Case
                                    When @Column_Delimit_Index_2 <= @Column_Delimit_Index_1 Or
                                    @Column_Delimit_Index_2 - @Column_Delimit_Index_1 = 0 Then Null
                                    Else Substring(@String, @Column_Delimit_Index_1, @Column_Delimit_Index_2 - @Column_Delimit_Index_1)
                                End


                Insert Into
                    @Output_Table (
                        column_1
                        ,column_2
                        ,column_3)
                Values
                    (
                    @String_1
                    ,@String_2
                    ,@String_3)

            End

            --set last last char index
            Select
                @Last_Delimit_Index = @Next_Delimit_Index

        End

        Return
    End


--sample output 

Select *    
 from [Split_Text_Into_Table_Multi_Column] (',','   ','
917064578   70
917581826   66
917635915   66
917663205   66
918320481   66

918752247   ER
918892315   70
919162748   52
919169913   JS

919169927   70
919187495   52
919207137   52
919226194   70
919252530   52
919252544   KG
919269942   52
919269957   KG
919269961   47
')

输出

row_id|column_1|column_2|column_3
1|917064578|70|NULL
2|917581826|66|NULL
3|917635915|66|NULL
4|917663205|66|NULL
5|918320481|66|NULL
6|918752247|ER|NULL
7|918892315|70|NULL
8|919162748|52|NULL
9|919169913|JS|NULL
10|919169927|70|NULL
11|919187495|52|NULL
12|919207137|52|NULL
13|919226194|70|NULL
14|919252530|52|NULL
15|919252544|KG|NULL
16|919269942|52|NULL
17|919269957|KG|NULL
18|919269961|47|NULL