Reputation: 1990
The question is self explanatory. Could you please point out a way to put spaces between each capital letter of a string.
SELECT dbo.SpaceBeforeCap('ThisIsATestString')
would result in
This Is A Test String.
Upvotes: 14
Views: 13122
Reputation: 39
To avoid loops altogether, use of a tally table can help here. If you are running on SQL 2022, then the generate_series
function can remove even this dependency. This method will be significantly faster than iterating through a loop.
create function core.ufnAddSpaceBeforeCapital
(
@inputString nvarchar(max)
)
returns nvarchar(max)
as
begin
declare @outputString nvarchar(max)
select
@outputString = string_agg(iif(t.value = 1, upper(substring(@inputString,t.value,1)),iif(ascii(substring(@inputString,t.value,1)) between 65 and 90, ' ','') + substring(@inputString,t.value,1)),'')
from
generate_series(1,cast(len(@inputString) as int)) t
return @outputString
end
The scalar function is not inlineable, so I've provided an alternative inline table-valued function if that's what you need.
create function core.ufnAddSpaceBeforeCapitalITVF
(
@inputString nvarchar(max)
)
returns table
as
return
(
select
string_agg(iif(t.value = 1, upper(substring(@inputString,t.value,1)),iif(ascii(substring(@inputString,t.value,1)) between 65 and 90, ' ','') + substring(@inputString,t.value,1)),'') as outputString
from
generate_series(1,cast(len(@inputString) as int)) t
)
end
Upvotes: 2
Reputation: 376
While I really like the char looping answers I was not thrilled with the performance. I have found this performs in a fraction of the time for my use case.
CREATE function SpaceBeforeCap
(@examine nvarchar(max))
returns nvarchar(max)
as
begin
DECLARE @index as INT
SET @index = PatIndex( '%[^ ][A-Z]%', @examine COLLATE Latin1_General_BIN)
WHILE @index > 0 BEGIN
SET @examine = SUBSTRING(@examine, 1, @index) + ' ' + SUBSTRING(@examine, @index + 1, LEN(@examine))
SET @index = PatIndex( '%[^ ][A-Z]%', @examine COLLATE Latin1_General_BIN)
END
RETURN LTRIM(@examine)
end
This makes use of the fact that
case sensitive pattern search only works in some collations. The character class [^ ]
means anything except space, so as we add the missing spaces we match farther into the string until it is complete.
Upvotes: 1
Reputation: 2580
This function combines previous answers. Selectively choose to preserve adjacent CAPS:
CREATE FUNCTION SpaceBeforeCap (
@InputString NVARCHAR(MAX),
@PreserveAdjacentCaps BIT
)
RETURNS NVARCHAR(MAX)
AS
BEGIN
DECLARE
@i INT, @j INT,
@previous NCHAR, @current NCHAR, @next NCHAR,
@result NVARCHAR(MAX)
SELECT
@i = 1,
@j = LEN(@InputString),
@result = ''
WHILE @i <= @j
BEGIN
SELECT
@previous = SUBSTRING(@InputString,@i-1,1),
@current = SUBSTRING(@InputString,@i+0,1),
@next = SUBSTRING(@InputString,@i+1,1)
IF @current = UPPER(@current) COLLATE Latin1_General_CS_AS
BEGIN
-- Add space if Current is UPPER
-- and either Previous or Next is lower or user chose not to preserve adjacent caps
-- and Previous or Current is not already a space
IF @current = UPPER(@current) COLLATE Latin1_General_CS_AS
AND (
@previous <> UPPER(@previous) COLLATE Latin1_General_CS_AS
OR @next <> UPPER(@next) collate Latin1_General_CS_AS
OR @PreserveAdjacentCaps = 0
)
AND @previous <> ' '
AND @current <> ' '
SET @result = @result + ' '
END
SET @result = @result + @current
SET @i = @i + 1
END
RETURN @result
END
GO
SELECT dbo.SpaceBeforeCap('ThisIsASampleDBString', 1)
GO
SELECT dbo.SpaceBeforeCap('ThisIsASampleDBString', 0)
Upvotes: 6
Reputation: 162
Another strategy would be to check the ascii value of each character:
create function SpaceBeforeCap
(@str nvarchar(max))
returns nvarchar(max)
as
begin
declare @result nvarchar(max)= left(@str, 1),
@i int = 2
while @i <= len(@str)
begin
if ascii(substring(@str, @i, 1)) between 65 and 90
select @result += ' '
select @result += substring(@str, @i, 1)
select @i += 1
end
return @result
end
/***
SELECT dbo.SpaceBeforeCap('ThisIsATestString')
**/
Upvotes: 1
Reputation: 111
This will add spaces only if the previous and next character is lowercase. That way 'MyABCAnalysis' will be 'My ABC Analysis'.
I added a check for a previous space too. Since some of our strings are prefixed with 'GR_' and some also contain underscores, we can use the replace function as follows:
select dbo.GR_SpaceBeforeCap(replace('GR_ABCAnalysis_Test','_',' ')) Returns 'GR ABC Analysis Test'
CREATE FUNCTION GR_SpaceBeforeCap (
@str nvarchar(max)
)
returns nvarchar(max)
as
begin
declare
@i int, @j int
, @cp nchar, @c0 nchar, @c1 nchar
, @result nvarchar(max)
select
@i = 1
, @j = len(@str)
, @result = ''
while @i <= @j
begin
select
@cp = substring(@str,@i-1,1)
, @c0 = substring(@str,@i+0,1)
, @c1 = substring(@str,@i+1,1)
if @c0 = UPPER(@c0) collate Latin1_General_CS_AS
begin
-- Add space if Current is UPPER
-- and either Previous or Next is lower
-- and Previous or Current is not already a space
if @c0 = UPPER(@c0) collate Latin1_General_CS_AS
and (
@cp <> UPPER(@cp) collate Latin1_General_CS_AS
or @c1 <> UPPER(@c1) collate Latin1_General_CS_AS
)
and @cp <> ' '
and @c0 <> ' '
set @result = @result + ' '
end -- if @co
set @result = @result + @c0
set @i = @i + 1
end -- while
return @result
end
Upvotes: 11
Reputation: 32690
Assuming SQL Server 2005 or later, this modified from code taken here: http://www.kodyaz.com/articles/case-sensitive-sql-split-function.aspx
CREATE FUNCTION SpaceBeforeCap
(
@str nvarchar(max)
)
returns nvarchar(max)
as
begin
declare @i int, @j int
declare @returnval nvarchar(max)
set @returnval = ''
select @i = 1, @j = len(@str)
declare @w nvarchar(max)
while @i <= @j
begin
if substring(@str,@i,1) = UPPER(substring(@str,@i,1)) collate Latin1_General_CS_AS
begin
if @w is not null
set @returnval = @returnval + ' ' + @w
set @w = substring(@str,@i,1)
end
else
set @w = @w + substring(@str,@i,1)
set @i = @i + 1
end
if @w is not null
set @returnval = @returnval + ' ' + @w
return ltrim(@returnval)
end
This can then be called just as you have suggested above.
Upvotes: 9
Reputation: 453327
CLR and regular expressions or 26 replace statements a case sensitive collate clause and a trim.
Upvotes: 1