在同一个表中动态解析列值

时间:2017-11-17 05:51:14

标签: sql-server sql-server-2008

我有一个存储过程接受InputTable作为参数:

create table inputTable (id int, ItemQty varchar(100))

insert into inputTable(id, ItemQty) 
values (1, 'a,b,c'), (2, 'x,y'), (3, 'l,m,n,o,p'),
       (4, 'a,b'), (5, 'm')

我写的存储过程是这样的:

ALTER PROCEDURE [dbo].[dynamic_tbl] 
    (@tablename VARCHAR(50))
AS 
BEGIN
    /* To get the maximum value of ItemQty with comma's [IN THIS CASE IT IS 5 ] */
    DECLARE @ColumnCount int
    DECLARE @rowcount TABLE (Value int);

    INSERT INTO @rowcount
        EXEC('SELECT MAX(len(ITEMQTY) - len(replace(ITEMQTY, '','', '''')) +1) from '+@tablename);

    SELECT @ColumnCount = Value FROM @rowcount;

    DECLARE @ColumnName NVARCHAR(10)='qty_'
    DECLARE @count INT = 0

    IF (@ColumnCount > 0)
    BEGIN
        SET @count = @count + 1;

        WHILE (@ColumnCount >= @count) 
        BEGIN
            SET @ColumnName = 'qty_'+CONVERT(varchar(2),@count)
            EXEC ('ALTER TABLE '+@tablename+' ADD ['+@ColumnName +'] varchar(20)')

            DECLARE @myvar AS VARCHAR(MAX)

            --set @myvar='update '+@tablename+' set '+@ColumnName +' =itemQty'

            /* HERE ACTUALLY I AM GETTING PROBLEM, BECAUSE I WANT TO PARSE VALUES IN THE RESPECTIVE COLUMNS. */
           EXEC(@myvar)

           /* HERE, I wish to write the Actual update statement DYNAMICALLY*/
           EXEC ('UPDATE '+@tablename+' set'+@ColumnName+'=     ' )
           SET @count = @count + 1;
       END
   END
END

我的输入和(所需输出应如下图所示)

enter image description here

输出

enter image description here

以类似动态的方式对代码

 declare @myvar as varchar(max)

                SET @MYVAR= 'update '+@tablename+ 'set' +@ColumnName+'=case  when '+@count+' = 1 then itemQty else  end'

                exec(@myvar)

2 个答案:

答案 0 :(得分:0)

请检查以下SQL Select语句

;with cte as (
select 
    i.id, i.ItemQty, s.id as rn, s.val
from inputTable as i
cross apply dbo.Split(ItemQty,',') as s
)
select
    id, ItemQty, 
    max(qty1) qty1,
    max(qty2) qty2,
    max(qty3) qty3,
    max(qty4) qty4,
    max(qty5) qty5
from (
select 
    id,
    ItemQty,
    case when rn = 1 then val else null end as qty1,
    case when rn = 2 then val else null end as qty2,
    case when rn = 3 then val else null end as qty3,
    case when rn = 4 then val else null end as qty4,
    case when rn = 5 then val else null end as qty5
from cte
) t
group by id, ItemQty

输出如下

enter image description here

请注意,此解决方案需要SQL string split function。 检查引用的用户定义函数,该函数可用于拆分字符串值。 虽然如果您有SQL Server 2016或更高版本的数据库平台,可以使用STRING_SPLIT function,但是我更喜欢使用自己的split函数,因为该函数也会返回分割字符串片段的顺序。

我希望它有所帮助,

答案 1 :(得分:0)

ItemQty包含仅包含一个符号'a,b,c'

的项目的变体
ALTER PROCEDURE [dbo].[dynamic_tbl] (@tablename varchar(50))
AS 

CREATE TABLE #inputTable(id int,ItemQty varchar(100))

-- insert all the data into temp table
DECLARE @copyScript varchar(MAX)
SET @copyScript='SELECT id,ItemQty FROM '+@tablename

INSERT #inputTable(id,ItemQty)
EXEC(@copyScript)

-- add columns and update data in #inputTable
DECLARE @maxColNum int
SET @maxColNum=(SELECT (MAX(LEN(ItemQty))+1)/2 FROM #inputTable)

DECLARE @curColNum int
SET @curColNum=1

DECLARE @updateScript varchar(MAX)
DECLARE @alterScript varchar(MAX)

WHILE @curColNum<=@maxColNum
BEGIN

  -- add new column
  SET @alterScript='ALTER TABLE '+@tablename+' ADD qty'+CAST(@curColNum AS varchar(5))+' varchar(100)'

  --PRINT @alterScript
  EXEC(@alterScript)

  -- update data in new column
  SET @updateScript='UPDATE i
SET
  qty'+CAST(@curColNum AS varchar(5))+'=q.word
FROM '+@tablename+' i
JOIN
  (
    SELECT id,SUBSTRING(ItemQty,'+CAST((@curColNum-1)*2+1 AS varchar(5))+',1) word
    FROM #inputTable
    WHERE LEN(ItemQty)+1>='+CAST(@curColNum*2 AS varchar(5))+'
  ) q
ON i.ID=q.ID'

  --PRINT @updateScript
  EXEC(@updateScript)

  SET @curColNum=@curColNum+1
END

DROP TABLE #inputTable    
GO

-- test
EXEC dynamic_tbl 'inputTable'

-- show result
SELECT *
FROM inputTable

下一个变体可以包含'a,bb,ccc'等字符串。

ALTER PROCEDURE [dbo].[dynamic_tbl] (@tablename varchar(50))
AS 

CREATE TABLE #inputTable(id int,ItemQty varchar(100))

-- insert all the data into temp table
DECLARE @copyScript varchar(MAX)
SET @copyScript='SELECT id,ItemQty FROM '+@tablename

INSERT #inputTable(id,ItemQty)
EXEC(@copyScript)

-- get all the words for each ID and put them into #words
;WITH wordsCTE AS(
  SELECT
    id,
    ItemQty,
    1 StartPosition,
    CASE WHEN CHARINDEX(',',ItemQty)>0 THEN CHARINDEX(',',ItemQty) ELSE LEN(ItemQty)+1 END CommaPos,
    1 WordNum
  FROM #inputTable

  UNION ALL

  SELECT
    i.id,
    i.ItemQty,
    c.CommaPos+1 StartPosition,
    CASE WHEN CHARINDEX(',',c.ItemQty,c.CommaPos+1)>0 THEN CHARINDEX(',',c.ItemQty,c.CommaPos+1) ELSE LEN(c.ItemQty)+1 END CommaPos,
    c.WordNum+1
  FROM #inputTable i
  JOIN wordsCTE c ON i.id=c.id
  WHERE c.CommaPos<LEN(c.ItemQty)
)
--SELECT *,SUBSTRING(ItemQty,StartPosition,CommaPos-StartPosition) word
SELECT id,SUBSTRING(ItemQty,StartPosition,CommaPos-StartPosition) word,WordNum INTO #words
FROM wordsCTE
--ORDER BY id,word

-- add columns and update data in #inputTable
DECLARE @maxColNum int
SET @maxColNum=(SELECT MAX(WordNum) FROM #words)

DECLARE @curColNum int
SET @curColNum=1

DECLARE @updateScript varchar(MAX)
DECLARE @alterScript varchar(MAX)

WHILE @curColNum<=@maxColNum
BEGIN

  -- add new column
  SET @alterScript='ALTER TABLE '+@tablename+' ADD qty'+CAST(@curColNum AS varchar(5))+' varchar(100)'

  --PRINT @alterScript
  EXEC(@alterScript)

  -- update data in new column
  SET @updateScript='UPDATE i
SET
  qty'+CAST(@curColNum AS varchar(5))+'=q.word
FROM '+@tablename+' i
JOIN
  (
    SELECT *
    FROM #words
    WHERE WordNum='+CAST(@curColNum AS varchar(5))+'
  ) q
ON i.ID=q.ID'

  --PRINT @updateScript
  EXEC(@updateScript)

  SET @curColNum=@curColNum+1
END

DROP TABLE #words
DROP TABLE #inputTable
GO

-- test
EXEC dynamic_tbl 'inputTable'

-- show result
SELECT *
FROM inputTable

如果您执行以下操作,则可以将其作为脚本进行测试

--ALTER PROCEDURE [dbo].[dynamic_tbl] (@tablename varchar(50))
--AS 
DECLARE @tablename varchar(50)='inputTable'
...