ORA-01861: literal does not match format string.while executing procedure - oracle

I have written procedure which have date paramters defined as below:
in_Spendpaidstartdt IN DATE,
in_Spendpaidenddt IN DATE,
while with in procedure i am calling these paramters as:
AND ( in_Spendpaidstartdt IS NULL
OR err.Spendpaiddt >= in_Spendpaidstartdt)
AND ( in_Spendpaidenddt IS NULL
OR err.Spendpaiddt <= in_Spendpaidenddt));
however oracle is giving following error:
"ORA-01861: literal does not match format string"
Some one please suggest the work around.

Here is dummy:
CREATE OR REPLACE PROCEDURE XYZ (
in_startdt IN DATE,
in_enddt IN DATE,
output OUT SYS_REFCURSOR)
IS
rcrdnums VARCHAR2 (32767);
rcrd_cnt INT;
BEGIN
rcrd_cnt := 500;
SELECT RTRIM (
XMLCAST (
XMLAGG (XMLELEMENT (e, RCRDNUM) ORDER BY RCRDNUM) AS CLOB),
',')
INTO rcrdnums
FROM (SELECT (ERR.RCRDNUM || ',') AS RCRDNUM
FROM Table_NAME ERR
WHERE ROWNUM <= rcrd_cnt
and ( in_startdt IS NULL
OR to_date(err.paiddt, 'dd/mm/yyyy') >= to_date(in_startdt, 'dd/mm/yyyy'))
AND ( in_enddt IS NULL
OR to_date(err.paiddt, 'dd/mm/yyyy') <= to_date(in_enddt, 'dd/mm/yyyy')));
IF LENGTH (rcrdnums) = 1
THEN
rcrdnums := NULL;
ELSE
rcrdnums := rcrdnums;
--SUBSTR (rcrdnums, 1, LENGTH (rcrdnums) - 1);
END IF;
DBMS_OUTPUT.PUT_LINE (rcrdnums);
OPEN outputFOR
SELECT *
FROM Table_NAME ERR
INNER JOIN ( SELECT REGEXP_SUBSTR (rcrdnums,
'[^,]+',
1,
LEVEL)
AS EVENT
FROM DUAL
CONNECT BY REGEXP_SUBSTR (rcrdnums,
'[^,]+',
1,
LEVEL)
IS NOT NULL) EVENT_P
ON EVENT_P.EVENT = ERR.RCRDNUM;
END;
/

As already mentioned to #XING, your issues are two-fold.
You are forcing Oracle to do an implicit conversion of a DATE back to a string, when you used to_date on something that's already a DATE - something I've already mentioned elsewhere on stackoverflow!
You are (probably) not passing in the parameters correctly when calling your procedure.
Here is how I'd amend your procedure:
CREATE OR REPLACE PROCEDURE XYZ (
in_startdt IN DATE,
in_enddt IN DATE,
output OUT SYS_REFCURSOR)
IS
rcrdnums VARCHAR2 (32767);
rcrd_cnt INT;
BEGIN
rcrd_cnt := 500;
SELECT RTRIM (
XMLCAST (
XMLAGG (XMLELEMENT (e, RCRDNUM) ORDER BY RCRDNUM) AS CLOB),
',')
INTO rcrdnums
FROM (SELECT (ERR.RCRDNUM || ',') AS RCRDNUM
FROM Table_NAME ERR
WHERE ROWNUM <= rcrd_cnt
and ( in_startdt IS NULL
OR to_date(err.paiddt, 'dd/mm/yyyy') >= in_startdt) -- in_startdt is already a DATE, so no need to convert it
AND ( in_enddt IS NULL
OR to_date(err.paiddt, 'dd/mm/yyyy') <= in_enddt)); -- in_enddt is already a DATE, so no need to convert it
IF LENGTH (rcrdnums) = 1
THEN
rcrdnums := NULL;
ELSE
rcrdnums := rcrdnums;
--SUBSTR (rcrdnums, 1, LENGTH (rcrdnums) - 1);
END IF;
DBMS_OUTPUT.PUT_LINE (rcrdnums);
OPEN output FOR
SELECT *
FROM Table_NAME ERR
INNER JOIN ( SELECT REGEXP_SUBSTR (rcrdnums,
'[^,]+',
1,
LEVEL)
AS EVENT
FROM DUAL
CONNECT BY REGEXP_SUBSTR (rcrdnums,
'[^,]+',
1,
LEVEL)
IS NOT NULL) EVENT_P
ON EVENT_P.EVENT = ERR.RCRDNUM;
END;
/
And to test, I'd call your procedure like so:
declare
v_refcur sys_refcursor;
begin
xyz(in_startdt => to_date('01/10/2016', 'dd/mm/yyyy'),
in_enddt => to_date('05/10/2016', 'dd/mm/yyyy'),
output => v_refcur);
end;
/
N.B. it's bad practice to use "select *" in production code - you should explicitly specify the columns you're wanting to get back; that way, if someone adds a column, your code won't cause something to break because it won't pass that extra column along.

Related

PLSQL no data found

I have a new problem and new question :) I want to save data in table but Oracle can't do this. He give error ora-01403 no data found. In code below I can't find then is this error. Thanks for helping.
DECLARE
CURSOR c_data IS
SELECT
ROWID, td.*
FROM cols td
WHERE td.col1 IS NOT NULL AND td.col2 IS NOT NULL AND td.col3 IS NOT NULL AND td.col4 IS NOT NULL AND td.col5 IS NOT NULL AND td.col6 IS NOT NULL
AND td.col7 IS NOT NULL OR td.col8 IS NOT NULL OR td.col9 IS NOT NULL OR td.col10 IS NOT NULL;
c_id NUMBER;
r_number NUMBER;
editor NUMBER;
intendant NUMBER;
supersu NUMBER;
BEGIN
FOR i IN c_data LOOP
c_id := 0;
r_number := 0;
r_number := random_number();
c_id := fn_name(i.col5);
in next section I want to insert data in table
INSERT INTO obj.tb#link_name (
first,
ip,
cuname,
data1,
tp,
lastest,
middle,
date3,
date2,
date
) VALUES (
i.col1,
i.col8 || i.col9,
c_id,
to_date(sysdate, 'MM/DD/YYYY'),
CASE
WHEN i.col8 IS NOT NULL THEN
(
SELECT tp
FROM vtp
WHERE tp = 10
)
ELSE
(
SELECT tp
FROM vtp
WHERE tp = 20
)
END,
i.col3,
i.col2,
to_date(i.col4, 'MM/DD/YYYY'),
to_date(i.col6, 'MM/DD/YYYY'),
to_date(i.col7, 'MM/DD/YYYY')
);
COMMIT;
in next section I find some value
SELECT DISTINCT ( ed ) NTO editor
FROM dt_table_2
WHERE upper(first) LIKE upper(i.col1) AND upper(last) LIKE upper(i.col3) AND upper(middle) LIKE upper(i.col2) AND ROWNUM = 1;
SELECT DISTINCT ( numberVar ) INTO intendant
FROM dt_table_1
WHERE numberVar = :p13_apex_item AND ROWNUM = 1;
in next section I want to insert data in table
INSERT INTO obj.tb2#link_name (
nb_val,
nb_val2
) VALUES (
1,
editor
);
COMMIT;
SELECT DISTINCT ( st ) INTO supersu
FROM obj.tbd12#link_name
WHERE ed = editor AND ROWNUM = 1;
INSERT INTO obj.tdb3#link_name (
data1,
solus,
snumber,
ter,
sd,
ad,
dd,
ed,
ssd,
data2,
id
) VALUES (
to_date(sysdate, 'MM/DD/YYYY'),
intendant,
r_number,
to_date(sysdate + 5, 'MM/DD/YYYY'),
1,
1,
1,
editor,
supersu,
to_date(sysdate, 'MM/DD/YYYY'),
intendant
);
COMMIT;
END LOOP;
END;
There are 3 SELECT ... INTO statements in code you posted:
SELECT DISTINCT (ed)
INTO editor
FROM dt_table_2
WHERE UPPER (FIRST) LIKE UPPER (i.col1)
AND UPPER (LAST) LIKE UPPER (i.col3)
AND UPPER (middle) LIKE UPPER (i.col2)
AND ROWNUM = 1;
SELECT DISTINCT (numbervar)
INTO intendant
FROM dt_table_1
WHERE numbervar = :p13_apex_item
AND ROWNUM = 1;
SELECT DISTINCT (st)
INTO supersu
FROM obj.tbd12#link_name
WHERE ed = editor
AND ROWNUM = 1;
At least one of them didn't find any results and has returned the NO_DATA_FOUND error. Which one? No idea, you'll have to debug it yourself and see what you'll going to do about it:
handle the exception, or
fix code you wrote

oracle function for: select distinct values count from comma separated string

I need an Oracle (11) function to handle this question.
I need to counting distinct values count from comma separated string.
For example the comma separated string:
'Lorem,Ipsum,is,simply,dummy,text,Lorem,Ipsum,is,simply,dummy,text,Lorem,Ipsum,is,simply,dummy,text'
The result have to be = 6
Beacuse of
Lorem
Ipsum
is
simply
dummy
text
I want to use like this
select fn_dist_count_values_in_list_arr('Lorem,Ipsum,is,simply,dummy,text,Lorem,Ipsum,is,simply,dummy,text,Lorem,Ipsum,is,simply,dummy,text') from dual;
Can anyone help to write this ("fn_dist_count_values_in_list_arr") oracle function?
You don't need a context switch from SQL to a PL/SQL function and can do it all in SQL:
SELECT ( SELECT COUNT( DISTINCT CAST(column_value AS VARCHAR2(20)) )
FROM XMLTABLE( ('"'||REPLACE(value,',','","')||'"') ) )
AS num_distinct_values
FROM table_name
Which, for the sample data:
CREATE TABLE table_name ( value ) AS
SELECT 'Lorem,Ipsum,is,simply,dummy,text,Lorem,Ipsum,is,simply,dummy,text,Lorem,Ipsum,is,simply,dummy,text' FROM DUAL;
Outputs:
| NUM_DISTINCT_VALUES |
| ------------------: |
| 6 |
If you want a pure PL/SQL function (so that you do not have multiple context-switches) then:
CREATE FUNCTION fn_dist_count_values_in_list_arr (
list_value IN VARCHAR2
) RETURN NUMBER DETERMINISTIC
IS
TYPE t_words IS TABLE OF NUMBER(1,0) INDEX BY VARCHAR2(200);
v_words t_words;
v_start PLS_INTEGER := 1;
v_end PLS_INTEGER;
BEGIN
IF list_value IS NULL THEN
RETURN 0;
END IF;
LOOP
v_end := INSTR( list_value, ',', v_start );
EXIT WHEN v_end = 0;
v_words(SUBSTR(list_value, v_start, v_end - v_start ) ) := 1;
v_start := v_end + 1;
END LOOP;
v_words(SUBSTR(list_value,v_start)) := 1;
RETURN v_words.COUNT;
END;
/
and then:
SELECT fn_dist_count_values_in_list_arr( value )
FROM table_name
outputs:
| FN_DIST_COUNT_VALUES_IN_LIST_ARR(VALUE) |
| --------------------------------------: |
| 6 |
db<>fiddle here
CREATE OR REPLACE FUNCTION DIST_COUNT_VALUES_IN_STR_ARR
(STR_ARR IN VARCHAR2)
RETURN NUMBER
AS
DIST_COUNT NUMBER(38);
BEGIN
SELECT COUNT(DISTINCT COL1)
INTO DIST_COUNT FROM (
SELECT REGEXP_SUBSTR(STR_ARR,'[^,]+', 1, LEVEL) COL1
FROM DUAL
CONNECT BY LEVEL <= REGEXP_COUNT(STR_ARR, ',') + 1
);
RETURN DIST_COUNT;
END;
This worked for me. The inner query separates the elements into rows by using regex on the comma character. I had to rename your function as i hit the limit of the max length of an object name for my version of Oracle.
And another approach would be to use hierarchical query to split comma separated values to a set of rows (with clause) and run a simple sql query against it
with str_parsed as (SELECT REGEXP_SUBSTR('Lorem,Ipsum,is,simply,dummy,text,Lorem,Ipsum,is,simply,dummy,text,Lorem,Ipsum,is,simply,dummy,text', '[^,]+', 1, LEVEL) val
FROM dual
CONNECT BY REGEXP_SUBSTR('Lorem,Ipsum,is,simply,dummy,text,Lorem,Ipsum,is,simply,dummy,text,Lorem,Ipsum,is,simply,dummy,text', '[^,]+', 1, LEVEL) IS NOT NULL)
select count(distinct val) from str_parsed

How to create dynamic sql by Pairing string in Oracle

What i need
i need to create dynamic sql based on param procedure receives.
Type :[A,B,C]
Code : [1,2,3]
Dynamic sql
AND ( (in_type = 'A' and in_code = '1' )
OR (in_type = 'B' and in_code = '2')
OR (in_type = 'C' and in_code = '3')
)
Solution i tried
splitting string from comma separated
CREATE OR REPLACE TYPE t_my_list AS TABLE OF VARCHAR2(100);
CREATE OR REPLACE
FUNCTION comma_to_table(p_list IN VARCHAR2)
RETURN t_my_list
AS
l_string VARCHAR2(32767) := p_list || ',';
l_comma_index PLS_INTEGER;
l_index PLS_INTEGER := 1;
l_tab t_my_list := t_my_list();
BEGIN
LOOP
l_comma_index := INSTR(l_string, ',', l_index);
EXIT
WHEN l_comma_index = 0;
l_tab.EXTEND;
l_tab(l_tab.COUNT) := TRIM(SUBSTR(l_string,l_index,l_comma_index - l_index));
l_index := l_comma_index + 1;
END LOOP;
RETURN l_tab;
END comma_to_table;
/
FOR x IN (select * from (table(comma_to_table(in_type)) ) )
LOOP
dbms_output.put_line(x.COLUMN_VALUE);
FOR y IN (select * from (table(comma_to_table(in_code )) ) )
LOOP
dbms_output.put_line(y.COLUMN_VALUE);
IF x.COLUMN_VALUE = 'A' THEN
l_where := l_where||' AND UPPER(column_name) IN (' || upper(in_code) || ')';
END IF;
END LOOP;
END LOOP;
You can define a single collection of objects and then do additional processing:
CREATE OR REPLACE TYPE t_my_item as object(
name varchar2(1),
code number
);
/
CREATE OR REPLACE TYPE t_my_list AS TABLE OF t_my_item;
declare
l_my_list t_my_list;
begin
select cast(multiset(
select t1.val, t2.val from (
select regexp_substr('A,B,C','[^,]+', 1, level ) val , level lvl
from dual
connect by regexp_substr('A,B,C', '[^,]+', 1, level) is not null) t1,
(select regexp_substr('1,2,3','[^,]+', 1, level) val , level lvl
from dual
connect by regexp_substr('1,2,3', '[^,]+', 1, level) is not null) t2
where t1.lvl = t2.lvl) as t_my_list) into l_my_list from dual;
end;
/
and here's a single select stmt:
select ' AND ( '||LISTAGG(
stmt,
' or '
) WITHIN GROUP(
ORDER BY lvl) || ' )'
from (
select t1.lvl, '(in_type = ''' || t1.val||''' and in_code = ''' || t2.val || ''' )' stmt
from (
select regexp_substr('A,B,C','[^,]+', 1, level ) val , level lvl
from dual
connect by regexp_substr('A,B,C', '[^,]+', 1, level) is not null) t1,
(select regexp_substr('1,2,3','[^,]+', 1, level) val , level lvl
from dual
connect by regexp_substr('1,2,3', '[^,]+', 1, level) is not null) t2
where t1.lvl = t2.lvl);
just copy paste what's above to your method:
create or replace FUNCTION comma_to_table(p_type IN VARCHAR2, p_code IN VARCHAR2)
RETURN t_my_list
AS
l_my_list t_my_list;
begin
select cast(multiset(
select t1.val, t2.val from (
select regexp_substr(p_type,'[^,]+', 1, level ) val , level lvl
from dual
connect by regexp_substr(p_type, '[^,]+', 1, level) is not null) t1,
(select regexp_substr(p_code,'[^,]+', 1, level) val , level lvl
from dual
connect by regexp_substr(p_code, '[^,]+', 1, level) is not null) t2
where t1.lvl = t2.lvl) as t_my_list) into l_my_list from dual;
return l_my_list;
end;
/
select comma_to_table(p_type => 'A,B,C', p_code => '1,2,3') from dual;

Count of each characters in given string

I require the count of each character in a string.
Example:
SELECT ('aabcccdee') from dual;
Result:
a(1),b(2), c(3), d(1),e(2).
Thanks in advance.
You can use a hierarchical query to split your string into it individual characters; this is using a CTE just to supply your example value:
with t (value) as (
select 'aabcccdee' from dual
)
select substr(value, level, 1) as a_char
from t
connect by level <= length(value);
Then you can use aggregation to count how may times each appears:
with t (value) as (
select 'aabcccdee' from dual
)
select a_char, count(*) a_count
from (
select substr(value, level, 1) as a_char
from t
connect by level <= length(value)
)
group by a_char
order by a_char;
A_CH A_COUNT
---- ----------
a 2
b 1
c 3
d 1
e 2
And you can use listagg() (if you're on 11g or above) to aggregate those characters and counts into a single string if that's what you really want:
with t (value) as (
select 'aabcccdee' from dual
)
select listagg(a_char || '(' || count(*) || ')', ',') within group (order by a_char)
from (
select substr(value, level, 1) as a_char
from t
connect by level <= length(value)
)
group by a_char;
LISTAGG(A_CHAR||'('||COUNT(*)||')',',')WITHINGROUP(ORDERBYA_CHAR)
-----------------------------------------------------------------
a(2),b(1),c(3),d(1),e(2)
If you particularly want to do this in PL/SQL - because you value is already in a PL/SQL variable perhaps - you can do the same thing with a context switch:
set serveroutput on
declare
l_value varchar2(30) := 'aabcccdee';
l_result varchar2(100);
begin
select listagg(a_char || '(' || count(*) || ')', ',') within group (order by a_char)
into l_result
from (
select substr(l_value, level, 1) as a_char
from dual
connect by level <= length(l_value)
)
group by a_char;
dbms_output.put_line(l_result);
end;
/
a(2),b(1),c(3),d(1),e(2)
PL/SQL procedure successfully completed.

How to group sequence numbers in oracle?

I have a string which is years separated by comma.
For example 2000,2001,2002,2005,2006,2007 and 2010.
I want to group the consecutive numbers.
My output should be 2000-2003,2005-2007 and 2010. Is there any way to do this in Oracle Stored procedure?
Disclaimer - I don't recommend using this solution "as is", but it can give ideas, and it was fun writing it
I assume you have a column with the csv strings in a table.
If you're using oracle 11gR2 then you can use recursive CTEs-
Here is a sqlfiddle demo
with t as
(
select replace(replace(v, ' and ', ','), ' ','') v
from strings
),
rcte(text, token, res) as
(
select v, regexp_substr(v, '^\d*[^,]'), regexp_substr(v, '^\d*[^,]') ||'-'
from t
union all
select regexp_replace(text, '^\d*,', ''),
regexp_substr(text, '^\d*[^,]'),
case when regexp_substr(text, '^\d*[^,]') = token then
res
when regexp_substr(text, '^\d*[^,]') = token+1 then
regexp_replace(res, '-\d*$', '-'||(token+1))
else rtrim(res, '-') || ',' || regexp_substr(text, '^\d*[^,]') || '-'
end
from rcte
where text <> token
)
select rtrim(res, '-') from rcte
where text = regexp_substr(rtrim(res, '-'), '\d*$');
(This can be done without regular expressions as well)
18:42:15 SYSTEM#dwal> l
1 with p as (
2 select replace('2000,2001,2002,2005,2006,2007 and 2010',' and ',',') s, '[0-9]{4}' r from dual
3 ), ex as (
4 select regexp_substr(s,r, 1, level) as y
5 from p
6 connect by level <= regexp_count(s, r)
7 ), grp as (
8 select connect_by_root(y) s, y
9 from ( select e1.y y, e2.y p from ex e1, ex e2 where e1.y - 1 = e2.y(+) )
10 connect by prior y = p
11 start with p is null
12 ), agg as (
13 select listagg(s||decode(max(y), s, null, '-'||max(y)), ',') within group (order by s) str
14 from grp group by s
15 )
16* select regexp_replace(str, ',', ' and ', 1, regexp_count(str, ',')) result from agg
18:42:16 SYSTEM#dwal> /
RESULT
------------------------------
2000-2002,2005-2007 and 2010
Elapsed: 00:00:00.02
use mathematics
select min(seq) as range_from , max(seq) range_to , count(*) as cnt
from sometable
group by ceil(seq/3) * 3
this part ceil(seq/3) * 3 is rounding a number to the nearest multiple of three. if you want a range of 5 just use ceil(seq/5) * 5.
Cheers !
It can be done with SQL by using analytical functions.
*Update 1: * Answer updated with parser functionality, missed in previous version.
*Update 2: * Added final string composition
with p as ( -- Parameter string
select replace('2000,2001,2002,2005,2006,2007 and 2010',' and ',',') s from dual
),
ex as ( -- Parse string to sequence
select
to_number(
substr(
s,
decode( level, 1, 1, instr(s,',',1,level-1)+1 ),
decode( instr(s,',',1,level), 0, length(s)+1, instr(s,',',1,level) )
-
decode( level, 1, 1, instr(s,',',1,level-1)+1 )
)
) as y
from p
connect by instr(s,',',1,level-1) > 0
),
period_set as (
select -- Make final string for each interval start
y,
lag(y) over (order by y) prior_y,
max(y) over (partition by 1) max_y,
y || (case when is_end > 1 then null else '-' ||end_y end) as interval_string
from
( -- For each start find interval end
select
y,
is_start,
is_end,
lead(y) over (order by y) end_y
from
( -- Find if previous/next value differs more then by one.
-- If so, mark as start/end
select
y,
nvl(y - prev_y, 100) is_start,
nvl(next_y - y, 100) is_end
from
( -- Find previous/next value in sequence
select
y,
lag(y) over (order by y) prev_y,
lead(y) over (order by y) next_y
from ex
)
)
where
is_start > 1 or is_end > 1
)
where is_start > 1
)
select
replace(
substr(
sys_connect_by_path(
decode(y,max_y,'m', null) || interval_string,
','
),2
),
',m',
' and '
) result_str
from
period_set
where
connect_by_isleaf = 1
start with
prior_y is null
connect by
prior y = prior_y
SQL Fiddle can be found here.
A great QUESTION!
Please check my logic...
with test as
(
select '2000,2002,2003,2004,2006,2007' str from dual
)
,test1 as (
select
split1,
lead(split1, 1, null) over (order by split1 asc) lead_no,
level1
from
(
select to_number(regexp_substr (str, '[^,]+', 1, rownum)) split1, level as level1
from test b
connect by level <= length (regexp_replace (str, '[^,]+')) + 1
)x
)
--select * from test1
,test2 (split1, lead_no, level1, op, op1) as(
select
split1,
lead_no,
level1,
(case when split1+1=lead_no then to_char(split1) else NULL end),
(case when split1+1=lead_no then NULL else to_char(split1) end)
from test1
where level1=1
union all
select
a.split1,
a.lead_no,
b.level1+1,
(case when a.split1+1=a.lead_no and to_char(b.op) is not null then to_char(b.op)
when a.split1+1=a.lead_no and to_char(b.op) is null then to_char(a.split1)
else null end),
(case when (a.split1+1<>a.lead_no and to_char(b.op)<>to_char(a.split1)) OR
(a.lead_no is null and to_char(b.op) is not null) then to_char(b.op) ||'-'||to_char(a.split1)
when a.lead_no is null then to_char(a.split1)
else null end)
from test1 a inner join test2 b on a.level1 = b.level1+1
)
select op1 from test2
where op1 is not null
Assuming your data will be comma seperated and input you can mentioned any years seperated by comma.
DECLARE
v_str VARCHAR(100) := '&n';
v_instr NUMBER;
v_instr1 NUMBER;
v_g VARCHAR(50);
v_F VARCHAR(50);
v_OUT VARCHAR(50);
v_OUT1 VARCHAR(50);
v_TEMP VARCHAR(50);
v_TMP VARCHAR(50) := ' ';
v_cnt NUMBER :=0;
V_FLAG NUMBER :=1;
BEGIN
FOR i IN 1..Length(v_str)-Length(REPLACE(v_str,',',''))+1 LOOP
IF i = 1 THEN
v_g := SubStr(v_str,1,InStr(v_str,',',1,i)-1);
V_F := V_G;
ELSE
v_instr := InStr(v_str,',',1,i-1);
v_instr1 := InStr(v_str,',',1,i);
IF(v_cnt+1 <= Length(v_str)-Length(REPLACE(v_str,',',''))) then
v_g := SubStr(v_str,v_instr+1,v_instr1-v_instr-1);
IF V_FLAG = 0 THEN V_F := V_G; V_FLAG :=1; END IF;
ELSE
v_g := SubStr(v_str,v_instr+1);
IF V_FLAG = 0 THEN V_F := V_G; V_FLAG :=1; END IF;
END IF;
END IF;
v_cnt := v_cnt+1;
--IF(I>1) THEN
IF(V_TEMP+1 = V_G) THEN
IF(V_OUT IS not NULL) THEN
V_OUT := V_OUT||'-'||V_G;
ELSE
V_OUT := V_F||'-'||V_G;
END IF;
ELSE
V_OUT1 := SubStr(V_OUT,1,5)||SubStr(V_OUT,-4);
V_OUT := NULL;
v_out := v_g;
V_FLAG := 0;
END IF;
--END IF;
V_TEMP := To_Number(V_G);
--Dbms_Output.put_line(v_g);
IF(v_tmp <> v_out1) THEN
SELECT Decode(instr(v_OUT1,'-'),0,subStr(V_OUT1,1,4),v_out1) INTO v_out1 FROM dual;
Dbms_Output.put_line('Year span : '||v_OUT1);
v_tmp := v_out1;
END IF;
END LOOP;
SELECT Decode(Length(v_OUT),4,v_out,subStr(V_OUT,1,5)||SubStr(V_OUT,-4)) INTO v_out1 FROM dual;
Dbms_Output.put_line('Year span : '||v_out1);
END;
You have to use cursor, to loop throught the years, and to do somethink like this :
CREATE OR REPLACE Function concatYears
RETURN varchar;
DECLARE
oldYear number;
concat varchar(300);
newGroup boolean;
cursor cur1 is
SELECT year
FROM dates
ORDER BY year ASC;
BEGIN
oldYear:=0;
newGroup:=true;
concat := '';
FOR row in c1
IF oldYear == 0 THEN
oldYear := row.year;
END IF;
IF newGroup == true THEN
concat := concat || CAST(oldYear AS varchar(4));
newGroup:= false;
ELSE
IF row.year > oldYear+1 THEN
concat:= concat || '-' || CAST(oldYear AS varchar(4)) || ' , ';
newGroup:=true;
END IF;
END IF;
oldYear:=row.year;
END LOOP;
RETURN concat;
END;

Resources