SQL Server Equivalent to ORACLE INSTR - sql

I wanted to know if in SQL Server there is an equivalent to the Oracle INSTR function?
I know that there is CHARINDEX and PATINDEX, but with the Oracle version I can also specify the Nth appearance of the character(s) I am looking for.
Oracle INSTR:
instr( string1, string2 [, start_position [, **nth_appearance** ] ] )
The CHARINDEX almost gets me there, but I wanted to have it start at the nth_appearance of the character in the string.

You were spot on that nth_appearance does not exist in SQL Server.
Shamelessly copying a function (Equivalent of Oracle's INSTR with 4 parameters in SQL Server) created for your problem (please note that #Occurs is not used the same way as in Oracle - you can't specify "3rd appearance", but "occurs 3 times"):
CREATE FUNCTION udf_Instr
(#str1 varchar(8000), #str2 varchar(1000), #start int, #Occurs int)
RETURNS int
AS
BEGIN
DECLARE #Found int, #LastPosition int
SET #Found = 0
SET #LastPosition = #start - 1
WHILE (#Found < #Occurs)
BEGIN
IF (CHARINDEX(#str1, #str2, #LastPosition + 1) = 0)
BREAK
ELSE
BEGIN
SET #LastPosition = CHARINDEX(#str1, #str2, #LastPosition + 1)
SET #Found = #Found + 1
END
END
RETURN #LastPosition
END
GO
SELECT dbo.udf_Instr('x','axbxcxdx',1,4)
GO
DROP FUNCTION udf_Instr
GO

Here is a version of Oracle's INSTR function which also works with a negative position for a reverse lookup as per Oracle's Doc here :- https://docs.oracle.com/cd/B28359_01/olap.111/b28126/dml_functions_1103.htm#OLADM564
CREATE FUNCTION dbo.INSTR(#str NVARCHAR(MAX), #substr NVARCHAR(MAX), #position INT = 1, #occurance INT = 1)
RETURNS INT
AS
BEGIN
DECLARE #loc INT = #position;
IF #loc < 0
BEGIN
SET #str = REVERSE(#str);
SET #substr = REVERSE(#substr);
SET #loc = #loc * -1;
END
IF #loc > 0
BEGIN
SET #loc = #loc - 1;
END
WHILE (#occurance > 0 AND CHARINDEX(#substr, #str, #loc + 1) > 0)
BEGIN
SET #loc = CHARINDEX(#substr, #str, #loc + 1);
SET #occurance = #occurance - 1;
END
IF #occurance > 0
BEGIN
SET #loc = 0;
END
IF #position < 0
BEGIN
SET #loc = LEN(#str) - #loc;
END
RETURN #loc
END

Change #str1 varchar(8000), #str2 varchar(1000) to #str1 varchar(1000), #str2 varchar(8000)
or
change CHARINDEX(#str1, #str2, #LastPosition + 1) to CHARINDEX(#str2, #str1, #LastPosition + 1)

You can use the following UDF (inline function rather than scalar)
CREATE FUNCTION dbo.INSTR
(
#str VARCHAR(8000),
#Substr VARCHAR(1000),
#start INT ,
#Occurance INT
)
RETURNS TABLE
AS
RETURN
WITH Tally (n) AS
(
SELECT TOP (LEN(#str)) ROW_NUMBER() OVER (ORDER BY (SELECT NULL))
FROM (VALUES (0),(0),(0),(0),(0),(0),(0),(0)) a(n)
CROSS JOIN (VALUES(0),(0),(0),(0),(0),(0),(0),(0),(0),(0)) b(n)
CROSS JOIN (VALUES(0),(0),(0),(0),(0),(0),(0),(0),(0),(0)) c(n)
CROSS JOIN (VALUES(0),(0),(0),(0),(0),(0),(0),(0),(0),(0)) d(n)
)
, Find_N_STR as
(
SELECT
CASE WHEN DENSE_RANK() OVER(PARTITION BY #Substr ORDER BY (CHARINDEX(#Substr ,#STR ,N))) = #Occurance
THEN MAX(N-#start +1) OVER (PARTITION BY CHARINDEX(#Substr ,#STR ,N) )
ELSE 0
END [Loc]
FROM Tally
WHERE CHARINDEX(#Substr ,#STR ,N) > 0
)
SELECT Loc= MAX(Loc)
FROM Find_N_STR
WHERE Loc > 0
How to use:
declare #T table
(
Name_Level_Class_Section varchar(25)
)
insert into #T values
('Jacky_1_B2_23'),
('Johnhy_1_B2_24'),
('Peter_2_A5_3')
select t.Name_Level_Class_Section , l.Loc
from #t t
cross apply dbo.INSTR (t.Name_Level_Class_Section, '_',1,2) l

Try this !!
CREATE FUNCTION dbo.INSTR (#str VARCHAR(8000), #substr VARCHAR(255), #start INT, #occurrence INT)
RETURNS INT
AS
BEGIN
DECLARE #found INT = #occurrence,
#pos INT = #start;
WHILE 1=1
BEGIN
-- Find the next occurrence
SET #pos = CHARINDEX(#substr, #str, #pos);
-- Nothing found
IF #pos IS NULL OR #pos = 0
RETURN #pos;
-- The required occurrence found
IF #found = 1
BREAK;
-- Prepare to find another one occurrence
SET #found = #found - 1;
SET #pos = #pos + 1;
END
RETURN #pos;
END
GO
Usage :
-- Find the second occurrence of letter 'o'
SELECT dbo.INSTR('Moscow', 'o', 1, 2);
-- Result: 5

Related

How find last number of sequence numbers in sql?

I want find last number of sequence in sql query for fill automatically suggestion field value. forexample my code field(column) is :1,2,3,4,10,20 so i want found 4 in my query
If your table is called table_name and looks like this:
id
1
2
3
4
10
20
Then this should work:
select min(previd) from
(select id, lag(id) over(order by id) as previd
from table_name) t
where id - previd > 1;
Fiddle
if the value of a column is for example '1,2,3,4,6,8,0' then you can use a function to get the last value from a sequence contained within a string.
create function dbo.FindLastNumberInSequence
(
#Delimited varchar(MAX),
#Delimiter varchar(50) = ','
)
returns int as
begin
declare #i int
declare #current int
declare #previous int
declare #sequenceFound bit = 0
set #Delimited = #delimiter + #Delimited + #delimiter
set #i = 1
while #i < LEN( #Delimited )
begin
set #current = SUBSTRING( #Delimited, #i+1, CHARINDEX( #Delimiter, #Delimited, #i+1 ) - #i-1 )
if(#sequenceFound=1)
if (#current != #previous + 1)
break
if (#current = #previous + 1)
set #sequenceFound = 1
set #previous = #current
set #i = CHARINDEX( #Delimiter, #Delimited, #i+1 )
end
if(#sequenceFound=0)
set #previous=-1
return #previous
end
select dbo.FindLastNumberInSequence ('1,2,3,4,10,20', ',') -- 4
select dbo.FindLastNumberInSequence ('9,8,1,2,3,4,10,20', ',') -- 4
select dbo.FindLastNumberInSequence ('9,8,1,13,4,10,20', ',') -- -1 (no sequence found)

Getting syntax error while using table variable in table value function in SQL Server 2012

I am trying to use table variable with table value function but I am getting a syntax error. Please help me to solve this.
Here is the code:
Create FUNCTION [dbo].[SplitStrings1]
(#List NVARCHAR(MAX),
#Delimiter NVARCHAR(255))
RETURNS #Results TABLE(Col1 int)
AS
BEGIN
declare #tblHelping table (Col1 int);
declare #i int
declare #rows_to_insert int
set #i = 1
set #rows_to_insert = 1000
while #i < #rows_to_insert
begin
INSERT INTO #tblHelping VALUES (#i)
set #i = #i + 1
end
(SELECT
Number = ROW_NUMBER() OVER (ORDER BY Number),
Item FROM (SELECT Number, Item = LTRIM(RTRIM(SUBSTRING(#List, Number,
CHARINDEX(#Delimiter, #List + #Delimiter, Number) - Number)))
FROM
(SELECT * FROM #tblHelping) AS n(Number)
WHERE
Number <= CONVERT(INT, LEN(#List))
AND SUBSTRING(#Delimiter + #List, Number, 1) = #Delimiter) AS y)
END
I am getting this error
A RETURN statement with a return value cannot be used in this context.
If you want to use your function then working version below. But there is better solution, please see:
Split strings the right way – or the next best way as recomended by
#SeanLange
How to split string using delimiter char using T-SQL?
How do I split a string so I can access item x?
.
CREATE FUNCTION [dbo].[SplitStrings1]
( #List NVARCHAR(MAX)
, #Delimiter NVARCHAR(255))
RETURNS #Results TABLE
(Number INT, Item NVARCHAR(MAX) )
AS
BEGIN
declare #tblHelping table (Col1 int);
declare #i int
declare #rows_to_insert int
SET #i = 1
SET #rows_to_insert = 1000
while #i < #rows_to_insert
begin
INSERT INTO #tblHelping VALUES (#i)
set #i = #i + 1
end
insert into #Results
SELECT Number = ROW_NUMBER() OVER (ORDER BY Number)
, Item
FROM (SELECT Number
, Item = LTRIM(RTRIM(SUBSTRING(#List, Number,CHARINDEX(#Delimiter, #List + #Delimiter, Number) - Number)))
FROM (SELECT * FROM #tblHelping) AS n(Number)
WHERE Number <= CONVERT(INT, LEN(#List))
AND SUBSTRING(#Delimiter + #List, Number, 1) = #Delimiter) AS y
RETURN
END
Or try something like that:
CREATE FUNCTION [dbo].[fnSplitString]
(
#string NVARCHAR(MAX),
#delimiter CHAR(1)
)
RETURNS #output TABLE(splitdata NVARCHAR(MAX)
)
BEGIN
DECLARE #start INT, #end INT
SELECT #start = 1, #end = CHARINDEX(#delimiter, #string)
WHILE #start < LEN(#string) + 1 BEGIN
IF #end = 0
SET #end = LEN(#string) + 1
INSERT INTO #output (splitdata)
VALUES(SUBSTRING(#string, #start, #end - #start))
SET #start = #end + 1
SET #end = CHARINDEX(#delimiter, #string, #start)
END
RETURN
END

Reverse only numerical parts of string in sql server

With T-SQL, I'm trying to find the easiest way to reverse numbers in string. so for string like Test123Hello have Test321Hello.
[Before] [After]
Test123Hello Test321Hello
Tt143 Hello Tt341 Hello
12Hll 21Hll
Tt123H3451end Tt321H1543end
you can use this function
CREATE FUNCTION [dbo].[fn_ReverseDigit_MA]
(
#Str_IN nVARCHAR(max)
)
RETURNS NVARCHAR(max)
AS
BEGIN
DECLARE #lenstr AS INT =LEN(#Str_IN)
DECLARE #lastdigend AS INT=0
while (#lastdigend<#lenstr)
BEGIN
DECLARE #strPart1 AS NVARCHAR(MAX)=LEFT(#Str_IN,#lastdigend)
declare #lenstrPart1 AS INT=LEN(#strPart1)
DECLARE #strPart2 AS NVARCHAR(MAX)=RIGHT(#Str_IN,#lenstr-#lastdigend)
declare #digidx as int=patindex(N'%[0-9]%' ,#strPart2)+#lenstrPart1
IF(#digidx=#lenstrPart1)
BEGIN
BREAK;
END
DECLARE #strStartdig AS NVARCHAR(MAX) = RIGHT(#Str_IN,#lenstr-#digidx+1)
declare #NDidx as int=patindex(N'%[^0-9]%' ,#strStartdig)+#digidx-1
IF(#NDidx<=#digidx)
BEGIN
SET #NDidx=#lenstr+1
END
DECLARE #strRet AS NVARCHAR(MAX)=LEFT(#Str_IN,#digidx-1) +REVERSE(SUBSTRING(#Str_IN,#digidx,#NDidx-#digidx)) +RIGHT(#Str_IN,#lenstr-#NDidx+1)
SET #Str_IN=#strRet
SET #lastdigend=#NDidx-1
END
return #Str_IN
END
Just make use of PATINDEX for searching, append to the result string part by part:
CREATE FUNCTION [dbo].[fn_ReverseDigits]
(
#Value nvarchar(max)
)
RETURNS NVARCHAR(max)
AS
BEGIN
IF #Value IS NULL
RETURN NULL
DECLARE
#TextIndex int = PATINDEX('%[^0-9]%', #Value),
#NumIndex int = PATINDEX('%[0-9]%', #Value),
#ResultValue nvarchar(max) = ''
WHILE LEN(#ResultValue) < LEN(#Value)
BEGIN
-- Set the index to end of the string if the index is 0
SELECT #TextIndex = CASE WHEN #TextIndex = 0 THEN LEN(#Value) + 1 ELSE LEN(#ResultValue) + #TextIndex END
SELECT #NumIndex = CASE WHEN #NumIndex = 0 THEN LEN(#Value) + 1 ELSE LEN(#ResultValue) + #NumIndex END
IF #NumIndex < #TextIndex
SELECT #ResultValue = #ResultValue + REVERSE(SUBSTRING(#Value, #NumIndex, #TextIndex -#NumIndex))
ELSE
SELECT #ResultValue = #ResultValue + (SUBSTRING(#Value, #TextIndex, #NumIndex - #TextIndex))
-- Update index variables
SELECT
#TextIndex = PATINDEX('%[^0-9]%', SUBSTRING(#Value, LEN(#ResultValue) + 1, LEN(#Value) - LEN(#ResultValue))),
#NumIndex = PATINDEX('%[0-9]%', SUBSTRING(#Value, LEN(#ResultValue) + 1, LEN(#Value) - LEN(#ResultValue)))
END
RETURN #ResultValue
END
Test SQL
declare #Values table (Value varchar(20))
INSERT #Values VALUES
('Test123Hello'),
('Tt143 Hello'),
('12Hll'),
('Tt123H3451end'),
(''),
(NULL)
SELECT Value, dbo.fn_ReverseDigits(Value) ReversedValue FROM #Values
Result
Value ReversedValue
-------------------- --------------------
Test123Hello Test321Hello
Tt143 Hello Tt341 Hello
12Hll 21Hll
Tt123H3451end Tt321H1543end
NULL NULL
hope this help:
declare #s nvarchar(128) ='Test321Hello'
declare #numStart as int, #numEnd as int
select #numStart =patindex('%[0-9]%',#s)
select #numEnd=len(#s)-patindex('%[0-9]%',REVERSE(#s))
select
SUBSTRING(#s,0,#numstart)+
reverse(SUBSTRING(#s,#numstart,#numend-#numstart+2))+
SUBSTRING(#s,#numend+2,len(#s)-#numend)
Use this function it will handle multiple occurrence of numbers too
create FUNCTION [dbo].[GetReverseNumberFromString] (#String VARCHAR(2000))
RETURNS VARCHAR(1000)
AS
BEGIN
DECLARE #Count INT
DECLARE #IntNumbers VARCHAR(1000)
declare #returnstring varchar(max)=#String;
SET #Count = 0
SET #IntNumbers = ''
WHILE #Count <= LEN(#String)
BEGIN
IF SUBSTRING(#String, #Count, 1) >= '0'
AND SUBSTRING(#String, #Count, 1) <= '9'
BEGIN
SET #IntNumbers = #IntNumbers + SUBSTRING(#String, #Count, 1)
END
IF (
SUBSTRING(#String, #Count + 1, 1) < '0'
OR SUBSTRING(#String, #Count + 1, 1) > '9'
)
AND SUBSTRING(#String, #Count, 1) >= '0'
AND SUBSTRING(#String, #Count, 1) <= '9'
BEGIN
SET #IntNumbers = #IntNumbers + ','
END
SET #Count = #Count + 1
END
declare #RevStrings table (itemz varchar(50))
INSERT INTO #RevStrings(itemz)
select items from dbo.Split(#IntNumbers,',')
select #returnstring = Replace(#returnstring, itemz,REVERSE(itemz))from #RevStrings
RETURN #returnstring
END
your sample string
select [dbo].[GetReverseNumberFromString]('Tt123H3451end')
result
Tt321H1543end
UPDATE :
if you do not have Split function then first create it
i have included it below
create FUNCTION Split
(
#Input NVARCHAR(MAX),
#Character CHAR(1)
)
RETURNS #Output TABLE (
Items NVARCHAR(1000)
)
AS
BEGIN
DECLARE #StartIndex INT, #EndIndex INT
SET #StartIndex = 1
IF SUBSTRING(#Input, LEN(#Input) - 1, LEN(#Input)) <> #Character
BEGIN
SET #Input = #Input + #Character
END
WHILE CHARINDEX(#Character, #Input) > 0
BEGIN
SET #EndIndex = CHARINDEX(#Character, #Input)
INSERT INTO #Output(Items)
SELECT SUBSTRING(#Input, #StartIndex, #EndIndex - 1)
SET #Input = SUBSTRING(#Input, #EndIndex + 1, LEN(#Input))
END
RETURN
END
GO
This is a set based approach:
;WITH Tally (n) AS
(
SELECT ROW_NUMBER() OVER (ORDER BY (SELECT NULL))
FROM (VALUES(0),(0),(0),(0),(0),(0),(0),(0),(0),(0)) a(n)
CROSS JOIN (VALUES(0),(0),(0),(0),(0),(0),(0),(0),(0),(0)) b(n)
CROSS JOIN (VALUES(0),(0),(0),(0),(0),(0),(0),(0),(0),(0)) c(n)
), UnpivotCTE AS (
SELECT id, x.c, n, y.isNumber,
n - ROW_NUMBER() OVER (PARTITION BY id, y.isNumber
ORDER BY n) AS grp
FROM mytable
CROSS JOIN Tally
CROSS APPLY (SELECT SUBSTRING(col, n, 1)) AS x(c)
CROSS APPLY (SELECT ISNUMERIC(x.c)) AS y(isNumber)
WHERE n <= LEN(col)
), ToConcatCTE AS (
SELECT id, c, n, isNumber,
grp + MIN(n) OVER (PARTITION BY id, isNumber, grp) AS grpAsc
FROM UnpivotCTE
)
SELECT id, col,
REPLACE(
(SELECT c AS [text()]
FROM ToConcatCTE AS t
WHERE t.id = m.id
ORDER BY id,
grpAsc,
CASE WHEN isNumber = 0 THEN n END,
CASE WHEN isNumber = 1 THEN n END DESC
FOR XML PATH('')), ' ',' ') AS col2
FROM mytable AS m
A tally table is used in order to 'unpivot' all characters of the string. Then ROW_NUMBER is used in order to identify islands of numeric and non-numeric characters. Finally, FOR XML PATH is used to reconstruct the initial string with numerical islands reversed: ORDER BY is used to sort islands of numeric characters in reversed order.
Fiddle Demo here
This would do the specific string you are asking for:
select
substring('Test123Hello',1,4)
+
reverse(substring('Test123Hello',5,3))
+
substring('Test123Hello',8,5)
Judging by the rest of the values it looks like you would need to make templates for any of the alphanumeric patterns you are getting. For example you would apply the above to any values that had the shape:
select * from [B&A] where [before] like '[a-z][a-z][a-z][a-z][0-9][0-9][0-9]
[a-z][a-z][a-z][a-z][a-z]'
In other words, if you put the values (before and after) into a table [B&A] and called the columns 'before' and 'after' then ran this:
select
substring(before,1,4)
+
reverse(substring(before,5,3))
+
substring(before,8,5) as [after]
from [B&A] where [before] like '[a-z][a-z][a-z][a-z][0-9][0-9][0-9][a-z]
[a-z][a-z][a-z][a-z]'
Then it would give you 'Test321Hello'.
However the other 3 rows would not be affected unless you created a similar
'[0-9][a-z]' type template for each alphanumeric shape and applied this to the [B&A] table. You would have to select the results into a temp table or another table.
By applying each template in turn you'd get most of it then you'd have to see how many rows were unaffected and check what the alphanumeric shape is and make more templates. Eventually you have a set of code which, if you ran it would capture all possible combinations.
You could just sit down and design a code in this way which captured all possible combinations of [a-z] and [0-9]. A lot depends on the maximum number of characters you are dealing with.

User Defined Function to get longest word in a string in SQL Server

I've trying to create a UDF in SQL to return the longest word in a string. I've created the following but I cant get it to work properly. Any suggestions?
CREATE FUNCTION [dbo].[ufn_Longestword] (#input varchar(255))
RETURNS varchar(100)
AS
BEGIN
declare #pos int
declare #pos2 int
declare #wordpos int
declare #longestword varchar(100)
declare #Letter1 varchar (1)
declare #Letter2 varchar (1)
declare #twords table (
words varchar(100))
SET #pos = 1
WHILE #pos <= len(#input)
BEGIN
SET #Letter1 = substring(#input, #pos, 1)
IF #Letter1 = ' '
BEGIN
SET #pos2 = #pos
WHILE #pos2 <= len(#input)
BEGIN
SET #Letter2 = substring(#input, #pos2, 1)
if #letter2 = ' '
BEGIN
insert into #twords
select SUBSTRING(#input, #pos,#pos2 - #pos)
END
SET #pos2 = #pos2 + 1
END
END
SET #pos = #pos + 1
END
SET #longestword = (select top 1 words from #twords
ORDER BY len(words)desc)
delete from #twords
RETURN #longestword
END
I#m trying to get the different between the 2 spaces and insert that word into a temp table but it doesnt work.
Instead you can use this.
DECLARE #str VARCHAR(5000)='aaaa bbbbb cccccccc'
SELECT TOP 1 Split.a.value('.', 'VARCHAR(100)') as longest_Word
FROM (SELECT Cast ('<M>' + Replace(#str, ' ', '</M><M>') + '</M>' AS XML) AS Data) AS A
CROSS APPLY Data.nodes ('/M') AS Split(a)
ORDER BY Len(Split.a.value('.', 'VARCHAR(100)')) DESC
Result : cccccccc
i found this as solution :
click Here
CREATE FUNCTION FN_ex06(#str varchar(8000) )
RETURNS #T TABLE
( position int IDENTITY PRIMARY KEY,
value varchar(8000) ,
length smallint null
)
AS
BEGIN
DECLARE #i int
SET #i = -1
WHILE (LEN(#str) > 0)
BEGIN
SET #i = CHARINDEX(' ' , #str) /* here i used space as delimiter*/
IF (#i = 0) AND (LEN(#str) > 0)
BEGIN
INSERT INTO #T (value, length) VALUES (#str, LEN(#str))
BREAK
END
IF (#i > 1)
BEGIN
INSERT INTO #T (value, length) VALUES (LEFT(#str, #i - 1),
LEN(LEFT(#str, #i - 1)))
SET #str = RIGHT(#str, (LEN(#str) - #i))
END
ELSE
SET #str = RIGHT(#str, (LEN(#str) - #i))
END
RETURN
END
to run this function you treat it as table because return is a table
select max(value) from FN_ex06('karim pentester')
result is : pentester
of course you select all other columns in return table

Splitting an SQL string into multiple strings

I am trying to split a single string containing multiple email address data into three variables. The strings mark the start/end of an email address with the ; character.
An example string would be:
'joebloggs#gmailcom;jimbowen#aol.com;dannybaker#msn.com'
The code I currently have for this is as follows:
DECLARE #Email VARCHAR(100),
#Email2 VARCHAR(100),
#Email3 VARCHAR(100)
SET #Email = 'joebloggs#gmailcom;jimbowen#aol.com;dannybaker#msn.com'
SET #Email2 = SUBSTRING(#Email, CHARINDEX(';', #Email)+1, LEN(#Email))
SET #Email3 = SUBSTRING(#Email, CHARINDEX(';', #Email)+1, LEN(#Email))
SET #Email = SUBSTRING(#Email, 1, CHARINDEX(';', #Email)-1)
Unfortunately this doesn't seem to work. Could someone please point out where I am going wrong and what I should do to fix my problem?
Thanks in advance.
Assuming that there will always be 3 email addresses - the following seems to work;
DECLARE #Email VARCHAR(100),
#Email2 VARCHAR(100),
#Email3 VARCHAR(100)
SET #Email = 'joebloggs#gmailcom;jimbowen#aol.com;dannybaker#msn.com'
SELECT #Email = LEFT(#Email, CHARINDEX(';', #Email) - 1)
,#Email2 = SUBSTRING (
#Email,
CHARINDEX(';', #Email) + 1,
CHARINDEX(';', #Email, CHARINDEX(';', #Email) + 1) - LEN(LEFT(#Email, CHARINDEX(';', #Email) )) - 1
)
,#Email3 = RIGHT(#Email, CHARINDEX(';', #Email)-1)
This solution:
create function dbo.SplitString
(
#str nvarchar(max),
#separator char(1)
)
returns table
AS
return (
with tokens(p, a, b) AS (
select
cast(1 as bigint),
cast(1 as bigint),
charindex(#separator, #str)
union all
select
p + 1,
b + 1,
charindex(#separator, #str, b + 1)
from tokens
where b > 0
)
select
p-1 ItemIndex,
substring(
#str,
a,
case when b > 0 then b-a ELSE LEN(#str) end)
AS Item
from tokens
);
GO
Taken from How do I split a string so I can access item x
In SQL Server 2016 you can use the built-in STRING_SPLIT function.
SELECT value FROM STRING_SPLIT(#var, ';')
Try using XML nodes to split and parse your string. Code sample below:
declare #Email as varchar(100), #del as varchar(10), #xml as xml;
set #Email='joebloggs#gmailcom;jimbowen#aol.com;dannybaker#msn.com';
set #del =';';
set #xml = '<root><c>' + replace(#Email,#del,'</c><c>') + '</c></root>';
select email.value('.','varchar(100)') as Email
from #xml.nodes('//root/c') as records(email);
Here this works I came across it quite sometime ago. Cannot take any credit for the work but this will work perfectly.
CREATE FUNCTION [dbo].[fnSplitString]
(
#string NVARCHAR(MAX),
#delimiter CHAR(1)
)
RETURNS #output TABLE(splitdata NVARCHAR(MAX)
)
BEGIN
DECLARE #start INT, #end INT
SELECT #start = 1, #end = CHARINDEX(#delimiter, #string)
WHILE #start < LEN(#string) + 1 BEGIN
IF #end = 0
SET #end = LEN(#string) + 1
INSERT INTO #output (splitdata)
VALUES(SUBSTRING(#string, #start, #end - #start))
SET #start = #end + 1
SET #end = CHARINDEX(#delimiter, #string, #start)
END
RETURN
END
select *from dbo.fnSplitString('joebloggs#gmailcom;jimbowen#aol.com;dannybaker#msn.com',';')
I wrote this function that I use on a regular basis...
CREATE FUNCTION func_split(#value VARCHAR(8000), #delim CHAR)
RETURNS
#outtable TABLE (
i INTEGER,
value VARCHAR(1024)
)
AS
BEGIN
DECLARE #pos INTEGER
DECLARE #count INTEGER
IF LEN(#value) > 0
BEGIN
SET #count = 1
SET #value = #value + #delim
SET #pos = CHARINDEX(#delim, #value, 1)
WHILE #pos > 0
BEGIN
INSERT INTO #outtable (i, value) VALUES (#count, LEFT(#value, #pos - 1))
SET #value = RIGHT(#value, LEN(#value) - #pos)
SET #pos = CHARINDEX(#delim, #value, 1)
SET #count = #count + 1
END
END
RETURN
END
You when then call it with...
DECLARE #emails AS TABLE (
i INTEGER,
value VARCHAR(1024)
)
INSERT INTO #split SEELCT * FROM func_split('joebloggs#gmailcom;jimbowen#aol.com;dannybaker#msn.com', ';');
...and you end up with a temp table full of email addresses, i being their input order.
Your best bet is to turn the delimited string into columnar form and work from there.
You can use the iterative method, or the method using the Numbers table (which I prefer):
declare
#list varchar(1000),
#sep char(1)
set #list = 'joebloggs#gmailcom;jimbowen#aol.com;dannybaker#msn.com';
set #sep = ';'
-- iterative method
declare #res table (
c varchar(100)
)
declare
#pos_start int,
#pos_end int,
#len_sep int,
#exit int
select #pos_start = 1, #pos_end = 1, #len_sep = len(#sep), #exit = 0
while #exit = 0
begin
set #pos_end = charindex(#sep, #list, #pos_start)
if #pos_end <= 0 begin
set #pos_end = len(#list) + 1
set #exit = 1
end
insert #res(c) select substring(#list, #pos_start, #pos_end - #pos_start)
set #pos_start = #pos_end + #len_sep
end
select * from #res
-- the Numbers table method
select substring(#list, n, charindex(#sep, #list + #sep, n) - n)
from numbers
where substring(#sep + #list, n, 1) = #sep
and n < len(#list) + 1