2017-01-15 106 views
-2

我想創建函數在另一個表中TBL_Sku柱(sku)的值被劃分到任何柱(SKU1,2,3,4,...)(TBL_Sku2如何將列的值拆分爲SQL Server中的動態列?

skuTBL_SkU

ROW1列德: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,&

和在列第2行德:1-SKU: 512MBDDR2-533-1038,Sony VAIO PCG-1J1L 512MB DDR2-533 PC4200 Memory Module,&

和在列第3行德:1-MSI S271 1GB DDR2-533 PC4200 Memory Module,SKU: 1GBDDR2-533-1068,&2-MSI S271 512MB DDR2-533 PC4200 Memory Module,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

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

在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,& 

其他方法:

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; 

DECLARE @str  VARCHAR(8000)[email protected], 
     @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 * 
    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 


    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 = '[email protected] 
--PRINT @sql 
----print SKU 
----print RetVal 
----print RetSeq 
------print 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] 

錯誤

INSERT語句的選擇列表包含多於插入列表中的項目少。 SELECT值的數量必須與INSERT列的數量相匹配。

+0

我認爲你需要先將字符串轉換爲表格然後進行轉換。我已經做了同樣的事給我5分鐘我會拿出解決方案 –

+0

值在表中@YashveerSingh,列類型是nvarchar(最大) – RedArmy

+0

謝謝@YashveerSingh – RedArmy

回答

1

這裏有一個表值函數可以使用:

CREATE FUNCTION dbo.tvfn_Extract_SKUs(
    @SKU_Line NVARCHAR(MAX) 
) 
RETURNS TABLE 
AS 
RETURN 

WITH sku_starts AS (
     SELECT CHARINDEX(':', @SKU_Line) + 2 AS SKU_1_Start 
       ,CHARINDEX(':', @SKU_Line, CHARINDEX(':', @SKU_Line) + 2) + 2 AS SKU_2_Start 
       ,CHARINDEX(':', @SKU_Line, CHARINDEX(':', @SKU_Line, CHARINDEX(':', @SKU_Line) + 2) + 2) + 2 AS SKU_3_Start 
       ,CHARINDEX(':', @SKU_Line, CHARINDEX(':', @SKU_Line, CHARINDEX(':', CHARINDEX(':', @SKU_Line) + 2) + 2) + 2) + 2 AS SKU_4_Start 
) 
SELECT SUBSTRING(@SKU_Line, s.SKU_1_Start, CHARINDEX(',', @SKU_Line, s.SKU_1_Start) - s.SKU_1_Start) AS SKU_1 
     ,CASE WHEN SKU_2_Start > SKU_1_Start THEN SUBSTRING(@SKU_Line, s.SKU_2_Start, CHARINDEX(',', @SKU_Line, s.SKU_2_Start) - s.SKU_2_Start) END SKU_2 
     ,CASE WHEN SKU_3_Start > SKU_2_Start THEN SUBSTRING(@SKU_Line, s.SKU_3_Start, CHARINDEX(',', @SKU_Line, s.SKU_3_Start) - s.SKU_3_Start) END AS SKU_3 
     ,CASE WHEN SKU_4_Start > SKU_3_Start THEN SUBSTRING(@SKU_Line, s.SKU_4_Start, CHARINDEX(',', @SKU_Line, s.SKU_4_Start) - s.SKU_4_Start) END AS SKU_4 
FROM sku_starts s 
+0

謝謝@Serge,測試和復出 – RedArmy

+0

好,好的。我試圖通過樞:) – RedArmy

+0

任何行良好的工作,但如何讓表並插入到TBL_SKU2 – RedArmy

1

這是一個動態的方法

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 

結果:

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

考慮正常化你的表結構來解析d更容易。有一個單獨的表爲sku號碼和值

我已經使用拆分字符串函數來分割每個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/

+0

謝謝@Prdp,無效的對象名稱'dbo.Delimitedsplit8k'。 – RedArmy

+0

@RedArmy - 我附加了'dbo.Delimitedsplit8k'函數代碼,以及請創建它並執行查詢 –

+0

看到功能和工作良好,但不創建表和保存spile在表中。 – RedArmy

1

這裏被稱作是一個方法,其將解析,然後PIVOT(不是動態的,但容易擴展到成品保持單元的最大數量)

Declare @YourTable table (ID int,SKU varchar(max)) 
Insert Into @YourTable values 
(1,'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,&') 

Select [ID],[Hits],[1] as SKU1,[2] as SKU2,[3] as SKU3,[4] as SKU4 
From (
     Select A.ID 
       ,Hits = sum(1) over (Partition By 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 = 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:%'  
     ) S 
Pivot (max(SKU) For [RN] in ([1],[2],[3],[4])) p 

返回

ID Hits SKU1    SKU2    SKU3    SKU4 
1 3 1GBDDR3-1066-21 2GBDDR3-1066-21 4GBDDR3-1066-414 NULL 
+0

謝謝@JohnCappelletti。 – RedArmy

+0

工作,但有點複雜。 – Deadsheep39

+0

@ Deadsheep39交叉應用解析成行,然後我們樞軸 –

1

變體1.實例le with delimiter &。當我看到其他答案時,我必須添加一些簡單的方法。沒有數據定義的腳本只有幾行。 你應該使用簡單有效的方法。

  1. 數據定義

    if object_id('tempdb..#TblTestStr') is not null drop table #TblTestStr 
    create table #TblTestStr (MyStr varchar(max)) 
    
    insert into #TblTestStr values 
        ('Value1&Value2&Value3&Value4&'), 
        ('Value2&Value3&Value0&'), 
        ('Value1&'), 
        ('Value3&Value4&') 
    
  2. 全用細繩工作

    update #TblTestStr set MyStr = '<element>' + replace(MyStr, '&','</element><element>') + '</element>' 
    
  3. 數據選擇

    select 
        x.xcol.value('(./element)[1]', 'varchar(800)') col1, 
        x.xcol.value('(./element)[2]', 'varchar(800)') col2, 
        x.xcol.value('(./element)[3]', 'varchar(800)') col3, 
        x.xcol.value('(./element)[4]', 'varchar(800)') col4 
    from (select cast(MyStr as xml) from #TblTestStr) x (xcol) 
    

如果你想使用charindexsubstring功能,你必須檢查太多的屬性。

變體2.一個select語句。

xquery
select 
     x.xcol.value('(./element)[1]', 'varchar(800)'), 
     x.xcol.value('(./element)[2]', 'varchar(800)'), 
     x.xcol.value('(./element)[3]', 'varchar(800)'), 
     x.xcol.value('(./element)[4]', 'varchar(800)') 
    from (
     select cast('<element>' + replace(MyStr, '&','</element><element>') + '</element>' as xml) 
     from #TblTestStr) x (xcol) 

解決方案可以很efective,容易可擴展