Разделить один столбец в строках, разделенных на несколько столбцов в соответствии со строкой - SQL Server - PullRequest
0 голосов
/ 30 июня 2018

У меня есть 2 таблицы, требующие разделения строки. Таблица результатов разделит строку, чтобы создать столбцы нового года, модели, модели. Вот пример 2 таблицы.

CREATE TABLE Table1
(
    sku varchar(50), 
    Conca varchar(max)
);

INSERT INTO Table1 (sku, Conca)
VALUES ('AVS1234','Game;Consoles/Year;2001/ Year;2002/ Year;2006/ Year;2007/ Year;2009/ Year;2011/Make;XBox/Model;100/'),
       ('AVS1234','Game;Consoles/Year;2016/Make;Nintendo/Model;DX/'),
       ('AVS1234','Game;Consoles/Year;2001/ Year;2002/ Year;2006/ Year;2007/ Year;2009/ Year;2011/Make;PS/Model;300/Model;500/');

CREATE TABLE Table2
(
    sku2 varchar(50), 
    Conca2 varchar(max)
);

INSERT INTO Table2 (sku2, Conca2)
VALUES ('AVS1234','<tr><td>2011 </td><td>Xbox </td><td>100; Notes : 2 pc.; Stainless</td></tr>'), 
       ('AVS1234','<tr><td>2005 </td><td>Xbox </td><td>100; Notes : 2 pc.; Stainless</td></tr>'), 
       ('AVS1234','<tr><td>2016 </td><td>Xbox </td><td>300</td></tr>'); 

Таблица предполагаемых результатов1

        Sku     year    make   model
        --------------------------
        avs1234 2001    XBox    100
        avs1234 2002    XBox    100
        avs1234 2006    XBox    100
        avs1234 2007    XBox    100
        avs1234 2009    XBox    100
        avs1234 2011    XBox    100
        AVS1234 2016    Nintendo    DX
        AVS1234 2001    PS      300
        AVS1234 2002    PS      300
        AVS1234 2006    PS      300
        AVS1234 2007    PS      300
        AVS1234 2009    PS      300
        AVS1234 2011    PS      300
        AVS1234 2001    PS      500
        AVS1234 2002    PS      500
        AVS1234 2006    PS      500
        AVS1234 2007    PS      500
        AVS1234 2009    PS      500
        AVS1234 2011    PS      500

Таблица ожидаемых результатов2

        Sku       year  make   model  Notes
        ------------------------------------
        avs1234   2001  XBox    100   2 pc.; Stainless
        avs1234   2002  XBox    100   2 pc.; Stainless
        AVS1234   2005  Xbox    100   2 pc.; Stainless
        AVS1234   2016  Xbox    300

Сначала я подумал попробовать метод позиции / обрезки:

Cross Apply 
    (Select Pos1 = ltrim(rtrim(xDim.value('/x[1]','varchar(max)'))) 

Но число лет может варьироваться от 1950-х до 2018 года и далее. Так что это создаст слишком много "pos".

В меру своих возможностей я тогда попробовал:

with firstpass as
(
    SELECT A.sku,  
           Split.a.value('.', 'VARCHAR(100)') AS Data  
    FROM  
        (SELECT sku,  
                Cast('<M>' + replace((Select replace(Conca,'year','§§Split§§') as [*] For XML Path('')),'§§Split§§','</M><M>')+'</M>' as xml) as Data
         FROM  table1) AS A 
    CROSS APPLY 
        Data.nodes ('/M') AS Split(a)
)
SELECT
    *, Year = SUBSTRING([data], 1 ,
            case when  CHARINDEX('/', [data] ) = 0 then LEN([data]) 
            else CHARINDEX('/', [data]) -1 end)
                   from firstpass

Я не могу понять, куда идти дальше ... Пожалуйста, помогите.

1 Ответ

0 голосов
/ 01 июля 2018

Если открыто для вспомогательной функции с табличным значением. Я изменил функцию split / parse, чтобы принимать два не похожих на разделители, потому что мне надоело извлекать строки (left, right, charindex, patindex и т. Д.).

Пример или dbFiddle

Select SKU
      ,Year  = B.RetVal
      ,Make  = C.RetVal
      ,Model = D.RetVal
 From  Table1 A
 Cross Apply  [dbo].[tvf-Str-Extract]('/'+A.Conca+'/','Year;','/')  B
 Cross Apply  [dbo].[tvf-Str-Extract]('/'+A.Conca+'/','Make;' ,'/') C
 Cross Apply  [dbo].[tvf-Str-Extract]('/'+A.Conca+'/','Model;','/') D


Select SKU2
      ,Year  = XMLData.value('tr[1]/td[1]','varchar(max)')
      ,Make  = XMLData.value('tr[1]/td[2]','varchar(max)')
      ,Model = ltrim(rtrim(left(XMLData.value('tr[1]/td[3]','varchar(max)'),charindex(';',XMLData.value('tr[1]/td[3]','varchar(max)')+';')-1)))
      ,Notes = ltrim(rtrim(substring(XMLData.value('tr[1]/td[3]','varchar(max)'),charindex(';',XMLData.value('tr[1]/td[3]','varchar(max)')+';')+1,100)))
 From  Table2
 Cross Apply (values (cast(Conca2 as xml)) )B(XMLdata)

Возвращает

enter image description here

TVF, если интересно

CREATE FUNCTION [dbo].[tvf-Str-Extract] (@String varchar(max),@Delimiter1 varchar(100),@Delimiter2 varchar(100))
Returns Table 
As
Return (  

with   cte1(N)   As (Select 1 From (Values(1),(1),(1),(1),(1),(1),(1),(1),(1),(1)) N(N)),
       cte2(N)   As (Select Top (IsNull(DataLength(@String),0)) Row_Number() over (Order By (Select NULL)) From (Select N=1 From cte1 N1,cte1 N2,cte1 N3,cte1 N4,cte1 N5,cte1 N6) A ),
       cte3(N)   As (Select 1 Union All Select t.N+DataLength(@Delimiter1) From cte2 t Where Substring(@String,t.N,DataLength(@Delimiter1)) = @Delimiter1),
       cte4(N,L) As (Select S.N,IsNull(NullIf(CharIndex(@Delimiter1,@String,s.N),0)-S.N,8000) From cte3 S)

Select RetSeq = Row_Number() over (Order By N)
      ,RetPos = N
      ,RetVal = left(RetVal,charindex(@Delimiter2,RetVal)-1) 
 From  (
        Select *,RetVal = Substring(@String, N, L) 
         From  cte4
       ) A
 Where charindex(@Delimiter2,RetVal)>1

)
/*
Max Length of String 1MM characters

Declare @String varchar(max) = 'Dear [[FirstName]] [[LastName]], ...'
Select * From [dbo].[tvf-Str-Extract] (@String,'[[',']]')
*/

Редактировать - Вы также можете использовать TVF для table2

Select SKU2
      ,B.Year
      ,B.Make
      ,Model = ltrim(rtrim(left(B.Model,charindex(';',B.Model+';')-1)))
      ,Notes = ltrim(rtrim(substring(B.Model,charindex(';',B.Model+';')+1,100)))
 From  Table2 A
 Cross Apply (
                Select Year  = max(case when RetSeq=1 then RetVal end)
                      ,Make  = max(case when RetSeq=2 then RetVal end)
                      ,Model = max(case when RetSeq=3 then RetVal end)
                 From [dbo].[tvf-Str-Extract](A.Conca2,'<td>','</td>')  
             )B
...