Selecting variables without tables in stored procedure - sql

I will pass some input parameters to stored procedure and do some calculation on tem. Finally I need to retrieve a few variables. I will not use any tables in database.
How to return only variables using stored procedure without any tables in Oracle?
Here is my code.
PROCEDURE SP_C3_PSTK_TAKING(
SCUR IN OUT CUST_CUR,
V_OUTLET IN VARCHAR2,
V_DATEOF_STOCK_TAKING IN DATE,
V_PSTK_VALUE IN NUMBER,
V_PSTK_VALUE_TP_CARDS IN NUMBER,
V_ACTUAL_STOCK_SHRINKAGE IN NUMBER,
V_NESCAFE_SHRINKAGE IN NUMBER,
V_ICE_CUBE_SHRINKAGE IN NUMBER,
V_TURN_OVER_FOR_THE_PERIOD IN NUMBER,
V_TP_CARD_SALES_FOR_THE_PERIOD IN NUMBER,
V_STOCK_TAKE_RECEIVED_DATE IN DATE,
V_STOCK_TAKE_DONE_BY IN VARCHAR2 ,
)
IS
TMPSTRSQL CLOB;
TMPSTRSQL1 CLOB;
V_ACTUAL_STOCK_SHRINKAGE_PER NUMBER;
V_TOTAL_SHORTAGE NUMBER;
V_T_OVER_EXCLUDING_TEL_CARDS NUMBER;
V_SHORTAGEPER NUMBER;
V_ALLOWANCE NUMBER;
V_VARIATION NUMBER
OUTLET VARCHAR(100);
DATEOF_STOCK_TAKING DATE;
PSTK_VALUE NUMBER;
PSTK_VALUE_TP_CARDS NUMBER;
ACTUAL_STOCK_SHRINKAGE NUMBER;
NESCAFE_SHRINKAGE NUMBER;
ICE_CUBE_SHRINKAGE NUMBER;
TURN_OVER_FOR_THE_PERIOD NUMBER;
TP_CARD_SALES_FOR_THE_PERIOD NUMBER;
STOCK_TAKE_RECEIVED_DATE DATE;
STOCK_TAKE_DONE_BY VARCHAR(100);
BEGIN
OUTLET := V_OUTLET ;
DATEOF_STOCK_TAKING := V_DATEOF_STOCK_TAKING ;
PSTK_VALUE := V_PSTK_VALUE ;
PSTK_VALUE_TP_CARDS := V_PSTK_VALUE_TP_CARDS ;
ACTUAL_STOCK_SHRINKAGE := V_ACTUAL_STOCK_SHRINKAGE ;
NESCAFE_SHRINKAGE := V_NESCAFE_SHRINKAGE ;
ICE_CUBE_SHRINKAGE := V_ICE_CUBE_SHRINKAGE ;
TURN_OVER_FOR_THE_PERIOD := V_TURN_OVER_FOR_THE_PERIOD ;
TP_CARD_SALES_FOR_THE_PERIOD := V_TP_CARD_SALES_FOR_THE_PERIOD ;
STOCK_TAKE_RECEIVED_DATE := V_STOCK_TAKE_RECEIVED_DATE ;
STOCK_TAKE_DONE_BY := V_STOCK_TAKE_DONE_BY ;
V_ACTUAL_STOCK_SHRINKAGE_PER := V_ACTUAL_STOCK_SHRINKAGE + ((V_ACTUAL_STOCK_SHRINKAGE/ 100) * 29 ) ;
V_TOTAL_SHORTAGE := V_ACTUAL_STOCK_SHRINKAGE_PER + V_NESCAFE_SHRINKAGE + V_ICE_CUBE_SHRINKAGE ;
V_T_OVER_EXCLUDING_TEL_CARDS := V_TURN_OVER_FOR_THE_PERIOD + V_TP_CARD_SALES_FOR_THE_PERIOD ;
V_SHORTAGEPER := V_TOTAL_SHORTAGE * 100 / V_T_OVER_EXCLUDING_TEL_CARDS ;
V_ALLOWANCE := (V_PSTK_VALUE /100 ) * 1 ;
TMPSTRSQL := ' ';
TMPSTRSQL1 := 'select V_ACTUAL_STOCK_SHRINKAGE_PER,V_TOTAL_SHORTAGE,V_T_OVER_EXCLUDING_TEL_CARDS,V_SHORTAGEPER,V_ALLOWANCE from dual';
OPEN SCUR FOR TMPSTRSQL1 || TMPSTRSQL;
END SP_C3_PSTK_TAKING;

Your problem lies in scope. In dynamic SQL we pass a string to a ref cursor the string must be runnable in the context of the database engine. In other words, could we run this query in SQL*Plus?
select V_ACTUAL_STOCK_SHRINKAGE_PER,
V_TOTAL_SHORTAGE,
V_T_OVER_EXCLUDING_TEL_CARDS,
V_SHORTAGEPER,
V_ALLOWANCE
from dual
Clearly not. DUAL has only one column, DUMMY. However we can select any number of literals from DUAL.
So what you need to do is turn that string into a projection of literals, like this:
TMPSTRSQL1 := 'select '
|| to_char(v_ACTUAL_STOCK_SHRINKAGE_PER) ||','
|| to_char(v_TOTAL_SHORTAGE) ||','
|| to_char(v_T_OVER_EXCLUDING_TEL_CARDS) ||','
|| to_char(v_SHORTAGEPER) ||','
|| to_char(v_ALLOWANCE) ||
' from dual';
OPEN SCUR FOR TMPSTRSQL1 || TMPSTRSQL;
END SP_C3_PSTK_TAKING;

I will pass some input parameters to stored procedure and I will do some calculation on it. Finally I need to retrieve few variables. Here I will not use any tables in database.

Related

How to use "Where" clause in Oracle database (stored procedures)

I am using the following stored procedures in an Oracle database to export the output of the query to a CSV file.
CREATE OR REPLACE PROCEDURE run_query(p_tbl_name IN VARCHAR2
) IS
Select_Stmt VARCHAR2(100) := 'select * from '||p_tbl_name;
p_dir VARCHAR2 (100) := 'DATA_PUMP_DIR';
v_finaltxt VARCHAR2(4000);
v_v_val VARCHAR2(4000);
v_n_val NUMBER;
v_d_val DATE;
v_ret NUMBER;
c NUMBER;
d NUMBER;
col_cnt INTEGER;
f BOOLEAN;
rec_tab DBMS_SQL.DESC_TAB;
col_num NUMBER;
v_fh UTL_FILE.FILE_TYPE;
BEGIN
c := DBMS_SQL.OPEN_CURSOR;
DBMS_SQL.PARSE(c, Select_Stmt, DBMS_SQL.NATIVE);
d := DBMS_SQL.EXECUTE(c);
DBMS_SQL.DESCRIBE_COLUMNS(c, col_cnt, rec_tab);
FOR j in 1..col_cnt
LOOP
CASE rec_tab(j).col_type
WHEN 1 THEN DBMS_SQL.DEFINE_COLUMN(c,j,v_v_val,2000);
WHEN 2 THEN DBMS_SQL.DEFINE_COLUMN(c,j,v_n_val);
WHEN 3 THEN DBMS_SQL.DEFINE_COLUMN(c,j,v_d_val);
ELSE
DBMS_SQL.DEFINE_COLUMN(c,j,v_v_val,2000);
END CASE;
END LOOP;
-- This part outputs the HEADER
v_fh := UTL_FILE.FOPEN(upper(p_dir),p_tbl_name||'.csv','w',32767);
FOR j in 1..col_cnt
LOOP
v_finaltxt := ltrim(v_finaltxt||','||lower(rec_tab(j).col_name),',');
END LOOP;
UTL_FILE.PUT_LINE(v_fh, v_finaltxt);
-- This part outputs the DATA
LOOP
v_ret := DBMS_SQL.FETCH_ROWS(c);
EXIT WHEN v_ret = 0;
v_finaltxt := NULL;
FOR j in 1..col_cnt
LOOP
CASE rec_tab(j).col_type
WHEN 1 THEN DBMS_SQL.COLUMN_VALUE(c,j,v_v_val);
v_finaltxt := ltrim(v_finaltxt||',"'||v_v_val||'"',',');
WHEN 2 THEN DBMS_SQL.COLUMN_VALUE(c,j,v_n_val);
v_finaltxt := ltrim(v_finaltxt||','||v_n_val,',');
WHEN 3 THEN DBMS_SQL.COLUMN_VALUE(c,j,v_d_val);
v_finaltxt := ltrim(v_finaltxt||','||to_char(v_d_val,'DD/MM/YYYY HH24:MI:SS'),',');
ELSE
DBMS_SQL.COLUMN_VALUE(c,j,v_v_val);
v_finaltxt := ltrim(v_finaltxt||',"'||v_v_val||'"',',');
END CASE;
END LOOP;
-- DBMS_OUTPUT.PUT_LINE(v_finaltxt);
UTL_FILE.PUT_LINE(v_fh, v_finaltxt);
END LOOP;
UTL_FILE.FCLOSE(v_fh);
DBMS_SQL.CLOSE_CURSOR(c);
END;
/
The above stored procedure is working perfectly and I got the output by using the following script to run this stored procedure
exec run_query person_list;
Here stored_procedure_name is run_query and the table_name is person_list.
Now my question is that how can I use the WHERE clause after the select statement on "line 3" -
Select_Stmt VARCHAR2(100) := 'select * from '||p_tbl_name;
or are there any other ways I can use the where clause.
Thanks in advance.
Select_Stmt VARCHAR2(100) := 'select * from '||p_tbl_name||' where name=''david''';
You have to have the character delimiters; and since you have it inside quotes you have to duplicate them.

DBMS_LOB.APPEND File is getting cut because of concatenation

I have the below block. I want to replace that concatenation and also have a records from the cursor in new line.
For small data set it works,but when the number of rows returned by the cursor is 600+ it cuts the file. I think its because of conversion of CLOB to Varchar2 when || is used.
How do I resolve it?
DECLARE
CURSOR cur IS
SELECT ID, CA_ID, STATUS,S_DATE, E_DATE FROM TEST; -- Here I have a big query
v_MESG CLOB := EMPTY_CLOB();
v_Interim CLOB := EMPTY_CLOB();
v_col VARCHAR2(500);
CRLF VARCHAR2(2) := CHR(13)||CHR(10);
v_fileHandler UTL_FILE.FILE_TYPE;
BEGIN
v_col:= '"ID","CA_ID","Status","S_Date","E_Date"'||CRLF;
dbms_lob.createtemporary(v_MESG, TRUE);
FOR v_cur IN cur LOOP
BEGIN
v_Interim := NULL;
v_Interim:= v_col||v_cur.ID||',"'||
v_cur.CA_ID||'","'||
v_cur.Status||'","'||
v_cur.S_Date||'","'||
v_cur.E_DATE||'"'||CRLF
;
DBMS_LOB.APPEND(v_mesg, v_Interim);
v_col := NULL;
END;
END LOOP;
v_fileHandler := UTL_FILE.FOPEN('XML_PROCESS_TEST',v_file_name , 'w');
UTL_FILE.PUT(v_fileHandler, v_MESG);
UTL_FILE.FCLOSE(v_fileHandler);
END;

Creating a table from a Comma Separated List in Oracle (> 11g) - Input string limit 4000 chars

I have an Oracle (11g) SP which takes a comma separated string of inputs (e.g. 'Cats, Dogs, Monkeys'). The crux of the code is taken from Tony Andrews' blog and works as expected but it will not accept input strings greater than 4000 characters:
PROCEDURE delimstring_to_table ( p_delimstring IN VARCHAR2
, p_table OUT VARCHAR2_TABLE
, p_nfields OUT INTEGER
, p_delim IN VARCHAR2 DEFAULT ','
)
IS
v_string VARCHAR2(32767) := p_delimstring;
v_nfields PLS_INTEGER := 1;
v_table VARCHAR2_TABLE;
v_delimpos PLS_INTEGER := INSTR(p_delimstring, p_delim);
v_delimlen PLS_INTEGER := LENGTH(p_delim);
BEGIN
WHILE v_delimpos > 0
LOOP
v_table(v_nfields) := SUBSTR(v_string,1,v_delimpos-1);
v_string := SUBSTR(v_string,v_delimpos+v_delimlen);
v_nfields := v_nfields+1;
v_delimpos := INSTR(v_string, p_delim);
END LOOP;
v_table(v_nfields) := v_string;
p_table := v_table;
p_nfields := v_nfields;
END delimstring_to_table;
Reference - Tony Andrews Blog
The functions above are then used inside any relevant SPs within the package:
PROCEDURE Get_Animals(
p_Animals IN VARCHAR2 := NULL
, resultset_out OUT resultset_typ
, error_out OUT VARCHAR2
)
IS
BEGIN
IF p_Animals IS NULL THEN
OPEN resultset_out FOR
SELECT /*+ ALL_ROWS */ * FROM ANIMALS ORDER BY NAME;
ELSE
OPEN resultset_out FOR
SELECT * FROM ANIMALS where NAME in (SELECT * FROM TABLE (Comma_To_Table(p_RICs)));
END IF;
error_out := NULL;
EXCEPTION
WHEN OTHERS THEN
error_out := 'Get_Animals() -> ' || SUBSTR(SQLERRM,1,200);
END Get_Animals;
If the input string is > 4000 characters, I get the following error from the SP:
ORA-00604: error occurred at recursive SQL level 1
ORA-01003: no statement parsed - GET_ANIMALS
I have two questions:
Can I make the function work with input strings greater than 4000 characters?
Is there a more effective way of achieving the same result?
Any help or suggestions would be much appreciated.
Can I make the function work with input strings greater than 4000 characters?
Yes, you can use for example CLOB
Is there a more effective way of achieving the same result?
I saw in the comments of the blog a good answer, which is about a recursive solution.
just make some datatype changes for making it to work e.g.:
change the varchar2_table type to CLOB
TYPE varchar2_table IS TABLE OF CLOB INDEX BY BINARY_INTEGER;
change the VARCHAR2 datatype to CLOB in all p_delimstring occurences
change original SUBSTR functions to DBMS_LOB.SUBSTR
(if you need more info about that: http://docs.oracle.com/cd/A91202_01/901_doc/appdev.901/a89852/dbms_23b.htm)
CREATE OR REPLACE PACKAGE parse AS
/*
|| Package of utility procedures for parsing delimited or fixed position strings into tables
|| of individual values, and vice versa.
*/
TYPE varchar2_table IS TABLE OF CLOB INDEX BY BINARY_INTEGER;
PROCEDURE delimstring_to_table
( p_delimstring IN CLOB
, p_table OUT varchar2_table
, p_nfields OUT INTEGER
, p_delim IN VARCHAR2 DEFAULT ','
);
PROCEDURE table_to_delimstring
( p_table IN varchar2_table
, p_delimstring OUT CLOB
, p_delim IN VARCHAR2 DEFAULT ','
);
END parse;
/
CREATE OR REPLACE PACKAGE BODY parse AS
PROCEDURE delimstring_to_table
( p_delimstring IN CLOB
, p_table OUT varchar2_table
, p_nfields OUT INTEGER
, p_delim IN VARCHAR2 DEFAULT ','
)
IS
v_string CLOB := p_delimstring;
v_nfields PLS_INTEGER := 1;
v_table varchar2_table;
v_delimpos PLS_INTEGER := INSTR(p_delimstring, p_delim);
v_delimlen PLS_INTEGER := LENGTH(p_delim);
BEGIN
WHILE v_delimpos > 0
LOOP
v_table(v_nfields) := DBMS_LOB.SUBSTR(v_string,1,v_delimpos-1);
v_string := DBMS_LOB.SUBSTR(v_string,v_delimpos+v_delimlen);
v_nfields := v_nfields+1;
v_delimpos := INSTR(v_string, p_delim);
END LOOP;
v_table(v_nfields) := v_string;
p_table := v_table;
p_nfields := v_nfields;
END delimstring_to_table;
PROCEDURE table_to_delimstring
( p_table IN varchar2_table
, p_delimstring OUT CLOB
, p_delim IN VARCHAR2 DEFAULT ','
)
IS
v_nfields PLS_INTEGER := p_table.COUNT;
v_string CLOB;
BEGIN
FOR i IN 1..v_nfields
LOOP
v_string := v_string || p_table(i);
IF i != v_nfields THEN
v_string := v_string || p_delim;
END IF;
END LOOP;
p_delimstring := v_string;
END table_to_delimstring;
END parse;
/

PLSQL getting value of property in record dynamically?

First of all I am really rusty on Oracle PLSQL, and I have seen several folks say this cannot be done and others that say it can, and I am just not able to make it happen. Any help would be greatly appreciated.
I am trying to read the value of a column in a record type dynamically.
I have a message with tokens and I need to replace the tokens with value from the record set.
So the message looks like: [status] by [agent_name]
I have another place where I am parsing out the tokens.
In java script I know this can be accomplished with: (Will run in Console)
var record = {
status : "Open",
agent_name : "John"
};
var record2 = {
status : "Close",
agent_name : "Joe"
};
var records = [record, record2];
var token1 = "status";
var token2 = "agent_name";
for( var i=0; i<records.length; i++){
console.log(records[i][token1] + " by " + records[i][token2]);
}
Results : Open by John
Close by Joe
I want to do the same thing in PLSQL
Here is my test PLSQL:
SET SERVEROUTPUT ON;
declare
TYPE my_record is RECORD
(
status VARCHAR2(30),
agent_name varchar2(30)
);
TYPE my_record_array IS VARRAY(6) OF my_record;
v_records my_record_array := my_record_array();
v_current_rec my_record;
v_current_rec2 my_record;
v_token varchar2(50):= 'agent_name';
v_token2 varchar2(50):= 'status';
begin
v_current_rec.status := 'Open';
v_current_rec.agent_name := 'John';
v_records.extend;
v_records(1) := v_current_rec;
v_current_rec2.status := 'Close';
v_current_rec2.agent_name := 'Ron';
v_records.extend;
v_records(2) := v_current_rec2;
FOR i IN 1..v_records.COUNT LOOP
--Hard coded
DBMS_OUTPUT.PUT_LINE(v_records(i).status || ' by ' || v_records(i).agent_name);
--Substitution vars entering v_records(i).status and v_records(i).agent_name for the prompts.
--How to do this without user interaction.
DBMS_OUTPUT.PUT_LINE(&status || ' by ' || &agent_name);
--Dynamically that doesn't work. How would this be accomplished
DBMS_OUTPUT.PUT_LINE(v_records(i).v_token || ' by ' || v_records(i).v_token2);
END LOOP;
END;
I tried using substitution variables, and that will work if I use:
DBMS_OUTPUT.PUT_LINE(&agent_name) and entering v_records(i).agent_name when prompted. How do I accomplish this on the fly?
ANSWER:
set serveroutput on;
DECLARE
type sr_record_map
IS
TABLE OF VARCHAR2(30) INDEX BY VARCHAR2(30);
type record_set
is
TABLE of sr_record_map index by BINARY_INTEGER;
v_current_rec sr_record_map;
v_record_set record_set;
v_token varchar2(30) := 'status';
v_token2 varchar2(30) := 'agent_name';
v_index number :=1;
begin
v_current_rec('status') := 'Open';
v_current_rec('agent_name') := 'John';
v_record_set(1) := v_current_rec;
v_current_rec('status') := 'Close';
v_current_rec('agent_name') := 'Joe';
v_record_set(2) := v_current_rec;
FOR i in 1..v_record_set.COUNT LOOP
v_current_rec := v_record_set(i);
DBMS_OUTPUT.PUT_LINE(v_current_rec(v_token) || ' by ' || v_current_rec(v_token2));
END LOOP;
end;
Using an ASSOCIATIVE ARRAY just like Maps in Java
DECLARE
type my_record_map
IS
TABLE OF VARCHAR2(30) INDEX BY VARCHAR2(30);
type my_record
IS
record
(
my_members my_record_map );
type final_map
IS
TABLE OF my_record INDEX BY VARCHAR2(20);
v_final_map final_map;
v_my_record_map my_record_map;
v_my_record my_record;
index_name VARCHAR2(100);
index_name_record VARCHAR2(100);
BEGIN
/* Individual Records as key value pairs with their Corresponding Columns */
/* You can put any member name inside */
v_my_record_map('status') := 'Open';
v_my_record_map('agent_name') := 'John';
v_my_record_map('added_by') := 'Maheshwaran';
/* Put it as a record */
v_my_record.my_members := v_my_record_map;
/* Put the record inside Another Map with any Key */
v_final_map('Record1') := v_my_record;
v_my_record_map('status') := 'Close';
v_my_record_map('agent_name') := 'Joe';
v_my_record_map('added_by') := 'Ravisankar';
v_my_record.my_members := v_my_record_map;
v_final_map('Record2') := v_my_record;
/* Take the First Key in the Outer most Map */
index_name := v_final_map.FIRST;
LOOP
/* status Here can be dynamic */
DBMS_OUTPUT.PUT_LINE(CHR(10)||'######'||v_final_map(index_name).my_members('status') ||' by '||v_final_map(index_name).my_members('agent_name')||'######'||CHR(10));
index_name_record := v_final_map(index_name).my_members.FIRST;
DBMS_OUTPUT.PUT_LINE('$ Ávailable Other Members + Values.. $'||CHR(10));
LOOP
DBMS_OUTPUT.PUT_LINE(' '||index_name_record ||'='||v_final_map(index_name).my_members(index_name_record));
index_name_record := v_final_map(index_name).my_members.NEXT(index_name_record);
EXIT WHEN index_name_record IS NULL;
END LOOP;
/* Next gives you the next key */
index_name := v_final_map.NEXT(index_name);
EXIT WHEN index_name IS NULL;
END LOOP;
END;
/
OUTPUT:
######Open by John######
$ Ávailable Other Members + Values.. $
added_by=Maheshwaran
agent_name=John
status=Open
######Close by Joe######
$ Ávailable Other Members + Values.. $
added_by=Ravisankar
agent_name=Joe
status=Close
I don't think it can be done with a record type. It may be possible with an object type since you could query the fields from the data dictionary, but there isn't normally anywhere those are available (though there's an 11g facility called PL/Scope that might allow it if it's enabled).
Since you're defining the record type in the same place you're using it, and if you have a manageable number of fields, it might be simpler to just try to replace each token, which just (!) wastes a bit of CPU if one doesn't exist in the message:
declare
TYPE my_record is RECORD
(
status VARCHAR2(30),
agent_name varchar2(30)
);
TYPE my_record_array IS VARRAY(6) OF my_record;
v_records my_record_array := my_record_array();
v_current_rec my_record;
v_current_rec2 my_record;
v_message varchar2(50):= '[status] by [agent_name]';
v_result varchar2(50);
begin
v_current_rec.status := 'Open';
v_current_rec.agent_name := 'John';
v_records.extend;
v_records(1) := v_current_rec;
v_current_rec2.status := 'Close';
v_current_rec2.agent_name := 'Ron';
v_records.extend;
v_records(2) := v_current_rec2;
FOR i IN 1..v_records.COUNT LOOP
v_result := v_message;
v_result := replace(v_result, '[agent_name]', v_records(i).agent_name);
v_result := replace(v_result, '[status]', v_records(i).status);
DBMS_OUTPUT.PUT_LINE(v_result);
END LOOP;
END;
/
anonymous block completed
Open by John
Close by Ron
It needs to be maintained of course; if a field is added to the record type than a matching replace will need to be added to the body.
I assume in the real world the message text, complete with tokens, will be passed to a procedure. I'm not sure it's worth parsing out the tokens though, unless you need them for something else.
As an example, I'm attaching simple code by which you can dynamically compare any two records of any table by name of table and id values, to get differences in data..
DECLARE
p_id_1 NUMBER DEFAULT 697403;
p_id_2 NUMBER DEFAULT 697402;
p_table_name VARCHAR2(200) DEFAULT 'Name of the table';
V_result_1 VARCHAR2(2000);
V_result_2 VARCHAR2(2000);
CURSOR cur IS
SELECT * FROM ALL_TAB_COLUMNS WHERE table_name = p_table_name ;
BEGIN
FOR rec IN cur LOOP
EXECUTE IMMEDIATE
'SELECT ' || rec.COLUMN_NAME || ' FROM ' || P_TABLE_NAME ||
' WHERE ID = :1 '
INTO V_result_1
USING P_ID_1
;
EXECUTE IMMEDIATE
'SELECT ' || rec.COLUMN_NAME || ' FROM ' || P_TABLE_NAME ||
' WHERE ID = :1 '
INTO V_result_2
USING P_ID_2
;
IF NVL(v_result_1, 0) <> NVL(v_result_2,0) THEN
DBMS_OUTPUT.PUT_LINE('Column_name ' || rec.column_name || ' ' || v_result_1 || '\' || v_result_2);
END IF;
END LOOP;
END;

A procedure to Reverse a String in PL/SQL

I just started learning PL/SQL and I'm not sure how to create a procedure. The logic seems about right but I think there's some syntactical mistake in the first line. Here's my code:-
CREATE OR REPLACE PROCEDURE ReverseOf(input IN varchar2(50)) IS
DECLARE
reverse varchar2(50);
BEGIN
FOR i in reverse 1..length(input) LOOP
reverse := reverse||''||substr(input, i, 1);
END LOOP;
dbms_output.put_line(reverse);
END;
/
Two things - you shouldn't specify the datatype size in procedure's/function's parameter list and you do not need the DECLARE keyword. Try this:
CREATE OR REPLACE PROCEDURE ReverseOf(input IN varchar2) IS
rev varchar2(50):='';
BEGIN
FOR i in reverse 1..length(input) LOOP
rev := rev||substr(input, i, 1);
END LOOP;
dbms_output.put_line(rev);
END;
Try it without PL/SQL!
WITH
params AS
(SELECT 'supercalifragilisticexpialidocious' phrase FROM dual),
WordReverse (inpt, outpt) AS
(SELECT phrase inpt, CAST(NULL AS varchar2(4000)) outpt FROM params
UNION ALL
SELECT substr(inpt,2,LENGTH(inpt)-1), substr(inpt,1,1) || outpt
FROM wordReverse
WHERE LENGTH(inpt) > 0
)
SELECT phrase,outpt AS reversed FROM wordReverse, params
WHERE LENGTH(outpt) = LENGTH(phrase) ;
PHRASE REVERSED
---------------------------------- -----------------------------------
supercalifragilisticexpialidocious suoicodilaipxecitsiligarfilacrepus
Citation: http://rdbms-insight.com/wp/?p=94
Another solution reverse string minimizing loop count
DECLARE
v_str VARCHAR2(100) DEFAULT 'MYSTRING';
v_len NUMBER;
v_left VARCHAR2(100);
v_right VARCHAR2(100);
BEGIN
v_len := LENGTH(v_str)/2;
FOR rec IN 1..v_len
LOOP
v_left := substr(v_str,rec,1) || v_left;
IF rec * 2 <= LENGTH(v_str) THEN
v_right := v_right || substr(v_str,-rec,1);
END IF;
END LOOP;
v_str := v_right || v_left;
dbms_output.put_line(v_str);
END;
set serveroutput on
declare
str1 varchar2(30);
len number(3);
str2 varchar2(30);
i number(3);
begin
str1:='&str1';
len:=length(str1);
for i in reverse 1..len
loop
str2:=str2 || substr(str1,i,1);
end loop;
dbms_output.put_line('Reverse string is: '||str2);
end;
/
create or replace procedure ap_reverse_number(input_no VARCHAR2) as
output_no VARCHAR2(100);
begin
for i in 1 .. length(input_no) loop
output_no := output_no || '' ||
substr(input_no, (length(input_no) - i + 1), 1);
end loop;
dbms_output.put_line('Input no. is :' || input_no);
dbms_output.put_line('Output no. is:' || output_no);
end;
This should do the job correctly.
Try this one line statement to reverse the string in sql
with a as (select 'brahma' k from dual)
select listagg(substr(k,length(k)-level+1,1),'') within group (order by 1) b from a connect by level<length(k)+1
Try this one line query to reverse a string or a number.
select reverse('HelloWorld') from dual;
declare
name varchar2(20):='&name';
rname varchar2(20);
begin
for i in reverse 1..length(name)
loop
rname:=rname||substr(name,i,1);
end loop;
display(rname);
end;