【问题标题】:How to find UPPER case characters in a string and replace them with a space with SQL or SSRS如何在字符串中查找大写字符并用 SQL 或 SSRS 的空格替换它们
【发布时间】:2017-05-24 19:43:51
【问题描述】:

我有一列包含混合大小写字符的字符串值,例如 (AliBabaSaidHello)。我想将此列值用于我的 SSRS 表格单元格标题,例如(阿里巴巴说你好)。首先,我喜欢找到每个大写字母并为其添加空格。

【问题讨论】:

    标签: sql-server tsql reporting-services replace


    【解决方案1】:

    Ascii 65-90 提示有助于为函数创建以下代码:

           declare @Reset bit;
           declare @Ret varchar(8000);
           declare @i int;
           declare @c char(1);
    
           select @Reset = 1, @i=1, @Ret = '';
           while (@i <= len('AliBabaSaidHello'))
           select @c= substring('AliBabaSaidHello',@i,1),
                  @Reset = case when ascii(@c) between 65 and 90 then 1 else 0 end,
                  @Ret = @Ret + case when @Reset=1 then ' ' + @c else @c end,
                  @i = @i +1     
    
           select @Ret 
    

    谢谢大家,在阅读了所有答案后,我创建了这个灵活且非常高效的功能:

    FUNCTION dbo.UDF_DelimitersForCases (@string NVARCHAR(MAX), @Delimiter char(1))
    RETURNS NVARCHAR(MAX)
    AS
    BEGIN
        DECLARE @len INT = LEN(@string)
            ,@iterator INT = 2 --Don't put space to left of first even if it's a capital
        ;
    
        WHILE @iterator <= LEN(@string)
        BEGIN
            IF PATINDEX('[ABCDEFGHIJKLMNOPQRSTUVWXYZ]',SUBSTRING(@string,@iterator,1) COLLATE Latin1_General_CS_AI) <> 0
            BEGIN
                SET @string = STUFF(@string,@iterator,0,@Delimiter);
                SET @iterator += 1;
            END
            ;
    
            SET @iterator += 1;
        END
    
        RETURN @string;
    END
    ;
    GO
    

    示例:
    SELECT dbo.udf_DelimitersForCases('AliBabaSaidHello','_');
    返回 "Ali_Baba_Said_Hello"(无引号)。

    【讨论】:

      【解决方案2】:

      逐一获取“A”、“l”、“i”等字符,查看方法ascii('&amp;i_char')的返回值是否在6590之间,这些是“大写字母”。
      (ascii('A')=65(capital), ascii('l')=108(non-capital), ascii('i')=105(non-capital))

      【讨论】:

      • 使用巴巴罗斯的提示,我能够编写解决方案。
      • @AnthonyHancock 请看link
      • 没那么快@AnthonyHancock。 Barbaros 发布的内容可能不是一个完整的解决方案,但我认为 ASCII 65-90 提示不仅有助于组合解决方案 - 它还可用于生成无 rbar 的高性能解决方案。请注意我放在一起的代码。
      【解决方案3】:

      为您的 qry 使用 case sensitive 排序规则,并与 每个字符 结合使用。当您迭代字符时,您可以轻松地将大写字符替换为大写字符 + 空格。

      WHERE SourceText COLLATE Latin1_General_CS_AI like '[A-Z]'
      -- or for variable @char COLLATE Latin1_General_CS_AI = upper(@char)
      

      Latin1_General_CS_AI 中的重要,其中“CS”区分大小写。

      【讨论】:

      • 这本来是我建议的路线,但是我得到了非常奇怪的结果,在逐个字符比较时只排除小写 A。必须更改为 LIKE 'ABCDEFGHIJKLMONPQRSTUVWXYZ'。不知道那里发生了什么。
      • 添加你的字符串和类似的模式,我会解释。
      【解决方案4】:

      如果您出于某种原因想要使其可重用,这里是使用户函数调用的代码。

      DROP FUNCTION IF EXISTS udf_SpacesforCases;
      GO
      
      CREATE FUNCTION udf_SpacesForCases (@string NVARCHAR(MAX))
      RETURNS NVARCHAR(MAX)
      AS
      BEGIN
          DECLARE @len INT = LEN(@string)
              ,@iterator INT = 2 --Don't put space to left of first even if it's a capital
          ;
      
          WHILE @iterator <= LEN(@string)
          BEGIN
              IF PATINDEX('[ABCDEFGHIJKLMNOPQRSTUVWXYZ]',SUBSTRING(@string,@iterator,1) COLLATE Latin1_General_CS_AI) <> 0
              BEGIN
                  SET @string = STUFF(@string,@iterator,0,' ');
                  SET @iterator += 1;
              END
              ;
      
              SET @iterator += 1;
          END
      
          RETURN @string;
      END
      ;
      GO
      
      SELECT dbo.udf_SpacesForCases('AliBabaSaidHello');
      

      【讨论】:

        【解决方案5】:

        任何涉及标量用户定义函数和/或循环的解决方案都不会像基于集合的解决方案那样执行。这是一个使用NGrams8K 的小菜一碟:

        DECLARE @string varchar(1000) = 'AliBabaSaidHello';
        
        SELECT newString = 
        ( SELECT
            CASE 
              WHEN ASCII(token) BETWEEN 65 AND 90 AND position > 1 THEN ' '+token ELSE token
            END+''
          FROM dbo.NGrams8k(@string, 1)
          FOR XML PATH(''));
        

        返回:“阿里巴巴说你好”(无引号)。

        请注意,第一个字符之前没有空格。或者,不使用该函数的基于集合的解决方案如下所示:

        WITH 
        E1(N) AS (SELECT 1 FROM (VALUES (1),(1),(1),(1),(1),(1),(1),(1),(1),(1))t(c)),
        iTally(N) AS
        (
          SELECT TOP (LEN(@string)) ROW_NUMBER() OVER (ORDER BY (SELECT 1))
          FROM E1 a, E1 b, E1 c, E1 d
        ),
        nGrams(NewString) AS
        (
          SELECT 
            CASE WHEN ASCII(SUBSTRING(@string, N, 1)) BETWEEN 65 AND 90 AND N > 1
              THEN ' '+SUBSTRING(@string, N, 1) ELSE SUBSTRING(@string, N, 1)
            END+''
          FROM iTally
          FOR XML PATH('')
        )
        SELECT NewString
        FROM nGrams;
        

        【讨论】:

        • 我并不完全相信“任何涉及标量用户定义函数和/或循环的解决方案都不会像基于集合的解决方案那样执行。”话虽如此,我还想衡量使用 ASCII 转换对类似 [capitalalphabet] 的性能。生成一个随机序列应该相当简单,比如 100 万个不同大小写的字符串,看看你的解决方案还是我的解决方案性能更好?
        • 顺便说一句,我真的不擅长使用 XML 的东西,所以我会用你的例子来了解更多,所以谢谢你帮助我学习。
        【解决方案6】:

        APL 方法是将输入拆分为字符,根据需要填充字符,然后重新组合字符串。在 T-SQL 中,它看起来更像这样:

        -- Sample data.
        declare @Samples as Table ( Sample  VarChar(32) );
        insert into @Samples ( Sample ) values ( 'AliBabaSaidHello' ), ( 'MeshMuscleShirt' );
        select * from @Samples;
        
        -- Stuff it.
        with
          Ten ( Number ) as ( select Number from ( values (0), (1), (2), (3), (4), (5), (6), (7), (8), (9) ) as Digits( Number ) ),
          TenUp2 ( Number ) as ( select 42 from Ten as L cross join Ten as R ),
          TenUp4 ( Number ) as ( select 42 from TenUp2 as L cross join TenUp2 as R ),
          Numbers ( Number ) as ( select Row_Number() over ( order by ( select NULL ) ) from TenUp4 ),
          Characters ( Sample, Number, PaddedCh ) as (
            select S.Sample, N.Number, PC.PaddedCh
              from @Samples as S inner join
                Numbers as N on N.Number <= Len( S.Sample ) cross apply
                ( select SubString( S.Sample, N.Number, 1 ) as Ch ) as SS cross apply
                ( select case when N.Number > 1 and ASCII( 'A' ) <= ASCII( SS.Ch ) and ASCII( SS.Ch ) <= ASCII( 'Z' ) then ' ' + Ch else Ch end as PaddedCh ) as PC )
          select S.Sample,
            ( select PaddedCh from Characters where Sample = S.Sample order by Number for XML path(''), type).value('.[1]', 'VarChar(max)' ) as PaddedSample
            from @Samples as S
            order by Sample;
        

        【讨论】:

        • 与@alanburstein 提交的相比,这看起来效率极低。此外,我不禁想知道您是否在参考 Hitchhiker's Guide 中的“选择 42”部分。
        • 是的,性能很糟糕,尽管对于 SSRS 中的标头来说可能已经足够了。这确实是对上一千年 APL 课程的回忆。 (“在字符串 Beta 中查找所有出现的字符串 Alpha。”效率不是问题。)至于 42,它是我选择的占位符(即使它不是答案)表明我考虑过需要什么答案是语法上令人满意且语义上不重要的东西。现在快速说 10 次“网状肌肉衬衫”。
        【解决方案7】:

        另一个选项(相当冗长)可能是:

        SELECT REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE('AliBabaSaidHello' COLLATE Latin1_General_CS_AS,'A',' A'),'B',' B'),'C',' C'),'D',' D'),'E',' E'),'F',' F'),'G',' G'),'H',' H'),'I',' I'),'J',' J'),'K',' K'),'L',' L'),'M',' M'),'N',' N'),'O',' O'),'P',' P'),'Q',' Q'),'R',' R'),'S',' S'),'T',' T'),'U',' U'),'V',' V'),'W',' W'),'X',' X'),'Y',' Y'),'Z',' Z')
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2020-01-02
          • 2014-09-05
          • 2011-03-12
          • 2016-01-20
          • 1970-01-01
          • 2013-05-06
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多