【问题标题】:We need to split data for two columns in sql server我们需要在 sql server 中为两列拆分数据
【发布时间】:2016-09-30 15:45:17
【问题描述】:

我们需要在 sql server 中为两列拆分数据。

CREATE TABLE #t (Id int,Number varchar(32),Name varchar(32));    

Insert Into #t(Id, Number,Name)
Values(123, '1,2,3','a~b~c') ,
(456, '1,2,3,4','a~b~c~d'),
(789, '1,2','a~b')

SELECT *FROM #t 

输入:

Id  Number  Name
123 1,2,3   a~b~c
456 1,2,3,4 a~b~c~d
789 1,2 a~b

我们需要以下格式的输出:

Id  Number  Name
123 1   a
123 2   b
123 3   c
456 1   a
456 2   b
456 3   c
456 4   d
789 1   a
789 2   b

【问题讨论】:

标签: sql sql-server sql-server-2008


【解决方案1】:

这是使用cte的方法..

;WITH cte_1
AS
    (SELECT Id,ROW_NUMBER()OVER(PARTITION BY ID ORDER BY ID,LTRIM(RTRIM(m.n.value('.[1]','varchar(8000)'))))RNO
    ,LTRIM(RTRIM(m.n.value('.[1]','varchar(8000)'))) AS Number

    FROM
    (
    SELECT Id
           ,CAST('<XMLRoot><RowData>' + REPLACE(Number,',','</RowData><RowData>') + '</RowData></XMLRoot>' AS XML) AS x
    FROM   #t 
    )t
    CROSS APPLY x.nodes('/XMLRoot/RowData')m(n))
,cte_2
AS
    (SELECT Id,ROW_NUMBER()OVER(PARTITION BY ID ORDER BY ID,LTRIM(RTRIM(m.n.value('.[1]','varchar(8000)'))))RNO
    ,LTRIM(RTRIM(m.n.value('.[1]','varchar(8000)'))) AS Name

    FROM
    (
    SELECT Id,CAST('<XMLRoot><RowData>' + REPLACE(Name,'~','</RowData><RowData>') + '</RowData></XMLRoot>' AS XML) AS x
    FROM   #t 
    )t
    CROSS APPLY x.nodes('/XMLRoot/RowData')m(n))
SELECT a.id,a.Number,b.name
FROM cte_1 a JOIN cte_2 b
    ON a.id=b.id and a.RNo=b.RNo

【讨论】:

    【解决方案2】:

    借助解析器和交叉应用

    Select A.ID
          ,B.*
     From  #t A
     Cross Apply (  Select Number=C.RetVal
                          ,Name  =D.RetVal
                      From (Select * from [dbo].[udf-Str-Parse](A.Number,',')) C
                      Join (Select * from [dbo].[udf-Str-Parse](A.Name,'~')) D
                        on (C.RetSeq=D.RetSeq)
                 ) B
    

    退货

    ID  Number  Name
    123 1       a
    123 2       b
    123 3       c
    456 1       a
    456 2       b
    456 3       c
    456 4       d
    789 1       a
    789 2       b
    

    UDF(如果需要)

    CREATE FUNCTION [dbo].[udf-Str-Parse] (@String varchar(max),@Delimiter varchar(10))
    Returns Table 
    As
    Return (  
        Select RetSeq = Row_Number() over (Order By (Select null))
              ,RetVal = LTrim(RTrim(B.i.value('(./text())[1]', 'varchar(max)')))
        From (Select x = Cast('<x>'+ Replace(@String,@Delimiter,'</x><x>')+'</x>' as xml).query('.')) as A 
        Cross Apply x.nodes('x') AS B(i)
    );
    

    如果您不能使用 UDF,请编辑 - 我们可以将 UDF 代码移动到两个子查询中以产生相同的结果

    Select A.ID
          ,B.*
     From  #t A
     Cross Apply (  Select Number=C.RetVal
                          ,Name  =D.RetVal
                      From (
                            Select RetSeq = Row_Number() over (Order By (Select null))
                                  ,RetVal = LTrim(RTrim(B.i.value('(./text())[1]', 'varchar(max)')))
                                From (Select x = Cast('<x>'+ Replace(A.Number,',','</x><x>')+'</x>' as xml).query('.')) as A 
                                Cross Apply x.nodes('x') AS B(i)
                            ) C
                      Join (
                            Select RetSeq = Row_Number() over (Order By (Select null))
                                  ,RetVal = LTrim(RTrim(B.i.value('(./text())[1]', 'varchar(max)')))
                                From (Select x = Cast('<x>'+ Replace(A.Name,'~','</x><x>')+'</x>' as xml).query('.')) as A 
                                Cross Apply x.nodes('x') AS B(i)
                            ) D
                        on (C.RetSeq=D.RetSeq)
                 ) B
    

    【讨论】:

      【解决方案3】:

      XML 和 CTE 的另一种方式:

      DECLARE @x xml
      
      SELECT @x = (
          SELECT  Id as '@id',
                  CAST('<n number="'+REPLACE(Number,',','"/><n number="')+'"/>' as xml),
                  CAST('<w name="'+REPLACE([Name],'~','"/><w name="')+'" />' as xml)
          FROM #t 
          FOR XML PATH('i')
      )
      
      ;WITH numbers AS (
      SELECT  t.v.value('../@id','int') as id,
              t.v.value('@number','nvarchar(10)') as number,
              ROW_NUMBER() OVER (PARTITION BY t.v.value('../@id','int') ORDER BY (SELECT NULL)) as RN
      FROM @x.nodes('/i/n') as t(v)
      ), names AS (
      SELECT  t.v.value('../@id','int') as id,
              t.v.value('@name','nvarchar(10)') as [name],
              ROW_NUMBER() OVER (PARTITION BY t.v.value('../@id','int') ORDER BY (SELECT NULL)) as RN
      FROM @x.nodes('/i/w') as t(v)
      )
      
      SELECT  n.id,
              n.number,
              nn.[name]
      FROM numbers n
      INNER JOIN names nn
          ON n.id = nn.id and n.rn = nn.rn
      

      输出:

      id  number  name
      123 1       a
      123 2       b
      123 3       c
      456 1       a
      456 2       b
      456 3       c
      456 4       d
      789 1       a
      789 2       b
      

      我在 ROW_NUMBER() 部分使用SELECT NULL 来按原样订购。但它可能会导致一些问题,例如不正确的排序。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2020-05-28
        相关资源
        最近更新 更多