拆分字符串并将部分插入表格中的正确列中

时间:2019-05-27 08:46:36

标签: sql-server tsql

我正在尝试使用sql server建立事实表。当前看起来像这样:

[Item]  [Variant Descr.] [Variant Order] [Dim_Colour] [Dim_Size] [Dim_Style]
----------------------------------------------------------------------------
01         NAVY/44        COLOUR/SIZE     NULL         NULL       NULL
02         BLACK/S4       COLOUR/STYLE    NULL         NULL       NULL

我需要在[Variant Descr。]中拆分String并将各部分插入正确的Dim_ Column中,以便表格最终看起来像这样:

[Item]  [Variant Descr.] [Variant Order] [Dim_Colour] [Dim_Size] [Dim_Style]
----------------------------------------------------------------------------
01         NAVY/44        COLOUR/SIZE     NAVY         44       NULL
02         BLACK/S4       COLOUR/STYLE    BLACK        NULL     S4

问题在于,[Variant Order]的部分和[Variant Descr。]的部分数目可能会针对每一行而有所不同。所以我基本上需要做类似的事情:

  1. 在[Variant Descr。]中获取字符串,在'/'之前
  2. 在[Variant顺序]中的'/'之前获取字符串
  3. 将第一个值插入第二个值指定的列中
  4. 对[Variant Descr。]的所有部分都执行此操作
  5. 对表中的每一行执行此操作

我已经尝试使用用户定义的函数解决此问题,只是发现我无法在UDF中使用动态SQL。

任何帮助将不胜感激

干杯!

4 个答案:

答案 0 :(得分:1)

一种可能的方法是拆分[变体说明]和[变体顺序]列中的文本,并使用动态语句更新表。 尽管从SQL Server 2016开始使用STRING_SPLIT()是第一个选择,但是在这种情况下,此功能不是选项,因为不能保证子字符串的顺序。 一种有效的解决方案是使用OPENJSON()-将列值转换为有效的JSON对象(例如,将'NAVY/44'转换为'["NAVY", "44"]'),并使用{{1 }}。

输入:

OPENJSON()

T-SQL:

CREATE TABLE #Data (
   [Item] varchar(10),  
   [Variant Descr.] varchar(50), 
   [Variant Order] varchar(50), 
   [Dim_Colour] varchar(50),   
   [Dim_Size] varchar(50),  
   [Dim_Style] varchar(50)
)
INSERT INTO #Data 
   ([Item], [Variant Descr.], [Variant Order], [Dim_Colour], [Dim_Size], [Dim_Style])
VALUES
   ('01', 'NAVY/44',    'COLOUR/SIZE',       NULL, NULL, NULL),
   ('02', 'BLACK/S4',   'COLOUR/STYLE',      NULL, NULL, NULL),
   ('03', 'NAVY/44/S4', 'COLOUR/SIZE/STYLE', NULL, NULL, NULL),
   ('04', 'GREEN',      'COLOUR',            NULL, NULL, NULL)

输出:

-- Dynamic statement
DECLARE @stm nvarchar(max) = N''
SELECT @stm = @stm + 
   N'UPDATE #Data ' +
   N'SET ' + 
   QUOTENAME('Dim_' + j1.[value]) +
   N' = ''' +
   j2.[value] +
   N''' WHERE Item = ''' +
   d.Item +
   N'''; '
FROM #Data d
CROSS APPLY OPENJSON(CONCAT('["', REPLACE([Variant Order], '/', '","'), '"]')) j1 
CROSS APPLY OPENJSON(CONCAT('["', REPLACE([Variant Descr.], '/', '","'), '"]')) j2
WHERE j1.[key] = j2.[key]

-- Execution and output
EXEC (@stm)
SELECT *
FROM #Data

答案 1 :(得分:0)

尝试一下

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

CREATE TABLE #temp (
            [Item] INT,  
            [Variant Descr.] VARCHAR(100),
            [Variant Order] VARCHAR(100) ,
            [Dim_Colour] VARCHAR(100),
            [Dim_Size]INT ,
            [Dim_Style]  VARCHAR(100)
            )

INSERT INTO  #temp
SELECT 01,'NAVY/44' ,'COLOUR/SIZE' ,NULL,NULL,NULL UNION ALL
SELECT 02,'BLACK/S4','COLOUR/STYLE',NULL,NULL,NULL

SELECT * FROM #temp

UPDATE o 
SET     o.[Dim_Colour]  = dt.Dim_Colour, 
        o.Dim_Size = dt.Dim_Size,
        o.Dim_Style = dt.Dim_Style
FROM #temp o
INNER JOIN
(    

SELECT [Item],  [Variant Descr.], [Variant Order] ,
            SUBSTRING([Variant Descr.],0,CHARINDEX('/',[Variant Descr.])) AS [Dim_Colour]   
           ,CASE WHEN ISNUMERIC(SUBSTRING([Variant Descr.],CHARINDEX('/',[Variant Descr.])+1,LEN ([Variant Descr.]))) = 1 AND [Variant Order] = 'COLOUR/SIZE'   
                  THEN SUBSTRING([Variant Descr.],CHARINDEX('/',[Variant Descr.])+1,LEN ([Variant Descr.]))
                  ELSE NULL END AS [Dim_Size]
           ,CASE WHEN ISNUMERIC(SUBSTRING([Variant Descr.],CHARINDEX('/',[Variant Descr.])+1,LEN ([Variant Descr.]))) <> 1 AND [Variant Order] ='COLOUR/STYLE'
                  THEN SUBSTRING([Variant Descr.],CHARINDEX('/',[Variant Descr.])+1,LEN ([Variant Descr.]))
                  ELSE NULL END AS [Dim_Style]
      FROM  #temp i
)dt ON dt.[Item] = o.[Item]

SELECT * FROM  #temp 

结果

Item    Variant Descr.  Variant Order       Dim_Colour  Dim_Size    Dim_Style
------------------------------------------------------------------------------
1       NAVY/44         COLOUR/SIZE            NAVY         44        NULL
2       BLACK/S4        COLOUR/STYLE           BLACK       NULL       S4

答案 2 :(得分:0)

您可以通过-

获得所需的输出
variable_Old>variable_new

答案 3 :(得分:0)

这是一种完全通用的方法(将其拆分为MCVE的Zhorov!)

CREATE TABLE #Data (
   [Item] varchar(10),  
   [Variant Descr.] varchar(50), 
   [Variant Order] varchar(50), 
   [Dim_Colour] varchar(50),   
   [Dim_Size] varchar(50),  
   [Dim_Style] varchar(50)
)
INSERT INTO #Data 
   ([Item], [Variant Descr.], [Variant Order], [Dim_Colour], [Dim_Size], [Dim_Style])
VALUES
   ('01', 'NAVY/44',       'COLOUR/SIZE',       NULL, NULL, NULL),
   ('02', 'BLACK/S4',      'COLOUR/STYLE',      NULL, NULL, NULL),
   ('03', 'NAVY/44/S4',    'COLOUR/SIZE/STYLE', NULL, NULL, NULL),
   ('04', 'GREEN/1/2/3/4', 'COLOUR/aNewOne/SIZE/EvenMore/STYLE', NULL, NULL, NULL);
GO

WITH Casted AS
(
    SELECT *
          ,CAST('<x>' + REPLACE(d.[Variant Order],'/','</x><x>') + '</x>' AS XML) AS OrderXml
          ,CAST('<x>' + REPLACE(d.[Variant Descr.],'/','</x><x>') + '</x>' AS XML) AS DescrXml
    FROM #Data d
)
SELECT c.Item
      ,A.Position 
      ,c.OrderXml.value('/x[sql:column("Position")][1]','nvarchar(max)') AS OrderKey
      ,c.DescrXml.value('/x[sql:column("Position")][1]','nvarchar(max)') AS DescrValue
FROM Casted c
CROSS APPLY(SELECT TOP(SELECT c.OrderXml.value('count(/*)','int')) ROW_NUMBER() OVER(ORDER BY(SELECT NULL)) FROM master..spt_values) A(Position)

GO

DROP TABLE #Data;

结果

+------+----------+----------+------------+
| Item | Position | OrderKey | DescrValue |
+------+----------+----------+------------+
| 02   | 1        | COLOUR   | BLACK      |
+------+----------+----------+------------+
| 02   | 2        | STYLE    | S4         |
+------+----------+----------+------------+
| 04   | 1        | COLOUR   | GREEN      |
+------+----------+----------+------------+
| 04   | 2        | aNewOne  | 1          |
+------+----------+----------+------------+
| 04   | 3        | SIZE     | 2          |
+------+----------+----------+------------+
| 04   | 4        | EvenMore | 3          |
+------+----------+----------+------------+
| 04   | 5        | STYLE    | 4          |
+------+----------+----------+------------+
| 01   | 1        | COLOUR   | NAVY       |
+------+----------+----------+------------+
| 01   | 2        | SIZE     | 44         |
+------+----------+----------+------------+
| 03   | 1        | COLOUR   | NAVY       |
+------+----------+----------+------------+
| 03   | 2        | SIZE     | 44         |
+------+----------+----------+------------+
| 03   | 3        | STYLE    | S4         |
+------+----------+----------+------------+

您可以继续进行条件聚合或使用PIVOT方法。

简而言之:

我使用强制转换为XML来允许按其位置到达片段。 此外,我将CROSS APPLYROW_NUMBER一起使用,这将根据当前行的片段列表的计数返回一个数字列表。现在,该数字在sql:column()中使用,以并排的方式读取合适的片段。