【问题标题】:insert multiple splited string column to separate rows into a sql table插入多个拆分字符串列以将行分隔到 sql 表中
【发布时间】:2017-11-07 04:01:08
【问题描述】:

我有一张如下图所示的表格

是否可以将上述表格数据分行插入到表格中?

我尝试对每一列使用拆分功能,并将每一列的结果存储在一个临时表中。我不知道如何根据 id 将所有这些行和列组合到新表中。任何帮助或建议都会有所帮助。

【问题讨论】:

  • 看我的回答,希望对你有帮助。

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


【解决方案1】:

试试这个答案。希望对您有所帮助。

DECLARE @Table TABLE(ID INT, NAME VARCHAR(10),TITLE VARCHAR(10))
INSERT INTO @Table VALUES (1,';a;b;c',';12;13;14')

DECLARE @ID INT=1

SELECT @ID ID,Items,ROW_NUMBER() OVER(ORDER BY (SELECT NULL)) RN1 INTO #T1 FROM dbo.split((SELECT NAME FROM @Table WHERE id=@ID),';')
SELECT @ID ID,Items,ROW_NUMBER() OVER(ORDER BY (SELECT NULL)) RN2 INTO #T2 FROM dbo.split((SELECT TITLE FROM @Table WHERE id=@ID),';')

SELECT T1.ID,T1.Items NAME,T2.Items TITLE
FROM #T1 T1 INNER JOIN #T2 T2 ON T1.RN1=T2.RN2 

DROP TABLE #T1
DROP TABLE #T2

如果你想要所有的值,你可以试试WHILE这样的循环方法。

DECLARE @Table TABLE(ID INT, NAME VARCHAR(10),TITLE VARCHAR(10))
INSERT INTO @Table VALUES (1,';a;b;c',';12;13;14'),(2,';c;f;u',';67;56;34'),(3,';l;k;m',';90;70;60')

DECLARE @MinID INT,@MaxID INT
SELECT @MinID=MIN(ID),@MaxID=MAX(ID) FROM @Table

CREATE TABLE #T1(ID INT,Items VARCHAR(10),RN1 INT)
CREATE TABLE #T2(ID INT,Items VARCHAR(10),RN2 INT)

WHILE @MinID<=@MaxID
BEGIN
    INSERT  INTO #T1
    SELECT @MinID ID,Items,ROW_NUMBER() OVER(ORDER BY (SELECT NULL)) RN1 
    FROM dbo.split((SELECT NAME FROM @Table WHERE id=@MinID),';')

    INSERT  INTO #T2
    SELECT @MinID ID,Items,ROW_NUMBER() OVER(ORDER BY (SELECT NULL)) RN2 
    FROM dbo.split((SELECT TITLE FROM @Table WHERE id=@MinID),';')

    SET @MinID=@MinID+1
END

    SELECT T1.ID,T1.Items NAME,T2.Items TITLE
    FROM #T1 T1 INNER JOIN #T2 T2 ON T1.ID=T2.ID AND T1.RN1=T2.RN2 

DROP TABLE #T1
DROP TABLE #T2

这将产生你想要的结果:

ID          NAME       TITLE
----------- ---------- ----------
1           a          12
1           b          13
1           c          14
2           c          67
2           f          56
2           u          34
3           l          90
3           k          70
3           m          60

这里是拆分函数,我是用来拆分字符串的:

CREATE FUNCTION [dbo].[Split]
(@String VARCHAR (max), @Delimiter CHAR (1))
RETURNS 
    @temptable TABLE (
        [items] VARCHAR (max) COLLATE SQL_Latin1_General_CP1_CI_AS NULL)
AS
begin        
    declare @idx int        
    declare @slice varchar(max)        

    select @idx = 1        
        if len(@String)<1 or @String is null  return        

    while @idx!= 0        
    begin        
        set @idx = charindex(@Delimiter,@String)        
        if @idx!=0        
            set @slice = left(@String,@idx - 1)        
        else        
            set @slice = @String        

        if(len(@slice)>0)   
            insert into @temptable(Items) values(@slice)        

        set @String = right(@String,len(@String) - @idx)        
        if len(@String) = 0 break        
    end    
return        
end

【讨论】:

  • 感谢您的帮助。我尝试了脚本。我收到错误消息说“无效的列名 'Items'”。
  • 什么是列,从你的 split 函数返回。如果您遇到同样的问题,请使用我建议的功能。
  • 效果很好!非常感谢。我的 split 函数返回 Item,我将 Items 更改为 Item,效果很好。
  • 我的回答有问题吗?
  • 没有问题。但是当我在有 34573 行的表上实现解决方案时,它断开了数据库并重新启动了数据库。这导致中途停止拆分过程。我想我们可以选择多个答案。不要意识到它会删除您的答案。对不起
【解决方案2】:

这是CTE 的另一种方法,在XML 节点的帮助下

不需要创建任何函数。

WITH cte AS (
     SELECT ID,
            split.a.value('.', 'NVARCHAR(MAX)') [name],
            ROW_NUMBER() OVER(ORDER BY ( SELECT 1)) RN
     FROM
     (
         SELECT ID,
                CAST('<A>'+REPLACE(name, ';', '</A><A>')+'</A>' AS XML) AS [name]
         FROM <table_name>
     ) a
     CROSS APPLY name.nodes('/A') AS split(a)),
     CTE1 AS (
     SELECT ID,
            split.a.value('.', 'NVARCHAR(MAX)') [title],
            ROW_NUMBER() OVER(ORDER BY ( SELECT 1 )) RN
     FROM
     (
         SELECT ID,
                CAST('<A>'+REPLACE(title, ';', '</A><A>')+'</A>' AS XML) AS [title]
         FROM <table_name>
     ) aa
     CROSS APPLY title.nodes('/A') AS split(a))
     SELECT C.ID, C.name, C1.title FROM CTE C
          JOIN CTE1 C1 ON C1.RN = C.RN
     WHERE C.name != '' AND C1.title != '';

结果:

ID  name title
1   a    12
1   b    13
1   s    45
2   c    67
2   f    56
2   u    34
3   l    90
3   k    70
3   m    60

【讨论】:

    【解决方案3】:

    试试下面的方法..这也可以节省时间和内存!

    此 T-SQL 块依赖于 dbo.SplitString 函数 ..
    T1 是我的源表
    T2 是我的目标表

    DECLARE @c_s AS CURSOR;
    DECLARE @id INT;
    DECLARE @name VARCHAR(1000);
    DECLARE @value VARCHAR(1000);
    
    SET @c_s = CURSOR FOR SELECT * FROM T1; 
    OPEN @c_s;
    FETCH @c_s INTO @id, @name, @value
    
    WHILE @@FETCH_STATUS = 0
    BEGIN
        INSERT INTO T2
        SELECT @id
            , a.Value NAME
            , b.value value
        FROM dbo.SplitString(@name, ';') a
        INNER JOIN dbo.SplitString(@value, ';') b
            ON a.OrdinalPosition = b.OrdinalPosition
    
      FETCH NEXT FROM @c_s INTO @id, @name, @value
    END
    

    这里是 dbo.SplitString

    CREATE FUNCTION [dbo].[SplitString](@givenString VARCHAR(8000) , @separator VARCHAR(100))
    RETURNS TABLE AS
    RETURN (
            WITH data([start], [end]) AS (
                    SELECT 0 AS [start]
                        , CHARINDEX(@separator, @givenString) AS [end]
                    UNION ALL
                    SELECT [end] + 1
                        , CHARINDEX(@separator, @givenString, [end] + 1)
                    FROM data
                    WHERE [end] > 0
                    )
            SELECT ROW_NUMBER() OVER (
                    ORDER BY OrdinalPosition
                    ) OrdinalPosition
                , RTRIM(LTRIM(Value)) Value
            FROM (
                SELECT ROW_NUMBER() OVER (
                        ORDER BY [start]
                        ) OrdinalPosition
                    , SUBSTRING(@givenString, [start], COALESCE(NULLIF([end], 0), len(@givenString) + 1) - [start]) Value
                FROM data
                ) r
            WHERE RTRIM(Value) <> ''
                AND Value IS NOT NULL
            )
    

    【讨论】:

      【解决方案4】:

      您可以通过编写一个表值函数来实现此目的,该函数将根据您的要求拆分字符串。创建此对象后,您可以使用第二个代码 sn-p 中的 T-SQL 来获取最终所需的表。

      这个表值函数的定义如下。只需将其复制并粘贴到 SSMS 中,然后针对您的数据库运行即可。

      拆分字符串函数

      -- =============================================
      -- Author:      B Vidhya
      -- Create date: Nov 7, 2017
      -- Description: Splits a string and returns a table
      -- =============================================
      CREATE FUNCTION [dbo].[SplitAString]
      (    
            @string NVARCHAR(MAX),
            @delimiter CHAR(1)
      )
      RETURNS @splitTable TABLE (
            ItemNumber INT IDENTITY(1,1),
            Item NVARCHAR(1000)
      )
      AS
      BEGIN
            DECLARE @startIndex INT,@endIndex INT
      
            SET @startIndex = 1
            IF SUBSTRING(@string, LEN(@string) - 1, LEN(@string)) <> @delimiter
            BEGIN
                  SET @string = @string + @delimiter
            END
      
            WHILE CHARINDEX(@delimiter, @string) > 0
            BEGIN
                  SET @endIndex = CHARINDEX(@delimiter, @string)
      
                  INSERT INTO @splitTable(Item)
                  SELECT SUBSTRING(@string, @startIndex, @endIndex - 1)
      
                  SET @string = SUBSTRING(@string, @endIndex + 1, LEN(@string))
            END
      
            RETURN
      END
      
      GO
      

      在下面的 T-SQL 中,我调用了原始表 StackOverflowTable1,您可以将这个表名替换为您的实际表名。另外,我将最后一行插入到表变量中。如果您想插入自定义表,那么您可以在 WHILE 循环的 END 之后对表执行 INSERT。

      T-SQL 让你的决赛桌

      DECLARE @myTable TABLE
      (Id    INT,
       Name  VARCHAR(5000),
       Title VARCHAR(5000)
      );
      
      DECLARE @lastId INT= 0, @id INT, @name VARCHAR(5000), @title VARCHAR(5000);
      
      --for each record in table perform splitting and insertion in new table
      WHILE EXISTS
      (
          SELECT 1
          FROM StackOverFlowTable1 soft
          WHERE Id > @lastId
      )
          BEGIN
              SELECT TOP (1) @id = Id,
                             @name = Name,
                             @title = Title
              FROM StackOverFlowTable1 soft
              WHERE Id > @lastId
              ORDER BY Id;
              SET @lastId = @id;
              INSERT INTO @myTable
              (Id,
               Name,
               Title
              )
                     SELECT @id,
                            ss1.Item,
                            ss2.Item
                     FROM dbo.SplitString(@name, ';') ss1
                          INNER JOIN dbo.SplitString(@title, ';') ss2 ON ss1.ItemNumber = ss2.ItemNumber
                     WHERE ss1.Item <> ''
                           AND ss2.Item <> '';
          END;
      
      SELECT * FROM @myTable;
      

      【讨论】:

        【解决方案5】:

        我不同意 Dinesh 脚本,因为它基于 RBAR。

        我有非常相似的拆分函数,也返回 row_number 和 item。

        所以测试我的脚本和其他示例数据。

        DECLARE @Table TABLE(ID INT, NAME VARCHAR(10),TITLE VARCHAR(10))
        INSERT INTO @Table VALUES (1,',a,b,c',',12,13,14')
        
        SELECT id
            ,t.RowVal
            ,a.RowVal
        FROM (
            SELECT t.id
                ,a.RowNum
                ,a.RowVal
                ,t.TITLE
            FROM @Table t
            CROSS APPLY (
                SELECT *
                FROM dbo.FN_SPLIT_VALUE(t.NAME)
                ) a
            ) t
        CROSS APPLY (
            SELECT *
            FROM dbo.FN_SPLIT_VALUE(t.TITLE)
            WHERE t.RowNum = RowNum
            ) a
        WHERE t.RowVal <> ''
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2018-09-22
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多