How to extract catalog name from connection string using TSQL? - tsql

Say I have the following string value:
declare #cs nvarchar(100) =
'Data Source=server\instance;Initial Catalog=MyDatabase;Integrated Security=True';
What is the T-SQL to extract the string MyDatabase from #cs?
Alternately, what T-SQL function(s) should I look at for a means to figure it out for myself?

It's not pretty and i'm sure you could do this faster using regex / clr patindex & substrings, but a very quick and easy way is to use the split functions below to do something like this:
Example Usage:
DECLARE #cs NVARCHAR(100) = 'Data Source=server\instance;Initial Catalog=MyDatabase;Integrated Security=True';
SELECT id ,
Data ,
dbo.fnParseString(2, '=', Data)
FROM dbo.fnc_Split(#cs, ';')
WHERE Data LIKE '%Initial Catalog%'
Returns:
2 Initial Catalog=MyDatabase MyDatabase
As for the split functions, the first splts strings into tables, and the second splits strings into columns:
Split into tables:
CREATE FUNCTION dbo.fnc_Split
(
#Data VARCHAR(2000) ,
#Sep VARCHAR(5)
)
RETURNS #Temp TABLE
(
Id INT IDENTITY(1, 1) ,
Data NVARCHAR(100)
)
AS
BEGIN
DECLARE #Cnt INT
SET #Cnt = 1
WHILE ( CHARINDEX(#Sep, #Data) > 0 )
BEGIN
INSERT INTO #Temp
( data
)
SELECT Data = LTRIM(RTRIM(SUBSTRING(#Data, 1, CHARINDEX(#Sep, #Data) - 1)))
SET #Data = SUBSTRING(#Data, CHARINDEX(#Sep, #Data) + 1, LEN(#Data))
SET #Cnt = #Cnt + 1
END
INSERT INTO #Temp
( data )
SELECT Data = LTRIM(RTRIM(#Data))
RETURN
END
GO
Split into columns:
CREATE FUNCTION dbo.fnParseString
(
#Section SMALLINT ,
#Delimiter CHAR ,
#Text VARCHAR(MAX)
)
RETURNS VARCHAR(8000)
AS
BEGIN
DECLARE #NextPos SMALLINT ,
#LastPos SMALLINT ,
#Found SMALLINT
--#### Uncomment the following 2 lines to emulate PARSENAME functionality
--IF #Section > 0
-- SELECT #Text = REVERSE(#Text)
SELECT #NextPos = CHARINDEX(#Delimiter, #Text, 1) ,
#LastPos = 0 ,
#Found = 1
WHILE #NextPos > 0
AND ABS(#Section) <> #Found
SELECT #LastPos = #NextPos ,
#NextPos = CHARINDEX(#Delimiter, #Text, #NextPos + 1) ,
#Found = #Found + 1
RETURN CASE
WHEN #Found <> ABS(#Section) OR #Section = 0 THEN NULL
--#### Uncomment the following lines to emulate PARSENAME functionality
--WHEN #Section > 0 THEN REVERSE(SUBSTRING(#Text, #LastPos + 1, CASE WHEN #NextPos = 0 THEN DATALENGTH(#Text) - #LastPos ELSE #NextPos - #LastPos - 1 END))
WHEN #Section > 0 THEN SUBSTRING(#Text, #LastPos + 1, CASE WHEN #NextPos = 0 THEN DATALENGTH(#Text) - #LastPos ELSE #NextPos - #LastPos - 1 END)
ELSE SUBSTRING(#Text, #LastPos + 1, CASE WHEN #NextPos = 0 THEN DATALENGTH(#Text) - #LastPos ELSE #NextPos - #LastPos - 1 END)
END
END

you might find this to be a lot easier, just change the from_table to your table and the connection string field to your field.
SELECT SUBSTRING(SUBSTRING([ConnectionString], CHARINDEX('Initial Catalog=', [ConnectionString]) + LEN('Initial Catalog='), 100), 0, CHARINDEX(';', SUBSTRING([ConnectionString], CHARINDEX('Initial Catalog=', [ConnectionString]) + LEN('Initial Catalog='), 100)))
AS DbName
FROM [from_table]
i assume after taking the strings you might want to do something like this:
(iteration over the names and query some dbs)
DECLARE #DbName varchar(max)
DECLARE #QaQuery NVARCHAR(MAX);
DECLARE MY_CURSOR CURSOR
LOCAL STATIC READ_ONLY FORWARD_ONLY
FOR
SELECT SUBSTRING(SUBSTRING([ConnectionString], CHARINDEX('Initial Catalog=', [ConnectionString]) + LEN('Initial Catalog='), 100), 0, CHARINDEX(';', SUBSTRING([ConnectionString], CHARINDEX('Initial Catalog=', [ConnectionString]) + LEN('Initial Catalog='), 100)))
AS Connections
FROM [ConnTable]
OPEN MY_CURSOR
FETCH NEXT FROM MY_CURSOR INTO #DbName
WHILE ##FETCH_STATUS = 0
BEGIN
--PRINT #DbName
SET #QaQuery = N'SELECT TOP 10 [RecordID],[SomeStuff]
FROM ['+#DbName+'].[dbo].[SomeTable]
WHERE [SomeStuff] IS NOT NULL'
EXEC sp_executesql #QaQuery;
FETCH NEXT FROM MY_CURSOR INTO #DbName
END
CLOSE MY_CURSOR
DEALLOCATE MY_CURSOR

Related

How to remove duplicate words from a string comparing to other string in SQL

I need to check string2 if there are words that I already have in string1 and remove those.
declare #Text1 nvarchar(500) = 'apple, orange, pear'
declare #Text2 nvarchar(500) = 'banana, apple'
The output should be 'banana'
Edit: I just realized I made the question not properly.
So, this is right:
DECLARE #Tab TABLE (Ingredients nvarchar(500))
insert #Tab select 'apple, orange, pear'
insert #Tab select 'banana, apple'
insert #Tab select 'pear, mango'
declare #Ingredients nvarchar(4000) = ''
select #Ingredients = #Ingredients + value + ',' from #Tab cross apply STRING_SPLIT(Ingredients, ',')
SELECT #Ingredients
current result:
apple,orange,pear,banana,apple,pear,mango,
expected result:
apple,orange,pear,banana,mango,
So if that is the case I would like to suggest you to create a function:
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE FUNCTION GetUniqueValues
(
#LIST AS NVARCHAR(MAX)
)
RETURNS NVARCHAR(MAX)
AS
BEGIN
DECLARE #POS INT
DECLARE #LEN INT
DECLARE #VALUE NVARCHAR(MAX)
SET #POS = 0
SET #LEN = 0
DECLARE #RESULT NVARCHAR(MAX) = ''
WHILE CHARINDEX(',', #LIST, #POS + 1) > 0
BEGIN
SET #LEN = CHARINDEX(',', #LIST, #POS + 1) - #POS
SET #VALUE = RTRIM(LTRIM(SUBSTRING(#LIST, #POS, #LEN)))
IF (CHARINDEX(#VALUE, #RESULT) = 0)
BEGIN
IF (LEN(RTRIM(LTRIM(#RESULT))) = 0)
BEGIN
SET #RESULT = #VALUE
END
ELSE
BEGIN
SET #RESULT = #RESULT + ', ' + #VALUE
END
END
SET #POS = CHARINDEX(',', #LIST, #POS + #LEN) +1
END
RETURN #RESULT
END
GO
You can use this function like this based on you example:
SELECT dbo.GetUniqueValues(#Ingredients)
Expected result
apple, orange, pear, banana, mango
you can use something like this:
DECLARE #Text1 NVARCHAR(500)= 'apple, orange, pear';
DECLARE #Text2 NVARCHAR(500)= 'banana, apple, cucumber, pear';
--convert to xml for further split using nodes
declare #xmlText1 as xml = convert(xml, Concat('<a>',replace(#Text1,',','</a><a>'),'</a>'))
declare #xmlText2 as xml = convert(xml, Concat('<a>',replace(#Text2,',','</a><a>'),'</a>'))
--remove duplicates from #text2 that exist in #text1
;with cte (val) as (
select ', ' + value
from (
select ltrim(n.value('.', 'VARCHAR(10)')) AS value
from #xmlText2.nodes('a') as t(n)
except
select ltrim(n.value('.', 'VARCHAR(10)')) AS value
from #xmlText1.nodes('a') as t(n)
) as t
for xml path ('')
)
-- reassign #text2
select #Text2 = stuff(val,1,1,'') from cte
--output
select #Text2 as newText2
result
You can try this:
DECLARE #Text1 NVARCHAR(500)= 'apple, orange, pear';
DECLARE #Text2 NVARCHAR(500)= 'banana, apple';
SELECT *
INTO #temp
FROM
(
SELECT *
FROM fn_split(#Text1, ',')
) a;
SELECT *
INTO #temp1
FROM
(
SELECT *
FROM fn_split(#Text2, ',')
) b;
SELECT item
FROM #temp1 a
WHERE NOT EXISTS
(
SELECT item
FROM #temp b
WHERE a.item = b.item
);

Random Number Generator - Create a matrix of random numbers where the values in each row sum to X in T-SQL?

How to generate a matrix of random numbers where the values in each row add up to X in T-SQL?
The solution matrix should be dynamic:
User can specify number of columns to be returned in the result
User can specify number of rows to be returned in the result
Each row must sum to X (eg. 1)
create proc RandomNumberGenerator
(
#rows int
, #cols int
, #rowsumtotal float
)
as
....
First create a UDF...
CREATE FUNCTION [dbo].[_ex_fn_SplitToTable] (#str varchar(5000), #sep varchar(1) = null)
RETURNS #ReturnVal table (n int, s varchar(5000))
AS
/*
Alpha Test
-----------
select * from [dbo].[_ex_fn_SplitToTable_t2]('a b c d e',' ')
*/
BEGIN
if #sep = ' '
begin
set #sep = CHAR(167)
set #str = REPLACE(#str,' ',CHAR(167))
end
declare #str2 varchar(5000)
declare #sep2 varchar(1)
if LEN(ISNULL(#sep,'')) = 0
begin
declare #i int
set #i = 0
set #str2 = ''
declare #char varchar(1)
startloop:
set #i += 1
--print #i
set #char = substring(#str,#i,1)
set #str2 = #str2 + #char + ','
if LEN(#str) <= #i
goto exitloop
goto startloop
exitloop:
set #str2 = left(#str2,LEN(#str2) - 1)
set #sep2 = ','
--print #str2
end
else
begin
set #str2 = #str
set #sep2 = #sep
end
;WITH Pieces(n, start, stop) AS (
SELECT 1, 1, CHARINDEX(#sep2, #str2)
UNION ALL
SELECT n + 1, stop + 1, CHARINDEX(#sep2, #str2, stop + 1)
FROM Pieces
WHERE stop > 0
)
insert into #ReturnVal(n,s)
SELECT n,
SUBSTRING(#str2, start, CASE WHEN stop > 0 THEN stop-start ELSE 5000 END) AS s
FROM Pieces option (maxrecursion 32767)
RETURN
END
GO
Then, create this stored proc...
CREATE PROC [dbo].[RandomNumberGenerator]
(
#Pockets int = 6,
#SumTo float = 100,
#i_iterations int = 100
)
/*
ALPHA TEST
----------
exec RandomNumberGenerator 10, 100, 500
*/
AS
if object_id('tempdb..#_Random_00') is not null drop table #_Random_00
declare #columnstring varchar(max) = (SELECT REPLICATE('c ',#Pockets) as Underline)
print #columnstring
if object_id('tempdb..#_Random_columns') is not null drop table #_Random_columns
select s+CONVERT(varchar,dbo.PadLeft(convert(varchar,n),'0',3)) cols
into #_Random_columns
from [dbo].[_ex_fn_SplitToTable](#columnstring,' ') where LEN(s) > 0
-- ===========================
--select * from #_Random_columns
-- ===========================
declare #columns_sql varchar(max)
set #columns_sql =
(
select distinct
stuff((SELECT distinct + cast(cols as varchar(50)) + ' float, '
FROM (
select cols
from #_Random_columns
) t2
--where t2.n = t1.n
FOR XML PATH('')),3,0,'')
from (
select cols
from #_Random_columns
) t1
)
set #columns_sql = LEFT(#columns_sql,LEN(#columns_sql) - 1)
print #columns_sql
declare #sql varchar(max)
set #sql = 'if object_id(''tempdb..##_proctable_Random_01'') is not null drop table ##_proctable_Random_01 '
print #sql
execute(#sql)
set #sql = 'create table ##_proctable_Random_01 (rowid int,' + #columns_sql + ')'
print #sql
execute(#sql)
declare #TotalOfRand float
declare #i_inner int
declare #i_outer int
set #i_outer = 0
start_outer:
set #i_outer = #i_outer + 1
set #i_inner = 0
declare #sumstring varchar(max)
set #sumstring = ''
start_inner:
set #i_inner = #i_inner+1
set #sumstring = #sumstring + CONVERT(varchar, rand()) + ','
if #i_inner >= #Pockets
goto exit_inner
goto start_inner
exit_inner:
set #TotalOfRand = ( select sum(convert(float,s)) from dbo._ex_fn_SplitToTable(#sumstring,',') )
declare #sumstring_quotient varchar(max)
set #sumstring_quotient = replace(#sumstring,',', '/' + Convert(varchar,#TotalOfRand) + '*' + convert(varchar,#SumTo) + ',')
set #sumstring_quotient = LEFT(#sumstring_quotient,len(#sumstring_quotient) - 1)
print #sumstring_quotient
set #sql = '
insert into ##_proctable_Random_01
select
( select count(*) + 1 from ##_proctable_Random_01 ) rowid,' + #sumstring_quotient
execute(#sql)
if #i_outer >= #i_iterations
goto exit_outer
goto start_outer
exit_outer:
select * from ##_proctable_Random_01
drop table ##_proctable_Random_01
GO

T-SQL Syntax - word parser

I have a word list in a variable. The words are comma delimited. I am trying to save them to individual record in a database. I found another question which does this and works, but it saves every word. I tried to modify it so that I only save unique words, and count duplicate as I go. I think the logic below is correct, but my syntax is not working.
if #FoundWord = 0 SET #WordsUsed = #WordsUsed + '' + #Word + ''
** is not concatenating the next word onto the end of the #WordsUsed variable
if #FoundWord = 0 INSERT INTO mydata.dbo.words (ProjectNumber, WordCount, Word) VALUES( '5', '1', #Word )
** doesn't seem to be doing anything at all ... I'm getting no records written to the words table
The entire code follows:
declare #SplitOn nvarchar(5) = ','
BEGIN
DECLARE #split_on_len INT = LEN(#SplitOn)
DECLARE #start_at INT = 1
DECLARE #end_at INT
DECLARE #data_len INT
DECLARE #WordsUsed varchar(max)
DECLARE #FoundWord int
DECLARE #Word varchar(100)
Set #WordsUsed = '**'
WHILE 1=1
BEGIN
SET #end_at = CHARINDEX(#SplitOn,#txt1,#start_at)
SET #data_len = CASE #end_at WHEN 0 THEN LEN(#txt1) ELSE #end_at-#start_at END
set #Word = SUBSTRING(#txt1,#start_at,#data_len)
SET #FoundWord = CHARINDEX('*' & #Word & '*', #WordsUsed)
if #FoundWord = 0 SET #WordsUsed = #WordsUsed & '*' & #Word & '*'
if #FoundWord = 0 INSERT INTO mydata.dbo.words (ProjectNumber, WordCount, Word) VALUES( '5', '1', #Word )
if #FoundWord > 0 Update mydata.dbo.words set WordCount = WordCount + 1 where projectnumber = 5 and word = #word
IF #end_at = 0 BREAK
SET #start_at = #end_at + #split_on_len
END
RETURN
END;
Here's a function you can try. It splits an unlimited-sized string based on the delimiter of your choice. The output of the function is a table - so you can then select distinct from that to store your word list. You'd call it something like:
insert YourWordlistTable ( Word ) --> just making up table/column names here
select distinct Data
from dbo.StringSplit( #yourVar, ',' )
Here's the definition of the function:
create function dbo.StringSplit
(
#string nvarchar( max ),
#delimiter nvarchar( 255 )
)
returns #t table ( Id int, Data nvarchar( 4000 ) )
as begin
with Split( startPosition, endPosition )
as
(
select
cast( 0 as bigint ) as startPosition,
charindex( #delimiter, #string ) as endPosition
union all
select
endPosition + 1, charindex( #delimiter, #string, endPosition + 1 )
from
Split
where
endPosition > 0
)
insert #t
select
row_number() over ( order by ( select 1 ) ) as Id,
substring( #string, startPosition, coalesce( nullif( endPosition, 0 ), len( #string ) + 1 ) - startPosition ) collate Latin1_General_CS_AS as Data
from
Split
option( maxrecursion 0 );
return;
end
I originally posted, then deleted, then reposted this when I realized I'd given an inline function I use that never got called on strings longer than 100 words. I've since modified it to support indefinite recursion - although it can't be an inline function this way.
Inline functions are generally faster because SQL can incorporate the inline function's statement into the query plan of the statements that call the function. However, that does not appear to be an option available in an inline function.
why can't you use
select count(distinct value) from dbo.split(',',#txt1) where #txt1 is he string will give you distinct count of words
If you are looking for word wise count
select value,count(1) from dbo.split(',',#txt1) group by value
shoud do this

How do I get the return value from a CASE statement as a dynamic SQL query?

DECLARE #command = 'SELECT CASE WHEN ( SELECT COUNT(*)
FROM [table] WITH ( NOLOCK )
WHERE DATEDIFF(minute, systemupdatedtimestamp, GETDATE()) < 10
) > 0 THEN 0
ELSE 1
END'
Now I need to get the 'return value' of the above command (0 or 1).
EXEC(#commnad)
How do I get the return value from the above?
I tried
SET #ReturnValue = EXEC(#command)
but no luck.
use sp_executesql
in your case it is something like:
declare #myOut bit
declare #SQLString nvarchar(500)
set #SQLString = N'SELECT #myOutValue = CASE WHEN ( SELECT COUNT(*)
FROM [table] WITH ( NOLOCK )
WHERE DATEDIFF(minute, systemupdatedtimestamp, GETDATE()) < 10
) > 0 THEN 0
ELSE 1
END'
declare #ParmDefinition nvarchar(500)
set #ParmDefinition = N'#myOutValue bit OUTPUT'
EXECUTE sp_executesql #SQLString, #ParmDefinition, #myOutValue = #myOut OUTPUT
select #myOut
Update:
To follow up on you comment. If you do not want to modify the original string containing your sql command (e.g. it is used in other places etc) you can wrap that command inside a new string something like:
#SQLString = 'select #myOutValue = (' + #yourOrigSqlCommand + ')'
And call sp_executesql in the same way as above.
I believe this solves your problem.
DECLARE #command nvarchar(max) = 'SELECT CASE WHEN ( SELECT COUNT(*)
FROM [table] WITH ( NOLOCK )
WHERE DATEDIFF(minute, systemupdatedtimestamp, GETDATE()) < 10
) > 0 THEN 0
ELSE 1
END'
exec sp_executesql #command

T-SQL Space Before Capital Letters

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.
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
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.
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)
CLR and regular expressions or 26 replace statements a case sensitive collate clause and a trim.
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')
**/
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
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.