how to pass in array in procedure call in oracle - sql

I have a procedure that takes in an array in postgres, this syntax works:
SELECT * from myMethod(array['test','test'], array[''], 554, 73430, 322234, 'shazam');
the array keyword is what I am referring to. this works in postgres but I cannot find the documentation to understand how this work in oracle. how do i pass in arrays to function call?
the error when running the above is:
ORA-00933: SQL command not properly ended
00933. 00000 - "SQL command not properly ended"
*Cause:
*Action:
Error at Line: 4 Column: 38

If you have the type:
CREATE TYPE array IS TABLE OF VARCHAR2(200);
and create your function:
CREATE FUNCTION myMethod(
p_words IN array,
p_suffixes IN array,
p_value1 IN INT,
p_value2 IN INT,
p_value3 IN INT,
p_prefix IN VARCHAR2
) RETURN array PIPELINED DETERMINISTIC
IS
value VARCHAR2(200);
BEGIN
FOR i IN 1 .. p_words.COUNT LOOP
value := p_prefix || p_words(i);
FOR j IN 1 .. p_suffixes.COUNT LOOP
value := value || p_suffixes(j);
END LOOP;
PIPE ROW ( value );
END LOOP;
END;
/
Then you can do:
SELECT * from myMethod(array('test1','test2'), array('ab','cd'), 554, 73430, 322234, 'shazam');
In earlier Oracle versions, you may need to use:
SELECT * from TABLE( myMethod(array('test1','test2'), array('ab','cd'), 554, 73430, 322234, 'shazam') );
and it outputs:
| COLUMN_VALUE |
| :-------------- |
| shazamtest1abcd |
| shazamtest2abcd |
db<>fiddle here

Here's an example; I'm using built-in sys.odcivarchar2list type. You'd use your own (if this doesn't suit your needs).
SQL> create or replace procedure p_arr (par_array in sys.odcivarchar2list) is
2 begin
3 dbms_output.put_line(par_array(1));
4 end;
5 /
Procedure created.
Testing:
SQL> set serveroutput on
SQL> exec p_arr(sys.odcivarchar2list('A', 'B'));
A
PL/SQL procedure successfully completed.
SQL>

Related

Oracle PL SQL function has no INTO clause, but it actually has a SELECT INTO

I have a function inside a package.
The body:
CREATE OR REPLACE PACKAGE BODY pkg_developers AS
FUNCTION lookup_developer_studio(p_studio_name IN DEVELOPERSTUDIOS.STUDIONAME%type)
RETURN INTEGER
IS
f_developerstudioid INTEGER;
BEGIN
SELECT developerstudioid INTO f_developerstudioid
FROM developerStudios
WHERE studioName = p_studio_name;
RETURN (f_developerstudioid);
END lookup_developer_studio;
END pkg_developers;
The header:
CREATE OR REPLACE PACKAGE pkg_developers
AS
FUNCTION lookup_developer_studio(p_studio_name IN DEVELOPERSTUDIOS.STUDIONAME%type) RETURN INTEGER;
END pkg_developers;
I'm testing the function like so:
BEGIN
select pkg_developers.lookup_developer_studio('some name') from dual;
END;
The Error I get:
[2022-04-16 13:06:06] [65000][6550]
[2022-04-16 13:06:06] ORA-06550: line 6, column 5:
[2022-04-16 13:06:06] PLS-00428: an INTO clause is expected in this SELECT statement
[2022-04-16 13:06:06] Summary: 1 of 1 statements executed, 1 failed in 15 ms (285 symbols in file)
But as far as I can see, I do have an INTO clause?
When I run the query statically like: SELECT developerstudioid FROM developerStudios WHERE studioName = 'some name'; I do get 1 result which is an integer.
Well, code you used to test it isn't equal to what you initially posted - this is actually a PL/SQL block which then requires an INTO clause:
SQL> BEGIN
2 select pkg_developers.lookup_developer_studio('some name') from dual;
3 END;
4 /
select pkg_developers.lookup_developer_studio('some name') from dual;
*
ERROR at line 2:
ORA-06550: line 2, column 5:
PLS-00428: an INTO clause is expected in this SELECT statement
If it were only SELECT (at SQL level) then yes - you don't need INTO:
SQL> select * from developerstudios;
STU DEVELOPERSTUDIOID
--- -----------------
MGM 100
SQL> select pkg_developers.lookup_developer_studio('MGM') from dual;
PKG_DEVELOPERS.LOOKUP_DEVELOPER_STUDIO('MGM')
---------------------------------------------
100
SQL>
If you want PL/SQL, then declare a variable which will store that value:
SQL> SET SERVEROUTPUT ON
SQL>
SQL> DECLARE
2 l_id developerstudios.developerstudioid%type;
3 BEGIN
4 select pkg_developers.lookup_developer_studio('MGM') INTO l_id from dual;
5 dbms_output.put_line('Result = ' || l_id);
6 END;
7 /
Result = 100
PL/SQL procedure successfully completed.
SQL>

is there a function without an input but return something

CREATE OR REPLACE FUNCTION FN_MULTAS_TOTAL
RETURN INTEGER
IS
V_TOTAL INT;
BEGIN
SELECT SUM(MULTA)
INTO V_TOTAL
FROM DETALLE_ARRIENDO;
RETURN V_TOTAL;
END;
im working on SQL Developer 11 and i have to call it with
exec FN_MULTAS_TOTAL;
and get an integer value
No problem with such a function:
SQL> create or replace function fn_multas_total
2 return integer
3 is
4 v_total int;
5 begin
6 select sum(multa)
7 into v_total
8 from detalle_arriendo;
9 return v_total;
10 end;
11 /
Function created.
However, you don't call it with exec - it is used in SQL*Plus or SQL Developer or some other tools which support it; it is a "shortcut" for an anonymous begin-end PL/SQL block:
SQL> exec p_test
PL/SQL procedure successfully completed.
SQL> begin
2 p_test;
3 end;
4 /
PL/SQL procedure successfully completed.
It is used for procedures, not functions. You can't just
SQL> begin
2 detalle_arriendo;
3 end;
4 /
detalle_arriendo;
*
ERROR at line 2:
ORA-06550: line 2, column 3:
PLS-00221: 'DETALLE_ARRIENDO' is not a procedure or is undefined
ORA-06550: line 2, column 3:
PL/SQL: Statement ignored
SQL>
which means that you have to declare a variable and fetch function's result into it, e.g.
SQL> declare
2 l_result number;
3 begin
4 l_result := fn_multas_total;
5 end;
6 /
PL/SQL procedure successfully completed.
SQL>
Usually, we use functions as
SQL> select fn_multas_total from dual;
FN_MULTAS_TOTAL
---------------
100
SQL>
If you desperately want to use exec, then you still have to declare a variable and
SQL> var result number
SQL> exec :result := fn_multas_total;
PL/SQL procedure successfully completed.
SQL> print :result
RESULT
----------
100
SQL>

PL / SQL Function to return varchar2 / numbers

I have this PL / SQL function that accepts the name of a student (f_name). The function then displays all of the information for the given student from a premade table called students. The table contains 5 columns, 2 number type, and 3 varchar2 type. If the name isn't found in the table an error message is returned. My code so far is
CREATE OR REPLACE FUNCTION studentName(
f_name IN VARCHAR2)
RETURN
IS
v_test students%rowtype;
CURSOR c1
IS
SELECT * FROM students WHERE first_name = f_name;
BEGIN
OPEN c1;
FETCH c1 INTO v_test;
IF c1%notfound THEN
v_test := NULL;
END IF;
CLOSE c1;
RETURN v_test;
END;
I keep getting:
PLS-00382: expression is of wrong type
I believe from my initial return varchar2 statement. How do I allow the return to accept both varchar2 type and number type?
RETURN varchar2
You need to return the rowtype, but you are returning a scalar. VARCHAR2 cannot hold a row, it can hold only a string value.
Modify it to:
RETURN students%rowtype;
Demo using standard EMP table:
SQL> CREATE OR REPLACE FUNCTION studentName(
2 f_name IN VARCHAR2)
3 RETURN emp%rowtype
4 IS
5 v_test emp%rowtype;
6 CURSOR c1
7 IS
8 SELECT * FROM emp WHERE ename = f_name;
9 BEGIN
10 OPEN c1;
11 FETCH c1 INTO v_test;
12 IF c1%notfound THEN
13 v_test := NULL;
14 END IF;
15 CLOSE c1;
16 RETURN v_test;
17 END;
18 /
Function created.
SQL> sho err
No errors.
NOTE : %ROWTYPE implies PL/SQL record type and PL/SQL types are not known to SQL. So you won't be able to use the function directly in plain SQL. You need to use SQL object type. Else you will get:
ORA-06553: PLS-801: internal error [55018]
Workaround to use it in SQL:
SQL> create or replace
2 type student_obj_type
3 as object(
4 student_id number,
5 stu_name varchar2(20),
6 dept varchar2(20)
7 )
8 /
Type created.
Use student_obj_type instead of students%rowtype to use the function in SQL.

Aberrant function behavior of PL/SQL procedure DBMS_OBFUSCATION_TOOLKIT.MD5

I use TOAD for executing my procedures and running my PL/SQL scripts.
Recently, I tried the dbms_obfuscation_toolkit.md5() function in PL/SQL to retrieve encrypted text of the string I pass as a parameter. I found that, the function gives the proper result when I call the function as a separate statement. However, when I call it via a SELECT query, it does not respond the same format.
Below, I have mentioned this peculiar case with the results that appear once I run it in TOAD application.
--CASE:1 (Proper result is obtained here)
DECLARE
vinput varchar2(255) := 'SRINI';
well varchar2(50);
BEGIN
WELL := DBMS_OBFUSCATION_TOOLKIT.MD5(input_string =>VINPUT);
dbms_output.put_line(well);
END;
Output: Áýg¿Zq!Ù´¿Ke>ÏQ
--CASE:2 (Unexpected response is obtained here)
DECLARE
vinput varchar2(255) := 'SRINI';
well varchar2(50);
BEGIN
SELECT DBMS_OBFUSCATION_TOOLKIT.MD5(VINPUT)
INTO WELL
FROM DUAL;
dbms_output.put_line(well);
END;
Error Output:
ORA-06550: line 5, column 37:
PLS-00307: too many declarations of 'MD5' match this call
ORA-06550: line 5, column 12:
PL/SQL: ORA-00904: "DBMS_OBFUSCATION_TOOLKIT"."MD5": invalid identifier
ORA-06550: line 5, column 5:
PL/SQL: SQL Statement ignored
Why is there such a disparity in the results, even though both of them are syntactically and semantically correct ? Please provide your opinion and feedback.
Your function calls are not identical, syntactically nor semantically. If you call the function in PL/SQL as you did in SQL, you will get the same exact error:
SQL> DECLARE
2 vinput VARCHAR2(255) := 'SRINI';
3 well VARCHAR2(50);
4 BEGIN
5 WELL := DBMS_OBFUSCATION_TOOLKIT.MD5(vinput);
6 dbms_output.put_line(well);
7 END;
8 /
ORA-06550: Ligne 5, colonne 12 :
PLS-00307: too many declarations of 'MD5' match this call
This is because the function called MD5 is overloaded in the package DBMS_OBFUSCATION_TOOLKIT and you need to specify which function exactly to call.
In Oracle 11g, you can use the same synthax as PL/SQL:
SQL> DECLARE
2 vinput VARCHAR2(255) := 'SRINI';
3 well VARCHAR2(50);
4 BEGIN
5 SELECT DBMS_OBFUSCATION_TOOLKIT.MD5(input_string => VINPUT)
6 INTO WELL FROM DUAL;
7 dbms_output.put_line(well);
8 END;
9 /
Áýg¿Zq!Ù´¿Ke>ÏQ
PL/SQL procedure successfully completed
Before 11g, you can define a wrapper function:
SQL> CREATE OR REPLACE FUNCTION wrap_md5(input_string VARCHAR2)
2 RETURN dbms_obfuscation_toolkit.varchar2_checksum
3 IS
4 BEGIN
5 RETURN dbms_obfuscation_toolkit.md5(input_string => input_string);
6 END;
7 /
Function created
SQL> DECLARE
2 vinput VARCHAR2(255) := 'SRINI';
3 well VARCHAR2(50);
4 BEGIN
5 SELECT wrap_md5(VINPUT)
6 INTO WELL FROM DUAL;
7 dbms_output.put_line(well);
8 END;
9 /
Áýg¿Zq!Ù´¿Ke>ÏQ
PL/SQL procedure successfully completed
select dbms_obfuscation_toolkit.md5(input_string => 'SRINI') from dual; use this in select for the such type of function uses input string.

oracle - mixing SQL code with PLSQL when no bindings in WHERE statement

I am trying to write a very simple sql script:
select * from table_X;
and I would like to see the results in oracle sqlplus, if there are any. These results are important for further analysis.
Also to mention, it depends, how many tables were created originally, so chances are that table_X may not be in the database at all. However I want to avoid getting an error, when parsing, that table_X doesn't exist, while running that script above.
So I was trying to wrap that SQL into some PLSQL dynamic code, like this:
Define table_X="MY_TAB"
DECLARE
stmt_ VARCHAR2(2000);
exist_ number := 0;
CURSOR table_exist IS
SELECT 1
FROM user_tables
WHERE table_name = '&table_X';
BEGIN
OPEN table_exist;
FETCH table_exist INTO exist_;
CLOSE table_exist;
IF exist_ = 1 THEN
stmt_ := 'SELECT * FROM &table_X';
EXECUTE IMMEDIATE stmt_;
ELSE
dbms_output.put_line('This functionality is not installed.');
END IF;
END;
/
Why I cannot see any result (records), if there is data in MY_TAB? Do I really need to bind some columns and use ex. dbms_output to be able to see some information?
Is there any simple way to query a table without getting 'ORA-00942: table or view does not exist'
if that table doesn't exist (ideally using SQL only)?
Thanks in advance
like this if you want it in sqlplus:
SQL> var c refcursor;
SQL> create or replace function get_table(p_tab in varchar2)
2 return sys_refcursor
3 is
4 v_r sys_refcursor;
5 NO_TABLE exception;
6 pragma exception_init(NO_TABLE, -942);
7 begin
8 open v_r for 'select * from ' || dbms_assert.simple_sql_name(p_tab);
9 return v_r;
10 exception
11 when NO_TABLE
12 then
13 open v_r for select 'NO TABLE ' || p_tab as oops from dual;
14 return v_r;
15 end;
16 /
Function created.
SQL> exec :c := get_table('DUAL2');
PL/SQL procedure successfully completed.
SQL> print c
OOPS
-----------------------------------------
NO TABLE DUAL2
SQL>
SQL> exec :c := get_table('DUAL');
PL/SQL procedure successfully completed.
SQL> print c
D
-
X
Instead of execute immediate 'query' you could use execute immediate 'query' bulk collect into and then loop over it and use dbms_output to print it.
http://docs.oracle.com/cd/B19306_01/appdev.102/b14261/executeimmediate_statement.htm