Sql server 如何在字符串中查找大写字符并用SQL或SSRS替换为空格

Sql server 如何在字符串中查找大写字符并用SQL或SSRS替换为空格,sql-server,tsql,reporting-services,replace,Sql Server,Tsql,Reporting Services,Replace,我有一个列,它的字符串值混合了大小写字符,比如AliBabaSaidHello。我想用这个列值作为我的SSRS表格单元格标题,比如阿里巴巴说你好。首先,我喜欢查找每个大写字母并为其添加空格。逐个获取字符,如A、l、I,然后查看方法ascii'&I_char'的返回值是否在65到90之间,这些都是大写字母。 ascii'A'=65大写,ascii'l'=108非大写,ascii'i'=105非大写像A,l,i一样逐个获取字符,然后查看方法ascii'&i_char'的返回值是否在65和90之间,这

我有一个列,它的字符串值混合了大小写字符,比如AliBabaSaidHello。我想用这个列值作为我的SSRS表格单元格标题,比如阿里巴巴说你好。首先,我喜欢查找每个大写字母并为其添加空格。

逐个获取字符,如A、l、I,然后查看方法ascii'&I_char'的返回值是否在65到90之间,这些都是大写字母。
ascii'A'=65大写,ascii'l'=108非大写,ascii'i'=105非大写

像A,l,i一样逐个获取字符,然后查看方法ascii'&i_char'的返回值是否在65和90之间,这些都是大写字母。 ascii'A'=65大写,ascii'l'=108非大写,ascii'i'=105非大写

对您的qry使用区分大小写的排序规则,并对每个字符使用like。当您输入字符时,您可以很容易地将大写字符替换为大写字符+空格

WHERE SourceText COLLATE Latin1_General_CS_AI like '[A-Z]'
-- or for variable @char COLLATE Latin1_General_CS_AI = upper(@char)
拉丁语1_General_CS_AI中的重要字符,其中CS区分大小写。

对您的qry使用区分大小写的排序规则,并对每个字符结合like。当您输入字符时,您可以很容易地将大写字符替换为大写字符+空格

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

拉丁文1_General_CS_AI中的重要提示,其中CS区分大小写。

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
谢谢大家,在阅读了所有答案后,我创建了这个灵活且非常高效的功能:

       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
例如: 选择dbo.udfu DelimitersForCases'alibabasaidholl','';
返回阿里巴巴说你好没有报价

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
谢谢大家,在阅读了所有答案后,我创建了这个灵活且非常高效的功能:

       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
例如: 选择dbo.udfu DelimitersForCases'alibabasaidholl','';
返回阿里巴巴说你好没有报价

如果出于某种原因希望使其可重用,下面是使要调用的用户函数可重用的代码

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');

如果出于某种原因希望使其可重用,那么下面的代码将用于调用用户函数

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');

任何涉及标量用户定义函数和/或循环的解决方案的性能都不如基于集合的解决方案。这是一个蛋糕散步,使用:

返回:阿里巴巴说你好,没有引用

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

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;

任何涉及标量用户定义函数和/或循环的解决方案的性能都不如基于集合的解决方案。这是一个蛋糕散步,使用:

返回:阿里巴巴说你好,没有引用

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

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;

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;

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;

另一个非常详细的选项可能是:

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')

另一个非常详细的选项可能是:

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')

使用Barbaros的提示,我能够编写一个解决方案。@AnthonyHancock请看一下notsofast@AnthonyHancock。Barbaros发布的内容可能不是一个完整的解决方案,但我认为ASCII 65-90技巧不仅有助于整合解决方案,还可以用于生成无rbar的高性能解决方案。请注意我编写的代码。使用Barbaros的提示,我能够编写一个解决方案。@AnthonyHancock请查看notsofast@AnthonyHancock。Barbaros发布的内容可能不是一个完整的解决方案,但我认为ASCII 65-90技巧不仅有助于整合解决方案,还可以用于生成无rbar的高性能解决方案。注意我放在一起的代码。这可能是我建议的路线,但是我得到了非常奇怪的结果,在逐字符比较时只排除小写字母A。不得不改成“ABCDEFGHIJKLMONPQRSTUVWXYZ”。不确定那里发生了什么。添加字符串和like模式,我会解释。这可能是我建议的路线,但是我得到了非常奇怪的结果,在进行逐字符比较时,只有小写A被排除在外。不得不改成“ABCDEFGHIJKLMONPQRSTUVWXYZ”。不确定发生了什么。添加您的字符串和类似模式,我将进行解释。我并不完全相信任何涉及标量用户定义函数和/或循环的解决方案的性能都不如基于集合的解决方案。也就是说,我还想衡量对类似的[capitalalphabet]使用ASCII强制转换的性能。生成一个随机序列(比如100万个不同大小写的字符串)应该相当简单,然后看看您的解决方案是否
r我的性能更好?顺便说一句,我真的不擅长使用XML的东西,所以我将用你的例子来了解更多,所以感谢你帮助我学习。我并不完全相信任何涉及标量用户定义函数和/或循环的解决方案的性能都不如基于集合的解决方案。也就是说,我还想衡量对类似的[capitalalphabet]使用ASCII强制转换的性能。生成一个随机序列(比如100万个不同大小写的字符串)应该相当简单,看看你的解决方案和我的解决方案的性能是否更好?顺便说一句,我真的不擅长使用XML的东西,所以我将用你的例子来进一步了解这一点,所以谢谢你帮我学习。与@alanburstein提交的相比,这看起来效率极低。此外,我不禁想知道,您是否参考了《搭便车者指南》中的42个部分。是的,性能很差,尽管对于SSR中的标题来说可能已经足够了。这真的是对上个千年APL课程的倒叙。查找字符串Beta中所有出现的字符串Alpha。效率不是一个问题。至于42,它是我选择的占位符,即使它不是表示我考虑了需要什么的答案,而答案在语法上令人满意,在语义上并不重要。现在,快速说10次网状肌肉衬衫。与@alanburstein提交的相比,这看起来效率极低。此外,我不禁想知道,您是否参考了《搭便车者指南》中的42个部分。是的,性能很差,尽管对于SSR中的标题来说可能已经足够了。这真的是对上个千年APL课程的倒叙。查找字符串Beta中所有出现的字符串Alpha。效率不是一个问题。至于42,它是我选择的占位符,即使它不是表示我考虑了需要什么的答案,而答案在语法上令人满意,在语义上并不重要。现在快速说10遍网眼肌肉衬衫。