【问题标题】:Get index of two consecutive upper case characters获取两个连续大写字符的索引
【发布时间】:2013-12-23 16:14:39
【问题描述】:

我正在尝试将 city/state/zip 字段分为城市、州和邮政编码。通常我会使用charindex',' 来获取城市和州,并使用isnumericright() 来获取zip。

这适用于 zip,但我正在使用的数据中的大多数行现在都没有逗号 City ST Zip。有没有办法识别两个大写字符的索引?

如果没有,除了单独检查每个状态的 case 语句之外,有没有人有更好的主意?

编辑:我发现 PATINDEX/COLLATE 选项的工作相当间歇。请参阅下面的答案。

【问题讨论】:

  • 有关您编写的代码问题的问题必须在问题本身中描述具体问题 - 并包括重现问题的有效代码。
  • 你不能用PATINDEX('[A-Z][A-Z]', [column_to_search])
  • @FreshPrinceOfSO 他没有说他写的代码有问题,他说他不知道如何完成他想做的事情。我认为“通常我会这样做”表明他自己对此进行了一些思考,但没有包含此类代码,因为这显然不是解决方案,并且可能会误导问题。
  • @AaronLS 至少需要展示一个具有预期结果的样本。
  • @Pat 默认情况下,您可以强制排序但无法使其正常工作 - COLLATE SQL_Latin1_General_CP1_CS_AS

标签: sql sql-server collation string-parsing


【解决方案1】:

PATINDEX 应该适合你:

PATINDEX('% [A-Z][A-Z] %', A COLLATE Latin1_general_cs_as)

所以你的完整摘录会是这样的:

WITH CTE AS
(   SELECT  i = PATINDEX('% [A-Z][A-Z] %', A COLLATE Latin1_general_cs_as) + 1,
            A
    FROM    (VALUES 
                ('City ST Zip'),
                ('Another City ST Zip'),
                ('City, with comma ST Zip')
            ) t (A)
)
SELECT  City = LEFT(A, i - 2),
        State = SUBSTRING(A, i, 2),
        Zip = SUBSTRING(A, i + 3, LEN(A))
FROM    CTE;

Example on SQL Fiddle

【讨论】:

  • 字符范围(即[A-Z]),即使是区分大小写的排序规则,也不能像人们通常期望的那样工作。尽管在这种特殊情况下,_BIN2 排序规则可能会起作用。详情请看我的answer
【解决方案2】:

如果您在字符串末尾有邮政编码和州,那么这可能有效:

select right(address, 5) as zip,
       left(right(address, 8), 2) as state,
       left(address, len(address) - 9) as city

您可以从地址中删除逗号和双空格开始。

【讨论】:

  • 不幸的是,邮政编码不一致。州也不是,否则我会做这样的事情。不过感谢您的回答。
【解决方案3】:

PATINDEX 似乎间歇性工作的原因是您不能使用字符范围(即A-Z)来完成区分大小写的搜索,即使使用区分大小写的排序规则也是如此。问题是字符范围像排序一样工作,并且区分大小写的排序将大写字母与其对应的小写字母组合在一起,就像在字典中排序一样。范围排序实际上是:a,A,b,B,c,C,d,D等。或者,根据排序规则,它可能是:A、a、B、b、C、c、D、d 等(有 31 个排序规则首先对大写进行排序)。在区分大小写的排序规则中执行此操作时,仅将所有 A 条目分组在一起,与 a 条目分开,而在区分大小写的排序中,它们将混合在一起。 p>

但是,如果您单独指定每个字母(因此不使用范围),那么它将按预期工作:

PATINDEX(N'%[ABCDEFGHIJKLMNOPQRSTUVWXYZ][ABCDEFGHIJKLMNOPQRSTUVWXYZ]%',
     [CityStZip] COLLATE Latin1_General_100_CS_AS)

PATINDEXLIKE(两者都允许 [A-Z] 的单个字符类)以这种方式工作的原因是 [start-end] 语法不是正则表达式.由于支持这种语法,许多人声称PATINDEXLIKE 支持“有限”正则表达式,但事实并非如此。它只是与 RegEx 非常相似(并且令人困惑地相似)的语法,其中[A-Z] 通常包含任何小写匹配。

当然,如果您保证只搜索 A-Z 的美英字母,那么请使用二进制排序规则(即以 _BIN2 结尾的排序规则;不要使用以 _BIN 结尾的排序规则)自引入 SQL Server 2005 以来已弃用,我相信)应该可以工作。

PATINDEX(N'%[A-Z][A-Z]%', [CityStZip] COLLATE Latin1_General_100_BIN2)

有关区分大小写匹配的更多详细信息,尤其是关于包含 Unicode / NVARCHAR 数据的信息,请参阅我在 DBA.StackExchange 上的相关答案:

How to find values with multiple consecutive upper case characters

【讨论】:

    【解决方案4】:

    如果您有一个包含缩写列的状态表(您应该这样做),您可以执行以下操作:

    SELECT a.* FROM Addresses a
    INNER JOIN States s ON
    a.CityStateZip Like '% ' + s.UpperCaseAbbreviation + ' %' --space on either side of abbreviation
    

    你可以让它同时用于逗号和空格:

    SELECT a.* FROM Addresses a
    INNER JOIN States s ON
    Replace(a.CityStateZip, ',' , ' ') Like '% ' + s.UpperCaseAbbreviation + ' %'
    

    【讨论】:

      【解决方案5】:

      我发现 PATINDEX/COLLATE 选项的工作相当间歇。这是我最终做的:

      --get rid of the sparsely used commas
      --get rid of the duplicate spaces
      update MyTable set
          CityStZip= 
              replace(
                  replace(
                      replace(CityStZip,'   ',' '),
                      '  ',' '),
                  ',','')
      
      select
          --check if state and zip are there and then grab the city
          case when isNumeric(right(CityStZip,1))=1
                  then left(CityStZip,len(CityStZip)-charindex(' ',reverse(CityStZip),
                                              charindex(' ',reverse(CityStZip))+1)+1)
              --no zip. check for state
              when left(right(CityStZip,3),1) = ' '
                  then left(CityStZip,len(CityStZip)-charIndex(' ',reverse(CityStZip)))
              else CityStZip
              end as City,
          --check if zip is there and then grab the city
          case when isNumeric(right(CityStZip,1))=1
                  then substring(CityStZip,
                          len(CityStZip)-charindex(' ',reverse(CityStZip),
                                                      charindex(' ',reverse(CityStZip))+1)+2,
                          2)
              --no zip. check if 3rd to last char is a space and grab the last two chars
              when left(right(CityStZip,3),1) = ' '
                  then right(CityStZip,2)
              end as [State],
          --grab everything after the last space if the last character is numeric
          case when isNumeric(right(CityStZip,1))=1
                  then substring(CityStZip,
                          len(CityStZip)-charindex(' ',reverse(CityStZip))+1,
                          charindex(' ',reverse(CityStZip)))
              end as Zip
      from MyTable
      

      【讨论】:

        猜你喜欢
        • 2017-11-22
        • 2017-09-05
        • 1970-01-01
        • 2012-05-02
        • 2018-07-13
        • 2017-03-15
        • 1970-01-01
        • 2022-06-29
        • 1970-01-01
        相关资源
        最近更新 更多