【问题标题】:How to extract pattern from string in SQL Server?如何从 SQL Server 中的字符串中提取模式?
【发布时间】:2022-01-12 18:57:48
【问题描述】:

我有一个存储在 SQL Server 数据库中的主机信息表,该表有一个文本列,存储一个格式类似于 Ansible 库存的字符串。有关文本列中的示例项目,请参阅下面的文本。

host-001.servers.company.com desc='Production Web Cache' env='Prod' patch_round='Beta' dc='Main' rhel_v='7.6' primary='admin@company.com' secondary='manager@company.com'

我需要从文本列中提取某些属性,例如提取desc='Production Web Cache',并获取其值Production Web Cache。我想在 SQL 查询中使用正则表达式,希望得到一些指针。

或者,如果您知道实现此目的的另一种方法,我也会非常感谢您的提示。如果您需要更多说明,请告诉我。

【问题讨论】:

  • T-SQL 是一种糟糕的语言选择,最好使用具有良好字符串操作工具的语言。
  • 您的任何键值对是否可以包含字符='?如果是这样,请不要在 SQL 中执行此操作。
  • 尝试通过String_Split() 运行它以查看它的可行性...撤销我的建议并同意Larnu。 T-SQL 和内置函数不适合这项工作。
  • 您可能可以使用字符串拆分器(不是STRING_SPLIT)@JNevill 来执行此操作,但它需要在几个假设下运行:1. 域出现在开头,后跟一个空格。 2. 值不能包含=。 3. 所有值都用单引号括起来('),所有名称都不是。然而,它真的很难看。
  • 根据问题指南,请展示您的尝试并告诉我们您发现了什么(在本网站或其他地方)以及为什么它不能满足您的需求。

标签: sql sql-server tsql


【解决方案1】:

与约翰非常相似的方法。我首先使用 JSON 拆分器将数据分成几部分,尽管这会将值放在 next 标头中。我使用CHARINDEX 找到值的end,然后使用LEFT/STUFF 将这两个值放入各自的部分。然后我使用LAG 来获取 actual 标头,而不是下一个值标头。最后,我删除了周围的引号。

这是基于我的comment 的假设:

  1. 域在开头,后跟一个空格。
  2. 值不能包含 =。
  3. 所有值都用单引号 (') 括起来,而所有名称都不是

请注意,我没有在结果中包含域,但 SQL 应该足以让您知道如何添加它:

DECLARE @YourString nvarchar(4000) = N'host-001.servers.company.com desc=''Production Web Cache'' env=''Prod'' patch_round=''Beta'' dc=''Main'' rhel_v=''7.6'' primary=''admin@company.com'' secondary=''manager@company.com''';

WITH CTE AS(
    SELECT *,
           LAG(ContentHeader) OVER (ORDER BY [Key]) AS ActualHeader
    FROM (VALUES(@YourString))V(YourString)
         CROSS APPLY(VALUES(STUFF(@YourString, 1, CHARINDEX(N' ',@YourString),N'')))S(NewString)
         CROSS APPLY OPENJSON('["' + REPLACE(NewString,'=','","') + '"]')OJ
         CROSS APPLY(VALUES(NULLIF(CHARINDEX('''',OJ.[value],2),0)))CI(I)
         CROSS APPLY(VALUES(LEFT(OJ.[Value],CI.I),STUFF(OJ.[Value],1,ISNULL(CI.I+1,0),'')))P(ContentValue,ContentHeader))
SELECT ActualHeader AS Header,
       REPLACE([ContentValue],'''','') AS [Value]
FROM CTE
WHERE ActualHeader IS NOT NULL;

db<>fiddle

【讨论】:

  • 那太美了,我讨厌它。太棒了。
  • 整洁。看起来 XML 也是一种选择
【解决方案2】:

有点难看,但使用了一点 JSON(对序列进行 GTD)和窗口函数lead() over()

示例

Declare @YourTable table (ID int,SomeCol varchar(max))
 Insert Into @YourTable values
 (1,'host-001.servers.company.com desc=''Production Web Cache'' env=''Prod'' patch_round=''Beta'' dc=''Main'' rhel_v=''7.6'' primary=''admin@company.com'' secondary=''manager@company.com''')

  Select A.ID
       ,Host = left(SomeCol,charindex(' ',SomeCol+' '))
       ,B.*
  From  @YourTable A
  Cross Apply (

                Select Item =  ltrim(rtrim(right(Value,charindex(' ',reverse(Value)+' '))))
                      ,Value = ltrim(rtrim(replace(
                               IsNull(lead( left(Value,nullif(len(Value)+1-charindex(' ',reverse(Value)+' '),0)),1) over (order by [Key])
                                     ,lead(right(Value,charindex(' ',reverse(Value)+' ')),1) over (order by [key])
                                     ),'''','')))
                 From  OpenJSON( '["'+replace(string_escape(SomeCol,'json'),'=','","')+'"]' )

              ) B
 Where B.Value is not null
 

结果

ID  Host                             Item           Value
1   host-001.servers.company.com     desc           Production Web Cache 
1   host-001.servers.company.com     env            Prod 
1   host-001.servers.company.com     patch_round    Beta 
1   host-001.servers.company.com     dc             Main 
1   host-001.servers.company.com     rhel_v         7.6 
1   host-001.servers.company.com     primary        admin@company.com 
1   host-001.servers.company.com     secondary      manager@company.com

编辑 - 注入“HOST="

Declare @YourTable table (ID int,SomeCol varchar(max))
 Insert Into @YourTable values
 (1,'host-001.servers.company.com desc=''Production Web Cache'' env=''Prod'' patch_round=''Beta'' dc=''Main'' rhel_v=''7.6'' primary=''admin@company.com'' secondary=''manager@company.com''')

 
 Select A.ID
       ,B.*
  From  @YourTable A
  Cross Apply (
                Select Item =  ltrim(rtrim(right(Value,charindex(' ',reverse(Value)+' '))))
                      ,Value = ltrim(rtrim(replace(
                               IsNull(lead(left(Value,nullif(len(Value)+1-charindex(' ',reverse(Value)+' '),0)),1) over (order by [Key])
                                     ,lead(right(Value,charindex(' ',reverse(Value)+' ')),1) over (order by [key])
                                     ),'''','')))
                 From  OpenJSON( '["'+replace(string_escape('host='+SomeCol,'json'),'=','","')+'"]' )

              ) B
 Where B.Value is not null

结果

ID  Item        Value
1   host        host-001.servers.company.com
1   desc        Production Web Cache
1   env         Prod
1   patch_round Beta
1   dc          Main
1   rhel_v      7.6
1   primary     admin@company.com
1   secondary   manager@company.com

【讨论】:

  • 与我所经历的路线非常相似,我会尽快发布。
  • @Larnu 只是我的第一个想法。期待您的到来。
  • 你去吧,约翰。
【解决方案3】:

理想情况下,您的数据应存储在单独的列中。但如果你要将它塞进一列,至少要使用可识别的格式,例如 XML 或 JSON。

鉴于单引号是有效的 XML 属性分隔符,您可以将其转换为 XML 并使用 XQuery。

不漂亮,因为主机名值没有分隔

SELECT
  v3.n.value('@host','varchar(255)'),
  v3.n.value('@desc','varchar(1000)')
FROM t
CROSS APPLY(VALUES(
    CHARINDEX(' ', t.value)
)) v1(space)
CROSS APPLY(VALUES(
    CAST(
        '<x host=''' +
        CASE WHEN v1.space = 0
            THEN t.value
            ELSE LEFT(t.value, v1.space - 1) + '''' + SUBSTRING(t.value, v1.space, LEN(t.value))
            END +
        ' />'
      AS xml)
)) v2(xml)
CROSS APPLY v2.xml.nodes('x') v3(n);

db<>fiddle

【讨论】:

    猜你喜欢
    • 2019-08-06
    • 1970-01-01
    • 2021-09-08
    • 2021-12-08
    • 2011-10-08
    • 2021-11-22
    • 2022-07-07
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多