comma separated string using CTE - sql-server

I have a string '1,2,3,4,5,6,', i want the result in array like :
1
2
3
4
5
I have tried it using function and also done by convertingit to the xml.
I have a query:
with cte1 (str1,str2) AS
(
SELECT SUBSTRING('1,2,3,4,5,6,',1,1) X,
SUBSTRING('1,2,3,4,5,6,',CHARINDEX(',','1,2,3,4,5,6,,') +1,LEN('1,2,3,4,5,6,')-2) Y
UNION all
SELECT SUBSTRING(str2,1,1) X ,SUBSTRING(str2,CHARINDEX(',',str2)+1,LEN(str2)-2) Y
FROM CTE1
WHERE SUBSTRING(str2,CHARINDEX(',',str2)+0,1) <> ' ' )
SELECT str1 FROM CTE1;
which gives the result as expected.
but if i am changing the string it gives random reults like :
with cte1 (str1,str2) AS
(
SELECT SUBSTRING('24,78,45,56,',1,1) X,
SUBSTRING('24,78,45,56,',CHARINDEX(',','24,78,45,56,') +1,LEN('24,78,45,56,')-2) Y
UNION all
SELECT SUBSTRING(str2,1,1) X ,SUBSTRING(str2,CHARINDEX(',',str2)+1,LEN(str2)-2) Y
FROM CTE1
WHERE SUBSTRING(str2,CHARINDEX(',',str2)+0,1) <> ' ' )
SELECT str1 FROM CTE1;
result :
2
7
4
5

This will work only when string is like '12,34,45,56....'i.e string contains two digit comm separated values
with cte1 (str1,str2) AS
(
SELECT SUBSTRING('24,78,45,56,',1,2) X,
SUBSTRING('24,78,45,56,',CHARINDEX(',','24,78,45,56,') +1,LEN('24,78,45,56,')-2) Y
UNION all
SELECT SUBSTRING(str2,1,2) X ,SUBSTRING(str2,CHARINDEX(',',str2)+1,LEN(str2)-2) Y
FROM CTE1
WHERE SUBSTRING(str2,CHARINDEX(',',str2)+0,2) <> ' ' )
SELECT str1 FROM CTE1;
You should go with generic solution by creating on user define function which accepts comma separated string and give table value for this string
Function definition like this
CREATE FUNCTION SplitItem( #ItemIDs VARCHAR(MAX))
RETURNS #ItemTable TABLE ( Item VARCHAR(200) )
AS
BEGIN
DECLARE #Item VARCHAR(200)
DECLARE #Index INT
WHILE LEN(#ItemIDs) <> 0
BEGIN
SET #Index = PATINDEX('%,%', #ItemIDs)
IF #Index > 0
BEGIN
SET #Item = SUBSTRING(#ItemIDs, 1, #Index - 1)
SET #ItemIDs = RIGHT(#ItemIDs, LEN(#ItemIDs) - #Index)
INSERT INTO #ItemTable
VALUES ( #Item )
END
ELSE
BEGIN
BREAK
END
END
SET #Item = #ItemIDs
INSERT INTO #ItemTable
VALUES ( #Item )
RETURN
END
And Use this function like this
SELECT Item
FROM SplitItem('1,2,3,44,55,66,77')
This will gives output like this
1
2
3
44
55
66
77

You could use a recursive CTE
Declare #list NVARCHAR(MAX) = '1,2,3,4,5'
DECLARE #length INT = LEN(#list) + 1;
WITH a AS
(
SELECT
[start] = 1,
[end] = COALESCE(NULLIF(CHARINDEX(',',
#List, 1), 0), #length),
[value] = SUBSTRING(#list, 1,
COALESCE(NULLIF(CHARINDEX(',',
#List, 1), 0), #length) - 1)
UNION ALL
SELECT
[start] = CONVERT(INT, [end]) + 1,
[end] = COALESCE(NULLIF(CHARINDEX(',',
#list, [end] + 1), 0), #length),
[value] = SUBSTRING(#list, [end] + 1,
COALESCE(NULLIF(CHARINDEX(',',
#list, [end] + 1), 0), #length)-[end]-1)
FROM a
WHERE [end] < #length
)
SELECT [value]
FROM a
WHERE LEN([value]) > 0
OPTION (MAXRECURSION 0);

You can do something like this:
DECLARE #string NVARCHAR(MAX) = '1,2,3,4,5,6,',
#xml xml
select #xml = cast('<d><q>'+REPLACE(#string,',','</q><q>')+'</q></d>' as xml)
SELECT n.v.value('.','nvarchar(2)')
FROM #xml.nodes('/d/q') AS n(v);
The result:
----
1
2
3
4
5
6
(7 row(s) affected)

Related

Char shifting for given string

Suppose we have a varchar value like 'Durgesh' and I want to increase (Char +2) of every characters which are given in 'Durgesh'. Example:
D --> F
U --> W
R --> T
G --> I
E --> G
S --> U
H --> J
So if we put the parameter 'Durgesh', then we need the output 'Fwtiguj'.
How can I implement this with a SQL query?
This would be your function:
CREATE FUNCTION [dbo].[shifting](#Input varchar(100))
RETURNS varchar(100)
AS
BEGIN
declare
#i INT=0,
#a varchar(100) =''
while #i<LEN(#Input)
BEGIN
SET #i = #i +1
set #a = #a + char(ascii(substring(#Input,#i,1))+2)
end
RETURN #a
END
You call it as:
select [dbo].[shifting]('Durgesh') as [a]
Here is a demo
Here is a new function that will resolve the YZ and yz problem:
CREATE FUNCTION [dbo].[shifting](#Input varchar(100))
RETURNS varchar(100)
AS
BEGIN
declare
#i INT=0,
#a varchar(100) =''
while #i<LEN(#Input)
BEGIN
SET #i = #i +1
if substring(#Input,#i,1) in ('y','Y')
if ascii(substring(#Input,#i,1)) = ascii('y')
set #a = #a + 'a'
else
set #a = #a + 'A'
else
if substring(#Input,#i,1) in ('z','Z')
if ascii(substring(#Input,#i,1)) = ascii('z')
set #a = #a + 'b'
else
set #a = #a + 'B'
else
set #a = #a + char(ascii(substring(#Input,#i,1))+2)
end
RETURN #a
END
Here is a demo
Use below code :-
declare #val varchar(10) = 'Durgesh'
DECLARE #i INT = 0
WHILE #i < LEN(#val)
BEGIN
PRINT( CHAR( ASCII(SUBSTRING(#val,#i,1)) +2 ) )
SET #i = #i + 1
End
You can do this pretty easily with a tally table. I keep one on my database a view. Here is the code for my tally table. The biggest advantage a tally table has here is we don't have to do any looping!!!
create View [dbo].[cteTally] as
WITH
E1(N) AS (select 1 from (values (1),(1),(1),(1),(1),(1),(1),(1),(1),(1))dt(n)),
E2(N) AS (SELECT 1 FROM E1 a, E1 b), --10E+2 or 100 rows
E4(N) AS (SELECT 1 FROM E2 a, E2 b), --10E+4 or 10,000 rows max
cteTally(N) AS
(
SELECT ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) FROM E4
)
select N from cteTally
Now that we have a tally table we can use it to make your character shift. This will work on nearly any string (until you hit the top end of the ASCII values).
declare #Val varchar(10) = 'Durgesh'
select stuff((
select char(ASCII(substring(#Val, t.N, 1)) + 2)
from cteTally t
where t.N <= len(#Val)
order by t.N
FOR XML PATH('')), 1, 0, '')
--EDIT--
Here is a new version that will "wrap" the characters back to the beginning.
declare #Val varchar(100) = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'
select stuff((
select case when (ASCII(substring(#Val, t.N, 1)) > 88 AND ASCII(substring(#Val, t.N, 1)) < 91) --Upper case
OR ASCII(substring(#Val, t.N, 1)) > 120 --lower case
then char(ASCII(substring(#Val, t.N, 1)) - 24) else char(ASCII(substring(#Val, t.N, 1)) + 2) end
from cteTally t
where t.N <= len(#Val)
order by t.N
FOR XML PATH('')), 1, 0, '')

Convert Csv string to a table and get the position

The following code converts the CSV string to a table with its original order (the order is returned in the column Pos).
declare #input nvarchar(max) = 'x,y,z';
declare #x xml = '<s>' + replace(#input, ',', '</s><s>') + '</s>';
select c.value('.', 'nvarchar(max)') Col, identity(int) Pos
into #t
from #x.nodes('/s') x ( c );
select * from #t;
drop table #t
returns
Col Pos
y 1
x 2
z 3
There are two problem
The Pos is not right, y got 1 and x got 2.
identity(int) requires select into
For input of 'z,y,x', z, y, and x should get 1, 2, and 3 respectively.
This is not guaranteed to be stable at all:
declare #input nvarchar(max) = 'x,y,z';
declare #x xml = '<s>' + replace(#input, ',', '</s><s>') + '</s>';
select c.value('.', 'nvarchar(max)') Col,
ROW_NUMBER() OVER(ORDER BY (SELECT 1)) AS pos
from #x.nodes('/s') x ( c )
OPTION (MAXDOP 1);
DBFiddle Demo
Disable parallelism
Identity(int) -> ROW_NUMBER
Modified split function:
CREATE FUNCTION dbo.Split(#origString varchar(max), #Delimiter char(1))
returns #temptable TABLE (pos INT IDENTITY(1,1), items varchar(max))
as
begin
declare #idx int
declare #split varchar(max)
select #idx = 1
if len(#origString )<1 or #origString is null return
while #idx!= 0
begin
set #idx = charindex(#Delimiter,#origString)
if #idx!=0
set #split= left(#origString,#idx - 1)
else
set #split= #origString
if(len(#split)>0)
insert into #temptable(Items) values(#split)
set #origString= right(#origString,len(#origString) - #idx)
if len(#origString) = 0 break
end
return
end
Call:
SELECT *
FROM dbo.Split('z,y,x', ',');
DBFiddle Demo2

How to get all the single number from [number -(to) number] with comma-seperated in a column in sql server

column of data in sqlserver like
numbers
1000-1050, 1054, 1090-1230, 1245
numbers
-------
1000-1050, 1054, 1090-1230, 1245
how to get the style like below:
numbers
-------
1000
1001
1002
1003
1004
...
1050
1054
1090
1091
1092
...
1245
You could use a split function and APPLY like this
DECLARE #SampleData AS TABLE
(
numbers varchar(200)
)
INSERT INTO #SampleData
VALUES ('1000-1050, 1054, 1090-1230, 1245')
;WITH temp AS
(
SELECT 1 AS Number
UNION ALL
SELECT t.Number + 1 FROM temp t
WHERE t.Number < 5000
) -- return table from 1 --> 5000
SELECT DISTINCT
ca2.*
FROM #SampleData sd
CROSS APPLY
(
SELECT pos, LTRIM(Value) AS Value
FROM dbo.SplitString(sd.Numbers,',')
) ca1
CROSS APPLY
(
SELECT *
FROM temp t WHERE t.Number BETWEEN LEFT(ca1.[Value], charindex('-', ca1.[Value] + '-') - 1) AND
CASE
WHEN len(ca1.[Value]) < charindex('-', ca1.[Value] + '-') THEN ca1.[Value]
ELSE RIGHT(ca1.[Value], len(ca1.[Value]) - charindex('-', ca1.[Value] + '-'))
END
) ca2
OPTION (MAXRECURSION 0)
Split function
CREATE FUNCTION [dbo].[SplitString] (#Text varchar(max),#Delimiter varchar(10))
Returns Table
As
Return (
Select Pos = Row_Number() over (Order By (Select null))
,Value = LTrim(RTrim(B.i.value('(./text())[1]', 'varchar(max)')))
From (Select x = Cast('<x>'+ Replace(#Text,#Delimiter,'</x><x>')+'</x>' as xml).query('.')) as A
Cross Apply x.nodes('x') AS B(i)
);
Demo link: http://rextester.com/GIPGR78132
First split the comma seperated values, then get the values using a recursive common table expression.
Declare #values nvarchar(max) = '1000-1050, 1054, 1090-1230, 1245'
;with ranges_cte as
(
select cast(case pos when 0 then ResultValue else left(ResultValue,pos-1) end as int) first, cast(case pos when 0 then ResultValue else substring(ResultValue,pos+1,len(ResultValue)-pos) end as int) Last
from (
select ResultValue, charINDEx('-',ResultValue) pos
from dbo.SplitString(#values,',')) x
)
, values_rte as
(
select first,last,first as active
from ranges_cte
union all
select first,last,active +1 as active
from values_rte
where active< last
)
select *
from values_rte
OPTION (MAXRECURSION 0)
Function to split the values:
Create FUNCTION [dbo].[SplitString] (#StringArray NVARCHAR(MAX), #Delimiter NVARCHAR(10))
RETURNS #ResultedValues table
(
nr int,
ResultValue nvarchar(max)
)
AS
--select * from dbo.splitstring ('123,456,789',',')
BEGIN
declare #string nvarchar(max)
declare #nr int = 1
set #string = #StringArray
WHILE (CHARINDEX(#Delimiter,#String)>0)
BEGIN
INSERT INTO #ResultedValues (nr,ResultValue) VALUES (#nr,LTRIM(RTRIM(SUBSTRING(#String,1,CHARINDEX(#Delimiter,#String)-1))))
SET #String = SUBSTRING(#String, CHARINDEX(#Delimiter,#String)+LEN(#Delimiter),LEN(#String))
set #nr = #nr +1
END
INSERT INTO #ResultedValues (nr,ResultValue ) VALUES ( #nr, LTRIM(RTRIM(#String)))
RETURN
END
Create a split function:
CREATE FUNCTION [dbo].[SplitIDsTest]
(
#idList varchar(4000)
)
RETURNS #parsedList TABLE
(
Id varchar(50),
Nr int
)
AS
BEGIN
DECLARE #id varchar(10), #pos int
DECLARE #nr int = 0;
SET #idList = LTRIM(RTRIM(#idList)) + ','
SET #Pos = CHARINDEX(',', #idList)
IF REPLACE(#idList, ',', '') <> ''
BEGIN
WHILE #Pos > 0
BEGIN
SET #id = LTRIM(RTRIM(LEFT(#idList, #pos - 1)))
IF #id <> ''
BEGIN
set #nr += 1;
INSERT INTO #ParsedList (Id, Nr)
VALUES (#id, #nr);
END
SET #idList = RIGHT(#idList, LEN(#idList) - #pos) -- 'WMPC,' (inklusive Komma) links vom Eingabeparameter abschneiden, weil jetzt der nächste Wert gesucht wird
SET #pos = CHARINDEX(',', #idList, 1) -- Nächste Position eines Kommas suchen und in der WHILE-Schleife weitermachen
END
END
RETURN
END
Then a tally table:
select top 1000000 N=identity(int, 1, 1)
into dbo.Tally
from master.dbo.syscolumns a cross join master.dbo.syscolumns b;
Then use:
select distinct Tally.N
from SplitIDsTest('10-12, 34, 9') splitted
join Tally on 1 = case
when CHARINDEX('-', splitted.Id) > 0 then
case
when Tally.N between cast(left(splitted.Id, CHARINDEX('-', splitted.Id) - 1) as int)
and cast(right(splitted.Id, len(splitted.Id) - CHARINDEX('-', splitted.Id)) as int) then 1
else 0
end
when Tally.N = cast(splitted.Id as int) then 1
else 0
end
order by Tally.N

T-SQL XML Reader causing Performance Bottleneck [duplicate]

I’m looking to split '1,2,3,4,5,6,7,8,9,10,11,12,13,14,15...' (comma delimited) into a table or table variable.
Does anyone have a function that returns each one in a row?
Try this
DECLARE #xml xml, #str varchar(100), #delimiter varchar(10)
SET #str = '1,2,3,4,5,6,7,8,9,10,11,12,13,14,15'
SET #delimiter = ','
SET #xml = cast(('<X>'+replace(#str, #delimiter, '</X><X>')+'</X>') as xml)
SELECT C.value('.', 'varchar(10)') as value FROM #xml.nodes('X') as X(C)
OR
DECLARE #str varchar(100), #delimiter varchar(10)
SET #str = '1,2,3,4,5,6,7,8,9,10,11,12,13,14,15'
SET #delimiter = ','
;WITH cte AS
(
SELECT 0 a, 1 b
UNION ALL
SELECT b, CHARINDEX(#delimiter, #str, b) + LEN(#delimiter)
FROM CTE
WHERE b > a
)
SELECT SUBSTRING(#str, a,
CASE WHEN b > LEN(#delimiter)
THEN b - a - LEN(#delimiter)
ELSE LEN(#str) - a + 1 END) value
FROM cte WHERE a > 0
Many more ways of doing the same is here How to split comma delimited string?
Here is somewhat old-fashioned solution:
/*
Splits string into parts delimitered with specified character.
*/
CREATE FUNCTION [dbo].[SDF_SplitString]
(
#sString nvarchar(2048),
#cDelimiter nchar(1)
)
RETURNS #tParts TABLE ( part nvarchar(2048) )
AS
BEGIN
if #sString is null return
declare #iStart int,
#iPos int
if substring( #sString, 1, 1 ) = #cDelimiter
begin
set #iStart = 2
insert into #tParts
values( null )
end
else
set #iStart = 1
while 1=1
begin
set #iPos = charindex( #cDelimiter, #sString, #iStart )
if #iPos = 0
set #iPos = len( #sString )+1
if #iPos - #iStart > 0
insert into #tParts
values ( substring( #sString, #iStart, #iPos-#iStart ))
else
insert into #tParts
values( null )
set #iStart = #iPos+1
if #iStart > len( #sString )
break
end
RETURN
END
In SQL Server 2008 you can achieve the same with .NET code. Maybe it would work faster, but definitely this approach is easier to manage.
You've tagged this SQL Server 2008 but future visitors to this question (using SQL Server 2016+) will likely want to know about STRING_SPLIT.
With this new builtin function you can now just use
SELECT TRY_CAST(value AS INT)
FROM STRING_SPLIT ('1,2,3,4,5,6,7,8,9,10,11,12,13,14,15', ',')
Some restrictions of this function and some promising results of performance testing are in this blog post by Aaron Bertrand.
This is most like .NET, for those of you who are familiar with that function:
CREATE FUNCTION dbo.[String.Split]
(
#Text VARCHAR(MAX),
#Delimiter VARCHAR(100),
#Index INT
)
RETURNS VARCHAR(MAX)
AS BEGIN
DECLARE #A TABLE (ID INT IDENTITY, V VARCHAR(MAX));
DECLARE #R VARCHAR(MAX);
WITH CTE AS
(
SELECT 0 A, 1 B
UNION ALL
SELECT B, CONVERT(INT,CHARINDEX(#Delimiter, #Text, B) + LEN(#Delimiter))
FROM CTE
WHERE B > A
)
INSERT #A(V)
SELECT SUBSTRING(#Text,A,CASE WHEN B > LEN(#Delimiter) THEN B-A-LEN(#Delimiter) ELSE LEN(#Text) - A + 1 END) VALUE
FROM CTE WHERE A >0
SELECT #R
= V
FROM #A
WHERE ID = #Index + 1
RETURN #R
END
SELECT dbo.[String.Split]('121,2,3,0',',',1) -- gives '2'
here is the split function that u asked
CREATE FUNCTION [dbo].[split](
#delimited NVARCHAR(MAX),
#delimiter NVARCHAR(100)
) RETURNS #t TABLE (id INT IDENTITY(1,1), val NVARCHAR(MAX))
AS
BEGIN
DECLARE #xml XML
SET #xml = N'<t>' + REPLACE(#delimited,#delimiter,'</t><t>') + '</t>'
INSERT INTO #t(val)
SELECT r.value('.','varchar(MAX)') as item
FROM #xml.nodes('/t') as records(r)
RETURN
END
execute the function like this
select * from dbo.split('1,2,3,4,5,6,7,8,9,10,11,12,13,14,15',',')
DECLARE
#InputString NVARCHAR(MAX) = 'token1,token2,token3,token4,token5'
, #delimiter varchar(10) = ','
DECLARE #xml AS XML = CAST(('<X>'+REPLACE(#InputString,#delimiter ,'</X><X>')+'</X>') AS XML)
SELECT C.value('.', 'varchar(10)') AS value
FROM #xml.nodes('X') as X(C)
Source of this response:
http://sqlhint.com/sqlserver/how-to/best-split-function-tsql-delimited
I am tempted to squeeze in my favourite solution. The resulting table will consist of 2 columns: PosIdx for position of the found integer; and Value in integer.
create function FnSplitToTableInt
(
#param nvarchar(4000)
)
returns table as
return
with Numbers(Number) as
(
select 1
union all
select Number + 1 from Numbers where Number < 4000
),
Found as
(
select
Number as PosIdx,
convert(int, ltrim(rtrim(convert(nvarchar(4000),
substring(#param, Number,
charindex(N',' collate Latin1_General_BIN,
#param + N',', Number) - Number))))) as Value
from
Numbers
where
Number <= len(#param)
and substring(N',' + #param, Number, 1) = N',' collate Latin1_General_BIN
)
select
PosIdx,
case when isnumeric(Value) = 1
then convert(int, Value)
else convert(int, null) end as Value
from
Found
It works by using recursive CTE as the list of positions, from 1 to 100 by default. If you need to work with string longer than 100, simply call this function using 'option (maxrecursion 4000)' like the following:
select * from FnSplitToTableInt
(
'9, 8, 7, 6, 5, 4, 3, 2, 1, 0, ' +
'9, 8, 7, 6, 5, 4, 3, 2, 1, 0, ' +
'9, 8, 7, 6, 5, 4, 3, 2, 1, 0, ' +
'9, 8, 7, 6, 5, 4, 3, 2, 1, 0, ' +
'9, 8, 7, 6, 5, 4, 3, 2, 1, 0'
)
option (maxrecursion 4000)
CREATE FUNCTION Split
(
#delimited nvarchar(max),
#delimiter nvarchar(100)
) RETURNS #t TABLE
(
-- Id column can be commented out, not required for sql splitting string
id int identity(1,1), -- I use this column for numbering splitted parts
val nvarchar(max)
)
AS
BEGIN
declare #xml xml
set #xml = N'<root><r>' + replace(#delimited,#delimiter,'</r><r>') + '</r></root>'
insert into #t(val)
select
r.value('.','varchar(max)') as item
from #xml.nodes('//root/r') as records(r)
RETURN
END
GO
usage
Select * from dbo.Split(N'1,2,3,4,6',',')
This simple CTE will give what's needed:
DECLARE #csv varchar(max) = '1,2,3,4,5,6,7,8,9,10,11,12,13,14,15';
--append comma to the list for CTE to work correctly
SET #csv = #csv + ',';
--remove double commas (empty entries)
SET #csv = replace(#csv, ',,', ',');
WITH CteCsv AS (
SELECT CHARINDEX(',', #csv) idx, SUBSTRING(#csv, 1, CHARINDEX(',', #csv) - 1) [Value]
UNION ALL
SELECT CHARINDEX(',', #csv, idx + 1), SUBSTRING(#csv, idx + 1, CHARINDEX(',', #csv, idx + 1) - idx - 1) FROM CteCsv
WHERE CHARINDEX(',', #csv, idx + 1) > 0
)
SELECT [Value] FROM CteCsv
This is another version which really does not have any restrictions (e.g.: special chars when using xml approach, number of records in CTE approach) and it runs much faster based on a test on 10M+ records with source string average length of 4000. Hope this could help.
Create function [dbo].[udf_split] (
#ListString nvarchar(max),
#Delimiter nvarchar(1000),
#IncludeEmpty bit)
Returns #ListTable TABLE (ID int, ListValue nvarchar(1000))
AS
BEGIN
Declare #CurrentPosition int, #NextPosition int, #Item nvarchar(max), #ID int, #L int
Select #ID = 1,
#L = len(replace(#Delimiter,' ','^')),
#ListString = #ListString + #Delimiter,
#CurrentPosition = 1
Select #NextPosition = Charindex(#Delimiter, #ListString, #CurrentPosition)
While #NextPosition > 0 Begin
Set #Item = LTRIM(RTRIM(SUBSTRING(#ListString, #CurrentPosition, #NextPosition-#CurrentPosition)))
If #IncludeEmpty=1 or LEN(#Item)>0 Begin
Insert Into #ListTable (ID, ListValue) Values (#ID, #Item)
Set #ID = #ID+1
End
Set #CurrentPosition = #NextPosition+#L
Set #NextPosition = Charindex(#Delimiter, #ListString, #CurrentPosition)
End
RETURN
END
/* *Object: UserDefinedFunction [dbo].[Split] Script Date: 10/04/2013 18:18:38* */
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER FUNCTION [dbo].[Split]
(#List varchar(8000),#SplitOn Nvarchar(5))
RETURNS #RtnValue table
(Id int identity(1,1),Value nvarchar(100))
AS
BEGIN
Set #List = Replace(#List,'''','')
While (Charindex(#SplitOn,#List)>0)
Begin
Insert Into #RtnValue (value)
Select
Value = ltrim(rtrim(Substring(#List,1,Charindex(#SplitOn,#List)-1)))
Set #List = Substring(#List,Charindex(#SplitOn,#List)+len(#SplitOn),len(#List))
End
Insert Into #RtnValue (Value)
Select Value = ltrim(rtrim(#List))
Return
END
go
Select *
From [Clv].[Split] ('1,2,3,3,3,3,',',')
GO
Using tally table here is one split string function(best possible approach) by Jeff Moden
CREATE FUNCTION [dbo].[DelimitedSplit8K]
(#pString VARCHAR(8000), #pDelimiter CHAR(1))
RETURNS TABLE WITH SCHEMABINDING AS
RETURN
--===== "Inline" CTE Driven "Tally Table" produces values from 0 up to 10,000...
-- enough to cover NVARCHAR(4000)
WITH E1(N) AS (
SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL
SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL
SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1
), --10E+1 or 10 rows
E2(N) AS (SELECT 1 FROM E1 a, E1 b), --10E+2 or 100 rows
E4(N) AS (SELECT 1 FROM E2 a, E2 b), --10E+4 or 10,000 rows max
cteTally(N) AS (--==== This provides the "base" CTE and limits the number of rows right up front
-- for both a performance gain and prevention of accidental "overruns"
SELECT TOP (ISNULL(DATALENGTH(#pString),0)) ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) FROM E4
),
cteStart(N1) AS (--==== This returns N+1 (starting position of each "element" just once for each delimiter)
SELECT 1 UNION ALL
SELECT t.N+1 FROM cteTally t WHERE SUBSTRING(#pString,t.N,1) = #pDelimiter
),
cteLen(N1,L1) AS(--==== Return start and length (for use in substring)
SELECT s.N1,
ISNULL(NULLIF(CHARINDEX(#pDelimiter,#pString,s.N1),0)-s.N1,8000)
FROM cteStart s
)
--===== Do the actual split. The ISNULL/NULLIF combo handles the length for the final element when no delimiter is found.
SELECT ItemNumber = ROW_NUMBER() OVER(ORDER BY l.N1),
Item = SUBSTRING(#pString, l.N1, l.L1)
FROM cteLen l
;
Referred from Tally OH! An Improved SQL 8K “CSV Splitter” Function
This blog came with a pretty good solution using XML in T-SQL.
This is the function I came up with based on that blog (change function name and result type cast per need):
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE FUNCTION [dbo].[SplitIntoBigints]
(#List varchar(MAX), #Splitter char)
RETURNS TABLE
AS
RETURN
(
WITH SplittedXML AS(
SELECT CAST('<v>' + REPLACE(#List, #Splitter, '</v><v>') + '</v>' AS XML) AS Splitted
)
SELECT x.v.value('.', 'bigint') AS Value
FROM SplittedXML
CROSS APPLY Splitted.nodes('//v') x(v)
)
GO
CREATE Function [dbo].[CsvToInt] ( #Array varchar(4000))
returns #IntTable table
(IntValue int)
AS
begin
declare #separator char(1)
set #separator = ','
declare #separator_position int
declare #array_value varchar(4000)
set #array = #array + ','
while patindex('%,%' , #array) <> 0
begin
select #separator_position = patindex('%,%' , #array)
select #array_value = left(#array, #separator_position - 1)
Insert #IntTable
Values (Cast(#array_value as int))
select #array = stuff(#array, 1, #separator_position, '')
end
This works great for me https://www.sqlshack.com/the-string-split-function-in-sql-server/
After two hours of resarching this topic this is the simplest solution (without using XML ect.).
You should only remember to use string_split after from.
DROP TABLE IF EXISTS #Countries
GO
DROP TABLE IF EXISTS #CityList
GO
CREATE TABLE #Countries
(Continent VARCHAR(100),
Country VARCHAR(100))
GO
CREATE TABLE #CityList
(Country VARCHAR(100),
City VARCHAR(5000))
GO
INSERT INTO #Countries
VALUES('Europe','France'),('Europe','Germany')
INSERT INTO #CityList
VALUES('France','Paris,Marsilya,Lyon,Lille,Nice'), ('Germany','Berlin,Hamburg,Munih,Frankfurt,Koln')
SELECT
CN.Continent,CN.Country,value
FROM #CityList CL CROSS APPLY string_split(CL.City,',') INNER JOIN
#Countries CN ON CL.Country = CN.Country
DROP TABLE IF EXISTS #Countries
GO
DROP TABLE IF EXISTS #CityList
You write this function in sql server after that problem will be solved.
http://csharpdotnetsol.blogspot.in/2013/12/csv-function-in-sql-server-for-divide.html

How do you prepend space in a string where Upper Case letter comes or where a space really needed [duplicate]

This question already has answers here:
Finding Uppercase Character then Adding Space
(3 answers)
Closed 8 years ago.
How do you prepends space in a string where Upper Case letter comes or where a space really needed.
The Sample code is:
DECLARE #teams TABLE (Team NVARCHAR(100))
INSERT INTO #teams
SELECT 'TataConsultencyServices'
UNION ALL
SELECT 'TataConsultencyCompany'
UNION ALL
SELECT 'CompanyHumanResource'
Expected Result
Tata Consultency Services
Tata Consultency Company
Company Human Resource
A set based solution:
DECLARE #s NVARCHAR(100);
SET #s = 'CompanyHumanResources';
DECLARE #Idx INT = 1;
WITH CteRecursive
AS
(
SELECT 1 AS Idx,
CONVERT(NVARCHAR(200), #s) AS String
UNION ALL
SELECT src.Idx + src.IsUpper + 1,
CONVERT(NVARCHAR(200),
CASE WHEN src.IsUpper = 1 THEN STUFF(src.String, src.Idx+1, 0, ' ') ELSE src.String END
)
FROM
(
SELECT rec.*,
CASE WHEN SUBSTRING(rec.String, rec.Idx, 1) <> ' ' AND SUBSTRING(rec.String, rec.Idx+1, 1) LIKE '[A-Z]' AND SUBSTRING(rec.String, rec.Idx+1, 1) COLLATE Romanian_CS_AS = UPPER(SUBSTRING(rec.String, rec.Idx+1, 1)) COLLATE Romanian_CS_AS THEN 1 ELSE 0 END AS IsUpper
FROM CteRecursive rec
WHERE rec.Idx + 1 <= LEN(rec.String)
) src
)
SELECT TOP(1) x.String
FROM CteRecursive x
ORDER BY x.Idx DESC;
Results:
String
-----------------------
Company Human Resources
You may surely get some help from this:-
CREATE FUNCTION CaseSensitiveSQLSplitFunction
(
#str nvarchar(max)
)
returns #t table (val nvarchar(max))
as
begin
declare #i int, #j int
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
insert into #t (val) select #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
insert into #t (val) select #w
return
end
Taking the sample as:-
declare #str nvarchar(max) = N'ThisIsATest'
select * from dbo.CaseSensitiveSQLSplitFunction(#str)
set #str = N'ThisIsASqlServerCaseSensitiveSplitStringFunction'
select * from dbo.CaseSensitiveSQLSplitFunction(#str)
It is now possible to sql concatenate string values in a way from rows to single column value.
We can just use any of the sql concatenation function.
declare #str nvarchar(max) = N'ThisIsATest'
SELECT LTRIM(STUFF((
SELECT ' ' + val FROM dbo.CaseSensitiveSQLSplitFunction(#str) FOR XML PATH('')
), 1, 1, '')) string
set #str = N'ThisIsASqlServerCaseSensitiveSplitStringFunction'
SELECT LTRIM(STUFF((
SELECT ' ' + val FROM dbo.CaseSensitiveSQLSplitFunction(#str) FOR XML PATH('')
), 1, 1, '')) string
WHILE 1 = 1
BEGIN
UPDATE #teams
SET TeamName = STUFF(TeamName, patindex('%[a-z,.][A-Z]%', TeamName COLLATE Latin1_General_BIN) + 1,0,' ')
WHERE patindex('%[a-z,.][A-Z]%', TeamName COLLATE Latin1_General_BIN) > 0
IF ##ROWCOUNT = 0 BREAK
END
UPDATE #teams
SET TeamName = STUFF(TeamName, patindex('%[A-Z][a-z]%', RIGHT(TeamName,LEN(TeamName) -1) COLLATE Latin1_General_BIN) +1 ,0,' ')
WHERE patindex('%[A-Z][a-z]%', TeamName COLLATE Latin1_General_BIN) > 0

Resources