规范化大写字符串,但保持括号中的内容相同

Normalize capitalised string but keep what's in brackets the same

我有一段文本,我希望对数据库中的字符串进行 ProperCase,但我在使用 ProperCase 存储过程时遇到了问题。

要解决以下问题,我需要我的函数来修复带有 ToUpper 的括号中的内容。我正在使用 SQL Server 2017。

我试过类似下面的方法。但问题是我有如下数据

我希望我的程序在一个我可以重用的函数中。这也需要在 2016 年工作

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

应该return

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

但它是 returning:

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, @Index-1, 1) ='-' or SUBSTRING(@string, @Index+1, 1) ='-') and @Index+1 <> LEN(@string))
BEGIN
--make the letter capital
SET
@ResultString = @ResultString + UPPER(SUBSTRING(@string,@Index, 1))
SET
@Index = @Index +1--increase the index
END
ELSE-- all others
BEGIN
-- make the letter simple
SET
@ResultString = @ResultString + LOWER(SUBSTRING(@string,@Index, 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,
               PATINDEX(@keysValueToSearchPattern, @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 row affected) Msg 537, Level 16, State 3, Line 4 Invalid length parameter passed to the LEFT or SUBSTRING function.

如果您知道您只有一组括号并且这是末尾,您可以拆分字符串,正确区分第一部分,然后 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, 1, 1, '') + RIGHT(@str, CHARINDEX('(', 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)

这不是一个理想的解决方案,但它似乎有效。我在这里使用了几个内联 table-value 函数。标量函数并不倾向于执行(任何地方)以及内联 table-value 函数,尽管 2019 支持标量内联,但您使用的是 2017.

首先,您需要获取 DelimitedSplit8K_LEAD 的副本,因为我们需要一个支持序数位置的拆分器(STRING_SPLIT 不支持)。然后我们可以使用一些窗口化的 SUMs 来检查括号的数量。如果我们是肯定的,那么我们就在括号内,不应该应用“适当的”外壳。我还 假设 在 括号后可能有数据 ,否则这实际上更容易:这给出了以下内容:

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,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