【问题标题】:Split string by two delimiters into two columns通过两个分隔符将字符串拆分为两列
【发布时间】:2017-09-25 14:12:07
【问题描述】:

我有一个字符串值,它的数值用逗号分隔,然后用管道分隔。我想把它们分成一个有两列的表。我可以将字符串拆分为一个分隔符,但不幸的是找不到将其拆分为两个的方法。请帮忙。

DECLARE @list NVARCHAR(MAX) = '1,101|2,202|3,303';

结果应该如下所示。

1   101
2   202
3   303

提前致谢。

【问题讨论】:

  • 你读过这个post了吗?
  • 只有两列吗?,我的意思是1,101

标签: sql sql-server


【解决方案1】:

如果您使用的是 SQL Server 2016 或 Azure,则可以访问新的 SPLIT_STRING 函数。如果不是,我推荐使用 Jeff Moden 的 DelimitedSplit8K 函数,它被广泛认为是最快、最有效的基于 SQL 的字符串拆分器......

DECLARE @list NVARCHAR(MAX) = '1,101|2,202|3,303';

SELECT 
    Col1 = LEFT(dsk.Item, sl.SplitLocation - 1),
    Col2 = SUBSTRING(dsk.Item, sl.SplitLocation + 1, LEN(dsk.Item))
FROM 
    dbo.DelimitedSplit8K(@list, '|') dsk    -- code for DelimitedSplit8K can be found here... http://www.sqlservercentral.com/articles/Tally+Table/72993/
    CROSS APPLY ( VALUES (ISNULL(NULLIF(CHARINDEX(',', dsk.Item, 1), 0), 1)) ) sl (SplitLocation);

【讨论】:

    【解决方案2】:
    CREATE  FUNCTION [dbo].[fn_Split_char](@text nvarchar(max), @delimiter varchar(20) = ' ')
    RETURNS @Strings TABLE
    (   
      position int IDENTITY PRIMARY KEY,
      value nvarchar(max)  
    )
    AS
    BEGIN
    
    DECLARE @index int
    SET @index = -1
    
    WHILE (LEN(@text) > 0)
      BEGIN 
        SET @index = CHARINDEX(@delimiter , @text) 
        IF (@index = 0) AND (LEN(@text) > 0) 
          BEGIN  
            INSERT INTO @Strings VALUES (@text)
              BREAK 
          END 
        IF (@index > 1) 
          BEGIN  
            INSERT INTO @Strings VALUES (LEFT(@text, @index - 1))  
            SET @text = RIGHT(@text, (LEN(@text) - @index)) 
          END 
        ELSE
          SET @text = RIGHT(@text, (LEN(@text) - @index))
        END
      RETURN
    END
    
    
    
    Select LEFT(value, Charindex(',', value) - 1) ,
    RIGHT(value, Charindex(',', Reverse(value)) - 1) ,
    * from [fn_Split_char] ('1,101|2,202|3,303', '|')
    

    【讨论】:

    • 使用 WHILE 循环解析字符串效率极低,永远不应该这样做。还有其他方法要好几个数量级。
    【解决方案3】:

    使用 xml 路径和交叉应用根据管道分隔符为单行创建多行,然后使用逗号 w.r.t 的子字符串来派生两个所需的列

    Create table #temp(list nvarchar(max))
    Insert into #temp values('1,101|2,202|3,303')
    SELECT 
      Substring(Tbl.Col.value('./text()[1]','varchar(50)'),1,1)as col1,
      Substring(Tbl.Col.value('./text()[1]','varchar(50)'),charindex(',',Tbl.Col.value('./text()[1]','varchar(50)'),1)+1,len(Tbl.Col.value('./text()[1]','varchar(50)')))
    FROM 
    (Select cast('<a>'+ replace((SELECT list As [*] FOR XML PATH  ('')), '|', '</a><a>') + '</a>' as xml)as t 
      from #temp) tl
    Cross apply 
    tl.t.nodes('/a') AS Tbl(Col)
    

    【讨论】:

      【解决方案4】:

      尝试使用这个表值函数,将这个 SP 嵌入到你的主 SP

      ALTER FUNCTION [dbo].[delimiter]
      (
      @PARAM_IDS AS VARCHAR(MAX)
      @PARAM_DELIMITER AS CHAR(1)
      )
      RETURNS 
      @NEW_TABLE TABLE 
      (
      NUM INT NOT NULL IDENTITY,
      ID INT NOT NULL
      )
      AS
      BEGIN
      DECLARE @NEXTSTRING AS NVARCHAR(MAX);
      DECLARE @POS AS INT;
      DECLARE @STRING AS NVARCHAR(MAX);
      DECLARE @DELIMITER AS NVARCHAR(MAX);
      SET @STRING = @PARAM_IDS;
      SET @DELIMITER = @PARAM_DELIMITER;
      SET @STRING = @STRING + @DELIMITER;
      SET @POS = CHARINDEX(@DELIMITER,@STRING);
      
      WHILE (@POS <> 0)
      BEGIN
          SET @NEXTSTRING = SUBSTRING(@STRING,1,@POS - 1);
          INSERT @NEW_TABLE (ID) VALUES (@NEXTSTRING);
          SET @STRING = SUBSTRING(@STRING,@POS+1,len(@STRING));
          SET @POS = CHARINDEX(@DELIMITER,@STRING);
      END 
          RETURN
      END
      

      然后是使用示例

      SET @DETAILS_COUNT = (SELECT COUNT(*) FROM delimiter(@PARAM_MS_UNIT_ID, @DELIMITER));
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2020-06-18
        • 2018-01-06
        • 2015-09-20
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2016-04-06
        相关资源
        最近更新 更多