【问题标题】:Parse certain string values surrounded by brackets解析括号括起来的某些字符串值
【发布时间】:2022-01-18 07:30:49
【问题描述】:

我正在尝试提取由特殊字符(如括号)包围的某些字符串值,并使用提取的字符串值创建新列。例如,原始列如下所示。

         Column
Dec12 SKLEKF [[ABC]] <<JK>>
Jan13 KEJ [[FJ]] <<UHJ>>
FEB12 JKEJSKEJG [[GBC]] <<JUIDJ>>

我正在尝试使用括号内的字符串值创建两个新列。新数据表应如下所示。

Code     ID
ABC      JK
FJ       UHJ
GBC      JUIDJ

我尝试了以下代码,但它不起作用,因为括号之前和括号内的字符串值的长度因行而异。有什么方法可以解析括号括起来的字符串值,不管它们的长度如何?

select SUBSTRING(Column, 13, 3) AS Code
from table

谢谢。

【问题讨论】:

  • 使用charindexpatindex 来确定子字符串的开始和结束。有很多人这样做的例子。
  • 我投票赞成在 SQL Server 之外提取您想要的内容,然后导入一个干净的 CSV 文件。

标签: sql sql-server tsql


【解决方案1】:

我会怎么做:

-- DDL and sample data population, start
DECLARE @tbl TABLE (seq INT identity PRIMARY KEY, tokens VARCHAR(100));
INSERT INTO @tbl (tokens) VALUES
('Dec12 SKLEKF [[ABC]] <<JK>>'),
('Jan13 KEJ [[FJ]] <<UHJ>>'),
('FEB12 JKEJSKEJG [[GBC]] <<JUIDJ>>');

-- DDL and sample data population, end    
SELECT 
  Seq    = t.seq,
  Tokens = t.tokens,
  [Code] = SUBSTRING(t.tokens,a1.Pos+2,a2.Pos-a1.Pos-2),
  [ID]   = SUBSTRING(t.tokens,b1.Pos+2,b2.Pos-b1.Pos-2)
FROM        @tbl                                       AS t
CROSS APPLY (VALUES(CHARINDEX('[[',t.tokens)))         AS a1(Pos)
CROSS APPLY (VALUES(CHARINDEX('<<',t.tokens)))         AS b1(Pos)
CROSS APPLY (VALUES(CHARINDEX(']',t.tokens,a1.Pos+2))) AS a2(Pos)
CROSS APPLY (VALUES(CHARINDEX('>',t.tokens,b1.Pos+2))) AS b2(Pos);

返回:

+-----+-----------------------------------+------+-------+
| seq |              tokens               | Code |  ID   |
+-----+-----------------------------------+------+-------+
|   1 | Dec12 SKLEKF [[ABC]] <<JK>>       | ABC  | JK    |
|   2 | Jan13 KEJ [[FJ]] <<UHJ>>          | FJ   | UHJ   |
|   3 | FEB12 JKEJSKEJG [[GBC]] <<JUIDJ>> | GBC  | JUIDJ |
+-----+-----------------------------------+------+-------+

注意简单的执行计划。没有比这更顺畅的了。

【讨论】:

    【解决方案2】:

    还有一个建议:

    SELECT A.casted.value('(/x/@code)[1]','nvarchar(100)') AS Code
          ,A.casted.value('(/x/@id)[1]','nvarchar(100)') AS I
          ,A.casted
    FROM YourTable t
    CROSS APPLY(VALUES(CAST(REPLACE(REPLACE(REPLACE(REPLACE(t.tokens,'[[','<x code="')
                                                                    ,']]','"/>')
                                                                    ,'<<','<x id="')
                                                                    ,'>>','"/>') AS xml))) A(casted)
    

    简而言之:

    • 由于您使用不同的“标记”字符,我们可以轻松地将您的字符串转换为有效的 XML,并进行一些替换
      结果Dec12 SKLEKF &lt;x code="ABC" /&gt;&lt;x id="JK" /&gt;
    • 现在可以很容易地使用.value() 直接从 XML 中获取属性值。

    更新

    如果您的代码可能包含 XML 中禁止使用的字符,您可以改用 APPLY

    CROSS APPLY(VALUES(CAST(REPLACE(REPLACE(REPLACE(REPLACE((SELECT t.tokens AS [*] FOR XML PATH(''))
                                                                    ,'[[','<x code="')
                                                                    ,']]','"/>')
                                                                    ,'&lt;&lt;','<x id="')
                                                                    ,'&gt;&gt;','"/>') AS xml))) A(casted);
    

    【讨论】:

      【解决方案3】:

      另一种使用 XML 和 XQuery 的方法。

      这个想法是对一串标记进行标记,并根据它们的位置获取单个标记:[last()][last()-1]

      SQL

      -- DDL and sample data population, start
      DECLARE @tbl TABLE (seq INT identity PRIMARY KEY, tokens VARCHAR(100));
      INSERT INTO @tbl (tokens) VALUES
      ('Dec12 SKLEKF [[ABC]] <<JK>>'),
      ('Jan13 KEJ [[FJ]] <<UHJ>>'),
      ('FEB12 JKEJSKEJG [[GBC]] <<JUIDJ>>');
      -- DDL and sample data population, end
      
      SELECT t.*
          , TRIM('[]' FROM c.value('(/root/r[last()-1]/text())[1]', 'VARCHAR(20)')) AS Code
          , TRIM('<>' FROM c.value('(/root/r[last()]/text())[1]', 'VARCHAR(20)')) AS ID
      FROM @tbl AS t
          CROSS APPLY (SELECT TRY_CAST('<root><r><![CDATA[' + 
            REPLACE(tokens, SPACE(1), ']]></r><r><![CDATA[') + 
            ']]></r></root>' AS XML)) AS t1(c);
      

      输出

      +-----+-----------------------------------+------+-------+
      | seq |              tokens               | Code |  ID   |
      +-----+-----------------------------------+------+-------+
      |   1 | Dec12 SKLEKF [[ABC]] <<JK>>       | ABC  | JK    |
      |   2 | Jan13 KEJ [[FJ]] <<UHJ>>          | FJ   | UHJ   |
      |   3 | FEB12 JKEJSKEJG [[GBC]] <<JUIDJ>> | GBC  | JUIDJ |
      +-----+-----------------------------------+------+-------+
      

      【讨论】:

      • 只要这些部分在预期的位置,它就可以工作。尽管给定的示例给人这种印象,但我更喜欢宽容的方法...
      【解决方案4】:

      使用SUBSTRING 操作:

      SELECT
          col,
          SUBSTRING(col, CHARINDEX('[[', col) + 2,
                    CHARINDEX(']]', col) - CHARINDEX('[[', col) - 2) AS Code,
          SUBSTRING(col, CHARINDEX('<<', col) + 2,
                    CHARINDEX('>>', col) - CHARINDEX('<<', col) - 2) AS ID
      FROM yourTable;
      

      【讨论】:

        猜你喜欢
        • 2021-11-18
        • 1970-01-01
        • 2014-05-20
        • 1970-01-01
        • 1970-01-01
        • 2021-09-21
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多