TABLE OF VARCHAR2 in WHERE clause - oracle

The code is simplified. I have a type:
CREATE TYPE str_tab_t IS TABLE OF VARCHAR2(20);
Here a function:
FUNCTION MYFUNCTION(myVar IN str_tab_t ) RETURN VARCHAR
myVar2 NUMBER;
BEGIN
SELECT MYCOLUMN INTO myVar2 FROM MYTABLE WHERE MYCOLUMN IN(SELECT * FROM myVar );
RETURN myVar2 ;
END MYFUNCTION;
I wonder if it possible to call the function like this:
MYFUNCTION(str_tab_t ('abc'));
As I'm getting error ORA-01722. I don't think, that it is because of wrong query. I think, I'm passing the argument in a wrong way. Could someone give me any clue? Thanks!

Did you by any chance OVERsimplified it?
Because, if table contents is something like this (i.e. strings in MYCOLUMN)
SQL> select * from mytable;
MYC
---
abc
def
and you want the function to return a number, then you can't select mycolumn (which is a string) into a number datatype local function variable myvar2. But, you can select some number (such as 1 in my example):
SQL> create or replace function myfunction (myvar in str_tab_t)
2 return number
3 is
4 myvar2 number;
5 begin
6 select 1 into myvar2
7 from mytable
8 where mycolumn in (select * from table(myvar));
9 return myvar2;
10 end;
11 /
Function created.
SQL> select myfunction(str_tab_t('abc')) from dual;
MYFUNCTION(STR_TAB_T('ABC'))
----------------------------
1
SQL>
Or, obviously, return a different datatype:
SQL> create or replace function myfunction (myvar in str_tab_t)
2 return mytable.mycolumn%type
3 is
4 myvar2 mytable.mycolumn%type;
5 begin
6 select mycolumn into myvar2
7 from mytable
8 where mycolumn in (select * from table(myvar));
9 return myvar2;
10 end;
11 /
Function created.
SQL> select myfunction(str_tab_t('abc')) from dual;
MYFUNCTION(STR_TAB_T('ABC'))
-------------------------------------------------------------------------
abc
SQL>

Related

PL/SQL Select and Update statement inside OPEN - FOR

So I have a pl/sql function and I want to select and update table inside open for statement
It looks like this:
table_a
id | status | document_id |
create or replace function a(p_document_id in number)
return sys_refcursor
is
result sys_refcursor;
begin
open result for
select id
from table_a t
where t.document_id = p_document_id;
update table_a t
set t.status = 1
where id in (select id
from table_a t
where t.document_id = p_document_id);
return result;
end;
/
But it is not working. Is there some method to do this? thanks in advance
This is a sample table:
SQL> select * from table_a;
ID DOCUMENT_ID STATUS
---------- ----------- ----------
1 100 0
Function you wrote compiles, but doesn't work because functions - normally - don't do DML:
SQL> select a(100) from dual;
select a(100) from dual
*
ERROR at line 1:
ORA-14551: cannot perform a DML operation inside a query
ORA-06512: at "SCOTT.A", line 11
SQL>
It, though, would work from PL/SQL, e.g.
SQL> declare
2 l_rc sys_refcursor;
3 begin
4 l_rc := a(100);
5 end;
6 /
PL/SQL procedure successfully completed.
SQL> select * from table_a;
ID DOCUMENT_ID STATUS
---------- ----------- ----------
1 100 1
SQL>
But, if you want to be able to call the function from PL/SQL, it has to be an autonomous transaction, which also means that you have to commit within:
SQL> rollback;
Rollback complete.
SQL> create or replace function a(p_document_id in number)
2 return sys_refcursor
3 is
4 result sys_refcursor;
5 pragma autonomous_transaction; --> this
6 begin
7 open result for
8 select id
9 from table_a t
10 where t.document_id = p_document_id;
11
12 update table_a t
13 set t.status = 1
14 where id in (select id
15 from table_a t
16 where t.document_id = p_document_id);
17
18 commit; --> this
19
20 return result;
21 end;
22 /
Function created.
Let's try it:
SQL> select a(100) from dual;
A(100)
--------------------
CURSOR STATEMENT : 1
CURSOR STATEMENT : 1
ID
----------
1
SQL> select * from table_a;
ID DOCUMENT_ID STATUS
---------- ----------- ----------
1 100 1
SQL>
This might, or might not be OK. Autonomous transactions can be tricky and we usually use them for logging purposes, so that their commit doesn't affect the main transaction.

Create type table of variable How do I set the field name?

create or replace type TableOfNumber as table of number;
I created a table of a number to be used as the output of a function
create or replace function GetItems()
return TableOfNumber as
Res TableOfNumber;
sqlstr varchar2(500);
begin
-- Test statements here
sqlstr:= 'select swid from ITEMS i' ;
dbms_output.put_line (sqlstr);
EXECUTE IMMEDIATE sqlstr
BULK COLLECT INTO Res ;
return(Res);
end ;
I want to change the field name from column_value hg To which ITid when creating the type And change the query clause from
select column_value from GetItems()
select ITid from GetItems()
create or replace type TableOfNumber as table of number ''' name ITid ''';
column_value is a pseudocolumn and - as far as I can tell - you can't avoid it if you're doing what you're doing.
But, if you switch to the following example, that's another story. You'll need two types:
SQL> create or replace type t_row as object (deptno number);
2 /
Type created.
SQL> create or replace type t_tab as table of t_row;
2 /
Type created.
Function (without dynamic SQL. Why did you use it? There's nothing dynamic in your code):
SQL> create or replace function getitems
2 return t_tab
3 as
4 res t_tab;
5 begin
6 select t_row(deptno)
7 bulk collect
8 into res
9 from dept;
10 return res;
11 end;
12 /
Function created.
Finally: column name is deptno, not column_value:
SQL> select * from table(getitems);
DEPTNO
----------
10
20
30
40
SQL>

ORA-06553: PLS-801: internal error [55018] when testing function returning ROWTYPE

I need to test some function returning ROWTYPE variable in Toad. When I try to run it, I aget the Internal error.
I run is as
SELECT MYPACKAGE.MyFunction(param1, aram2, param3) FROM DUAL
Is there any way to test a function returning ROWTYPE for Toad?
As you just want to test the function you could use an anonymous PL/SQL block to call it and assign its result to a matching rowtype variable, e.g.:
declare
l_row mytable%rowtype;
begin
-- call the function and assign the result to a variable
l_row := mypackage.myfunction(1, 2, 3);
-- do something with the result
dbms_output.put_line(l_row.some_columns);
end;
/
Quick demo with a made-up table and expanded function:
create table mytable (col1, col2, col3, col4, col5) as
select 1, 2, 3, 'test', sysdate from dual;
create or replace package mypackage as
function myfunction (param1 number, param2 number, param3 number)
return mytable%rowtype;
end mypackage;
/
create or replace package body mypackage as
function myfunction (param1 number, param2 number, param3 number)
return mytable%rowtype is
l_row mytable%rowtype;
begin
select * into l_row
from mytable
where col1 = param1
and col2 = param2
and col3 = param3;
return l_row;
end myfunction;
end mypackage;
/
Calling from SQL gets the same error you see now:
select mypackage.myfunction(1, 2, 3) from dual;
SQL Error: ORA-06553: PLS-801: internal error [55018]
But with a block (run here through SQL Developer with output enabled):
set serveroutput on
declare
l_row mytable%rowtype;
begin
-- call the function and assign the result to a variable
l_row := mypackage.myfunction(1, 2, 3);
-- do something with the result
dbms_output.put_line(l_row.col4 ||':'|| l_row.col5);
end;
/
test:2019-04-29
PL/SQL procedure successfully completed.
db<>fiddle
True, it won't work. Function, when used in a SQL query, is supposed to return a SQL datatype, while %ROWTYPE is a PL/SQL record.
This is what you, probably, have now:
SQL> create or replace function f_test (par_deptno in number)
2 return dept%rowtype
3 is
4 retval dept%rowtype;
5 begin
6 select deptno, dname, loc
7 into retval
8 from dept
9 where deptno = par_deptno;
10 return retval;
11 end;
12 /
Function created.
SQL> select f_test(10) From dual;
select f_test(10) From dual
*
ERROR at line 1:
ORA-06553: PLS-801: internal error [55018]
SQL>
Option you might choose is to create (and return) an object type. Here's an example:
SQL> create or replace type dept_type as object
2 (deptno number,
3 dname varchar2(20),
4 loc varchar2(20));
5 /
Type created.
SQL> create or replace function f_test (par_deptno in number)
2 return dept_type
3 is
4 retval dept_type;
5 begin
6 select dept_type(deptno, dname, loc)
7 into retval
8 from dept
9 where deptno = par_deptno;
10 return retval;
11 end;
12 /
Function created.
SQL> select f_test(10).dname From dual;
F_TEST(10).DNAME
--------------------
ACCOUNTING
SQL>

Wrap select with an Oracle function

In my mind, I'm writing a function such that calling something like
select get_foo() from dual;
or
select * from table (get_foo);
returns the same result as
select * from foo;
So, I've got a function that compiles...
create or replace function get_foo return sys_refcursor as
rc_foo sys_refcursor;
begin
open rc_foo for 'select * from foo';
return rc_foo;
end;
but select get_foo() from dual returns 1 row.
((ID=1,NAME=Sarah1),(ID=2,NAME=Sarah2),(ID=3,NAME=Sarah3),)
whilst select * from table( get_foo() ) gives me ORA-22905.
How do I change the function definition and/or the call to get the desired outcome?
you use a pipelined function.
for example:
SQL> create table foo(id , name) as select rownum, 'Sarah'||rownum from dual connect by level <= 3;
Table created.
SQL> create or replace package pipeline_test
2 as
3 type foo_tab is table of foo%rowtype;
4 function get_foo
5 return foo_tab PIPELINED;
6 end;
7 /
Package created.
SQL> create or replace package body pipeline_test
2 as
3 function get_foo
4 return foo_tab PIPELINED
5 is
6 v_rc sys_refcursor;
7 t_foo foo_tab;
8
9 begin
10 open v_rc for select * from foo;
11 loop
12 fetch v_rc bulk collect into t_foo limit 100;
13 exit when t_foo.count = 0;
14 for idx in 1..t_foo.count
15 loop
16 pipe row(t_foo(idx));
17 end loop;
18 end loop;
19 end;
20 end;
21 /
Package body created.
SQL> select * from table(pipeline_test.get_foo());
ID NAME
---------- ---------------------------------------------
1 Sarah1
2 Sarah2
3 Sarah3

How do I test if a column equals empty_clob() in Oracle?

The naïve FOO = empty_clob() complains about incompatible types. I tried Googling, but (once again) had little success searching for help with Oracle. Thanks.
Are you just wanting to check for a CLOB that doesn't have any length? While not exactly what your asking, it's basically the same thing?
select *
from bar
where dbms_lob.getlength(foo) = 0;
Here is the complete test:
SQL> create table bar (foo clob);
Table created.
SQL> insert into bar values (empty_clob());
1 row created.
SQL> select *
2 from bar
3 where dbms_lob.getlength(foo) = 0;
FOO
--------------------------------------------------------------------------------
If you are trying to do the comparison in PL/SQL, you can just test equality as Igor's solution does
SQL> ed
Wrote file afiedt.buf
1 DECLARE
2 dummy clob;
3 BEGIN
4 dummy := empty_clob();
5 IF dummy = empty_clob() THEN
6 dbms_output.put_line( 'Dummy is empty' );
7 ELSE
8 dbms_output.put_line( 'Dummy is not empty' );
9 END IF;
10* END;
SQL> /
Dummy is empty
PL/SQL procedure successfully completed.
If you are trying to do this in SQL, thougyh, you need to use the DBMS_LOB.COMPARE function. A LOB column in a table is really a LOB locator (i.e. pointer), so what you really care about is that the value pointed to by the LOB is comparable to the value pointed to by the LOB locator returned by the EMPTY_CLOB() function.
SQL> desc bar
Name Null? Type
----------------------------------------- -------- ------------------------
FOO CLOB
SQL> insert into bar values ('123');
1 row created.
SQL> insert into bar values( empty_clob() );
1 row created.
SQL> insert into bar values( empty_clob() );
1 row created.
SQL> ed
Wrote file afiedt.buf
1 select count(*)
2 from bar
3* where dbms_lob.compare( foo, empty_clob() ) = 0
SQL> /
COUNT(*)
----------
2
SQL> ed
Wrote file afiedt.buf
1 select count(*)
2 from bar
3* where dbms_lob.compare( foo, empty_clob() ) != 0
SQL> /
COUNT(*)
----------
1
something like this should work for initialization:
DECLARE
dummy clob;
dummy2 clob;
BEGIN
dummy := empty_clob();
IF dummy = empty_clob() THEN
dummy2 := dummy;
END IF;
END;
A simple way to test for empty clobs in SQLplus is to convert all the CLOBS to varchar2 (using the TO_CHAR function) before performing the test:
SELECT *
FROM table1
WHERE TO_CHAR(table1.column1) IS NULL
DECLARE
dummy CLOB := 'fxsgf';
dummy1 CLOB;
BEGIN
IF dummy1 = EMPTY_CLOB ()
THEN
DBMS_OUTPUT.put_line ('Dummy1 is empty');
ELSE
DBMS_OUTPUT.put_line ('Dummy1 is not empty');
END IF;
IF dummy = EMPTY_CLOB ()
THEN
DBMS_OUTPUT.put_line ('Dummy is empty');
ELSE
DBMS_OUTPUT.put_line ('Dummy is not empty');
END IF;
END;

Resources