如何在SQL Server中将列的值拆分为动态列? [英] How to split value of column to dynamic column in SQL Server?

查看:250
本文介绍了如何在SQL Server中将列的值拆分为动态列?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想创建一个函数,将 TBL_Sku 中的一列( sku TBL_Sku2

I want to create function to split the value of a column (sku) in TBL_Sku to any column (Sku1, 2, 3, 4, ...) in another table (TBL_Sku2)

TBL_SkU 中的$ c> sku c $ c> 1-Acer Aspire 3811TZG 1GB DDR3-1066 PC8500内存模块,SKU:

1GBDDR3-1066-21,2-Acer Aspire 3811TZG 2GB DDR3-1066 PC8500内存模块,SKU:
2GBDDR3-1066-21,& 3-Acer Aspire 3811TZG 4GB DDR3-1066 PC8500内存模块,SKU:
4GBDDR3-1066-414,&

Row1 in column Des : 1-Acer Aspire 3811TZG 1GB DDR3-1066 PC8500 Memory Module,SKU:
1GBDDR3-1066-21,&2-Acer Aspire 3811TZG 2GB DDR3-1066 PC8500 Memory Module,SKU: 2GBDDR3-1066-21,&3-Acer Aspire 3811TZG 4GB DDR3-1066 PC8500 Memory Module,SKU: 4GBDDR3-1066-414,&

和列Des: 1-SKU:512MBDDR2-533-1038中的第2行,Sony VAIO PCG-1J1L 512MB DDR2-533 PC4200存储器模块, code>

and row 2 in column Des : 1-SKU: 512MBDDR2-533-1038,Sony VAIO PCG-1J1L 512MB DDR2-533 PC4200 Memory Module,&

和第3列Des: 1-MSI S271 1GB DDR2-533 PC4200内存模块,SKU:1GBDDR2-533 -1068,& 2-MSI S271 512MB DDR2-533 PC4200内存模块,SKU:512MBDDR2-533-1033&

TBL_Sku2

 id       sku1                     sku2                sku3                 sku4            sku5       
 ---     -----------       -----------------    -------------------  -----------------     ---------

 1   1GBDDR3-1066-21    2GBDDR3-1066-21        4GBDDR3-1066-414            Null               Null

 2   512MBDDR2-533-1038             null              null                     null            null
 3   1GBDDR2-533-1068        512MBDDR2-533-1033       Null                 Null                    Null

我想要专业的功能。稍后发送给我我的代码。在子字符串和charindex中使用

I want to professional function. Send for you my code later. Use in substring and charindex

sp:

ALTER PROCEDURE [dbo].[SP_SpilitSKU] 
(@ID int ,@SKU   nvarchar(Max) )

AS
BEGIN

Declare @YourTable table (ID int,SKU varchar(max))
Insert Into @YourTable values
(@ID,@SKU)

;with cte as (
      Select A.ID
            ,RN  = Row_Number() over (Partition By ID Order by RetSeq) 
            ,SKU = LTrim(RTrim(Replace(RetVal,'SKU:','')))
       From @YourTable A
       Cross Apply (
                    Select RetSeq = Row_Number() over (Order By (Select null))
                          ,RetVal = LTrim(RTrim(B.i.value('(./text())[1]', 'varchar(max)')))
                    From  (Select x = Cast('<x>'+ replace((Select A.SKU as [*] For XML Path('')),',','</x><x>')+'</x>' as xml).query('.')) as A 
                    Cross Apply x.nodes('x') AS B(i)
                   ) B
        Where RetVal like 'SKU:%'      
)
INSERT INTO [dbo].[TBL_Sku]
           (id
           ,[SKU1]
           ,[SKU2]
           ,[SKU3]
           ,[SKU4]
           ,[SKU5]
           ,[SKU6]
           )
Select ID,[1] as SKU1,[2] as SKU2,[3] as SKU3,[4] as SKU4,[5] as SKU5,[6] as SKU6
From   cte
Pivot (max(SKU) For [RN] in ([1],[2],[3],[4],[5],[6]) ) p

SP:

ALTER PROCEDURE [dbo].[SP_CreateTableSku4Column] 


AS
BEGIN
declare @DesOrginal as nvarchar(max);
Declare @i as int = 0;
Declare @Count as int = (select COUNT(des) from TBL_Product_Test);
while (@i<= @Count)
Begin
WITH TBL_SKUTemp AS
(
    SELECT 
    ROW_NUMBER() OVER( ORDER BY (SELECT null)) AS 'RowNumber',[des]

    FROM TBL_Product_Test
) 
SELECT  @DesOrginal = [des]     
FROM TBL_SKUTemp 
WHERE RowNumber = @i+1;
set @i = @i+1;
EXECUTE  [dbo].[SP_SpilitSKU] @i,@DesOrginal ;
END

END




计数列在表中= 15563

count column Des in the Table = 15563

在TBL_Sku = 8449中影响

affect in the TBL_Sku = 8449



(1 row(s) affected)
8449
15563
des = 1-Samsung NP940X5JI (NP940X5J-S01US) 4GB DDR3L-1600 PC12800 1.35V Memory Module,SKU: 4GBDDR3L-1600-9,&

(1 row(s) affected)

(0 row(s) affected)
8450
15563
des = 1-SKU: 512MBDDR2-533-1038,Sony VAIO PCG-1J1L 512MB DDR2-533 PC4200 Memory Module,&
other Method :


$ b declare @DesOrginal as nvarchar(max);
声明@i为int = 0;
将@Count声明为int =(从TBL_Product_Test中选择COUNT(des));
while(@ i <= @Count)
begin
WITH TBL_SKUTemp AS

SELECT
ROW_NUMBER()OVER(ORDER BY )AS'RowNumber',[des]

FROM TBL_Product_Test

SELECT @DesOrginal = [des]
FROM TBL_SKUTemp
WHERE RowNumber = @i +1;
set @i = @ i + 1;

DECLARE @str VARCHAR(8000)= @ DesOrginal,
@col_list VARCHAR(1000)='',
@sql NVARCHAR(max)
$ b b SET @col_list =(SELECT Concat(',',Quotename(Concat('sku',Row_number()
OVER(
ORDER BY ItemNumber))))
FROM dbo.Delimitedsplit8k @str,',sku:')
WHERE Item LIKE'sku:%'
FOR xml path(''))
SET @col_list = Stuff(@col_list, '')

SET @sql ='SELECT *
FROM(SELECT Concat(''sku'',Row_number()OVER(ORDER BY ItemNumber))rn,
(item,1,5,'''')AS item
FROM dbo.Delimitedsplit8k(@str,'',sku:'')
WHERE Item LIKE''sku:%'')a
PIVOT(Max(item)
For rn IN('+ @col_list +'))pv'

--PRINT @sql

EXEC Sp_executesql
@sql,
N'@ str VARCHAR(8000)',
@ str = @str


INSERT INTO [dbo] TBL_Sku6]
([SKU1]
,[SKU2]
,[SKU3]
,[SKU4]
,[SKU5]
)SELECT *
FROM(SELECT Concat('sku',Row_number()OVER(ORDER BY ItemNumber))rn,
Stuff(item,1,5,'')AS item
FROM dbo.Delimitedsplit8k @str,',sku:')
WHERE Item LIKE'sku:%')a
PIVOT(Max(item)
FOR rn IN(+ @col_list +))pv

print @i
print @Count
--print @str
--print RowNumber
print'des ='+ @ DesOrginal
--PRINT @sql
----打印SKU
----打印RetVal
----打印RetSeq
------打印cte

end

TBL_Sku6:

TABLE [dbo].[TBL_Sku6](
    [SKU1] [varchar](7996) NULL,
    [SKU2] [varchar](7996) NULL,
    [SKU3] [varchar](7996) NULL,
    [SKU4] [varchar](7996) NULL,
    [SKU5] [varchar](7996) NULL,
    [SKU6] [varchar](7996) NULL
) ON [PRIMARY]

/ p>

Error


INSERT语句的选择列表包含的项目少于插入列表。 SELECT值的数量必须与INSERT列的数量相匹配。

The select list for the INSERT statement contains fewer items than the insert list. The number of SELECT values must match the number of INSERT columns.


推荐答案

方法

DECLARE @str      VARCHAR(8000)='1-Acer Aspire 3811TZG 1GB DDR3-1066 PC8500 Memory Module,SKU: 1GBDDR3-1066-21,&2-Acer Aspire 3811TZG 2GB DDR3-1066 PC8500 Memory Module,SKU: 2GBDDR3-1066-21,&3-Acer Aspire 3811TZG 4GB DDR3-1066 PC8500 Memory Module,SKU: 4GBDDR3-1066-414,&',
        @col_list VARCHAR(1000)='',
        @sql      NVARCHAR(max)

SET @col_list =(SELECT Concat(',', Quotename(Concat('sku', Row_number()
                                                             OVER(
                                                               ORDER BY ItemNumber))))
                FROM   dbo.Delimitedsplit8k(@str, ',sku:')
                WHERE  Item LIKE 'sku:%'
                FOR xml path(''))
SET @col_list = Stuff(@col_list, 1, 1, '')

SET @sql = 'SELECT * into TBL_Sku2
    FROM   (SELECT Concat(''sku'',Row_number()OVER(ORDER BY ItemNumber)) rn,
                   Stuff(item, 1, 5, '''')    AS item
            FROM   dbo.Delimitedsplit8k(@str, '',sku:'')
            WHERE  Item LIKE ''sku:%'') a
           PIVOT (Max(item)
                 FOR rn IN (' + @col_list + ')) pv '

PRINT @sql

EXEC Sp_executesql
  @sql,
  N'@str VARCHAR(8000)',
  @str= @str 

Result :

+-----------------+-----------------+------------------+
|      sku1       |      sku2       |       sku3       |
+-----------------+-----------------+------------------+
| 1GBDDR3-1066-21 | 2GBDDR3-1066-21 | 4GBDDR3-1066-414 |
+-----------------+-----------------+------------------+

考虑规范化表结构以更轻松地解析数据。有一个单独的表 sku 数字和值

Consider normalizing your table structure to parse data easier. Have a separate table for sku number and value

我已经使用分割字符串函数分割每个 sku

I have used split string function to split the records for each sku.

Create FUNCTION [dbo].[DelimitedSplit8K]

        (@pString VARCHAR(8000), @pDelimiter CHAR(1))
RETURNS TABLE WITH SCHEMABINDING AS
 RETURN
--===== "Inline" CTE Driven "Tally Table" produces values from 0 up to 10,000...
     -- enough to cover NVARCHAR(4000)
  WITH E1(N) AS (
                 SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL 
                 SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL 
                 SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1
                ),                          --10E+1 or 10 rows
       E2(N) AS (SELECT 1 FROM E1 a, E1 b), --10E+2 or 100 rows
       E4(N) AS (SELECT 1 FROM E2 a, E2 b), --10E+4 or 10,000 rows max
 cteTally(N) AS (--==== This provides the "base" CTE and limits the number of rows right up front
                     -- for both a performance gain and prevention of accidental "overruns"
                 SELECT TOP (ISNULL(DATALENGTH(@pString),0)) ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) FROM E4
                ),
cteStart(N1) AS (--==== This returns N+1 (starting position of each "element" just once for each delimiter)
                 SELECT 1 UNION ALL
                 SELECT t.N+1 FROM cteTally t WHERE SUBSTRING(@pString,t.N,1) = @pDelimiter
                ),
cteLen(N1,L1) AS(--==== Return start and length (for use in substring)
                 SELECT s.N1,
                        ISNULL(NULLIF(CHARINDEX(@pDelimiter,@pString,s.N1),0)-s.N1,8000)
                   FROM cteStart s
                )
--===== Do the actual split. The ISNULL/NULLIF combo handles the length for the final element when no delimiter is found.
 SELECT ItemNumber = ROW_NUMBER() OVER(ORDER BY l.N1),
        Item       = SUBSTRING(@pString, l.N1, l.L1)
   FROM cteLen l
;

参考 http://www.sqlservercentral.com/articles/Tally+Table/72993/

这篇关于如何在SQL Server中将列的值拆分为动态列?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆