【问题标题】:T-SQL: Separate String Into Multiple ColumnsT-SQL:将字符串分成多列
【发布时间】:2017-04-16 18:58:06
【问题描述】:

例如

Column 1:
| word1 word2 word3 word4 |

 Col 1:  Col 2:  Col 3:  Col 4:
| word1 | word2 | word3 | word |

是否可以将字符串中的不同单词或短语分成多个列?字符串中的所有单词和短语通常用双空格分隔,仅此而已。是否有一个我可以使用的预定义函数,例如 CAST 或 INTERSECT,SQL Server 已经提供,还是我必须自己编写?

【问题讨论】:

    标签: sql sql-server tsql sql-server-2012


    【解决方案1】:

    这里是一个动态的 sql 版本。约翰的,以防你不知道最大字数。完成您想要的关键技术是拆分字符串和枢轴(或条件聚合)。因为你有点同时做这两件事。约翰的方法是一个不错的捷径。

    IF OBJECT_ID('tempdb..#TblName') IS NOT NULL
        BEGIN
            DROP TABLE #TblName
        END
    
    CREATE TABLE #TblName (
        ID INT IDENTITY(1,1)
        ,String VARCHAR(500)
    )
    
    INSERT INTO #TblName VALUES ('word1  word2  word3  word4'),('abcd  efgh  ijkl')
    
    DECLARE @NumWords INT
    
    SELECT @NumWords = ISNULL(MAX((LEN(String) - LEN(REPLACE(String,'  ','')))/2 + 1), 0)
    FROM
        #TblName
    
    DECLARE @i INT = 1
    DECLARE @SQL NVARCHAR(MAX)
    
    SET @SQL = '
    SELECT
        t.Id
        ,t.String
        ,c.*
    FROM
        #TblName t 
        CROSS APPLY (
           SELECT
    
              '
    WHILE @i <= @NumWords
    BEGIN
        SET @SQL = @SQL
                 + IIF(@i > 1,', ','')
                 + 'Column' + CAST(@i AS NVARCHAR(MAX)) + '1 = x.value (''/x[' + CAST(@I AS NVARCHAR(MAX)) + ']'',''varchar(max)'')'
    
        SET @i = @i + 1
    END
    
    SET @SQL = @SQL + '
           FROM
              (SELECT CAST(''<x>'' + REPLACE(String,''  '',''</x><x>'') + ''</x>'' as XML) x) a
        ) c'
    
    
    EXECUTE (@SQL)
    

    【讨论】:

    • 最大列数为 4,所以总会有 4 列,但如果一个字符串只有 3 个单词,那么该行只会使用 3 个新列。你的功能很好,但是我可以输入列名作为插入的值,还是我必须一个接一个地写下每个单词?原始列中的每一行的每一行都有一组不同的单词,有时单词每天或每周都会改变,所以我不能每次都重新输入所有单词,所以有没有办法让你的函数自动从原始列?
    • 如果最多有 4 列,我会使用 John 的答案,因为这太过分了。我没有按照您的意思写下每个单词,因为作品会自动为您拆分。您可能会因为我将列命名为 word1、word2 等而被抛弃。所以我刚刚更新并将其更改为 Column 希望这能消除混乱。
    【解决方案2】:

    借助 CROSS APPLY 和一些 XML。易于扩展和/或收缩

    Declare @YourTable table (id int,Column1 varchar(max))
    Insert Into @YourTable values
    (1,'word1 word2 word3 word4'),
    (2,'some other words')
    
    Select A.ID
          ,B.*
     From  @YourTable A
     Cross Apply (
            Select Pos1 = xDim.value('/x[1]','varchar(max)')
                  ,Pos2 = xDim.value('/x[2]','varchar(max)')
                  ,Pos3 = xDim.value('/x[3]','varchar(max)')
                  ,Pos4 = xDim.value('/x[4]','varchar(max)')
                  ,Pos5 = xDim.value('/x[5]','varchar(max)')
                  ,Pos6 = xDim.value('/x[6]','varchar(max)')
                  ,Pos7 = xDim.value('/x[7]','varchar(max)')
                  ,Pos8 = xDim.value('/x[8]','varchar(max)')
                  ,Pos9 = xDim.value('/x[9]','varchar(max)')
             From (Select Cast('<x>' + Replace(A.Column1,' ','</x><x>')+'</x>' as XML) as xDim) A
           ) B
    

    返回

    【讨论】:

    • 这个很好,但是问题是我没有定义单词列表;原始列有多行,每行都有一个不同的字符串,我不能一个接一个地写下每个字符串,该函数需要自动完成,特别是因为每个帐户的字符串可能不同并且它们每天都在变化。有没有办法让你的函数更具动态性,以便它可以与多个变量一起运行?
    • @K.Ventura (at)YourTable 是一个演示表变量。将 (at)YourTable 替换为您的实际表名,将 A.Column1 替换为您的实际字段名,它将运行。当您说动态时,您到底在寻找什么,
    【解决方案3】:

    这个怎么样?

    Create Table AllData (Column0 varchar(500))
    Insert Into AllData Values ('word1 word2 word3 word4')
    Select parsename(replace(replace(replace([Column0],'  ',' '),'  ',' '),' ','.'), 4) [Col1],
    parsename(replace(replace(replace([Column0],'  ',' '),'  ',' '),' ','.'), 3) [Col2],
    parsename(replace(replace(replace([Column0],'  ',' '),'  ',' '),' ','.'), 2)  [Col3],  
    parsename(replace(replace(replace([Column0],'  ',' '),'  ',' '),' ','.'), 1)   [Col4]
    
    from AllData
    

    【讨论】:

      【解决方案4】:

      如果你的字数不是固定的,你可以使用动态脚本,这是一个示例:

      IF OBJECT_ID('tempdb..#tb') IS NOT NULL DROP TABLE #tb
      CREATE TABLE #tb (id int,Column1 varchar(max))
      insert Into #tb values
      (1,'word1 word2 word3 word'),
      (2,'w1 w2 w3 w4 w5 w6')
      
      DECLARE  @Cols NVARCHAR(max),@sql nvarchar(MAX)
      DECLARE @MaxWordCount INT 
      SELECT @MaxWordCount=MAX(LEN(t.Column1)-len(replace(t.Column1,' ',''))+1) from #tb as t
          SELECT @Cols=ISNULL(@Cols+',','')+'[Col '+LTRIM(sv.number)+']' FROM master.dbo.spt_values as sv WHERE  sv.Type='P' and sv.number BETWEEN 1 AND @MaxWordCount
      
      PRINT @Cols
      SET @sql='SELECT * from (
                   SELECT t.*, w.* FROM #tb AS t
                   CROSS APPLY (VALUES (convert(XML, ''<n>'' + replace(t.Column1, '' '', ''</n><n>'') + ''</n>''))) x(c)
                   CROSS APPLY (SELECT ''Col '' + ltrim(row_number()OVER (ORDER BY getdate()))  AS col
                                       ,s.b.value(''.'', ''varchar(200)'') AS wd
                                FROM x.c.nodes(''n'') s(b)) w
               ) a PIVOT (max(wd) for col in ('+@Cols+')) p'
      PRINT @sql
      EXEC(@sql)
      IF OBJECT_ID('tempdb..#tb') IS NOT NULL DROP TABLE #tb
      
      id 列 1 列 1 列 2 列 3 列 4 列 5 列 6 2 w1 w2 w3 w4 w5 w6 w1 w2 w3 w4 w5 w6 1 字 1 字 2 字 3 字 字 1 字 2 字 3 字 空 空

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2020-03-28
        • 1970-01-01
        • 2019-03-14
        • 2021-06-23
        • 1970-01-01
        • 2014-03-28
        • 2021-08-20
        • 2012-05-23
        相关资源
        最近更新 更多