J. E. Flint
J. E. Flint

Reputation: 43

SQL Proper Case Function Query Refinement

I have a table of names that I am trying to convert from UPPERCASE to Proper Case. And the below code does almost exactly what I am. When I was testing it out I noticed that I had folks who had Roman Numerals in their name, Mc* and O'* in the table. Naturally the query converted any multiple Roman Numeral into Iv like it was supposed to and any MCDONALDS or O'DANIEL were converted into Mcdonalds and O'daniel. I was trying to figure out how to make a clean change to this function so I can run my update query but I'm still peacemilling my SQL knowledge together in off hours. Any help/suggestions would be much appreciated. I did a google search and found several examples but the ones I tried didn't work. The amount of corrections I would have to do is relatively minor (17 corrections out of 1000 row table), but I'd like to try and tidy it up to limit as many human errors as possible.

Thank you in advance for your help.

CREATE FUNCTION [dbo].[f_ProperCase]
(@Text as varchar(80))
RETURNS varchar(80) as
BEGIN

DECLARE @Reset bit
DECLARE @Ret varchar(80)
DECLARE @i int
DECLARE @c char(1)

SELECT @Reset = 1, @i=1, @Ret = ''

WHILE @i <= LEN(@Text)
SELECT @c= SUBSTRING(@Text,@i,1),
@Ret = @Ret + CASE WHEN @Reset=1 THEN UPPER(@c) ELSE LOWER(@c) END,
@Reset= CASE WHEN 
CASE WHEN SUBSTRING(@Text,@i-4,5) like '_[a-z] [DOL]''' THEN 1 
WHEN SUBSTRING(@Text,@i-4,5) like '_[a-z] [D][I]' THEN 1 
WHEN SUBSTRING(@Text,@i-4,5) like '_[a-z] [M][C]' THEN 1 
WHEN SUBSTRING(@Text,@i-4,5) like '_[a-z] [M][c][*]' THEN 1 --Convert MCDONALD to     McDonald
WHEN SUBSTRING(@Text,@I-4,5) like '_[a-z] [O][''][*]' THEN 1 --Convert O'DONNEL to O'Donnel
ELSE 0 
END = 1 
THEN 1 
ELSE CASE WHEN @c like '[a-zA-Z]' or @c in ('''') THEN 0 
ELSE 1 
END 
END,
@i = @i +1
RETURN @Ret
end

Upvotes: 4

Views: 6392

Answers (2)

Brock McDonald
Brock McDonald

Reputation: 1

I tweaked some to handle suffixes being included in the name. This handled almost anything I threw at it. We sometimes had hyphenated names as well as periods after some suffixes. I know handling from I-IX was overkill, but was easy enough to add the X to the V check.

ALTER FUNCTION dbo.udf_ProperCase
(@Text as varchar(1024))
RETURNS varchar(1024) 
AS
/* 
Created to ProperCase most common LastName (Mc/Mac/O'/D'/L') and handle I(first)-IX(nineteenth) suffixes

Original Code: https://stackoverflow.com/questions/22923616/sql-proper-case-function-query-refinement
SELECT dbo.udf_ProperCase('iitest demetrii mcdonald o''neil victor second 2nd ii iii iv v vi vii viii ix x xi test')
*/
BEGIN
    DECLARE @Reset bit
    DECLARE @Ret varchar(1024)
    DECLARE @i int
    DECLARE @c char(1)
    DECLARE @Text1 varchar(1025)

    SELECT @Reset = 1, @i=1, @Ret = '', @Text1 = ' ' + @Text + '  '  --Ensure one space before to make first char upper and after to handle suffixes
    --Loop through each character, comparing prior/next to determine if need to handle
    WHILE @i <= LEN(@Text1)
      BEGIN
        SELECT 
             @c= SUBSTRING(@Text1,@i,1)
            ,@Ret = @Ret + CASE WHEN @Reset=1 THEN UPPER(@c) ELSE LOWER(@c) END
            ,@Reset= CASE 
                        WHEN 
                            CASE 
                                WHEN SUBSTRING(@Text1,@i-2,3) like '[ -][DdOoLl][''`]' THEN 1   --Names (including hyphenated) beginning with D/O/L                             
                                WHEN SUBSTRING(@Text1,@i-2,4) like ' [Mm][cC][a-zA-Z]' THEN 1  --Names with Mc
                                WHEN SUBSTRING(@Text1,@i-3,5) like ' [Mm][Aa][cC][a-zA-Z]' THEN 1  --Names with Mac
                                WHEN SUBSTRING(@Text1,@i-1,4) like ' [Ii][Ii][ .]' THEN 1  --Handle II (include ending with Space or period)
                                WHEN SUBSTRING(@Text1,@i-1,5) like ' [Ii][Ii][Ii][ .]' THEN 1  --Handle III
                                WHEN SUBSTRING(@Text1,@i-2,4) like ' [Ii][Ii][ .]' THEN 1  --Handle II
                                WHEN SUBSTRING(@Text1,@i-2,5) like ' [Ii][Ii][Ii][ .]' THEN 1  --Handle III
                                WHEN SUBSTRING(@Text1,@i-3,4) like ' [Ii][Ii][ .]' THEN 1  --Handle II
                                WHEN SUBSTRING(@Text1,@i-3,5) like ' [Ii][Ii][Ii][ .]' THEN 1  --Handle III
                                
                                WHEN SUBSTRING(@Text1,@i-1,4) like ' [Ii][VvXx][ .]' THEN 1 --Handle IV

                                WHEN SUBSTRING(@Text1,@i-1,4) like ' [VvXx][Ii][ .]' THEN 1  --Handle VI
                                WHEN SUBSTRING(@Text1,@i-1,5) like ' [VvXx][Ii][Ii][ .]' THEN 1  --Handle VII
                                WHEN SUBSTRING(@Text1,@i-1,6) like ' [VvXx][Ii][Ii][Ii][ .]' THEN 1  --Handle VIII
                                WHEN SUBSTRING(@Text1,@i-2,4) like ' [VvXx][Ii][ .]' THEN 1  --Handle VI
                                WHEN SUBSTRING(@Text1,@i-2,5) like ' [VvXx][Ii][Ii][ .]' THEN 1  --Handle VII
                                WHEN SUBSTRING(@Text1,@i-2,6) like ' [VvXx][Ii][Ii][Ii][ .]' THEN 1  --Handle VIII
                                WHEN SUBSTRING(@Text1,@i-3,4) like ' [VvXx][Ii][ .]' THEN 1  --Handle VI
                                WHEN SUBSTRING(@Text1,@i-3,5) like ' [VvXx][Ii][Ii][ .]' THEN 1  --Handle VII
                                WHEN SUBSTRING(@Text1,@i-3,6) like ' [VvXx][Ii][Ii][Ii][ .]' THEN 1  --Handle VIII
                                ELSE 0 
                            END = 1 THEN 1 
                        ELSE 
                            CASE 
                                WHEN @c like '[a-zA-Z`]' or @c in ('''') or @c like '[0-9]' THEN 0  --If any letter, single-quote or number, then keep next lowercase
                                ELSE 1  --Anything else (e.g. Space dash, And(&), etc. make next Upper-Case)
                            END 
                      END
            ,@i = @i +1 
      END   
    
    RETURN stuff(@Ret, 1, 1, '')
END

GO

Upvotes: 0

cha
cha

Reputation: 10411

I would do it differently:

CREATE FUNCTION [dbo].[f_ProperCase]
(@Text as varchar(80))
RETURNS varchar(80) as
BEGIN

DECLARE @Reset bit
DECLARE @Ret varchar(80)
DECLARE @i int
DECLARE @c char(1)
DECLARE @Text1 varchar(81)

SELECT @Reset = 1, @i=1, @Ret = '', @Text1 = ' ' + @Text

WHILE @i <= LEN(@Text1)
    SELECT @c= SUBSTRING(@Text1,@i,1),
    @Ret = @Ret + CASE WHEN @Reset=1 THEN UPPER(@c) ELSE LOWER(@c) END,
    @Reset= CASE WHEN 
    CASE WHEN SUBSTRING(@Text1,@i-2,3) like ' [DdOoLl]''' THEN 1 
    WHEN SUBSTRING(@Text1,@i-2,4) like ' [Mm][cC][a-zA-Z]' THEN 1 
    WHEN SUBSTRING(@Text1,@i-3,5) like ' [Mm][Aa][cC][a-zA-Z]' THEN 1 
    ELSE 0 
    END = 1 
    THEN 1 
    ELSE CASE WHEN @c like '[a-zA-Z]' or @c in ('''') THEN 0 
    ELSE 1 
    END 
    END,
    @i = @i +1
RETURN stuff(@Ret, 1, 1, '')
end

This function supports O', L', D', as well as Mc, and Mac. The function is also converts from any case (not only the upper case) to the proper case

SQL Fiddle

select dbo.f_ProperCase('CORMACK')
      ,dbo.f_ProperCase('Mcdonald ronald')
      ,dbo.f_ProperCase('o''hara')

|         |                 |        |
|---------|-----------------|--------|
| Cormack | McDonald Ronald | O'Hara |

Upvotes: 8

Related Questions