【问题标题】:Split string based on count of comma separated in SQL Server根据在 SQL Server 中分隔的逗号计数拆分字符串
【发布时间】:2017-10-09 16:00:23
【问题描述】:

需要 SQL Server 查询。下面是例子

1) 19003 IH-10 West, 圣安东尼奥, 78257, TX, 美国

在第一个示例中,字符串中有四个逗号。所以输出应该是

19003 IH-10 West

2) Chevron Pipe Line Company, 4800 Fournace Place, Bellaire, 77401-2324, TX, 美国

在第二个示例中,字符串中有五个逗号。所以输出应该是

Chevron Pipe Line Company, 4800 Fournace Place

如果有四个逗号,则输出应该是主字符串的子字符串,直到第一个逗号,如果有五个逗号,那么输出应该是主字符串的子字符串,直到第二个逗号。

在输入中,只有 4 或 5 个逗号。

我可以通过使用 UDF 来实现这一要求。但我需要直接查询。

【问题讨论】:

    标签: sql-server sql-server-2008 tsql split


    【解决方案1】:

    不确定这将如何执行。我强烈推荐一种更好的方法来拆分地址并存储为单独的字段,有许多可用的地址解析解决方案。

    如果这不是一个选项,以下方法会起作用,但可能需要根据性能进行更改。

    Drop Table #Temp
    Create Table #Temp (Field Varchar(8000))
    Insert #Temp Values ('19003 IH-10 West, San Antonio, 78257, TX, United States')
    Insert #Temp Values ('Chevron Pipe Line Company, 4800 Fournace Place, Bellaire, 77401-2324, TX, United States')
    
    ;With cteFindCommas As
    (
    Select  CharIndex(',', Field, CharIndex(',', Field, CharIndex(',', Field, CharIndex(',', Field, CharIndex(',', Field)+1)+1)+1)+1) FifthCommaPos,
            CharIndex(',', Field, CharIndex(',', Field, CharIndex(',', Field, CharIndex(',', Field)+1)+1)+1) FourthCommaPos,
            CharIndex(',', Field) FirstCommaPos,
            CharIndex(',', Field, CharIndex(',', Field)+1) SecondCommaPos,
            *
        From #Temp
    )
    Select  Case When FifthCommaPos > 0 Then Substring(Field, 0, SecondCommaPos)
                 Else Substring(Field, 0, FirstCommaPos) End String,            
            * 
        From cteFindCommas
    

    【讨论】:

    • 这是一个非常好的解决方案。但是,您不需要只计算字符串中每个逗号的位置。您只需要第 2 个位置以及分隔符的数量。注意我的解决方案。
    • 逗号计数非常简洁。我没有时间测试,但我想知道使用替换与多个字符索引是否会产生性能差异,如果在这种情况下替换速度较慢,那么它可能会以另一种方式倾斜。
    • 我添加了一个性能测试。看起来你放在一起的速度快了约 15%。太棒了。
    【解决方案2】:

    我会这样做(注意我的代码中的 cmets):

    -- sample data
    declare @sometable table (someid int identity, someAddress varchar(200));
    insert @sometable (someAddress) 
    values ('19003 IH-10 West, San Antonio, 78257, TX, United States'),
           ('Chevron Pipe Line Company, 4800 Fournace Place, Bellaire, 77401-2324, TX, US');
    
    -- solution
    select 
      someid,
      someAddress,
      newAddress = case commaCount when 4 then C4 when 5 then C5 end -- conditions for 4 or 5 commas only
    from
    (
      select 
        someid,
        someAddress,
        commaCount = len(v.a) - len(replace(v.a,',','')), -- calculate the number of commas
        C4 = substring(v.a, 1, charindex(',', v.a)-1), -- grab everything up to the 1st comma
        C5 = substring(v.a, 1, charindex(',', v.a, charindex(',', v.a)+1)-1) -- everything up to 2nd comma
      from @sometable t
        cross apply (values (t.someAddress)) v(a)   -- how I avoid repeated references to t.SomeAddress
    ) formatStrings;
    

    结果

    someid  someAddress                                              newAddress
    ------- -------------------------------------------------------  ----------------------------------------------
    1       19003 IH-10 West, San Antonio, 78257, TX, United States  19003 IH-10 West
    2       Chevron Pipe Line Company, 4800 Fournace Place...        Chevron Pipe Line Company, 4800 Fournace Place
    

    更新 - 添加性能测试

    下面是我整理的一个测试工具;注意我的cmets。

    样本数据

    -- #1: Create Sample Data
    ------------------------------------------------------------------------------------------
    declare @rows int = 100000;
    if object_id('tempdb..#base') is not null drop table #base;
    if object_id('tempdb..#address') is not null drop table #address;
    
    -- grabbed 50 random addresses from here: https://www.randomlists.com/random-addresses
    -- manually added the first comma (between street address and city)
    select 
      someId      = identity(int,1,1),
      someAddress = stuff(addr,patindex('%'+replicate('[0-9]',5)+'%',addr),0,',')
    into #base from (values
    ('886 Hartford Ave., Gwynn Oak, MD 21207'),
    ('322 Wakehurst St., Deerfield, IL 60015'),
    ('62 South Oak Valley St., Lorain, OH 44052'),
    ('72 53rd St., New Bern, NC 28560'),
    ('569 Swanson Ave., Snellville, GA 30039'),
    ('15 Walnut St., New Bern, NC 28560'),
    ('94 Kingston St., North Royalton, OH 44133'),
    ('77 Rock Creek St., Ocean Springs, MS 39564'),
    ('688 S. Bellevue St., Mableton, GA 30126'),
    ('61 Queen Rd., Potomac, MD 20854'),
    ('72 Jockey Hollow Drive, Elgin, IL 60120'),
    ('777 School St., Clarksville, TN 37040'),
    ('50 North 1st Street, Mount Prospect, IL 60056'),
    ('8004 Valley Drive, Long Beach, NY 11561'),
    ('8569 Franklin Court, Lakeland, FL 33801'),
    ('837 Buckingham St., Newnan, GA 30263'),
    ('46 Birch Hill St., Helena, MT 59601'),
    ('617 E. Brookside Drive, Jersey City, NJ 07302'),
    ('8133 Valley View St., Clearwater, FL 33756'),
    ('42 South Ave., Greensburg, PA 15601'),
    ('8782 Oak Meadow St., Helotes, TX 78023'),
    ('35 Valley Farms Ave., Racine, WI 53402'),
    ('7613 Cobblestone Road, Orlando, FL 32806'),
    ('27 Broad Lane, Kaukauna, WI 54130'),
    ('9213 Corona Dr., Rockville, MD 20850'),
    ('7390 W. Bay Court, Mason, OH 45040'),
    ('561 W. St Louis Ave., Silver Spring, MD 20901'),
    ('7447 Evergreen Ave., Rocky Mount, NC 27804'),
    ('24 NW. Pilgrim Road, Sun Prairie, WI 53590'),
    ('846 E. Hall St., Lake Villa, IL 60046'),
    ('919 Green Hill Street, New Orleans, LA 70115'),
    ('532 Newbridge Lane, Hanover, PA 17331'),
    ('3 E. Rose Rd., Waukegan, IL 60085'),
    ('15 South Euclid Rd., Springfield Gardens, NY 11413'),
    ('453 Mulberry Ave., Parlin, NJ 08859'),
    ('8128 New Saddle Court, Fullerton, CA 92831'),
    ('9143 Lafayette Ave., Jackson Heights, NY 11372'),
    ('481 Edgewater St., Dacula, GA 30019'),
    ('8243 Hilltop St., Camp Hill, PA 17011'),
    ('70 Lookout St., Marlborough, MA 01752'),
    ('9370 South Shirley Drive, King Of Prussia, PA 19406'),
    ('8071 Plymouth Road, Huntersville, NC 28078'),
    ('593 Charles St., Buckeye, AZ 85326'),
    ('9092 Atlantic Ave., Yuma, AZ 85365'),
    ('81 Longbranch Road, Ontario, CA 91762'),
    ('868 Garfield St., New Lenox, IL 60451'),
    ('8333 Kirkland Rd., Plainview, NY 11803'),
    ('9714 Prospect Ave., Monroe Township, NJ 08831'),
    ('7 N. Atlantic Ave., Reidsville, NC 27320'),
    ('9283 Cherry Lane, Waukegan, IL 60085')) a(addr);
    
    -- index to support large sample data requests:
    create unique clustered index uq_cl_base on #base(someid);
    
    -- Create randomized addresses: up to 6,250,000 dummy rows (50^4)
    with r(x) as (select top(@rows/50) 1 from #base a, #base b, #base c, #base d),
    base(Field) as
    (
      select Field  =
        max(
          case itemNumber when 1 then substring(item,charindex(' ',item)+1, len(item)+1) end+
            case abs(checksum(newid())%10)
            when 0 then ', Unit '+ cast(abs(checksum(newid())%100)+1 as varchar(3))
            when 1 then ', Suite '+ cast(abs(checksum(newid())%100)+1 as varchar(3))
            when 2 then ', Penthouse' else '' end)+','+
        max(case ItemNumber when 2 then item end)+','+
        max(case ItemNumber when 3 then left(item,3)+', ' end)+
        max(case ItemNumber when 4 then item end)+', United States'
      from #base t
      cross apply dbo.DelimitedSplit8K(t.someAddress,',')
      group by t.someId
    )
    select addressId = identity(int,1,1),
           field     =
             left(cast(abs(checksum(newid())%10000)+1 as varchar(5)), 
               case checksum(newid())%3 when 4 then 4 when 1 then 1 else 3 end)+' '+b.Field
    into #address
    from base b
    cross join r
    order by newid();
    go
    

    性能测试

    set nocount on;
    print 'solution 1'+char(10)+replicate('-',50);
    go
    declare @st datetime = getdate(), @field varchar(100);
      ;With cteFindCommas As
      (
      Select  CharIndex(',', Field, CharIndex(',', Field, CharIndex(',', Field, CharIndex(',', Field, CharIndex(',', Field)+1)+1)+1)+1) FifthCommaPos,
              CharIndex(',', Field, CharIndex(',', Field, CharIndex(',', Field, CharIndex(',', Field)+1)+1)+1) FourthCommaPos,
              CharIndex(',', Field) FirstCommaPos,
              CharIndex(',', Field, CharIndex(',', Field)+1) SecondCommaPos, *
      From #Address
      )
      select @field = case when FifthCommaPos > 0 Then Substring(Field, 0, SecondCommaPos)
                      else Substring(Field, 0, FirstCommaPos) end
      From cteFindCommas;
    print datediff(ms,@st,getdate());
    go 5
    
    print 'solution 2'+char(10)+replicate('-',50);
    go
    declare @st datetime = getdate(), @field varchar(100);
      select @field = case commaCount when 4 then C4 when 5 then C5 end -- conditions for 4 or 5 commas only
      from
      (
        select
          addressId,
          field,
          commaCount = len(v.a) - len(replace(v.a,',','')), -- calculate the number of commas
          C4 = substring(v.a, 1, charindex(',', v.a)-1), -- grab everything up to the 1st comma
          C5 = substring(v.a, 1, charindex(',', v.a, charindex(',', v.a)+1)-1) -- everything up to 2nd comma
        from #address t cross apply (values (t.field)) v(a)
      ) formatStrings;
    print datediff(ms,@st,getdate());
    go 5
    

    结果(100,000 行测试)

    solution 1
    --------------------------------------------------
    Beginning execution loop
    133
    133
    130
    126
    126
    Batch execution completed 5 times.
    
    solution 2
    --------------------------------------------------
    Beginning execution loop
    156
    160
    156
    157
    153
    Batch execution completed 5 times.
    

    看起来 Joe C 的解决方案(解决方案 #1)更快。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2018-12-21
      • 2015-03-07
      • 1970-01-01
      • 1970-01-01
      • 2014-01-03
      • 2023-01-03
      • 1970-01-01
      相关资源
      最近更新 更多