标准化大写的字符串,但括号中的内容保持不变

问题描述

我想在数据库中输入一个适当的字符串,但是我在使用ProperCase存储过程时遇到问题。

解决以下问题,我需要使用toupper功能修复括号中的内容。我正在使用sql Server 2017。

我尝试了以下类似方法。但是问题是我有如下数据

我希望我的程序处于我可以重用的函数中。这也需要在2016年生效

TEST DATA (BA1) 
TEST DATA 2 (BA2) 
TEST DATA 3 (BA3)

它应该返回

Test Data (BA1) 
Test Data 2 (BA2) 
Test Data 3 (BA3)

但是它正在返回:

Test Data (Ba1) 
Test Data 2 (Ba2) 
Test Data 3 (Ba3)

有什么想法可以阻止它变成小写吗?因此,我的意思是(左括号和右括号中的内容应保持大写或变为大写)

问题出现在括号中(这也会使A小写)。我希望函数忽略括号内的文本。

IF OBJECT_ID('dbo.ProperCase') IS NOT NULL
    DROP FUNCTION dbo.ProperCase
GO

CREATE FUNCTION dbo.PROPERCASE 
     (@str VARCHAR(8000))
RETURNS VARCHAR(8000)
AS
BEGIN
    SET @str = ' ' + @str
    SET @str = 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( @str,' 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')
    RETURN RIGHT(@str,LEN(@str) - 1)
END
GO

编辑2

我也尝试过以下最佳答案,但结果相同

SQL: capitalize first letter only

CREATE FUNCTION [dbo].[CapitalizefirstLetter]
(
--string need to format
@string VARCHAR(200)--increase the variable size depending on your needs.
)
RETURNS VARCHAR(200)
AS

BEGIN
--Declare Variables
DECLARE @Index INT,@ResultString VARCHAR(200)--result string size should equal to the @string variable size
--Initialize the variables
SET @Index = 1
SET @ResultString = ''
--Run the Loop until END of the string

WHILE (@Index <LEN(@string)+1)
BEGIN
IF (@Index = 1)--first letter of the string
BEGIN
--make the first letter capital
SET @ResultString =
@ResultString + UPPER(SUBSTRING(@string,@Index,1))
SET @Index = @Index+ 1--increase the index
END

-- IF the prevIoUs character is space or '-' or next character is '-'

ELSE IF ((SUBSTRING(@string,@Index-1,1) =' 'or SUBSTRING(@string,1) ='-' or SUBSTRING(@string,@Index+1,1) ='-') and @Index+1 <> LEN(@string))
BEGIN
--make the letter capital
SET
@ResultString = @ResultString + UPPER(SUBSTRING(@string,1))
SET
@Index = @Index +1--increase the index
END
ELSE-- all others
BEGIN
-- make the letter simple
SET
@ResultString = @ResultString + LOWER(SUBSTRING(@string,1))
SET
@Index = @Index +1--incerase the index
END
END--END of the loop

IF (@@ERROR
<> 0)-- any error occur return the sEND string
BEGIN
SET
@ResultString = @string
END
-- IF no error found return the new string
RETURN @ResultString
END

但是我尝试使用以下内容

Create FUNCTION [dbo].[ProperCase2] 
     (@str VARCHAR(MAX))
     RETURNS VARCHAR(8000)
AS
BEGIN
DECLARE @keysValuetoSearch NVARCHAR(4000) = '('
DECLARE @untilThisCharappears NVARCHAR(4000) = ')'
DECLARE @keysValuetoSearchPattern NVARCHAR(4000) = '%' + @keysValuetoSearch + '%'
DECLARE @leftString NVARCHAR(4000)
DECLARE @Length      INT

DECLARE @test NVARCHAR(4000)
DECLARE @lhb NVARCHAR(4000)
SET @lhb = SUBSTRING(
           @str,PATINDEX(@keysValuetoSearchPattern,@str) + LEN(@keysValuetoSearch),CHARINDEX(
               @untilThisCharappears,@str,@str) + LEN(@keysValuetoSearch)
           ) -(PATINDEX(@keysValuetoSearchPattern,@str) + LEN(@keysValuetoSearch))
       );

 SET @Length = CHARINDEX(@keysValuetoSearch,@str)

set @leftString=  SUBSTRING(@str,1,CASE WHEN @Length - 1 < 0 
                      THEN LEN(@str) 
                      ELSE @Length - 1 END)  
 RETURN  dbo.CapitalizefirstLetter(@LeftString) + ' (' +@lhb + ')'
END

我收到以下错误

当我通过以下测试数据

SELECT dbo.ProperCase2('Test DBA (BA1)') AS Test1 
SELECT dbo.ProperCase2('Test DBA EA1') AS Test2

(受影响的1行)消息537,级别16,状态3,第4行无效长度 参数传递给LEFT或SUBSTRING函数

解决方法

这不是理想的解决方案,但它似乎可以工作。我在这里使用了两个内联表值函数。标量函数通常不像行内表值函数那样在任何地方执行,尽管2019支持标量内联,但您使用的是2017。

首先,您需要获取DelimitedSplit8K_LEAD的副本,因为我们需要一个支持顺序位置的分隔符(STRING_SPLIT不支持)。然后,我们可以使用一些窗口式SUM来检查括号的数量。如果我们处于正面,则我们位于方括号内,不应使用“适当”的大写字母。我还假定,在括号后可能有数据 ,否则实际上会更容易:这给出了以下内容:

USE Sandbox;
GO

CREATE OR ALTER FUNCTION dbo.ProperCase (@String varchar(8000))
RETURNS table
AS RETURN
    WITH Split AS(
        SELECT DS.ItemNumber,DS.Item,SUM(CASE CHARINDEX('(',DS.Item) WHEN 0 THEN 0 ELSE 1 END) OVER (ORDER BY DS.ItemNumber ASC ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) - 
               ISNULL(SUM(CASE CHARINDEX(')',DS.Item) WHEN 0 THEN 0 ELSE 1 END) OVER (ORDER BY DS.ItemNumber ASC ROWS BETWEEN UNBOUNDED PRECEDING AND 1 PRECEDING),0) AS InBrackets
        FROM dbo.DelimitedSplit8K_LEAD(@String,' ') DS)
    SELECT STRING_AGG(CASE S.InBrackets WHEN 0 THEN STUFF(LOWER(S.Item),1,UPPER(LEFT(S.Item,1))) ELSE S.Item END,' ') WITHIN GROUP (ORDER BY S.ItemNumber) AS NewString
    FROM Split S;
GO

SELECT *
FROM (VALUES('TEST DATA (BA1)'),('TEST DATA 2 (BA2)'),('TEST DATA 3 (BA3)'),('TEST DATA 4 (BA4) TEST'))V(YourString)
     CROSS APPLY dbo.ProperCase(V.YourString);

GO

db<>fiddle

,

如果您知道只有一组paren,并且在末尾,则可以拆分字符串,第一部分用适当的大小写,第二部分用glom返回。像这样:

BEGIN
    SET @newstr = ' ' + LEFT(@str,CHARINDEX('(',@str);
    SET @newstr = 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( @str,' 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')
    RETURN STUFF(@newstr,'') + RIGHT(@str,REVERSE(@str))
END;

注意:这是一个非常具体的答案,可以根据您输入的内容来破解当前功能。

通常,我建议您重写该函数以逐个字符地遍历字符串,以便跟踪多个括号表达式。

,

您可以仅使用SQL来执行此操作,而无需任何过程。

WITH
-- your input
indata(s) AS (
          SELECT 'TEST DATA (BA1)'
UNION ALL SELECT 'TEST DATA 2 (BA2)'
UNION ALL SELECT 'TEST DATA 3 (BA3)'
),-- add an identifier,which you need for grouping later ...
w_id AS (
  SELECT
    ROW_NUMBER() OVER(ORDER BY s) AS id,*
  FROM indata
),-- "explode" into one row per space delimited sub-string using STRING_SPLIT() ...
words AS (
  SELECT 
   id,value
  FROM w_id
  CROSS APPLY STRING_SPLIT(s,' ')
),-- check if the "value" you got begins with a left paren 
-- and ends with a right paren,and proceed accordingly ...
right_case AS (
  SELECT
    id,CASE WHEN LEFT(value,1) <> '(' AND RIGHT(value,1) <> ')'
      THEN UPPER(LEFT(value,1))+LOWER(RIGHT(value,LEN(value)-1))
      ELSE value
    END AS val
  FROM words
)
-- finally,re-aggregate all together ...
SELECT
  STRING_AGG(val,' ') AS s
FROM right_case
GROUP BY id;

s
Test Data (BA1)
Test Data 2 (BA2)
Test Data 3 (BA3)