getting Warning: Function created with compilation errors - plsql

I am getting error while executing the following function.I have been banging my head for quite sometime now.I am new to oracle so I am not able to correct it.Can someone please help?
create or replace function rever(x int)
return number
is
y varchar2(30);
c varchar2(30);
v int;
begin
y:=to_char(x);
c:=reverse(y);
v:=to_number(c);
return v;
end rever;
/
the following is showing as error message
LINE/COL ERROR
-------- -----------------------------------------------------------------
9/1 PL/SQL: Statement ignored
9/4 PLS-00201: identifier 'REVERSE' must be declared

Oracle does not provide a function in plsql to do a string reverse.
create or replace function rever(x int)
return number
is
y varchar2(30);
c varchar2(30);
v int;
begin
y:=to_char(x);
-- Loop other each char in a string from the last to the first element
for i in reverse 1.. length(y)
loop
c:= c|| substr(y,i,1);
end loop;
v:=to_number(c);
return v;
end rever;
/
Test:
begin
dbms_output.put_line(rever(1));
dbms_output.put_line(rever(12));
dbms_output.put_line(rever(21));
dbms_output.put_line(rever(123));
end;
/
Result:
dbms_output
1
21
12
321
db<>fiddle here

There is no PL/SQL reverse() function in Oracle. So this does not work:
declare
v varchar2(20);
begin
v:= reverse('krow not does ti');
dbms_output.put_line(v);
end;
/
It throws the PLS-00201 error you got.
However, there is an undocumented SQL function which we can use in PL/SQL, but only by invoking the SQL engine:
declare
v varchar2(20);
begin
select reverse('skrow ti') into v from dual;
dbms_output.put_line(v);
end;
/
Of course, because reverse() is undocumented we're not supposed to use it, at least in production code. Not sure why it's undocumented. I think Oracle uses it for reverse indexes, so maybe there's some limit on reversible string size.
Here is a db<>fiddle demo.
The performace is a bit worse
I think that is the cost of moving from the PL/SQL engine to the SQL engine and back again. So it comes down to use case. If we're writing a function which will only be used in pure PL/SQL then I think your approach is the better one. But if we're writing a function to be used in SQL then I would consider using the Oracle built-in instead, even though it's not supported.
Although to be honest I can't remember the last time I used a reverse() function - Oracle's or hand-rolled - in real life (as opposed to answering questions in forums or similar Code Golf questions :) ).

Just for amusement, without PL/SQL, using a little bit of regular expressions with hierarchical query:
SQL> with test (col) as
2 (select 'Littlefoot' from dual)
3 select listagg(one, '') within group (order by lvl desc) reversed
4 from (select level lvl, regexp_substr(col, '.', 1, level) one
5 from test
6 connect by level <= length(col)
7 );
REVERSED
--------------------------------------------------------------------------
toofelttiL
SQL>
Or, rewritten as a function:
SQL> create or replace function f_reverse (par_col in varchar2)
2 return varchar2
3 is
4 retval varchar2(1000);
5 begin
6 select listagg(one, '') within group (order by lvl desc)
7 into retval
8 from (select level lvl, regexp_substr(par_col, '.', 1, level) one
9 from dual
10 connect by level <= length(par_col)
11 );
12 return retval;
13 end;
14 /
Function created.
SQL> select empno, f_reverse(empno) rev_empno,
2 ename, f_reverse(ename) rev_ename
3 from emp
4 where rownum <= 3;
EMPNO REV_EMPNO ENAME REV_ENAME
---------- ---------- ---------- ----------
7369 9637 SMITH HTIMS
7499 9947 ALLEN NELLA
7521 1257 WARD DRAW
SQL>

Related

PLSQL Return Sequence.currval When Sequence Name Is Built Using Substitution Strings

I would like to use the same application in different instances so I need to specify the workspace and sequence ID.
Example query
BEGIN
INSERT INTO STEP (STEP_CHART_TITLE)
VALUES ('Action', 'Action');
RETURN '"'||:v_workspace||'"."'||:v_seqid||'".currval';
END;
If I use:
"FREEADMIN"."ISEQ$$_111997".currval;
in the return statement it works fine.
If I use the substitution strings, it will build the string correctly, but won't return the sequence number.
Is there a way to get the sequence number?
Thanks
"PL/SQL" as Oracle's procedural extension to SQL? Asking because I don't quite understand what "workspace" represents (we call it a "user" or a "schema" or even "owner" in Oracle).
If so, then you can't fetch current value unless you first fetch next sequence value (you didn't post whole code you use so I'm not sure whether you did that or not; also, insert statement is wrong - you're inserting 2 values into a single column).
SQL> select seq.currval from dual;
select seq.currval from dual
*
ERROR at line 1:
ORA-08002: sequence SEQ.CURRVAL is not yet defined in this session
SQL> select seq.nextval from dual;
NEXTVAL
----------
6
SQL> select seq.currval from dual;
CURRVAL
----------
6
SQL>
Therefore, the whole code might look like this - you'd use dynamic SQL. As this is SQL*Plus, I'm using substitution variables; you'd use bind variables (i.e. :v_workspace instead of '&v_workspace') (if that's what you're doing). This code just displays the value - you'd return it.
SQL> create table step (id number, step_chart_title varchar2(10));
Table created.
SQL> declare
2 l_str varchar2(200);
3 l_id number;
4 begin
5 l_str := 'select ' || '&v_workspace' ||'.'|| '&v_seqid' ||'.nextval from dual';
6 execute immediate l_str into l_id;
7
8 insert into step (id, step_chart_title) values (l_id, 'Action');
9
10 dbms_output.put_line(l_id); --> L_ID now contains CURRVAL
11 end;
12 /
Enter value for v_workspace: scott
Enter value for v_seqid: seq
8 --> here it is
PL/SQL procedure successfully completed.
SQL>

Passing parameter to PL/SQL procedure using select from database

Is it possible to define a variable as current_scn:= 'select current_scn from V$database' in the declaration section of a procedure. Using the one below gives me the output. But I would like to define it in the declare section, so each time it gets executed it will get the current scn. Need to execute the procedure based on the input. Is it possible do so?
DECLARE
currnet_scn VARCHAR2(500);
BEGIN
EXECUTE IMMEDIATE 'select current_scn from V$database' INTO currnet_scn;
DBMS_OUTPUT.PUT_LINE(currnet_scn);
END;
No, you can't do it exactly as you wanted (if you could, you'd already do it, I presume).
But, you can create a function which can then be used in declare section. See how:
SQL> create or replace function f_scn
2 return number
3 is
4 retval number;
5 begin
6 select current_scn into retval from V$database;
7 return retval;
8 end;
9 /
Function created.
SQL> set serveroutput on
SQL> declare
2 current_scn number := f_scn; --> function is used here
3 begin
4 dbms_output.put_line('SCN = ' || current_scn);
5 end;
6 /
SCN = 16535194799153
PL/SQL procedure successfully completed.
SQL>
Though, now you don't need to declare anything at all:
SQL> begin
2 dbms_output.put_line('SCN = ' || f_scn);
3 end;
4 /
SCN = 16535194799171
PL/SQL procedure successfully completed.
SQL>
Your example uses dynamic SQL. Why? There's nothing dynamic in it, which means that
select current_Scn into current_scn from v$database;
works just fine.
Finally: don't name variables as column names; it causes confusion. I'd suggest to make it e.g. declare v_current_scn number;

PL/SQL procedure successfully completed, but when I want to display Max(DEPARTMENT_ID), can anyone pls suggest?

Max(DEPARTMENT_ID), can anyone Please suggest
<set serveroutput on;
DECLARE
v_max_deptno DEPARTMENTS.DEPARTMENT_ID%TYPE;
BEGIN
SELECT Max(department_id)
INTO v_max_deptno
FROM departments
Where department_id = v_max_deptno;
DBMS_OUTPUT.PUT_LINE('The maximum department_id is: '|| v_max_deptno);
END;/>
This declaration:
v_max_deptno DEPARTMENTS.DEPARTMENT_ID%TYPE;
is for a local variable. Since you provide no default value and it is never assigned any value, it defaults to NULL.
This predicate in the SQL query:
Where department_id = v_max_deptno
means that the query should only return rows where the department_id is equal to the value in that variable. Since the variable is NULL, and since no value can equal NULL, the query returns no rows.
Since your query is based on a simple aggregate (MAX), the query returns without error. Since there were no rows, the MAX function returns NULL back into the same variable again.
For further assistance, you need to explain what the purpose of your code is and what value you expect to get. If you need the maximum department_id currently in the table across all rows, you would simply remove the where clause.
Note: if your purpose in finding the current maximum ID is to set the department_id for a new record, there are some other issues with this approach (to do with concurrent sessions) that you need to consider.
Your query doesn't make much sense, as it is trying to find the MAX(department_id) (and put its value into the V_MAX_DEPTNO variable), while the same variable is used in the WHERE clause.
At the time of declaration, V_MAX_DEPTNO is NULL, it is NULL in the WHERE clause as well, and no department's ID is "equal" to NULL, so that query returns nothing.
If you initialized the V_MAX_DEPTNO (which is, of course, stupid), you'd get something:
SQL> DECLARE
2 v_max_deptno departments.department_id%TYPE := 20;
3 BEGIN
4 SELECT MAX (department_id)
5 INTO v_max_deptno
6 FROM departments
7 WHERE department_id = v_max_deptno;
8
9 DBMS_OUTPUT.put_line ('The maximum department_id is: ' || v_max_deptno);
10 END;
11 /
The maximum department_id is: 20
PL/SQL procedure successfully completed.
SQL>
So the question is: what are you trying to do? In my opinion, the right way to do it is to remove this WHERE clause, i.e.
SQL> DECLARE
2 v_max_deptno departments.department_id%TYPE := 20;
3 BEGIN
4 SELECT MAX (department_id)
5 INTO v_max_deptno
6 FROM departments;
7
8 DBMS_OUTPUT.put_line ('The maximum department_id is: ' || v_max_deptno);
9 END;
10 /
The maximum department_id is: 40
PL/SQL procedure successfully completed.
SQL>

cursor value in other select query

create or replace PROCEDURE "RESULT" (res1 OUT SYS_REFCURSOR )
IS
Cursor c is Select distinct id,fname,lname,dob,gender,address1 from emp where name like '%B%'
d c%rowtype;
BEGIN
OPEN c;
LOOP
fetch c into d;
exit when c%notfound;
OPEN res1 FOR select e.id from emp e where e.poi_user_id IN (d.id);
End Loop;
END;
Procedure RESULT compiled.
if i run query without procedure i get 5 results but when i am using the above code, it only returns the last result.
SET SERVEROUTPUT ON;
Declare
c SYS_REFCURSOR;
id number;
begin
RESULT(c);
loop
fetch c into id; -- and other columns if needed
exit when c%notfound;
dbms_output.put_line(id);
end loop;
END;
Result 5
You shouldn't make the coding difficult for yourself to understand. You dont need any loop here in this case a simple SELECT with FILTER condition will be enough to suffice your requirement. Hope below query helps. Also its not a good coding practice to user "" for naming convention.
As far as your question is concerned REFCURSOR is not that intelligent to keep all the records for each iteration and print you the collaborated output.
CREATE OR REPLACE PROCEDURE "RESULT"(
res1 OUT SYS_REFCURSOR )
IS
BEGIN
OPEN res1 FOR
SELECT e.id FROM emp e
WHERE EXISTS
( SELECT 1
FROM emp E1
WHERE e1.name LIKE '%B%'
AND e1.poi_user_id = e.id
);
END;

PL/SQL Blocks - seeing output simply ? [A Very simple question I'm sure !]

I'm sure what I want is very simple but I cannot figure out how.
I want :
To declare some variables and initialize them to certain values
To excecute a number of selects (predicated by the above variable values) and see the results as if i had executed the results straight on the sqlplus command line
I believe it's necessary to use the block structure in order that I may declare and make use of variables within the predicates of the queries. Although the examples shown here are quite simple in the real case there are numberous, much more complex SELECT's.
I tried doing this (forgetting about predicates for a moment) ...
DECLARE
EMP_EMPLOYEE_ID_IN VARCHAR2(12);
BEGIN
EXECUTE IMMEDIATE 'SELECT * FROM DEPT WHERE DEPNO';
END;
/
... but when I do that I get to execute the select without seeing the output.
I've also tried this ...
DECLARE
EMP_EMPLOYEE_ID_IN VARCHAR2(12);
BEGIN
SELECT * FROM DEPT;
END;
/
... but then I get ...
PLS-00428: an INTO clause is expected in this SELECT statement
... I really don't want to have to declare a variable for every column which would appear in my output.
Can anyone tell me how I can execute the SELECTs but simply and easily see the output as if I were on the sqlplus command line, ie to see the same output as if I did this
SQL> SELECT * FROM DEPT;
DEPTNO DNAME LOC
---------- -------------- -------------
10 ACCOUNTING NEW YORK
20 RESEARCH DALLAS
30 SALES CHICAGO
40 OPERATIONS BOSTON
Thanks
I have now tested the answer given by Shannon Severance below and found that it will do what I want.
For the sake of later readers I thought it might be useful to show the complete script here.
set line 32000;
set trimspool on;
var V_CURSOR1 REFCURSOR;
var V_CURSOR2 REFCURSOR;
var V_CURSOR3 REFCURSOR;
DECLARE
DEPT_NUM_IN VARCHAR2(12);
BEGIN
DEPT_NUM_IN := '10';
OPEN :V_CURSOR1 FOR SELECT * FROM DEPT;
OPEN :V_CURSOR2 FOR SELECT * FROM DEPT ORDER BY LOC;
OPEN :V_CURSOR3 FOR SELECT * FROM DEPT WHERE DEPTNO = DEPT_NUM_IN ORDER BY LOC;
END;
/
print V_CURSOR1
print V_CURSOR2
print V_CURSOR3
From sqlplus, other tools may be different.
First declare a sqlplus refcursor variable
SQL> var l_cursor refcursor
Then open that cursor within a PL/SQL block, where you will have access to declared variables and everything:
SQL> edit
Wrote file afiedt.buf
1 declare
2 l_number number;
3 begin
4 open :l_cursor for select table_name from all_tables where rownum < 10;
5* end;
SQL> /
PL/SQL procedure successfully completed.
Notice above that the refcursor variable is prepended with a :, this is because we are binding a sqlplus variable into the PL/SQL anonymous block
Next, use the SQLPLUS print command:
SQL> print l_cursor
TABLE_NAME
------------------------------
ICOL$
CON$
UNDO$
PROXY_ROLE_DATA$
FILE$
UET$
IND$
SEG$
COL$
9 rows selected.
DECLARE
v_result VARCHAR2(400);
BEGIN
SELECT dummy
INTO v_result
FROM dual;
dbms_output.put_line(v_result);
END;

Resources