【问题标题】:Parse description string to populate NULL fields解析描述字符串以填充 NULL 字段
【发布时间】:2018-12-01 05:46:57
【问题描述】:

Informix 12.10

tblItems
(
Type        SMALLINT,       {Precious Metal = 1, Other = 2}
Description VARCHAR,
Quantity    SMALLINT,
Name        VARCHAR,
Weight      DECIMAL(5,1),
Purity      SMALLINT,
Brand       VARCHAR,
Model       VARCHAR,
SerialNum   VARCHAR
);

编辑更新:下面的示例数据存储在 tblItems.Type 和 tblItems.Description 中。请注意,Description 栏中的内容均为大写字符,也可能包含标点符号。

2|1LAPTOP APPLE 15.5" MODEL MACKBOOK PRO,S/N W80461WCAGX, WITH CHARGER||||||||
1|1RING 2.3PW 14K||||||||
2|DRILL RIOBY, MODEL D5521 S/N77720||||||||
2|TRIMMER TORO, MODEL 0242 S/N 66759||||||||
2|CELL SAMSUNG NOTE3, MODEL SM-N900T S/N RV8F90YLZ9W||||||||

我需要使用 cmets 中提到的规则将示例项目描述解析到下面的列中:

Quantity,      {if description string does not start with a number, then Quantity = 1}
Name,          {Always the first element if description has no quantity, second element if quantity present] 
Weight,        {Always before "PW" if Type = 1, Default to zero if Type = 2}
Purity,        {Always before "K" if Type = 1, Default to NULL if Type = 2} 
Brand,         {Always the second element in description, if present} 
Model,         {Always after "MODEL", with or without a space}
Serial Number  {Always after "S/N", with or without a space}

我想使用 UPDATE 语句来执行此操作,但如果 Informix 具有像 SQL-Server 的 SSIS 这样的导入实用工具,那么这可能是一个更好的选择。

更新,预期结果:

Quantity   1               1       1        1         1
Name       LAPTOP          RING    DRILL    TRIMMER   CELL
Weight     0.0             2.3     0.0      0.0       0.0
Purity                     14
Brand      APPLE                   RIOBY    TORO      SAMSUNG
Model      MACKBOOK PRO            D5521    0242      SM-N900T
SerialNum  W8046WCAGX              77720    66759     RV8F90YLZ9W

【问题讨论】:

  • 我对你的标签有点困惑。看来您的问题是基于使用 Informix 数据库。目前尚不清楚您的“样本”存在于何处。似乎 sql server 在这里也没有任何作用。如果你想使用 SSIS 作为导入工具,你可以(我相信它可以使用 Informix db)。
  • 在我的帖子的最顶部,它显示 Informix 12.10,示例数据位于表定义下方。如果我无法在 Informix 中完成我所需要的,那么 SQL-Server 和 SSIS 可能是一个可行的解决方案。
  • 如果您还可以发布您期望的样本实际结果,那将会有所帮助。一些规则。例如RING 条目,它没有品牌,没有型号,也没有序列号。话虽如此,Informix 12.10.XC8 具有 Regex 扩展,可用于您想要的。另一种方法是创建一个或多个存储过程来解析描述。
  • 我想我会首先确定 case 语句的项目类型是 1 还是 2,扫描整个描述字符串,检查 description[1] 是字母还是数字,如果 description[1] 是一个数字,检查 description[2] 是否是一个数字...,检查 BRAND 的间距,定位 MODEL,S/N 检查前后的空格等...
  • 除了将描述字段分解为离散值之外,是否需要对结果输出进行旋转?

标签: parsing ssis etl informix


【解决方案1】:

假设您使用的是 Informix 12.10.XC8 或更高版本,您可以尝试使用正则表达式来解析描述字符串(请参阅此处的在线documentation)。

以序列号为例,可以这样做:

UPDATE tblitems
SET
serialnum = 
DECODE 
(
    regex_match(description, '(.*)(S\/N)(.*)', 3)
    , 't'::BOOLEAN, regex_replace(description, '(.*)(S\/N)([[:blank:]]?)([[:alnum:]]*)(.*)', '\4', 0, 3)
    , 'f'::BOOLEAN, ''
)

所以在前面的示例中,我正在测试描述是否包含 S/N 字符串,如果是,我使用 regex_replace 返回它后面的值,在这种情况下是正则表达式中的第 4 个匹配组(我我没有使用regex_extract 来获取值,因为它似乎返回多个值并且我得到错误-686)。

您可以将此方法扩展到其余列,并查看正则表达式是否足以解析描述列。

【讨论】:

  • Henry Spencer 的正则表达式库是救命稻草。忘了说description栏的内容都是大写字母,还可能包含标点符号,所以regex搜索类型可以是默认的,1(Extended POSIX)。我还将用 [0-9A-Z] 和 :punct: 替换“alnum”元字符,因为 alnum 还包括小写字母。它可能会产生性能差异,因为模式匹配有大约 830K 描述。感谢您提供 lib 扩展和示例。
  • Marques:我忽略这篇文章太久了,所以自动将一半的赏金奖励给了错误的答案。我明确指定使用 Informix 解决目标,除非不可能。您的回答解决了目标。对于没有及时向您发放赏金,我深表歉意。
【解决方案2】:

如果您正在寻找 SQL Server 选项并打开维护序列的拆分/解析函数

示例

Select A.Type
      ,A.Description
      ,C.*
 From  YourTable A
 Cross Apply (values ( replace(
                       replace(
                       replace(
                       replace(A.Description,',',' ')
                       ,'  ',' ')
                       ,'Model ','Model')
                       ,'S/N ','S/N')
                     ) 
             )B(CleanString)

 Cross Apply (
                Select Quantity  = IsNull(left(max(case when RetSeq=1 then RetVal end),NullIf(patindex('%[^0-9]%',max(case when RetSeq=1 then RetVal end)) -1,0)),1)
                      ,Name      = substring(max(case when RetSeq=1 then RetVal end),patindex('%[^0-9]%',max(case when RetSeq=1 then RetVal end)),charindex(' ',max(case when RetSeq=1 then RetVal end)+' ')-1)
                      ,Weight    = IIF(A.Type=2,null,try_convert(decimal(5,1),replace(max(case when RetVal like '%PW' then RetVal end),'PW','')))
                      ,Purity    = try_convert(smallint    ,replace(max(case when RetVal like '%K'  then RetVal end),'K',''))
                      ,Brand     = IIF(A.Type=1,null,max(case when RetSeq=2 then RetVal end))
                      ,Model     = replace(max(case when RetVal Like 'Model[0-9,A-Z]%' then RetVal end),'Model','')
                      ,SerialNum = replace(max(case when RetVal Like 'S/N[0-9,A-Z]%' then RetVal end),'S/N','')
                 From [dbo].[tvf-Str-Parse](CleanString,' ') B1
             ) C

退货

有兴趣的 TVF

CREATE FUNCTION [dbo].[tvf-Str-Parse] (@String varchar(max),@Delimiter varchar(10))
Returns Table 
As
Return (  
    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 replace(@String,@Delimiter,'§§Split§§') as [*] For XML Path('')),'§§Split§§','</x><x>')+'</x>' as xml).query('.')) as A 
    Cross Apply x.nodes('x') AS B(i)
);

编辑 - 如果您不想或不能使用 TVF

dbFiddle

Select A.Type
      ,A.Description
      ,C.*
 From  YourTable A
 Cross Apply (values ( replace(
                       replace(
                       replace(
                       replace(A.Description,',',' ')
                       ,'  ',' ')
                       ,'Model ','Model')
                       ,'S/N ','S/N')
                     ) 
             )B(CleanString)

 Cross Apply (
                Select Quantity  = IsNull(left(max(case when RetSeq=1 then RetVal end),NullIf(patindex('%[^0-9]%',max(case when RetSeq=1 then RetVal end)) -1,0)),1)
                      ,Name      = substring(max(case when RetSeq=1 then RetVal end),patindex('%[^0-9]%',max(case when RetSeq=1 then RetVal end)),charindex(' ',max(case when RetSeq=1 then RetVal end)+' ')-1)
                      ,Weight    = IIF(A.Type=2,null,try_convert(decimal(5,1),replace(max(case when RetVal like '%PW' then RetVal end),'PW','')))
                      ,Purity    = try_convert(smallint    ,replace(max(case when RetVal like '%K'  then RetVal end),'K',''))
                      ,Brand     = IIF(A.Type=1,null,max(case when RetSeq=2 then RetVal end))
                      ,Model     = replace(max(case when RetVal Like 'Model[0-9,A-Z]%' then RetVal end),'Model','')
                      ,SerialNum = replace(max(case when RetVal Like 'S/N[0-9,A-Z]%' then RetVal end),'S/N','')
                 From  (
                        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 replace(CleanString,' ','§§Split§§') as [*] For XML Path('')),'§§Split§§','</x><x>')+'</x>' as xml).query('.')) as A 
                        Cross Apply x.nodes('x') AS B(i)
                       ) B1
             ) C

【讨论】:

  • Luis Marques 对使用 Informix 正则表达式函数的回答是有效的,否则我会求助于使用 SSIS。
猜你喜欢
  • 1970-01-01
  • 2018-11-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-05-24
  • 1970-01-01
相关资源
最近更新 更多