【问题标题】:How to use Substring to pull multiple items from a field如何使用子字符串从字段中提取多个项目
【发布时间】:2018-01-20 08:30:57
【问题描述】:

第一篇文章 - 我试图从一个字段中提取十个不同的信息。让我从这不是我的桌子开始,这只是我被赋予的工作。这是一个 varchar max 字段。

'3350|@|1234567|~|3351|@|8/1/2017|~|3352|@|Acme|~|3353|~|10000.00|~|3354|@||~|3355|@||~3356|@|Yes|~|3357|@|Doe,John|~|3358|@|CA|~|3359|@|5551212'

我知道以 33 开头的数字是告诉我该部分中包含哪些信息的键。 3350 的发票为 #1234567。 3351 的发票日期为 2017 年 8 月 1 日。等 3354 和 3355 为空。键是不变的,并且对于表中的每条记录都是相同的。

我需要从 3350|@| 之间提取数据和 |~|3351 获取我的发票# 和 3351|@| 之间和 |~|3352 来获取我的日期等,但我正在努力解决这个问题。任何帮助将不胜感激,对我的第一篇文章的任何批评都将被建设性地接受。

【问题讨论】:

  • 您运行的是什么版本的 SQL?我认为在~ 上拆分字符串可能是最简单的,从而产生一个包含每个数据块的表。从那里,您可以很容易地对每个单独的值进行子串化。如果您运行的是 SQL 2016+,则有一个本机字符串拆分功能。否则,你必须自己动手。
  • 您是否只从单个记录的这一字段中提取数据?还是您必须跨多行执行此操作?
  • 我建议您从获取字符串拆分器例程开始(或者,如果您使用的是 SQL Server 2016 或更高版本,请使用内置的 SPLIT_STRING() 函数)。在|~| 或只是波浪号上拆分字符串。然后你就有了多个片段。在片段上使用CHARINDEX()LEFT()/RIGHT()(或SUBSTRING()),很容易得到你要找的信息
  • 请注意,如果将字符串拆分为波浪号,则需要从单个数据片段中删除任何前导或尾随管道字符,以便于处理。例如第一项是3350|@|1234567|(没有前导管道),最后一项是'|3359|@|5551212'(没有尾随管道),中间的所有项目都有前导管道和尾随管道。在|~| 上拆分消除了前导和尾随管道。
  • 我在 SQL2008 上运行,这将跨多行完成。我将研究字符串拆分。感谢大家的快速回复。

标签: sql-server substring


【解决方案1】:

@YourTable 只是一个用于演示/插图的表变量

对于行 - 示例

Declare @YourTable table (ID int,SomeCol varchar(max))
Insert Into @YourTable values
(1,'3350|@|1234567|~|3351|@|8/1/2017|~|3352|@|Acme|~|3353|@|10000.00|~|3354|@||~|3355|@||~|3356|@|Yes|~|3357|@|Doe,John|~|3358|@|CA|~|3359|@|5551212')


Select A.ID
      ,Item  = left(RetVal,charindex('|@|',RetVal+'|@|')-1)
      ,Value = right(RetVal,len(RetVal)-charindex('|@|',RetVal+'|@|')-2)
 From  @YourTable A
 Cross Apply [dbo].[udf-Str-Parse](A.SomeCol,'|~|') B

退货

对于列 - 示例

Declare @YourTable table (ID int,SomeCol varchar(max))
Insert Into @YourTable values
(1,'3350|@|1234567|~|3351|@|8/1/2017|~|3352|@|Acme|~|3353|@|10000.00|~|3354|@||~|3355|@||~|3356|@|Yes|~|3357|@|Doe,John|~|3358|@|CA|~|3359|@|5551212')

Select *
 From (
        Select A.ID
              ,Item  = left(RetVal,charindex('|@|',RetVal+'|@|')-1)
              ,Value = right(RetVal,len(RetVal)-charindex('|@|',RetVal+'|@|')-2)
         From  @YourTable A
         Cross Apply [dbo].[udf-Str-Parse](A.SomeCol,'|~|') B
       ) A
 Pivot (max([Value]) For [Item] in ([3350],[3351],[3352],[3353],[3354],[3355],[3356],[3357],[3358],[3359]) ) p

退货

有兴趣的 UDF

CREATE FUNCTION [dbo].[udf-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)
);
--Thanks Shnugo for making this XML safe
--Select * from [dbo].[udf-Str-Parse]('Dog,Cat,House,Car',',')
--Select * from [dbo].[udf-Str-Parse]('John Cappelletti was here',' ')

【讨论】:

  • 成功了,这是我见过的最美丽的东西。非常感谢!
  • @JDN469 乐于助人,
【解决方案2】:

您可以尝试如下基于计数的拆分器

declare @t table ( id int, col nvarchar(max));
insert into @t values
(1, '3350|@|1234567|~|3351|@|8/1/2017|~|3352|@|Acme|~|3353|~|10000.00|~|3354|@||~|3355|@||~3356|@|Yes|~|3357|@|Doe,John|~|3358|@|CA|~|3359|@|5551212')
,(2, '3350|@|123334567|~|3351|@|8/2/2017|~|3352|@|Acme|~|3353|~|10000.00|~|3354|@||~|3355|@||~3356|@|Yes|~|3357|@|Doe,John|~|3358|@|CA|~|3359|@|5551212');

select 
    id,
    case 
    when split_values like '3350|@|%' then 'id' 
    when split_values like '3351|@|%' then 'date'
    end as fieldname,
    SUBSTRING(split_values,8,LEN(split_values)-7) as value
from 
(
    select 
        --t.col as col,
        row_number() over (partition by t.col order by t1.N asc) as row_num,
        t.id,
        SUBSTRING( t.col, t1.N, ISNULL(NULLIF(CHARINDEX('|~|',t.col,t1.N),0)-t1.N,8000)) as split_values
    from @t t 
        join
        (
            select 
                t.col,
                1 as N 
            from @t t  
                UNION ALL
            select 
                t.col,
                t1.N + 3 as N
            from @t t 
                join
                (
                 select 
                    top 8000
                        row_number() over(order by (select NULL)) as N 
                 from 
                    sys.objects s1 
                        cross join 
                   sys.objects s2 
                ) t1 
            on SUBSTRING(t.col,t1.N,3) = '|~|'
         ) t1
         on t1.col=t.col
)a
where 
    split_values like '3350|@|%' or 
    split_values like '3351|@|%'

Live demo

【讨论】:

    猜你喜欢
    • 2021-01-09
    • 1970-01-01
    • 2011-08-02
    • 1970-01-01
    • 1970-01-01
    • 2019-02-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多