【问题标题】:Split single column in rows separted out by mulitple columns according to string - SQL Server根据字符串拆分由多列分隔的行中的单列 - SQL Server
【发布时间】:2018-06-30 20:46:35
【问题描述】:

我有 2 个需要字符串拆分的表。结果表将拆分字符串以创建 new year、make、model 列。这是示例 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

我不知道下一步该去哪里……请帮忙。

【问题讨论】:

  • 老实说,SQL Server 使用该数据的情况很糟糕。您可能可以为第一个表创建预期的结果,但第二个将是一个问题。 SQL Server 没有 html 上下文,因此您不会有简单的方法将数据从标记中剥离出来。我认为您可能会更好地使用文本解析工具,而不是 SQL Server。
  • @Larnu 虽然主要是正确的方法是使用 patindex 进行正则表达式和模式匹配,但无论如何它都很可怕并且感觉不对。
  • 另外,你为什么还要在表格中保存 html 标记??
  • 我正在考虑使用替换从表 2 中删除所有 html 标签。但是对表 1 有什么建议吗?
  • 我没有时间测试或编写查询,但我会使用几个SFRING_SPLITs 来获得第一个结果。首先是/,然后是;。您将需要更多 CROSS APPLY 和一些 CASE 表达式

标签: sql sql-server split


【解决方案1】:

如果打开表值辅助函数。我修改了一个拆分/解析函数来接受两个不同的分隔符,因为我厌倦了提取字符串(左、右、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)

退货

有兴趣的 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

【讨论】:

  • 我得到:“无效的对象名称'dbo.tvf-Str-Extract'”
  • @wsi 您需要添加该功能。先运行create函数
  • @John Cappelletti 请查看我对数据所做的修改,现在会产生重复的结果dbfiddle.uk/…
  • 我的数据集非常大(超过 1MM 行)。使用 dups 查询花费的时间太长。请帮忙。
  • @Wsi 我建议,由于数据的格式如此糟糕,您可以让进程运行并存储结果。修复 1MM+ 行将产生一次性成本,但从长远来看非常值得。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-10-19
  • 1970-01-01
相关资源
最近更新 更多