I have a sql table that contains an employee's full name in all caps (i.e. SMITH-EASTMAN,JIM M).
I need to be able to separate the full name into two separate columns (Last Name and First Name). This part is going well. Now I could use some assistance with removing the capital letters and putting it in normal case.
How can I take the results of my common table expression and pass them into a function?
WITH CTE AS
(
SELECT FullName = [Employee Name],
LastName = SUBSTRING([Employee Name], 1, CHARINDEX(',',[Employee Name])-1),
FirstNameStartPos = CHARINDEX(',',[Employee Name]) + 1,
MidlleInitialOrFirstNameStartPos = CHARINDEX(' ',[Employee Name]),
MiddleInitialOrSecondFirstName = SUBSTRING([Employee Name], CHARINDEX(' ',[Employee Name]),LEN([Employee Name])),
MiddleInitialOrSecondFirstNameLen = LEN(SUBSTRING([Employee Name], CHARINDEX(' ',[Employee Name]),LEN([Employee Name]))) - 1
FROM ['Med-PS PCN Mapping$']
WHERE [PS Employee ID] IS NOT NULL
),
CTE2 AS
(
SELECT FullName = CTE.FullName,
DerivedFirstName = CASE
WHEN CTE.MiddleInitialOrSecondFirstNameLen = 1
THEN SUBSTRING(CTE.FullName, CTE.FirstNameStartPos, CTE.MidlleInitialOrFirstNameStartPos - CTE.FirstNameStartPos)
ELSE SUBSTRING(CTE.FullName, CTE.FirstNameStartPos, CTE.FirstNameStartPos + CTE.MiddleInitialOrSecondFirstNameLen)
END,
DerivedLastName = CTE.LastName
FROM CTE
)
SELECT *
FROM CTE2
RESULTS
FullName DerivedFirstName DerivedLastName
SMITH-EASTMAN,JIM M JIM SMITH-EASTMAN
O'DAY,MARTIN C MARTIN O'DAY
TROUT,MADISON MARIE MADISON MARI TROUT
CREATE FUNCTION [dbo].[FixCap] ( #InputString varchar(4000) )
RETURNS VARCHAR(4000)
AS
BEGIN
DECLARE #Index INT
DECLARE #Char CHAR(1)
DECLARE #PrevChar CHAR(1)
DECLARE #OutputString VARCHAR(255)
SET #OutputString = LOWER(#InputString)
SET #Index = 1
WHILE #Index <= LEN(#InputString)
BEGIN
SET #Char = SUBSTRING(#InputString, #Index, 1)
SET #PrevChar = CASE WHEN #Index = 1 THEN ' '
ELSE SUBSTRING(#InputString, #Index - 1, 1)
END
IF #PrevChar IN (' ', ';', ':', '!', '?', ',', '.', '_', '-', '/', '&', '''', '(')
BEGIN
IF #PrevChar != '''' OR UPPER(#Char) != 'S'
SET #OutputString = STUFF(#OutputString, #Index, 1, UPPER(#Char))
END
SET #Index = #Index + 1
END
RETURN #OutputString
END
GO
select [dbo].[FixCap] (pass in DerivedFirstName from CTE2);
select [dbo].[FixCap] (pass in DerivedLastName from CTE2);
Do you want something like INITCAP ?
CREATE FUNCTION dbo.F_INITCAP (#PHRASE NVARCHAR(max))
RETURNS NVARCHAR(max)
WITH RETURNS NULL ON NULL INPUT
AS
BEGIN
IF LEN(#PHRASE) < 1 RETURN #PHRASE;
DECLARE #I INT = 1, #C CHAR(1), #P BIT = 0, #OUT VARCHAR(max) = '';
WHILE #I <= LEN(#PHRASE)
BEGIN
SET #C = SUBSTRING(#PHRASE, #I, 1);
IF #C BETWEEN 'A' AND 'Z' COLLATE Latin1_General_CI_AI
BEGIN
IF #P = 0
SET #OUT = #OUT + UPPER(#C);
ELSE
SET #OUT = #OUT + LOWER(#C);
SET #P = 1
END
ELSE
BEGIN
SET #P = 0;
SET #OUT = #OUT + LOWER(#C);
END
SET #I = #I + 1;
END
RETURN #OUT;
END
GO
I don't want to create a custom function for that if such function already exists in SQL Server.
Input string: This is my string to convert
Expected output: This Is My String To Convert
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE FUNCTION [dbo].[InitCap] ( #InputString varchar(4000) )
RETURNS VARCHAR(4000)
AS
BEGIN
DECLARE #Index INT
DECLARE #Char CHAR(1)
DECLARE #PrevChar CHAR(1)
DECLARE #OutputString VARCHAR(4000)
SET #OutputString = LOWER(#InputString)
SET #Index = 1
WHILE #Index <= LEN(#InputString)
BEGIN
SET #Char = SUBSTRING(#InputString, #Index, 1)
SET #PrevChar = CASE WHEN #Index = 1 THEN ' '
ELSE SUBSTRING(#InputString, #Index - 1, 1)
END
IF #PrevChar IN (' ', ';', ':', '!', '?', ',', '.', '_', '-', '/', '&', '''', '(')
SET #OutputString = STUFF(#OutputString, #Index, 1, UPPER(#Char))
SET #Index = #Index + 1
END
RETURN #OutputString
END
Declare #str nvarchar(100)
SET #str = 'my string to convert'
SELECT #str = [dbo].[InitCap](#str)
SELECT #str
AFAIK, SQL Server has no built-in function for this.
You have to write custom function for it.
Try this.
CREATE FUNCTION [dbo].[CamelCase]
(#Str varchar(8000))
RETURNS varchar(8000) AS
BEGIN
DECLARE #Result varchar(2000)
SET #Str = LOWER(#Str) + ' '
SET #Result = ''
WHILE 1=1
BEGIN
IF PATINDEX('% %',#Str) = 0 BREAK
SET #Result = #Result + UPPER(Left(#Str,1))+
SubString (#Str,2,CharIndex(' ',#Str)-1)
SET #Str = SubString(#Str,
CharIndex(' ',#Str)+1,Len(#Str))
END
SET #Result = Left(#Result,Len(#Result))
RETURN #Result
END
Output :
Input String : 'microSoft sql server'
Output String : 'Microsoft Sql Server'
I'd have to go with "No, that does not exist". This based on several years of perusing the available string-functions in T-SQL and some pretty recent 5-day courses in SQL Server 2008 R2.
Of course, I still could be wrong :).
If the goal of your operation is to prettify strings of Names then proper capitalization could be defined as the first letter of each word separated by non-alphabet characters.
Other solutions do not take into account:
Preserving spacing (especially trailing spaces).
Preserving NULL, empty-string, or a string of just spaces.
Handling more than just spaces (e.g. dashes, commas, underscores, etc...)
Handling more than one non-alpha character between words/tokens.
Handling exceptions (e.g. McDonald or III like in "James William
Bottomtooth the III").
Note: My solution does not handle exceptions.
If you are very concerned about those, then I suggest writing a CLR C# assembly for those as it will be tricky, and strings are an area where C# excels.
Another solution on here tries to account for this, but it would still take "ivan terrible the iv" and output "**IV***an Terrible The IV*".
This is the function I came up with:
IF EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[fs_PascalCase]') AND type in (N'FN', N'IF', N'TF', N'FS', N'FT'))
DROP FUNCTION [dbo].[fs_PascalCase]
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE FUNCTION [dbo].[fs_PascalCase]
(
#Text nVarChar(MAX)
)
RETURNS nVarChar(MAX)
AS
BEGIN
SET #Text = LOWER(#Text)--This step is optional. Keep if you want the code below to control all casing. - 11/26/2013 - MCR.
DECLARE #New nVarChar(MAX) = (CASE WHEN #Text IS NULL THEN NULL ELSE '' END)--Still return null when source is null. - 11/26/2013 - MCR.
DECLARE #Len Int = LEN(REPLACE(#Text, ' ', '_'))--If you want to count/keep trailing-spaces, you MUST use this!!! - 11/26/2013 - MCR.
DECLARE #Index Int = 1--Sql-Server is 1-based, not 0-based.
WHILE (#Index <= #Len)
IF (SUBSTRING(#Text, #Index, 1) LIKE '[^a-z]' AND #Index + 1 <= #Len)--If not alpha and there are more character(s).
SELECT #New = #New + UPPER(SUBSTRING(#Text, #Index, 2)), #Index = #Index + 2
ELSE
SELECT #New = #New + SUBSTRING(#Text, #Index, 1) , #Index = #Index + 1
--If #Text is null, then #Len will be Null, and everything will be null.
--If #Text is '', then (#Len - 1) will be -1, so ABS() it to use 1 instead, which will still return ''.
RETURN ( UPPER(LEFT(#New, 1)) + RIGHT(#New, ABS(#Len - 1)) )
END
GO
You would call it like so:
SELECT dbo.fs_PascalCase(NULL)[Null],
dbo.fs_PascalCase('')[EmptyString],
dbo.fs_PascalCase('hello how are-you TODAY ')[LongString]
The output will look like this:
My Strategy
If the name is already in mixed case, trust that it’s right.
If the name is not in mixed case, then do the following:
Trim up the name to eliminate white space
Account for the names that start with “Mc” like “McDavid”
Account for names with apostrophes like O’Reilly
Account for hyphenated names (married names) “Anderson-Johnson”
Account for multiple word names like “La Russa”
Make sure suffixes included in the names field are capitalized appropriately
The Code
Here's my original post on this: Converting String to Camel Case in SQL Server
CREATE FUNCTION [dbo].[GetCamelCaseName]
(
#Name varchar(50)
)
RETURNS VARCHAR(50) WITH SCHEMABINDING
AS
BEGIN
-- Declare the return variable here
DECLARE #NameCamelCase VARCHAR(50)
-- This is determining whether or not the name is in camel case already (if the 1st character is uppercase
-- and the third is lower (because the 2nd could be an apostrophe). To do this, you have to cast the
-- character as varbinary and compare it with the upper case of the character cast as varbinary.
IF (CAST(SUBSTRING(#Name, 1,1) as varbinary) = CAST(SUBSTRING(UPPER(#Name), 1, 1) as varbinary)
AND ((CAST(SUBSTRING(#Name, 2,1) as varbinary) = CAST(SUBSTRING(LOWER(#Name), 2, 1) as varbinary)
AND SUBSTRING(#Name, 2,1) != '''')
or
(CAST(SUBSTRING(#Name, 4,1) as varbinary) = CAST(SUBSTRING(LOWER(#Name), 4, 1) as varbinary)
AND SUBSTRING(#Name, 2,1) = '''')))
BEGIN
SELECT #NameCamelCase = RTRIM(LTRIM(#Name))
SELECT #NameCamelCase = REPLACE(#NameCamelCase, ' sr', ' Sr')
SELECT #NameCamelCase = REPLACE(#NameCamelCase, ' jr', ' Jr')
SELECT #NameCamelCase = REPLACE(#NameCamelCase, ' ii', ' II')
SELECT #NameCamelCase = REPLACE(#NameCamelCase, ' iii', ' III')
SELECT #NameCamelCase = REPLACE(#NameCamelCase, ' DE ', ' de ')
SELECT #NameCamelCase = REPLACE(#NameCamelCase, 'macdonald', 'MacDonald')
if (#NameCamelCase LIKE '% iv') -- avoid changing "Ivan" to "IVan"
SELECT #NameCamelCase = REPLACE(#NameCamelCase, ' iv', ' IV')
if ((#NameCamelCase = 'i') or (#NameCamelCase = 'ii') or (#NameCamelCase = 'iii') or (#NameCamelCase = 'iv'))
SELECT #NameCamelCase = UPPER(#NameCamelCase)
RETURN #NameCamelCase
END
ELSE
BEGIN
SELECT #NameCamelCase = RTRIM(LTRIM(#Name))
-- "Mc-"
SELECT #NameCamelCase =
CASE
WHEN #Name LIKE 'mc%'
THEN UPPER(SUBSTRING(#Name, 1, 1)) + LOWER(SUBSTRING(#Name, 2, 1)) + UPPER(SUBSTRING(#Name, 3, 1)) + LOWER(SUBSTRING(#Name, 4, 47))
ELSE
UPPER(SUBSTRING(#Name, 1, 1)) + LOWER(SUBSTRING(#Name, 2, 49))
END
-- Apostrophes
SELECT #NameCamelCase =
CASE
WHEN #NameCamelCase LIKE '%''%'
THEN SUBSTRING(#NameCamelCase, 1, CHARINDEX('''', #NameCamelCase) - 1) + '''' + UPPER(SUBSTRING(#NameCamelCase, CHARINDEX('''', #NameCamelCase) + 1, 1)) + SUBSTRING(#NameCamelCase, CHARINDEX('''', #NameCamelCase) + 2, 50)
ELSE
#NameCamelCase
END
-- Hyphenated names (do it twice to account for double hyphens)
SELECT #NameCamelCase =
CASE
WHEN #NameCamelCase LIKE '%-%'
THEN SUBSTRING(#NameCamelCase, 1, CHARINDEX('-', #NameCamelCase) - 1) + '^' + UPPER(SUBSTRING(#NameCamelCase, CHARINDEX('-', #NameCamelCase) + 1, 1)) + SUBSTRING(#NameCamelCase, CHARINDEX('-', #NameCamelCase) + 2, 50)
ELSE
#NameCamelCase
END
SELECT #NameCamelCase =
CASE
WHEN #NameCamelCase LIKE '%-%'
THEN SUBSTRING(#NameCamelCase, 1, CHARINDEX('-', #NameCamelCase) - 1) + '^' + UPPER(SUBSTRING(#NameCamelCase, CHARINDEX('-', #NameCamelCase) + 1, 1)) + SUBSTRING(#NameCamelCase, CHARINDEX('-', #NameCamelCase) + 2, 50)
ELSE
#NameCamelCase
END
SELECT #NameCamelCase = REPLACE(#NameCamelCase, '^', '-')
-- Multiple word names (do it twice to account for three word names)
SELECT #NameCamelCase =
CASE
WHEN #NameCamelCase LIKE '% %'
THEN SUBSTRING(#NameCamelCase, 1, CHARINDEX(' ', #NameCamelCase) - 1) + '?' + UPPER(SUBSTRING(#NameCamelCase, CHARINDEX(' ', #NameCamelCase) + 1, 1)) + SUBSTRING(#NameCamelCase, CHARINDEX(' ', #NameCamelCase) + 2, 50)
ELSE
#NameCamelCase
END
SELECT #NameCamelCase =
CASE
WHEN #NameCamelCase LIKE '% %'
THEN SUBSTRING(#NameCamelCase, 1, CHARINDEX(' ', #NameCamelCase) - 1) + '?' + UPPER(SUBSTRING(#NameCamelCase, CHARINDEX(' ', #NameCamelCase) + 1, 1)) + SUBSTRING(#NameCamelCase, CHARINDEX(' ', #NameCamelCase) + 2, 50)
ELSE
#NameCamelCase
END
SELECT #NameCamelCase = REPLACE(#NameCamelCase, '?', ' ')
-- Names in Parentheses
SELECT #NameCamelCase =
CASE
WHEN #NameCamelCase LIKE '%(%'
THEN SUBSTRING(#NameCamelCase, 1, CHARINDEX('(', #NameCamelCase) - 1) + '(' + UPPER(SUBSTRING(#NameCamelCase, CHARINDEX('(', #NameCamelCase) + 1, 1)) + SUBSTRING(#NameCamelCase, CHARINDEX('(', #NameCamelCase) + 2, 50)
ELSE
#NameCamelCase
END
SELECT #NameCamelCase = REPLACE(#NameCamelCase, ' sr', ' Sr')
SELECT #NameCamelCase = REPLACE(#NameCamelCase, ' jr', ' Jr')
SELECT #NameCamelCase = REPLACE(#NameCamelCase, ' ii', ' II')
SELECT #NameCamelCase = REPLACE(#NameCamelCase, ' iii', ' III')
SELECT #NameCamelCase = REPLACE(#NameCamelCase, ' DE ', ' de ')
SELECT #NameCamelCase = REPLACE(#NameCamelCase, 'macdonald', 'MacDonald')
if (#NameCamelCase LIKE '% iv')
SELECT #NameCamelCase = REPLACE(#NameCamelCase, ' iv', ' IV')
if ((#NameCamelCase = 'i') or (#NameCamelCase = 'ii') or (#NameCamelCase = 'iii') or (#NameCamelCase = 'iv'))
SELECT #NameCamelCase = UPPER(#NameCamelCase)
-- Return the result of the function
RETURN ISNULL(#NameCamelCase, '')
END
RETURN ISNULL(#NameCamelCase, '')
END
With SQL 2017 the function could look like this:
create function dbo.cap_words (#str varchar(max))
returns varchar(max)
as
begin
declare #result varchar(max);
select #result = string_agg( upper(left(value,1)) + substring(value,2,999),' ') from string_split(lower(#str),' ')
return #result;
end
Like me, many people may be looking for an in-query solution, query creating function, well I figured out a different approach:
SELECT REPLACE(
STUFF(
(SELECT' '+ LTRIM(RTRIM(UPPER(SUBSTRING(value, 1,1))+LOWER(SUBSTRING(value, 2, LEN(value)))))
FROM STRING_SPLIT([Message], ' ')
FOR XML PATH('')
), 1, 1, ''
), ''/*Control delimiters here*/, '') FROM [dbo].[MessageQueue]
Change [MessageQueue] table for your own table, and [Message] for your field.
The function STRING_SPLIT may require to increase your SQL compatibility level to 130.
Use the outer REPLACE function to set any delimiter you want.
Here is simple thing, don't make it complicated.
Oracle:
SELECT initcap(lower('This is MY striNg to conVerT')) FROM dual;
I currently have a function in SQL that places a tilde every 60 characters, but the original text already has tilde's so , basically I want to change that if there is a tilde and the next tilde is under 60 characters away, then skip to the next tilde. If its over 60 characters, then only then place a extra tilde.
My current function looks like;
function [dbo].[AddTilde] (
#string varchar(max),
#count int
)
returns varchar(max)
as
begin
declare #result varchar(max) = ''
declare #token varchar(max) = ''
while DATALENGTH(#string) > 0
begin
select #token = left(#string, #count)
select #string = REPLACE(#string, #token, '')
select #result +=#token +case when DATALENGTH(#string)=0 then '' else '~' end
end
return #result
end
Any help appreciated
Many Thanks
DECLARE #string1 VARCHAR(max),
#string2 VARCHAR(max) = '',
#i1 INT,
#i2 INT
SET #string1 = '12345678901234567890~1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890'
WHILE LEN(#string1) > 0
BEGIN
SET #i1 = CHARINDEX('~',#string1)
SET #i2 = #i1 - (ABS(#i1-60) + (#i1-60)) / 2 -- MINIMUM OF ~ LOCATION AND 60
SET #i1 = LEN(#string1)
IF #i2 = 0 SET #i2 = #i1 - (ABS(#i1-60) + (#i1-60)) / 2 -- MINIMUM OF LENGTH OF #string1 LOCATION AND 60
IF #i2 < 60
BEGIN
SET #string2 = #string2 + LEFT(#string1,#i2)
SET #string1 = RIGHT(#string1,#i1-#i2)
END
ELSE
BEGIN
SET #string2 = #string2 + LEFT(#string1,60) + '~'
SET #string1 = RIGHT(#string1,#i1-60)
END
END
Results: 12345678901234567890~123456789012345678901234567890123456789012345678901234567890~1234567890123456789012345678901234567890
This is based on my String Splitting function, which has a very good performance.
This function should be quite efficient, albeit hard to understand (I have added a few commenets in attempt to make it easier).
You can change internal parameters easily e.g. #Delimitor can be multiple characters
Test cases are included at the bottom.
ALTER FUNCTION [dbo].[AddTilde]
(
#String VARCHAR( MAX ),
#Count INT
)
RETURNS VARCHAR(MAX)
AS
BEGIN
DECLARE #CurrentPosition BIGINT, #NextDelimiterPosition BIGINT, #NextFixedPosition BIGINT, #NextPosition BIGINT,
#DelimiterLength INT, #Delimiter VARCHAR( 5 ), #Result VARCHAR( MAX )
-- Initialise
SET #Delimiter = '~'
SET #DelimiterLength = LEN( #Delimiter )
SET #Result = ''
-- Ensures we can terminate loop without using an extra IF condition, saves a few = 0 checks
SET #String = #String + #Delimiter
SET #CurrentPosition = 1
-- Check for next Tilde position
SET #NextDelimiterPosition = CHARINDEX( #Delimiter, #String, #CurrentPosition )
-- Initialise fixed increment position
SET #NextFixedPosition = #Count
-- Compare, which one is earlier and use that one. Handle cases where the next token begins with Tilde
SET #NextPosition = CASE WHEN #NextDelimiterPosition - #DelimiterLength > #NextFixedPosition THEN #NextFixedPosition ELSE #NextDelimiterPosition END
WHILE #NextDelimiterPosition > 0
BEGIN
SET #Result = #Result + SUBSTRING( #String, #CurrentPosition, #NextPosition - #CurrentPosition + 1 )
-- Handle cases where the next token begins with Tilde and avoids doubling up Tildes
+ ( CASE WHEN #NextPosition = #NextDelimiterPosition THEN '' ELSE #Delimiter END )
SET #CurrentPosition = #NextPosition + 1
-- Increment fixed position
SET #NextFixedPosition = #CurrentPosition + #Count - 1
-- Check for next Tilde position
SET #NextDelimiterPosition = CHARINDEX( #Delimiter, #String, #CurrentPosition )
SET #NextPosition = CASE WHEN #NextDelimiterPosition - #DelimiterLength > #NextFixedPosition THEN #NextFixedPosition ELSE #NextDelimiterPosition END
END
-- Remove trailing Tilde
SET #Result = SUBSTRING( #Result, 1, LEN( #Result ) - #DelimiterLength )
RETURN #Result
END
/* Test Cases
SELECT dbo.[AddTilde]( 'ab~c~defghijkl~', 3 ) --> 'ab~c~def~ghi~jkl~'
SELECT dbo.[AddTilde]( '~ab~c~defghijkl', 3 ) --> '~ab~c~def~ghi~jkl'
SELECT dbo.[AddTilde]( 'ab~c~~defghijkl', 3 ) --> 'ab~c~~def~ghi~jkl'
SELECT dbo.[AddTilde]( 'abcdefghijkl', 3 ) --> 'abc~def~ghi~jkl'
SELECT dbo.[AddTilde]( 'a', 3 ) --> 'a'
*/
Here is the example which i want in output...
I have this input = "Automatic email sent"
But I want this output = "AutomaticEmailSent"
Thanks In Advance!
Use TextInfo.ToTitleCase
// Defines the string with mixed casing.
string myString = "Automatic email sent";
// Creates a TextInfo based on the "en-US" culture.
TextInfo myTI = new CultureInfo("en-US",false).TextInfo;
// Changes a string to titlecase, then replace the spaces with empty
string outputString = myTI.ToTitleCase(myString).Replace(" ", "");
Stealing a function from this answer which takes an text input and make it proper case (otherwise known as title case):
create function ProperCase(#Text as varchar(8000))
returns varchar(8000)
as
begin
declare #Reset bit;
declare #Ret varchar(8000);
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 #c like '[a-zA-Z]' then 0 else 1 end,
#i = #i +1
return #Ret
end
Then you can combine this function with REPLACE:
SELECT REPLACE(dbo.ProperCase(column), ' ', '')
FROM MyTable
SQL Server
declare #value varchar(64) = rtrim(' ' + 'Automatic email sent')
;with t(n) as (
select n = charindex(' ', #value, 0)
union all
select n = charindex(' ', #value, n + 1)
from t
where n > 0
)
select #value = stuff(#value, n + 1, 1, upper(substring(#value, n + 1, 1))) from t where n > 0
select replace(#value, ' ', '')