create or replace function get_token( the_list varchar2, the_index number, delim varchar2 := ',' ) return varchar2 is start_pos number; end_pos number; begin if the_index = 1 then start_pos := 1; else start_pos := instr(the_list, delim, 1, the_index - 1); if start_pos = 0 then return null; else start_pos := start_pos + length(delim); end if; end if; end_pos := instr(the_list, delim, start_pos, 1); if end_pos = 0 then return substr(the_list, start_pos); else return substr(the_list, start_pos, end_pos - start_pos); end if; end get_token; /Example usage:
select get_token('foo,bar,baz',1), -- 'foo' get_token('foo,bar,baz',3), -- 'baz' -- get_token('a,,b',2), -- '' (null) get_token('a,,b',3), -- 'b' -- get_token('a|b|c',2,'|'), -- 'b' get_token('a|b|c',4,'|') -- '' (null) from dual /Notes
- Remember that the index starts at one not zero just like strings in PL/SQL.
- Empty tokens are counted. You get null if you ask for an empty token.
- You also get null if you ask for an index greater than the number of tokens.
- The delimiter defaults to a comma but you can specify any delimiter. A delimiter of more than one char should work also.
Update 17/5/07
I've added the notes above and colourised the code to make it look prettier. To the commenters below, you're welcome!
Update 12/9/08
This version provided by Anonymous below supports negative indexes, eg
get_token('foo,bar,baz',-1), -- 'baz'
get_token('foo,bar,baz',-2), -- 'bar'
Also check the comments below for a regex based version that can handle quoted csv style fields.
create or replace function get_token (
the_list varchar2,
the_index number,
delim varchar2 :=';'
)
return varchar2
is
start_pos number;
end_pos number;
begin
if the_index = 1 then
start_pos := 1;
elsif the_index < 0 then
start_pos := instr(the_list, delim, -1, abs(the_index)) + 1;
else
start_pos := instr(the_list, delim, 1, the_index - 1);
if start_pos = 0 then
return null;
else
start_pos := start_pos + length(delim);
end if;
end if;
if the_index < 0 then
end_pos := instr(the_list, delim, start_pos+1, 1);
else
end_pos := instr(the_list, delim, start_pos, 1);
end if;
if end_pos = 0 then
return substr(the_list, start_pos);
else
return substr(the_list, start_pos, end_pos - start_pos);
end if;
end get_token;

64 comments:
Hey! Thanks for the SQL token function, it was just what I needed!
This function rocks my socks. Thank you, stranger!
Thanks.
Great ! ! !
Nice and simple - just what I needed. Many thanks!
pretty much the best tokenizer ever! thanks!
thanks a lot. Very usefull !
Very GOOD!!!
thank you mr. simon(i hope,... it's your name... )
Good work, friend! Helped me a lot :)
Yeah Thanks
Thank you so much! You are my new hero.
Thank's a lot. I saved a lot of time. I've posted your solution on my blog and I've put a link pointing to your blog. Hope it's ok for you.
http://fabrixb.blogspot.com/2007/07/oracle-plsql-function-to-split-strings.html
Thank you! It was just what i need!
You saved my life :) Thank you very much.
Awesome! Thanks!
Thank you so much, this rocks!
Not quite the same, but similar ... Oracle supplies a PL/SQL function to parse a[.b[.c]][@link] into a,b,c,link. It actually uses the Oracle parser to do the parsing.
dbms_utility.name_tokenize()
Excellent function! Thanks for posting it.
I do never write comments. But this solution is something. Thank you a lot for saving a working day to me. Maybe more. I wouдd give you a matrioska from Russia for this :-)
In Spanish: Excelente función. Gracias por compartirla!!
hey thanks a lot for this wonderful function ....really helped me a lot
Here is a version that uses regular expressions:
create or replace function get_token(
the_list varchar2,
the_index number,
delim varchar2 := ','
)
return varchar2
is
begin
return regexp_substr(the_list, '[[:alnum:]]+', regexp_instr(the_list, delim, 1, the_index, 1));
end get_token;
hi! Thanks, exactly what I needed, had to modify it a bit to support multiple whitespaces, I posted a comment on my blog about it
http://blog.thej2eestruggle.com/2008/01/removing-multiple-whitespace-from.html
thank you
this function has helped me much
Thank you
your function help me
Pon007
The world's a better place when someone like you takes the time to write cool functions and then share them. Thanks!!
Dear friend. You saved me a couple of days in writing and testing this. world is a beautiful place because of people who SHARE. Thanks friend. best wishes, Gopal.
thx1.0e+6 4 this function
Thanks, this script got me started. I found that I sometimes had quoted data with the delimiter inside such as (,"comma,inside",) and modified this to work for me.
Here it is:
function get_token(
the_list varchar2,
the_index number,
delim varchar2 := ',',
l_escape varchar2 := '"'
)
return varchar2
is
reg_exp varchar2(200);
result varchar2(3000);
begin
-- Comment exampls assume , delimiter and " escape
-- Search for ,text until , or text until comma from start of string
-- before variables are inserted (^|,)(".*"|[^,]*)'
reg_exp:= '(^|' || delim ||')(' || l_escape || '.*' || l_escape || '|[^' || delim || ']*)';
result:= regexp_substr(the_list, reg_exp , 1,the_index);
--remove any begining delimiters
reg_exp := '^' || delim;
result:= regexp_replace(result,reg_exp,'');
return result;
end get_token;
Worked great! Thanks for posting.
great function
Hey !! This function is excellent. Thanks for posting this.
Thanks a lot for writing a generic tokenizer - I was looking for exactly this one! -Mukul
You simply rule! Thank you so much
if you add feature of getting tokens from the end, the function will rock
example : get_token ('a,b,c', -1) will return 'c'
yeah that would be cool.
I modified the function for the backend feature :
create or replace
function get_token
(
the_list varchar2,
the_index number,
delim varchar2 :=';'
)
return varchar2
is
start_pos number;
end_pos number;
begin
if the_index = 1 then
start_pos := 1;
elsif the_index < 0 then
start_pos := instr(the_list, delim, -1, abs(the_index))+1;
else
start_pos := instr(the_list, delim, 1, the_index - 1);
if start_pos = 0 then
return null;
else
start_pos := start_pos + length(delim);
end if;
end if;
if the_index < 0 then
end_pos := instr(the_list, delim, start_pos+1, 1);
else
end_pos := instr(the_list, delim, start_pos, 1);
end if;
if end_pos = 0 then
return substr(the_list, start_pos);
else
return substr(the_list, start_pos, end_pos - start_pos);
end if;
end get_token;
Thanks man, exactly what I needed!
you are the best!!!
u r a crack my man!!!
congratulations
Thank you a lot!
Mil gracias!
Here is one that is recursive:
/*
Recursive PL/SQL function that returns the nth element in a delimited String.
The default delimiter is a ',' (comma) and the level indicator defaults to 0.
*/
CREATE OR REPLACE FUNCTION getStringElement(p_string VARCHAR2,
p_element NUMBER,
p_delimiter VARCHAR2 := ',',
p_level NUMBER := 0) RETURN VARCHAR2
IS
v_string VARCHAR2(2000) := NULL;
v_element VARCHAR2(2000) := NULL;
v_level NUMBER(4) := 0;
BEGIN
v_level := p_level + 1;
v_element := substr(p_string||p_delimiter,1,instr(p_string||p_delimiter,p_delimiter)-1);
IF ((v_level >= p_element) OR (v_element IS NULL)) THEN
RETURN v_element;
ELSE
v_string := substr(p_string||p_delimiter,instr(p_string||p_delimiter,p_delimiter)+1,length(p_string));
RETURN getStringElement(v_string,p_element,p_delimiter,v_level);
END IF;
END;
/
Example:
The statement:
SELECT getStringElement('This is an interesting test of recursion in PL/SQL',7,' ') from dual;
will return the value "recursion".
http://radio.weblogs.com/0137094/2008/04/12.html
Its a great job. It helped me a lot and saved my lot of time.
Great... Great... Great...
Hey thanks man,
You have done a great job. It saved my time.
Very nice. Just used it in some code.
Ethan Post
OK. Gonna use this for a SQL script for Geographic Information Project for dutch municipalities called BAG.
Thanks.
I have added your code to my site.
http://www.coders2coders.com/articles.php?article_id=78
Thank you very much.
Exactly what I needed.
You are great!!
Really useful, saved some time. Thanks!!
I just searched for the split function and urs was the first to get on Google...thanks buddy... and thanks google too..cheers!
thank you very much for your code :D
very helpfull..
Very helpful - thanks! I am using it in my Data Warehouse ETL code.
thank you so much!!!
if the_index =0 you will get an Error.
:(
Anyone fixed that ???
For index = 0 just change first line to
if (the_index = 1 or the_index = 0) then
Dear Sir,
I hope you are doing well. I got this email address from one of your contribution web site. I have launched a web site www.codegain.com and it is basically aimed C#,JAVA,VB.NET,ASP.NET,AJAX,Sql Server,Oracle,WPF,WCF and etc resources, programming help, articles, code snippet, video demonstrations and problems solving support. I would like to invite you as an author and a supporter.
Looking forward to hearing from you and hope you will join with us soon.
Thank you
RRaveen
Founder CodeGain.com
SAVED ME FROM LOOPING THROUGH A CURSOR & SUBSTR ing the text
Thanks a lot!!!!
you`re so nice.
it`s help & inspire manythings for my final project of undergraduate..
i`ve been searching & trying few days to make this 'tokenizer' on pl-sql but always failed.
thanks for your help.
^_^
hi, great tip; you save a lot time for me.
thk´s.
This is why I love internet :)
Here is my small contribution:
The get_token from Anonimus that supports index < 0 has a bug for delimiters longer then 1 and when U have a list that ends with delimiter.
So I fixed it and here it is:
create or replace function get_token (
p_red varchar2 := '111;222;333;444;',
p_index number :=1,
p_delimiter varchar2 :=';'
)
return varchar2
is
l_start number;
l_end number;
begin
if p_index = 1 then
l_start := 1;
elsif p_index < 0 then
if substr (p_red, -length(p_delimiter))= p_delimiter then
l_start := instr(p_red, p_delimiter, -1, abs(p_index)+1) + length(p_delimiter);
else
l_start := instr(p_red, p_delimiter, -1, abs(p_index))+ length(p_delimiter);
end if;
else
l_start := instr(p_red, p_delimiter, 1, p_index - 1) ;
if l_start = 0 then
return null;
else
l_start := l_start + length(p_delimiter);
end if;
end if;
if p_index < 0 then
l_end := instr(p_red, p_delimiter, l_start+1, 1);
else
l_end := instr(p_red, p_delimiter, l_start, 1);
end if;
if l_end = 0 then
return substr(p_red, l_start);
else
return substr(p_red, l_start, l_end - l_start);
end if;
end get_token;
Thanks for the function. I was looking for the same one. As I am not much familar to functions it really helped me a lot
Thank you so much for posting the function.
Post a Comment