【问题标题】:Split string into two columns将字符串拆分为两列
【发布时间】:2020-01-19 06:54:21
【问题描述】:

我将以下字符串分成两列:

给定:

DECLARE @String VARCHAR(MAX) = 'Mak^1,Jak^2,Smith^3,Lee^4,Joseph^5'

我想把它分成两列:

column1  column2
-----------------
Mak      1
Jak      2
Smith    3
Lee      4
Joseph   5

我的尝试:

表值函数:

CREATE FUNCTION [dbo].[udf_Split]
(
@InputString VARCHAR(8000), 
@Delimiter VARCHAR(50)
)
RETURNS @Items TABLE (ID INTEGER IDENTITY(1,1), Item VARCHAR(8000))

AS
BEGIN
      IF @Delimiter = ' '
      BEGIN
            SET @Delimiter = ','
            SET @InputString = REPLACE(@InputString, ' ', @Delimiter)
      END
      IF (@Delimiter IS NULL OR @Delimiter = '')
            SET @Delimiter = ','

      DECLARE @Item VARCHAR(8000)
      DECLARE @ItemList VARCHAR(8000)
      DECLARE @DelimIndex INT

      SET @ItemList = @InputString
      SET @DelimIndex = CHARINDEX(@Delimiter, @ItemList, 0)
      WHILE (@DelimIndex != 0)
      BEGIN
            SET @Item = SUBSTRING(@ItemList, 0, @DelimIndex)
            INSERT INTO @Items VALUES (@Item)

            SET @ItemList = SUBSTRING(@ItemList, @DelimIndex+1, LEN(@ItemList)-@DelimIndex)
            SET @DelimIndex = CHARINDEX(@Delimiter, @ItemList, 0)
      END -- End WHILE

      IF @Item IS NOT NULL 
      BEGIN
            SET @Item = @ItemList
            INSERT INTO @Items VALUES (@Item)
      END

      ELSE INSERT INTO @Items VALUES (@InputString)

      RETURN

END 

函数调用:

SELECT Item FROM [dbo].[udf_Split](@String ,',');

输出:

Item
--------------
Mak^1
Jak^2
Smith^3
Lee^4
Joseph^5

【问题讨论】:

  • 可能SELECT LEFT(Item, CHARINDEX('^', Item) - 1) AS Column1, RIGHT(Item, LEN(Item) - CHARINDEX('^', Item)) AS Column2 FROM [dbo].[udf_Split](@String ,',') 就足够了。

标签: sql sql-server tsql sql-server-2008-r2


【解决方案1】:

首先,请注意 SQL Server 2008 r2 不再提供扩展支持。是时候升级到新版本了。

对于单个字符串,我可能会使用一点动态 SQL 魔术技巧:

DECLARE @String VARCHAR(MAX) = 'Mak^1,Jak^2,Smith^3,Lee^4,Joseph^5'

DECLARE @Sql VARCHAR(MAX) = 'SELECT Name,Id FROM (VALUES (''' + REPLACE(REPLACE(REPLACE(@String,'''',''''''), ',', '),('''), '^', ''',') + ')) V(Name, Id)';

-- @Sql now contains this:
-- SELECT Name,Id FROM (VALUES ('Mak',1),('Jak',2),('Smith',3),('Lee',4),('Joseph',5)) V(Name, Id)

EXEC(@Sql)

结果:

Name    Id
Mak     1
Jak     2
Smith   3
Lee     4
Joseph  5

【讨论】:

  • 这实际上是一种非常有趣且与众不同的方法。
  • 我推荐使用REPLACE(@String,'''','''''');否则这对注入是开放的(因为我们不知道@String 的值来自哪里)。
【解决方案2】:

我觉得一个更好的方法是摆脱那个糟糕的WHILE 并使用基于集合的方法;我们将在这里使用delimitedsplit8K(如果您在 2012 年以上使用 delimitedsplit8k_lead 或在 2016 年以上,您可以使用 STRING_SPLIT)。

考虑到这一点,上面的内容变得非常简单:

DECLARE @String varchar(MAX) = 'Mak^1,Jak^2,Smith^3,Lee^4,Joseph^5';

SELECT LEFT(DS.Item,CHARINDEX('^',DS.Item)-1) AS Col1,
       STUFF(DS.Item,1, CHARINDEX('^',DS.Item),'') AS Col2
FROM dbo.DelimitedSplit8K(@String, ',') DS;

【讨论】:

    【解决方案3】:

    在最新版本的 SQL Server 中,您可以使用string_split():

    select left(s.value, charindex('^', value) - 1) as column1,
           stuff(s.value, 1, charindex('^', value), '') as column2
    from string_split(@string, ',') s ;
    

    你可能会发现下载一个拆分函数来处理这个最方便。

    否则,我认为递归 CTE 是一种足够简单的方法:

    with cte as (
          select convert(varchar(max), null) as row,
                 @string as str
          union all
          select convert(varchar(max), left(str, charindex(',', str + ',') - 1)),
                 convert(varchar(max), stuff(str, 1, charindex(',', str + ','), ''))
          from cte
          where str <> '' 
         )
    select left(cte.row, charindex('^', cte.row) - 1) as column1,
           stuff(cte.row, 1, charindex('^', cte.row), '')
    from cte
    where row is not null;
    

    Here 是一个 dbfiddle。

    【讨论】:

      【解决方案4】:

      在下面试试这个脚本

      DECLARE @String VARCHAR(MAX) = 'Mak^1,Jak^2,Smith^3,Lee^4,Joseph^5';
      
      DECLARE @TempTable AS TABLE(data VARCHAR(MAX))
      INSERT INTO @TempTable
      SELECT @String
      
      ;WITH CTE
      AS
      (
      SELECT Split.A.value('.','nvarchar(1000)') AS data
      FROM 
      (
      SELECT CAST('<S>'+REPLACE(data,',','</S><S>')+'</S>' AS XML ) AS data
      FROM @TempTable
      )AS A
      CROSS APPLY data.nodes('S') AS Split(A)
      )
      SELECT LTRIM(RTRIM(SUBSTRING(data,0,CHARINDEX('^',data)))) AS column1,
             LTRIM(RTRIM(SUBSTRING(data,CHARINDEX('^',data)+1,LEN (data)))) AS column2
      FROM CTE
      

      结果

      column1 column2
      -------------------
      Mak         1
      Jak         2
      Smith       3
      Lee         4
      Joseph      5
      

      使用上面的脚本创建表值参数函数

      CREATE FUNCTION [dbo].[udf_SplitFun](@InputData VARCHAR(MAX))
      RETURNS @Return TABLE ( column1 VARCHAR(200),column2 INT)
      AS
      BEGIN
      DECLARE @TempTable AS TABLE
      (
      data VARCHAR(MAX)
      )
      INSERT INTO @TempTable
      SELECT @InputData
      
      ;WITH CTE
      AS
      (
      SELECT Split.A.value('.','nvarchar(1000)') AS data
      FROM 
      (
      SELECT CAST('<S>'+REPLACE(data,',','</S><S>')+'</S>' AS XML ) AS data
      FROM @TempTable
      )AS A
      CROSS APPLY data.nodes('S') AS Split(A)
      )
      INSERT INTO @Return(column1,column2)
      SELECT LTRIM(RTRIM(SUBSTRING(data,0,CHARINDEX('^',data)))) AS column1,
             LTRIM(RTRIM(SUBSTRING(data,CHARINDEX('^',data)+1,LEN (data)))) AS column2
      FROM CTE
      
      RETURN;
      
      END
      

      执行下面的函数

      DECLARE @InputData VARCHAR(MAX) = 'Mak^1,Jak^2,Smith^3,Lee^4,Joseph^5';
      
      SELECT * FROM [dbo].[udf_SplitFun] (@InputData)
      GO
      

      【讨论】:

        【解决方案5】:

        您可以再次使用该拆分功能通过插入符号拆分每一行。喜欢:

        SELECT SplitByCaret1.Item, SplitByCaret2.Item 
        FROM [dbo].[udf_Split](@String ,',') SplitByComma
        CROSS APPLY (SELECT * FROM [dbo].[udf_Split](SplitByComma.Item ,'^') Splitted WHERE Splitted.ID=1) SplitByCaret1
        CROSS APPLY (SELECT * FROM [dbo].[udf_Split](SplitByComma.Item ,'^') Splitted WHERE Splitted.ID=2) SplitByCaret2
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2020-06-18
          • 1970-01-01
          • 2021-12-28
          • 2021-11-21
          • 1970-01-01
          • 2019-04-16
          • 1970-01-01
          相关资源
          最近更新 更多