SQL将列拆分为X列数,然后插入表

时间:2018-08-16 06:47:23

标签: sql sql-server ssms

我有一个表,其中包含1列。每个条目都有一个X号分隔符。我想使用定界符将其拆分为X + 1列,然后将其插入另一个已创建的表中,该表包含x + 1列但不包含任何条目。

例如,对于表1 第1列

1,2,3,4
a,b,c,d

所以我想在表2中插入以下内容(表中已经有正确的列数)

column1 column2 column3 column4
1   2   3   4
a   b   c   d

我对此有一个解决方案,但是它效率太低(大约花5分钟才能完成10列)。

首先,我向表1添加一个行号并填充它,然后向表2添加一个行号列,然后将表2中的所有行号插入表1。

然后我将运行以下循环:

DECLARE @DELIMITER NVARCHAR(10) = ',',
@columns nvarchar(max) ='Column1,column2,column3,column4', 
@SQL NVARCHAR(MAX), 
@table nvarchar(100) = 'dbo.test'

WHILE (1=1)
BEGIN


IF(@columns LIKE '%' +@DELIMITER +'%')
BEGIN
SET @SQL = 'UPDATE A 
SET A.[' +SUBSTRING(@columns,0,CHARINDEX(@DELIMITER,@columns)) +'] =SUBSTRING(B.FULLTABLE,0,CHARINDEX('''+@DELIMITER+''',B.FULLTABLE))
FROM ' + @TABLE + ' A
INNER JOIN #FULLTABLE B ON B.ROW_NUMBER = A.ROW_NUMBER
SELECT  FROM #FULLTABLE'

EXEC(@SQL)
SET @columns = SUBSTRING(@columns,1+CHARINDEX(@DELIMITER,@columns),LEN(@columns))

UPDATE #FULLTABLE
SET FULLTABLE = SUBSTRING(FULLTABLE,1+CHARINDEX(@DELIMITER,FULLTABLE),LEN(FULLTABLE))
END

ELSE 
BEGIN 
SET @SQL = 'UPDATE A 
SET A.[' +@columns +'] =B.FULLTABLE
FROM ' + @TABLE + ' A
INNER JOIN #FULLTABLE B ON B.ROW_NUMBER = A.ROW_NUMBER
SELECT  FROM #FULLTABLE'

EXEC(@SQL)
BREAK
END


END

请注意,我通常不会根据我正在使用的表自动对“列”的值进行硬编码,但我只是在此处对其进行硬编码,以使自己更清楚地了解要执行的操作。

有没有更有效的方法来实现这一目标?

2 个答案:

答案 0 :(得分:1)

将字符串转换为table的函数。如果您给@ input ='1,2,3,4' 它返回 表 值1值2值3值4 1 2 3 4

    create FUNCTION [dbo].[fn_listtotable1](@input AS nVarchar(max))
    RETURNS
    @Result TABLE(Value1 INT,Value2 INT,Value3 INT,Value4 INT)
  AS
      BEGIN
       DECLARE @str VARCHAR(20)
      DECLARE @str1 VARCHAR(20)
       DECLARE @str2 VARCHAR(20)
       DECLARE @str3 VARCHAR(20)
       DECLARE @ind Int
       IF(@input is not null)
         BEGIN
        SET @ind = CharIndex(',',@input)

              SET @str = SUBSTRING(@input,1,@ind-1)
             SET @input = SUBSTRING(@input,@ind+1,LEN(@input)-@ind)
              INSERT INTO @Result(Value1) values (@str)
              SET @ind = CharIndex(',',@input)

              SET @str1 = SUBSTRING(@input,1,@ind-1)
             SET @input = SUBSTRING(@input,@ind+1,LEN(@input)-@ind)
             update @Result 
             set Value2 = (@str1) where Value1=@str
              SET @ind = CharIndex(',',@input)

              SET @str2 = SUBSTRING(@input,1,@ind-1)
             SET @input = SUBSTRING(@input,@ind+1,LEN(@input)-@ind)
               update @Result 
             set Value3 = (@str2) where Value1=@str
              SET @ind = CharIndex(',',@input)


        SET @str3 = @input
         update @Result 
             set Value4 = (@str3) where Value1=@str
  END
  RETURN

END

将以下内容放入循环,

     insert into table1(column1, column2,column3,column4) 
         select * from [fn_listtotable1]( @string) 

答案 1 :(得分:1)

我不清楚询问的内容,是否可以让我知道

  • 每行中是否有多行数据
  • 以上行是否包含标题
  • 两行数据的模拟样本

我猜这就是你的要求。我假设您使用的是SQL Server 2016及更高版本(如果看不到下面的注释来替换string_split)

CREATE TABLE OneColumn  (
    MyDelimitedColumn NVARCHAR(1000)
)

INSERT INTO OneColumn (MyDelimitedColumn)
VALUES 
    ('Col1,Col2,Col3')
,   ('x,y,z')
,   ('1,2,3,4,5,6')


SELECT 
MyDelimitedColumn,
DENSE_RANK() OVER (ORDER BY MyDelimitedColumn) as RowKey,
ROW_NUMBER() OVER (PARTITION BY MyDelimitedColumn ORDER BY MyDelimitedColumn) as ColumnKey,
value as ColValue
FROM OneColumn
CROSS APPLY string_split(MyDelimitedColumn,',')

这将导致

MyDelimitedColumn|RowKey|ColumnKey|ColValue
1,2,3,4,5,6      |1     |1        |1
1,2,3,4,5,6      |1     |2        |2
1,2,3,4,5,6      |1     |3        |3
1,2,3,4,5,6      |1     |4        |4
1,2,3,4,5,6      |1     |5        |5
1,2,3,4,5,6      |1     |6        |6
a,b,c,d,e        |2     |1        |a
a,b,c,d,e        |2     |2        |b
a,b,c,d,e        |2     |3        |c
a,b,c,d,e        |2     |4        |d
a,b,c,d,e        |2     |5        |e
x,y,z            |3     |1        |x
x,y,z            |3     |2        |y
x,y,z            |3     |3        |z

一旦获得了以上结果集,就可以进行动态透视将行转换为列。

如果您使用的是SQL Server的旧版本,只需实现这些string_split等效功能中的任何一个

enter image description here

完整示例

IF OBJECT_ID('tempdb..#ResultSet') IS NOT NULL
    DROP TABLE #ResultSet

IF OBJECT_ID('tempdb..#OneColumn') IS NULL
CREATE TABLE #OneColumn  (
    MyDelimitedColumn NVARCHAR(1000)
)
TRUNCATE TABLE #OneColumn  

INSERT INTO #OneColumn (MyDelimitedColumn)
VALUES 
    ('Col1,Col2,Col3')
,   ('x,y,z')
,   ('1,2,3,4,5,6')


SELECT 
MyDelimitedColumn,
DENSE_RANK() OVER (ORDER BY MyDelimitedColumn) as RowKey,
ROW_NUMBER() OVER (PARTITION BY MyDelimitedColumn ORDER BY MyDelimitedColumn) as ColumnKey,
value as ColValue
INTO #ResultSet
FROM #OneColumn
CROSS APPLY string_split(MyDelimitedColumn,',')


DECLARE @MaxRowKey INT       ,
        @I INT = 1           ,
        @DySQL NVARCHAR(MAX) ,
        @PivotCols NVARCHAR(MAX)

SELECT @MaxRowKey= MAX(RowKey) FROM #ResultSet

WHILE @I <= @MaxRowKey
BEGIN

    -- REPLACE STRING_AGG with FOR_XML PATH Method (https://www.red-gate.com/simple-talk/sql/t-sql-programming/concatenating-row-values-in-transact-sql/)
    SET @PivotCols = (SELECT DISTINCT STRING_AGG(quotename(ColumnKey),',') FROM #ResultSet WHERE RowKey = @I)

    SET @DySQL = N'
    SELECT pvt.*
    FROM 
    (
    SELECT * FROM 
        #ResultSet 
    WHERE RowKey = '+cast(@i as Nvarchar(100))+'
    ) p
    PIVOT 
    (
        MAX(P.ColValue) FOR p.ColumnKey IN ('+@PivotCols+')
    ) as Pvt
    '

    EXEC (@dysql)




    SET @I = @I + 1
END