【问题标题】:How to split a field in Microsoft sql server into multiple columns如何将Microsoft sql server中的字段拆分为多列
【发布时间】:2018-02-01 11:58:36
【问题描述】:

我有一个表格,其中有一列包含以下条目-

Drug
Sertraline 100mg tablets
Phenobarbitol 20mg capsules 

我希望将此列拆分为四个-

Drugname    Strength   Units   Form
Sertraline  100        mg      tablets

有人可以指导我执行此操作吗?

【问题讨论】:

  • 你有没有尝试过?这个网上有很多资源
  • 不要在开头写这样的条目。这在所有数据库中都是一个坏主意。它甚至打破了第一范式。如果您希望能够查询这些值,请将它们存储在单独的表中
  • 是的。我尝试了 LEFT 和 SUBSTRING。但我并没有得到我想要的。
  • 您可以在 SQL Server 2016 中尝试 STRING_SPLIT,但在插入数据时清理数据要容易得多。 SQL 不是字符串操作的最佳语言,而且绝对不适合解析。
  • 我猜你有很多数据,而且每种情况下的格式都可能不同。因此,我们可以为该数据编写一些东西,但它不适用于其他数据。

标签: sql-server split


【解决方案1】:

用一点 XML 和 CROSS APPLY

模式清晰,易于根据需要扩展或收缩

示例

Select A.* 
      ,B.*
 From  YourTable A
 Cross Apply (
                Select Pos1 = ltrim(rtrim(xDim.value('/x[1]','varchar(max)')))
                      ,Pos2 = ltrim(rtrim(xDim.value('/x[2]','varchar(max)')))
                      ,Pos3 = ltrim(rtrim(xDim.value('/x[3]','varchar(max)')))
                      ,Pos4 = ltrim(rtrim(xDim.value('/x[4]','varchar(max)')))
                      ,Pos5 = ltrim(rtrim(xDim.value('/x[5]','varchar(max)')))
                From  (Select Cast('<x>' + replace((Select replace(A.[Drug],' ','§§Split§§') as [*] For XML Path('')),'§§Split§§','</x><x>')+'</x>' as xml) as xDim) as B1
             ) B

退货

Pos1            Pos2    Pos3        Pos4    Pos5
Sertraline      100mg   tablets     NULL    NULL
Phenobarbitol   20mg    capsules    NULL    NULL

【讨论】:

    【解决方案2】:

    还有一个建议:

    第一个 CTE 将您的 CSV 字符串转换为 XML,从而可以分别处理每个部分。
    第二个 CTE 检索三个部分。
    最后的SELECT使用了一些字符串方法来分隔强度和单位。

    DECLARE @tbl TABLE(Drug VARCHAR(100));
    INSERT INTO @tbl VALUES('Sertraline 100mg tablets')
                          ,('Phenobarbitol 20mg capsules');
    WITH Splitted AS
    (
        SELECT CAST('<x>' + REPLACE((SELECT Drug AS [*] FOR XML PATH('')),' ','</x><x>') + '</x>' AS XML) AS Casted
        FROM @tbl 
    )
    ,Parts AS
    (
        SELECT Casted.value('/x[1]/text()[1]','nvarchar(100)') AS Drugname
              ,Casted.value('/x[2]/text()[1]','nvarchar(100)') AS CombinedStrenthUnit
              ,Casted.value('/x[3]/text()[1]','nvarchar(100)') AS Form
        FROM Splitted
    )
    SELECT *
          ,LEFT(CombinedStrenthUnit,PATINDEX('%[a-zA-Z]%',CombinedStrenthUnit)-1) AS Strength 
          ,SUBSTRING(CombinedStrenthUnit,PATINDEX('%[a-zA-Z]%',CombinedStrenthUnit),1000) AS Unit
    FROM Parts;
    

    结果

    Drugname        S&U     Form        Strength    Unit
    Sertraline      100mg   tablets     100         mg
    Phenobarbitol   20mg    capsules    20          mg
    

    【讨论】:

      【解决方案3】:

      我使用user-defined split function 将文本拆分为由空格字符分隔的 3 部分,如下所示

      当然,如果你有 SQL Server 2016 或更高版本,那么你也可以使用STRING_SPLIT SQL 函数

      with rawdata as (
          select rn = ROW_NUMBER() over (order by txt), * from drugs
      ), cte as (
      select
          rn,
          d.txt,
          s.id,
          s.val
      from rawdata d
      cross apply dbo.Split(rtrim(ltrim(d.txt)),' ') s
      )
      select * from cte
      

      请注意,需要 Row_Number rn 列来标识以下脚本中的每一行。如果源表中有 PK 字段,可以直接使用那些 Primary Key 字段,而不是使用 Row_Number 函数创建的 rn 字段

      为了拆分第二列(强度和单位),我再次更喜欢使用自定义 SQL 函数; ClearNumericCharactersClearNonNumericCharacters 当然你可以使用内联函数或者 RegExp 来代替 UDFs

      这是最终的 SQL CTE 表达式

      with rawdata as (
          select rn = ROW_NUMBER() over (order by txt), * from drugs
      ), cte as (
      select
          rn,
          d.txt,
          s.id,
          s.val
      from rawdata d
      cross apply dbo.Split(rtrim(ltrim(d.txt)),' ') s
      ), cte2 as (
      select
      rn,
      case when id = 1 then val end as Drugname,
      case when id = 2 then dbo.ClearNonNumericCharacters(val) end as Strength,
      case when id = 2 then dbo.ClearNumericCharacters(val) end as Units,
      case when id = 3 then val end as Form
      from cte
      )
      select
          max(Drugname) Drugname,
          max(Strength) Strength,
          max(Units) Units,
          max(Form) Form
      from cte2
      group by rn
      

      输出是

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2023-04-02
        • 1970-01-01
        • 1970-01-01
        • 2017-01-16
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多