PL / Sql Query to store array of variables - oracle

Input variable (Variable1,variable2).
Variable1=Mobile
Variable2=1:0,2:1,3:0
I want to store the above variable into oracle database like below format using the stored procedure:
Prod_name
Accessories_no
Include
Mobile
1
0
Mobile
2
1
Mobile
3
0

You can use regexp as follows:
insert into your_Table
select Variable1,
regexp_substr(regexp_substr(Variable2,'[^,]+', level),'[^:]+',1),
regexp_substr(regexp_substr(Variable2,'[^,]+', level),'[^:]+',2)
from dual
connect by level <= regexp_count(Variable2,',') + 1

You could write simple split-replace logic in your procedure like below:
declare
Variable1 varchar2(10) := 'Mobile';
Variable2 varchar2(100) := '1:0,2:1,3:0';
temp varchar2(10) := '';
col1 varchar2(10) := '';
col2 varchar2(10) := '';
begin
while length(Variable2) > 0 loop
--split by comma
if instr(Variable2, ',') > 0 then
temp := substr(Variable2, 0, instr(Variable2, ','))
else
temp := substr(Variable2, 0, length(Variable2));
end if;
--remove the 1st set
Variable2 := replace(Variable2, temp,'');
--split by colon and get the values
col1 := substr(temp, 0, instr(temp, ':')-1);
col2 := substr(temp, instr(temp, ':')+1, length(temp));
insert into my_table values(Variable1, col1, col2);
end loop;
exception when others then
dbms_output.put_line(sqlerrm);
end;

Related

String concatenation select query is not giving result pl/sql script

I am trying to construct query dynamically but after string concatenation the select statement not producing any result in pl/sql.
Please help me on this
DECLARE
person_id NUMBER;
BEGIN
DECLARE
age_where VARCHAR2(100 CHAR);
TEMP_WHERE VARCHAR2(100 CHAR) := '';
add_temp_where BOOLEAN := true;
begin
age_where := q'[ and age=28]';
IF(ADD_TEMP_WHERE) THEN
TEMP_WHERE := age_where;
END IF;
SELECT id INTO person_id FROM PERSON WHERE name = 'David' || TEMP_WHERE ;
EXCEPTION
WHEN NO_DATA_FOUND THEN
DBMS_OUTPUT.PUT_LINE('no data');
END;
DBMS_OUTPUT.PUT_LINE('result : ' || person_id);
END;
Table entries
ID NAME AGE ADDRESS SALARY
-----------------------------------------------
1 David 28 PURAM 30000
2 Vimal 30 MARUR 20000
Output:
anonymous block completed
no data
result :
You'll need dynamic SQL for that.
SQL> DECLARE
2 person_id NUMBER;
3 age_where VARCHAR2 (100 CHAR);
4 TEMP_WHERE VARCHAR2 (100 CHAR) := '';
5 add_temp_where BOOLEAN := TRUE;
6 l_str VARCHAR2 (400);
7 BEGIN
8 age_where := q'[ and age=28]';
9
10 IF (ADD_TEMP_WHERE)
11 THEN
12 TEMP_WHERE := age_where;
13 END IF;
14
15 l_str := q'[SELECT id FROM PERSON WHERE name = 'David']' || TEMP_WHERE;
16
17 EXECUTE IMMEDIATE l_str
18 INTO person_id;
19
20 DBMS_OUTPUT.PUT_LINE ('result : ' || person_id);
21 END;
22 /
result : 1
PL/SQL procedure successfully completed.
SQL>
Your objective is to have dynamic predicate in a query, based on input parameters. This is a frequent task.
The most important think is to realize, that you should always use bind variables for production queries, i.e. ommit construction such as WHERE name = 'David' || TEMP_WHERE.
There are three options in PL/SQL
Use IF - ELSE
This is the simplest option where for each combination of the input parameter there is one IF/ELSE branch
declare
l_person_id int;
l_name varchar2(10) := 'David';
l_age int := 28;
add_temp_where int := 1;
begin
if add_temp_where = 0 then
select id into l_person_id from person where name = l_name;
else
select id into l_person_id from person where name = l_name and age = l_age;
end if;
dbms_output.put_line(l_person_id);
end;
/
This will work in your case with one optional parameter, but is not practicable with more parameters. For 3 parameters you will need 8 branches.
Use OR to Disable Predicates
This option use only one static statement and use the OR logic to disable unwanted parameters.
Note that I'm using the parameter add_temp_where and int 0,1 to be able to use it in SQL.
declare
l_person_id int;
l_name varchar2(10) := 'David';
l_age int := 28;
add_temp_where int := 1;
begin
select id into l_person_id from person where name = l_name and (add_temp_where = 0 or age = l_age);
dbms_output.put_line(l_person_id);
end;
/
So basically if add_temp_where = 0 than the predicate age = l_age is ignored due to shortcut evaluation.
This option works fine in case that for all options the query returns similar number of rows (technically the same execution plan is used).
It fails badly in case that for one option the whole table is returned and for other ony some small part (via index).
In such case you need to use dynamic SQL.
Dynamic SQL
You use the same trick as above with OR, so you generate following SQL strings
-- for add_temp_where = 1
select id from person where name = :1 and age = :2
-- for add_temp_where = 0
select id from person where name = :1 and (0=0 or age = :2)'
Note that both statement have two bind variables, so they can be used in one EXECUTE IMMEDIATE as follows:
declare
l_person_id int;
l_name varchar2(10) := 'David';
l_age int := 28;
add_temp_where BOOLEAN := true;
--
l_sql1 varchar2(1000) := 'select id from person where name = :1 and age = :2';
l_sql2 varchar2(1000) := 'select id from person where name = :1 and (0=0 or age = :2)';
begin
execute immediate l_sql1 into l_person_id using l_name, l_age;
dbms_output.put_line(l_person_id);
execute immediate l_sql2 into l_person_id using l_name, l_age;
dbms_output.put_line(l_person_id);
end;
/
Both statements are independent and can have therefore different execution plans, which solves the problem of the second option.
More info and credits to this option here and here

Take Oracle Type Array and insert contents into an Oracle table

I have a variable l_rec of type wwv_flow_global.vc_arr2 within my package procedure, where:
type vc_arr2 is table of varchar2(32767) index by binary_integer;
Within l_rec, I have populated a number of records.
Within my debug statement, I can access the records using the following query:
FOR i IN 1..l_rec.COUNT
LOOP
insert into msg_log(msg)
values
('Record info: Index: ' || i || ' - Value: ' || l_rec(i));
END LOOP;
FYI, I actually also have an outer loop that repeats the below info but with different data, i.e. a loop within a loop.
Sample dataset looks like:
Record info: Index: 1 - Value: AA
Record info: Index: 2 - Value: BB
Record info: Index: 3 - Value: CC
Record info: Index: 4 - Value: DD
Record info: Index: 5 - Value: EE
Record info: Index: 1 - Value: AAA
Record info: Index: 2 - Value: BBB
Record info: Index: 3 - Value: CCC
Record info: Index: 4 - Value: DDD
Record info: Index: 5 - Value: EEE
etc....
Based on the above, I have created a table called message_log that has the following columns:
SEQ_ID NUMBER,
C001 VARCHAR2(4000),
C002 VARCHAR2(4000),
C003 VARCHAR2(4000),
C004 VARCHAR2(4000),
C005 VARCHAR2(4000)
My question is, how can I take my l_rec array of type wwv_flow_global.vc_arr2 and insert the whole contents into my message_log Oracle table?
Please note that SEQ_ID here will be a counter of my outer loop so I would expect to see message log table data as follows:
1,AA,BB,CC,DD,EE
2,AAA,BBB,CCC,DDD,EEE
If you work with apex_t_varchar2, you can
select * from table(apex_string.split('1,2,3',','));
or
declare
l_table apex_t_varchar2;
begin
apex_string.push(l_table, 'a');
apex_string.push(l_table, 'b');
sys.dbms_output.put_line(apex_string.join(l_table, ','));
end;
/
Which I think would cover a few of your needs.
You have a few options.But direct insert is not possible.
create table msg_log(
seq_id number,
col1 varchar2(4000),
col2 varchar2(4000),
col3 varchar2(4000),
col4 varchar2(4000),
col5 varchar2(4000),
col6 varchar2(4000),
col7 varchar2(4000),
col8 varchar2(4000));
Preparing test table.
In static approach each value from your record is assigned to rowtype record.
In dynamic approach whole insert is generated.
declare
type vc_arr2 is table of varchar2(32767) index by binary_integer;
rec vc_arr2;
row_msg_log msg_log%rowtype ;
function populate(how_many number) return vc_arr2 is
tmp vc_arr2;
begin
for i in 1 .. how_many loop
tmp(i) := 'VALUE'||i;
end loop;
return tmp;
end;
function static_approach(id number , rec vc_arr2) return msg_log%rowtype
is
tmp msg_log%rowtype;
begin
tmp.seq_id := id;
if rec.exists(1) then
tmp.col1 := rec(1);
end if;
if rec.exists(2) then
tmp.col2 := rec(2);
end if;
if rec.exists(3) then
tmp.col3 := rec(3);
end if;
--etc.
return tmp;
end;
procedure dynamic_insert(id number , rec vc_arr2) is
v_sql varchar2(4000);
function generate_stament return varchar2 is
idx number;
column_list varchar2(4000);
value_list varchar2(4000);
begin
column_list := '(seq_id';
value_list := '('||id;
idx := rec.first;
while (idx is not null)
loop
column_list := column_list||' ,col'||idx;
value_list := value_list||' ,'''||rec(idx)||'''';
idx := rec.next(idx);
end loop;
column_list := column_list||') ';
value_list := value_list||') ';
return 'insert into msg_log'||column_list||'values'||value_list;
end;
begin
v_sql := generate_stament;
execute immediate v_sql;
end;
begin
row_msg_log := static_approach(1,populate(3));
insert into msg_log values row_msg_log;
row_msg_log := static_approach(2,populate(4));
insert into msg_log values row_msg_log;
dynamic_insert(3,populate(8));
dynamic_insert(4,populate(1));
-- with a not dens array
rec := populate(1);
rec(5) := 'blblb';
rec(8) := 'fofofo';
dynamic_insert(4,rec);
end;

How to use Date type bind variable in ORACLE SQL Statement?

I have a loop in my query So I have to use Oracle procedure but It's working only if I am giving date hardcoded, but I want to do it with bind variables, How can it be done?
My Query:
DECLARE
TABLE_NAME VARCHAR2(100);
SQL_STATEMENT VARCHAR2(2000);
TOTAL_CASES NUMBER(10) := 0;
LOOP_CASES NUMBER(10) := 0;
BEGIN
FOR MY_ROW IN 00..99
LOOP
TABLE_NAME:= 'history'||TRIM(TO_CHAR(MY_ROW,'00'));
SQL_STATEMENT:='SELECT COUNT(DISTINCT USR_ID)
FROM ' || TABLE_NAME || '#hr WHERE ATTRIBUTE_ID = 109
AND OLD_VALUE IS NULL AND UPDATED_BY_SCREEN = ''CRM'' AND TRUNC(UPDATE_DATE) between TO_DATE(''15-MAY-2016'') and TO_DATE(''21-MAY-2016'')';
EXECUTE IMMEDIATE SQL_STATEMENT INTO LOOP_CASES;
TOTAL_CASES := TOTAL_CASES + LOOP_CASES;
END LOOP;
DBMS_OUTPUT.PUT_LINE(TOTAL_CASES);
END;
Above query is giving me output but I want to use Bind Variable for date parameters, How can it be done?
Solutions I tried:
DECLARE
TABLE_NAME VARCHAR2(100);
SQL_STATEMENT VARCHAR2(2000);
TOTAL_CASES NUMBER(10) := 0;
LOOP_CASES NUMBER(10) := 0;
START_DATE Date:= TO_CHAR(:start_dt);
END_DATE Date := TO_CHAR(:end_dt);
BEGIN
FOR MY_ROW IN 00..99
LOOP
TABLE_NAME:= 'history'||TRIM(TO_CHAR(MY_ROW,'00'));
SQL_STATEMENT:='SELECT COUNT(DISTINCT USR_ID)
FROM ' || TABLE_NAME || '#hr WHERE ATTRIBUTE_ID = 109
AND OLD_VALUE IS NULL AND UPDATED_BY_SCREEN = ''CRM'' AND TRUNC(UPDATE_DATE) between TO_DATE('||START_DATE||') and TO_DATE('||END_DATE||')';
EXECUTE IMMEDIATE SQL_STATEMENT INTO LOOP_CASES;
TOTAL_CASES := TOTAL_CASES + LOOP_CASES;
END LOOP;
DBMS_OUTPUT.PUT_LINE(TOTAL_CASES);
END;
But it shows ORA-00907: Missing Right Parenthesis
Another Solution that I tried:
DECLARE
TABLE_NAME VARCHAR2(100);
SQL_STATEMENT VARCHAR2(2000);
TOTAL_CASES NUMBER(10) := 0;
LOOP_CASES NUMBER(10) := 0;
START_DATE Date:= TO_DATE(:start_dt,'dd-mm-yyyy');
END_DATE Date := TO_DATE(:end_dt,'dd-mm-yyyy');
BEGIN
FOR MY_ROW IN 00..99
LOOP
TABLE_NAME:= 'history'||TRIM(TO_CHAR(MY_ROW,'00'));
SQL_STATEMENT:='SELECT COUNT(DISTINCT USR_ID)
FROM ' || TABLE_NAME || '#hr WHERE ATTRIBUTE_ID = 109
AND OLD_VALUE IS NULL AND UPDATED_BY_SCREEN = ''CRM'' AND TRUNC(UPDATE_DATE) between '||START_DATE||' and'||END_DATE||'';
EXECUTE IMMEDIATE SQL_STATEMENT INTO LOOP_CASES;
TOTAL_CASES := TOTAL_CASES + LOOP_CASES;
END LOOP;
DBMS_OUTPUT.PUT_LINE(TOTAL_CASES);
END;
But it gives ERROR ORA-00905: Missing Keyword
How can it be solved?
There could be two possible ways to use bind variables. The first way - if you need to set parameter's values inside the anonymous block:
DECLARE
TABLE_NAME VARCHAR2(100);
SQL_STATEMENT VARCHAR2(2000);
TOTAL_CASES NUMBER(10) := 0;
LOOP_CASES NUMBER(10) := 0;
-- here you calculate values to use in dynamic SQL:
start_date date := TO_DATE('15-MAY-2016','dd-mom-yyyy');
end_date date := TO_DATE('21-MAY-2016','dd-mon-yyyy');
BEGIN
FOR MY_ROW IN 00..99
LOOP
TABLE_NAME:= 'history'||TRIM(TO_CHAR(MY_ROW,'00'));
SQL_STATEMENT:=
'SELECT COUNT(DISTINCT USR_ID)
FROM ' || TABLE_NAME || '#hr
WHERE ATTRIBUTE_ID = 109
AND OLD_VALUE IS NULL
AND UPDATED_BY_SCREEN = ''CRM''
AND TRUNC(UPDATE_DATE) between :P_START and :P_END';
-- there are 2 parameters above - :P_START and :P_END
EXECUTE IMMEDIATE SQL_STATEMENT INTO LOOP_CASES
using start_date, end_date;
TOTAL_CASES := TOTAL_CASES + LOOP_CASES;
END LOOP;
DBMS_OUTPUT.PUT_LINE(TOTAL_CASES);
END;
The second way - when the anonymous block has to take parameters too:
DECLARE
TABLE_NAME VARCHAR2(100);
SQL_STATEMENT VARCHAR2(2000);
TOTAL_CASES NUMBER(10) := 0;
LOOP_CASES NUMBER(10) := 0;
-- one way is when you receive string dates:
start_date date := TO_DATE(:P_OUTER_START_DATE,'dd-mom-yyyy');
end_date date := TO_DATE(:P_OUTER_END_DATE,'dd-mon-yyyy');
-- or another way if you can set parameter in date format outside:
start_date date := :P_OUTER_START_DATE;
end_date date := :P_OUTER_END_DATE;
BEGIN
FOR MY_ROW IN 00..99
LOOP
TABLE_NAME:= 'history'||TRIM(TO_CHAR(MY_ROW,'00'));
SQL_STATEMENT:=
'SELECT COUNT(DISTINCT USR_ID)
FROM ' || TABLE_NAME || '#hr
WHERE ATTRIBUTE_ID = 109
AND OLD_VALUE IS NULL
AND UPDATED_BY_SCREEN = ''CRM''
AND TRUNC(UPDATE_DATE) between :P_START and :P_END';
-- there are 2 parameters above - :P_START and :P_END
EXECUTE IMMEDIATE SQL_STATEMENT INTO LOOP_CASES
using start_date, end_date;
TOTAL_CASES := TOTAL_CASES + LOOP_CASES;
END LOOP;
DBMS_OUTPUT.PUT_LINE(TOTAL_CASES);
END;
There are two sets of parameters: the first pair is :P_OUTER_START_DATE and :P_OUTER_END_DATE, they are parameters for the whole block; the second pair is :P_START and :P_END, they are parameters for inner SQL query, which is executed inside the block. In any case, I would recommend to use parameters inside execute immediate too.
I got the solution by declaring the variables assigned to Bind Variables. The main problem was that it's the Date type variable so I need to give special attention to the inverted commas.
See The Solution Below:
set serveroutput on;
set echo on;
DECLARE
TABLE_NAME VARCHAR2(100);
SQL_STATEMENT VARCHAR2(2000);
TOTAL_CASES NUMBER(10) := 0;
LOOP_CASES NUMBER(10) := 0;
START_DATE DATE := to_date(:st_date,'DD-MM-YYYY');
END_DATE DATE:= to_date(:en_date,'DD-MM-YYYY');
BEGIN
FOR MY_ROW IN 00..99
LOOP
TABLE_NAME:= 'history'||TRIM(TO_CHAR(MY_ROW,'00'));
SQL_STATEMENT:='SELECT COUNT(DISTINCT USR_ID)
FROM ' || TABLE_NAME || '#hr WHERE ATTRIBUTE_ID = 109
AND OLD_VALUE IS NULL AND UPDATED_BY_SCREEN = ''CRM'' AND TRUNC(UPDATE_DATE)
between '''||START_DATE ||''' and '''||END_DATE||'''';
EXECUTE IMMEDIATE SQL_STATEMENT INTO LOOP_CASES;
TOTAL_CASES := TOTAL_CASES + LOOP_CASES;
END LOOP;
DBMS_OUTPUT.PUT_LINE(TOTAL_CASES);
END;
/
Pay the special attention to the ' in SQL_STATEMENT.
=====================================================================
Edit:
Using only Explicit conversion (Better Solution):
SET serveroutput ON;
SET echo ON;
DECLARE
TABLE_NAME VARCHAR2(100);
SQL_STATEMENT VARCHAR2(2000);
TOTAL_CASES NUMBER(10) := 0;
LOOP_CASES NUMBER(10) := 0;
START_DATE VARCHAR2(20) := TO_CHAR(:st_date);
END_DATE VARCHAR2(20) := TO_CHAR(:en_date);
BEGIN
FOR MY_ROW IN 00..99
LOOP
TABLE_NAME := 'history'||TRIM(TO_CHAR(MY_ROW,'00'));
SQL_STATEMENT:='SELECT COUNT(DISTINCT USR_ID)
FROM ' || TABLE_NAME || '#hr WHERE ATTRIBUTE_ID = 109
AND OLD_VALUE IS NULL AND UPDATED_BY_SCREEN = ''CRM'' AND TRUNC(UPDATE_DATE)
between to_date('''||START_DATE ||''',''dd-mm-yyyy'') and to_date('''||END_DATE||''',''dd-mm-yyyy'')';
EXECUTE IMMEDIATE SQL_STATEMENT INTO LOOP_CASES;
TOTAL_CASES := TOTAL_CASES + LOOP_CASES;
END LOOP;
DBMS_OUTPUT.PUT_LINE(TOTAL_CASES);
END;
/
You may try this:
DECLARE
TABLE_NAME varchar2(100);
SQL_STATEMENT varchar2(2000);
TOTAL_CASES number(10) := 0;
LOOP_CASES number(10) := 0;
START_DATE date := to_date(:start_dt,'dd-mm-yyyy');
END_DATE date := to_date(:end_dt,'dd-mm-yyyy');
v_ubs varchar2(50):='CRM'
v_ai pls_integer := 109;
BEGIN
FOR MY_ROW IN 00..99
LOOP
TABLE_NAME:= 'history'||TRIM(TO_CHAR(MY_ROW,'00'));
SQL_STATEMENT:='SELECT COUNT(DISTINCT USR_ID)
FROM '||TABLE_NAME||'#hr
WHERE ATTRIBUTE_ID = :ai
AND OLD_VALUE IS NULL
AND UPDATED_BY_SCREEN = :ubs
AND TRUNC(UPDATE_DATE) between :START_DATE and :END_DATE ';
EXECUTE IMMEDIATE SQL_STATEMENT using v_ai, v_ubs, START_DATE, END_DATE;
TOTAL_CASES := TOTAL_CASES + LOOP_CASES;
END LOOP;
DBMS_OUTPUT.PUT_LINE(TOTAL_CASES);
END;

Printing Non Repeating Characters first

I am new to PL/SQL and I am trying to write a procedure which would print the non repeating characters of an input string first and Repeating characters of the string in the last. For example if the input string is "Array" then the output should be "yArra".
I wrote a part of it for searching the no. of occurrences of a repeating character, but don't know how exactly should it be printed at the first place.
I wrote an algorithm on this on how can this be made to work but finding difficult to code
Thanks in advance for your help!
I am trying to write a procedure which would print the non repeating
characters of an input string first and Repeating characters of the
string in the last.
You can do this using a pure PLSQL code as below:
create or replace procedure prnt_letter(strng varchar2) as
var varchar2(1);
var1 varchar2(1000) := '';
var2 varchar2(1);
var3 varchar2(1000) := '';
strn_len number;
begin
dbms_output.put_line('Input String --> ' || strng);
strn_len := length(strng);
var := substr(strng, 1, 1);
for i in 1 .. strn_len loop
if (var = substr(strng, i, 1)) then
var2 := substr(strng, i, 1);
var3 := var3 || var2;
var := substr(strng, i, 1);
else
var1 := var1 || substr(strng, i, 1);
var := substr(strng, i, 1);
end if;
end loop;
dbms_output.put_line('Output String --> '||var1 || var3);
end;
EDIT:
Here is my revised solution both in PLSQL and SQL. This works for any string.
PLSQL:
create or replace procedure prnt_letter(strng varchar2) as
var1 varchar2(1000) := '';
strn_len number;
begin
dbms_output.put_line('Input String --> ' || strng);
strn_len := length(strng);
SELECT reverse (LISTAGG (vertical, '') WITHIN GROUP (ORDER BY 1 DESC))
into var1
FROM (
SELECT SUBSTR (strng, LEVEL, 1) Vertical
FROM DUAL
CONNECT BY LEVEL <= strn_len
) ;
dbms_output.put_line('Output String --> '||var1 );
end;
Output:
SQL> execute prnt_letter('rajjjjkkmmaaljjjl');
Input String --> rajjjjkkmmaaljjjl
Output String --> rmmllkkjjjjjjjaaa
PL/SQL procedure successfully completed.
SQL> execute prnt_letter('bubble');
Input String --> bubble
Output String --> ulebbb
PL/SQL procedure successfully completed.
SQL:
-- Logic used:
1) The input string is first arranged vertically in separate rows and
then ordered
2) Using LISTAGG, the result was assembled as a single ordered string
3) Using REVERSE the non-repeating string is brought to the starting
of the string.
SELECT reverse (LISTAGG (vertical, '') WITHIN GROUP (ORDER BY 1 DESC)) col1
FROM ( SELECT SUBSTR ('rajjjjkkmmaaljjjl', LEVEL, 1) Vertical
FROM DUAL
CONNECT BY LEVEL <= LENGTH ('rajjjjkkmmaaljjjl')
)
Try and use the REGEXP_COUNT function for the same. You can first provide a filter where this result >1 to find repeating characters and then concatenate them with the ones whose count = 1.
Check how to use regexp_count
I think the solution can be acheived by just using pure SQL rather than using PLSQL. Hope below snippet helps.
SELECT a.COL
||REPLACE('&Enter_text',a.col,'') output
FROM
(SELECT regexp_count('&Enter_text',SUBSTR('&Enter_text',level,1)) col1,
SUBSTR('&Enter_text',level,1) col
FROM DUAL
CONNECT BY level <=LENGTH('&Enter_text')
)a
WHERE a.col1 = 1;
That's fun, so I came out with something easily understandable using associative arrays as Hashmap; there's something subtle also with the non-case-sensitiveness:
CREATE OR REPLACE FUNCTION f(p_str in varchar2)
RETURN varchar2
AS
TYPE map_v IS TABLE OF integer INDEX BY varchar2(1);
l_dup map_v;
i PLS_INTEGER;
l_c varchar2(1);
l_tc varchar2(1);
l_nb_occurrences integer := NULL;
l_out_sngl varchar2(2000) := '';
l_out_dupl varchar2(2000) := '';
BEGIN
-- l_dup('a'):=0;
-- l_dup('b'):=0;
-- first loop to count occurrences
i:=1;
LOOP
l_c := lower(substr(p_str, i, 1));
begin
l_nb_occurrences := l_dup(l_c);
l_dup(l_c) := l_nb_occurrences + 1;
dbms_output.put_line(l_c||':incr:'||i);
exception
when no_data_found then
l_dup(l_c) := 1;
dbms_output.put_line(l_c||':pushed:'||i);
when others then
raise;
end;
i := i+1;
EXIT WHEN i > length(p_str);
END LOOP;
-- second loop for building output
i:=1;
LOOP
l_c := lower(substr(p_str, i, 1));
l_tc := substr(p_str, i, 1);
begin
l_nb_occurrences := l_dup(l_c);
dbms_output.put_line(l_c||':xx:'||i||'||'||l_nb_occurrences);
if l_nb_occurrences = 1 then
l_out_sngl := l_out_sngl || l_tc;
else
l_out_dupl := l_out_dupl || l_tc;
end if;
exception
when no_data_found then
dbms_output.put_line('why? there should be (see first loop).');
when others then
raise;
end;
i := i+1;
EXIT WHEN i > length(p_str);
END LOOP;
return l_out_sngl || l_out_dupl;
exception
when others then
dbms_output.put_line(sqlerrm);
END f;
/
Which gives results:
select f('Array') from dual;
-- yArra
select f('Bubbles') from dual;
-- ulesBbb
Try this function...
CREATE OR REPLACE FUNCTION non_repeating_char_first (v_input IN varchar2)
RETURN varchar2
IS
str1 varchar2 (100);
str2 varchar2 (100);
v_match number;
v_output varchar2 (100);
BEGIN
str1 := '';
str2 := '';
FOR i IN 1 .. LENGTH (v_input)
LOOP
SELECT REGEXP_COUNT (v_input,
SUBSTR (v_input, i, 1),
1,
'i')
INTO v_match
FROM DUAL;
IF v_match =1 THEN
str1 :=str1||SUBSTR (v_input, i, 1);
else
str2 :=str2||SUBSTR (v_input, i, 1);
END IF;
END LOOP;
v_output:=str1||str2;
RETURN v_output;
END;

split string into multiple rows with multiple columns

I have a string with user information. I have to write a function that takes string as input and inserts into some table.
Input string contains multiple rows with multiple columns like following:
inputString= "1,cassey,1222,12-12-12:2,timon,,02-02-12:3,john,3333,03-03-12"
what i want is to create insert from this...
How it can be achieved?
Solution in a single Query as following:
But I replaced the ',,' with 'NULL'
inputString= "1,cassey,1222,12-12-12:2,timon,NULL,02-02-12:3,john,3333,03-03-12"
SELECT REGEXP_SUBSTR (REGEXP_SUBSTR (inputString, '[^:]+', 1, LEVEL), '[^,]+', 1, 1) AS Col1,
REGEXP_SUBSTR (REGEXP_SUBSTR (inputString, '[^:]+', 1, LEVEL), '[^,]+', 1, 2) AS Col2,
REGEXP_SUBSTR (REGEXP_SUBSTR (inputString, '[^:]+', 1, LEVEL), '[^,]+', 1, 3) AS Col3,
REGEXP_SUBSTR (REGEXP_SUBSTR (inputString, '[^:]+', 1, LEVEL), '[^,]+', 1, 4) AS Col4
FROM DUAL
CONNECT BY LEVEL <= LENGTH (REGEXP_REPLACE (inputString, '[^:]+')) + 1;
Result:
Col1 Col2 Col3 Col4
------ ------ ------ ------
1 cassey 1222 12-12-12
2 timon NULL 02-02-12
3 john 3333 03-03-12
3 rows selected.
Package Spec:
CREATE OR REPLACE PACKAGE string_conversion AS
TYPE string_test_tab IS TABLE OF VARCHAR2(2000);
TAB string_test_tab;
PROCEDURE string_convert(v_input IN VARCHAR2, v_output OUT string_test_tab);
END string_conversion;
/
Package Body:
CREATE OR REPLACE PACKAGE BODY string_conversion AS
PROCEDURE string_convert(v_input IN VARCHAR2, v_output OUT string_test_tab) IS
v_index NUMBER := 1;
v_index_comma NUMBER := 1;
TAB2 string_test_tab;
v_input_str VARCHAR2(2000):= v_input||',';
BEGIN
v_output := string_test_tab();
LOOP
v_index_comma := INSTR (v_input_str, ',',v_index);
EXIT WHEN v_index_comma = 0;
v_output.extend();
v_output(v_output.count):= SUBSTR(v_input_str, v_index, v_index_comma - v_index);
dbms_output.put_line(v_output(v_output.count));
v_index := v_index_comma + 1;
END LOOP;
END string_convert;
END string_conversion;
/
Testing:
DECLARE
v_out1 string_conversion.string_test_tab;
BEGIN
string_conversion.string_convert('a,b,c,d,e',v_out1);
FOR j IN v_out1.FIRST .. v_out1.LAST
LOOP
dbms_output.put_line(v_out1(j));
END LOOP;
END;
/
OUTPUT:
a
b
c
d
e
DECLARE
v_string VARCHAR2(20) := 'a,b,c,d,e';
v_val VARCHAR2(2000);
BEGIN
dbms_output.put_line('v_string = '||v_string);
LOOP
v_val := SUBSTR(v_string,1, INSTR(v_string, ',', 1)-1);
IF INSTR(v_string, ',', 1) = 0 THEN
v_val := v_string;
END IF;
dbms_output.put_line(v_val);
EXIT WHEN INSTR(v_string, ',', 1) = 0;
v_string := SUBSTR(v_string,INSTR(v_string, ',', 1)+1);
END LOOP;
END;

Resources