Select data from DBMS_UTILITY.LNAME_ARRAY - sql

DECLARE
ltab_vals DBMS_UTILITY.LNAME_ARRAY;
v_tablen BINARY_INTEGER;
BEGIN
DBMS_UTILITY.COMMA_TO_TABLE(list => 'AA,B,CC,DEF'
,tablen => v_tablen
,tab => ltab_vals);
END;
/
How to use ltab_vals variable in select statement?
I need to write something like this:
SELECT val INTO variable_ FROM v_tablen WHERE ...;

you can only use SQL types in SQL statements. so you have to convert that PL/SQL array into a SQL one.
for example if we create a your_array type.
SQL> create table your_table(col varchar2(3));
Table created.
SQL> insert into your_table values ('AA');
1 row created.
SQL> insert into your_table values ('DEF');
1 row created.
SQL> create type your_array as table of varchar2(4000);
2 /
Type created.
SQL> DECLARE
2 ltab_vals DBMS_UTILITY.LNAME_ARRAY;
3 v_tablen BINARY_INTEGER;
4 v_t your_array := your_array();
5 BEGIN
6
7 DBMS_UTILITY.COMMA_TO_TABLE(list => 'AA,B,CC,DEF'
8 ,tablen => v_tablen
9 ,tab => ltab_vals);
copy the array over to an SQL one:
10 for idx in 1..ltab_vals.count
11 loop
12 v_t.extend;
13 v_t(v_t.last) := ltab_vals(idx);
14 end loop;
15
now form the select using the table() function. The cardinality hint should be a reasonable guess on the number of elements in the array. without this hint, you may find that oracle picks a worse plan (as by default, it will assume ~8k rows in the array).
17 for r_row in (select /*+ cardinality(p, 10) */ t.*
18 from your_table t
19 inner join table(v_t) p
20 on t.col = p.column_value)
21 loop
22 dbms_output.put_line(r_row.col);
23 end loop;
24
25 END;
26 /
AA
DEF
PL/SQL procedure successfully completed.

Related

Select a column from table return by function in Oracle

I have a function that returns a table of custom objects. I wish to select a certain column by name from the returned result.
create or replace type sd_Serial_Number as object (
serial_number VARCHAR2(32)
);
The table of objects
create or replace type sd_Serial_Number_Table as table of sd_Serial_Number;
The function
create function get_result
return sd_Serial_Number_Table as
v_ret sd_Serial_Number_Table;
begin
select sd_Serial_Number(selected.SERIAL_NUMBER)
bulk collect into v_ret
from (
selection here
) selected;
return v_ret;
end get_result;
When I call the function this way, I get a result with a single column called SERIAL_NUMBER
select * from table(get_result());
However, I can't do something like this
select SERIAL_NUMBER from table(get_result());
Is there a way to select the column SERIAL_NUMBER ?
"I can't" is difficult to debug. I'll show you that I can (on the same database version you use).
SQL> SELECT * FROM v$version WHERE rownum = 1;
BANNER
--------------------------------------------------------------------------------
Oracle Database 11g Enterprise Edition Release 11.2.0.4.0 - 64bit Production
SQL> CREATE OR REPLACE TYPE sd_serial_number AS OBJECT
2 (
3 serial_number VARCHAR2 (32)
4 );
5 /
Type created.
SQL> CREATE OR REPLACE TYPE sd_serial_number_table AS TABLE OF sd_serial_number;
2 /
Type created.
SQL> CREATE OR REPLACE FUNCTION get_result
2 RETURN sd_serial_number_table
3 AS
4 v_ret sd_serial_number_table;
5 BEGIN
6 SELECT sd_serial_number (deptno)
7 BULK COLLECT INTO v_ret
8 FROM dept;
9
10 RETURN v_ret;
11 END get_result;
12 /
Function created.
Testing:
SQL> SELECT * FROM TABLE (get_result ());
SERIAL_NUMBER
--------------------------------
10
20
30
40
SQL> SELECT serial_number FROM TABLE (get_result ());
SERIAL_NUMBER
--------------------------------
10
20
30
40
SQL>

Oracle - Anonymous Procedure to loop through multiple tables (dynamically) - Query returning multiple rows

I need to fire the same query on multiple tables. Query might return zero, one or more number of rows.
I can loop through the tables using EXECUTE IMMEDIATE but for returning multiple rows I would need a datatype so I think I would need to keep it as CURSOR.
for ease, lets say I need to execute below query on 2 tables - table1 and table2
Table1 has following columns
datetime
device_name
value1
value2
Table2 has following columns
datetime
device_name
value3
value4
Query to be executed on both the tables as below:
select datetime, count(*) from table_name group by datetime;
Whats the best approach here?
please note that I can't create any DB objects (proc/function). Has to be anonymous block only.
As long as the cursor structures are the same, you can loop through with some dynamic ref cursors, eg
SQL> set serverout on
SQL> declare
2 tablist sys.odcivarchar2list :=
3 sys.odcivarchar2list('ALL_OBJECTS','USER_OBJECTS');
4 rc sys_refcursor;
5
6 date_results sys.odcidatelist := sys.odcidatelist();
7 count_results sys.odcinumberlist := sys.odcinumberlist();
8 begin
9 for i in 1 .. tablist.count
10 loop
11 open rc for
12 replace(q'{select trunc(created,'YYYY'), count(*) from ### group by trunc(created,'YYYY') order by 1}', '###',tablist(i));
13 fetch rc bulk collect into date_results, count_results;
14 close rc;
15
16 dbms_output.put_line(tablist(i));
17 for c in 1 .. date_results.count
18 loop
19 dbms_output.put_line(rpad(date_results(c),20)||lpad(count_results(c),20));
20 end loop;
21 end loop;
22 end;
23 /
ALL_OBJECTS
01-JAN-17 67892
01-JAN-18 6228
USER_OBJECTS
01-JAN-18 1093
PL/SQL procedure successfully completed.

SQL Oracle, returning a table from a Function

I was trying create a function (maybe procedure will be better?) which return a table. Presently I have this:
CREATE OR REPLACE TYPE rowx AS OBJECT
(
nam1 VARCHAR2 (100),
nam2 VARCHAR2 (100)
);
/
CREATE OR REPLACE TYPE tablex
IS TABLE OF rowx;
/
CREATE OR REPLACE FUNCTION example(FS varchar2)
RETURN tablex
IS
tab tablex;
BEGIN
select y.ident as PARENT, x.ident as CHILD into tab
from relation2 rt
inner join plate x on rt.child = x.id
inner join plate y on rt.parent =y.id
where x.ident like 'string1' or y.ident like 'string2';
RETURN tab;
END;
After compilation above function I recive ORA-00947. Any tips?
Your query is selecting two scalar values, and trying to put them into a table of an object type. That type has two fields, but there is no automatic comversion. So you need to build the object explicitly, which you can do as part of the query.
You should also use a bulk query to populate your collection:
select rowx(y.ident, x.ident)
bulk collect into tab
from relation2 rt
...
Have a look at this example; does it help?
My TEST table represents your tables. This function returns a collection, which is then used in SELECT statement along with the TABLE operator.
SQL> create table test (nam1 varchar2(10), nam2 varchar2(10));
Table created.
SQL> insert into test values ('Little', 'Foot');
1 row created.
SQL> insert into test values ('Stack', 'Overflow');
1 row created.
SQL> create or replace type t_tf_row as object (nam1 varchar2(10), nam2 varchar2(10));
2 /
Type created.
SQL> create or replace type t_tf_tab is table of t_tf_row;
2 /
Type created.
SQL>
SQL> create or replace function get_tab_tf return t_tf_tab as
2 l_tab t_tf_tab := t_tf_tab();
3 begin
4 for cur_r in (select nam1, nam2 from test) loop
5 l_tab.extend;
6 l_tab(l_tab.last) := t_tf_row(cur_r.nam1, cur_r.nam2);
7 end loop;
8 return l_tab;
9 end;
10 /
Function created.
SQL>
SQL> select * From table(get_Tab_tf);
NAM1 NAM2
--------------------
Little Foot
Stack Overflow
SQL>

default value, oracle sp call

I have an oralcle SP forced on me that will not accept an empty parameter in an update. So if I wanted to set a value back to the default of ('') it will not let me pass in the empty string. Is there a keyword you can use such as default, null, etc that oracle would interpret back to the default specified for a particular column?
Sometimes things are just as simple as you hope they might be.
First, a table with a default value ...
SQL> create table t23 (
2 id number not null primary key
3 , col_d date default sysdate not null )
4 /
Table created.
SQL> insert into t23 values (1, trunc(sysdate, 'yyyy'))
2 /
1 row created.
SQL> select * from t23
2 /
ID COL_D
---------- ---------
1 01-JAN-10
SQL>
Next a procedure which updates the default column ...
SQL> create or replace procedure set_t23_date
2 ( p_id in t23.id%type
3 , p_date in t23.col_d%type )
4 is
5 begin
6 update t23
7 set col_d = p_date
8 where id = p_id;
9 end;
10 /
Procedure created.
SQL>
... but which doesn't work as we would like:
SQL> exec set_t23_date ( 1, null )
BEGIN set_t23_date ( 1, null ); END;
*
ERROR at line 1:
ORA-01407: cannot update ("APC"."T23"."COL_D") to NULL
ORA-06512: at "APC.SET_T23_DATE", line 6
ORA-06512: at line 1
SQL>
So, let's try adding a DEFAULT option ...
SQL> create or replace procedure set_t23_date
2 ( p_id in t23.id%type
3 , p_date in t23.col_d%type )
4 is
5 begin
6 if p_date is not null then
7 update t23
8 set col_d = p_date
9 where id = p_id;
10 else
11 update t23
12 set col_d = default
13 where id = p_id;
14 end if;
15 end;
16 /
Procedure created.
SQL>
... and lo!
SQL> exec set_t23_date ( 1, null )
PL/SQL procedure successfully completed.
SQL>
SQL> select * from t23
2 /
ID COL_D
---------- ---------
1 28-FEB-10
SQL>
I ran this example on an 11g database. I can't remember when Oracle introduced this exact support for DEFAULT, but it has been quite a while (9i???)
edit
The comments are really depressing. The entire point of building PL/SQL APIs is to make it easier for application developers to interact with the database. That includes being sensible enough to rewrite stored procedures when necessary. The big difference between building something out of software and, say, welding cast-iron girders together is that software is malleable and easy to change. Especially when the change doesn't alter the signature or behaviour of an existing procedure, which is the case here.
The procedure that's been forced on you:
create or replace procedure notEditable(varchar2 bar) as
begin
--update statement
null;
end;
How to use:
begin
notEditable(bar=>null);
end;
I didn't actually compile, but I believe this is the correct syntax.

web form insert and trigger before insert

I have a oracle table called:
create table InsertHere(
generate_id varchar2(10),
name varchar2(100)
);
I have to insert data in this table from a web from. In the web form there are two
elements:
type and name
. I have to generate 'generate_id' from the type user has selected.
Task :
Before insert into the InsertHere table i have to generate the generate_id. Then i have to insert
'generate_id' and 'name' into the InsertHere table. BUT THE GENERATION OF ID MUST BE DONE INSIDE THE
DATABASE(may be with a procedure).
Question:
How this thing can be done effectively?
I need suggestions.
Thanks in advance
It all depends what you mean by "generating ID". The ID is I presume a primary key, so its value must be unique regardless of TYPE. So what rules do you want to apply to its generation?
Here is an indicative approach. This uses a sequence to get a value and prepends a character, depending on type.
SQL> create or replace function generator
2 (p_type varchar2
3 , p_name in inserthere.name%type)
4 return inserthere.generate_id%type
5 is
6 c char(1);
7 return_value inserthere.generate_id%type;
8 begin
9 case p_type
10 when 'EXTREME' then
11 c := 'X';
12 when 'REGULAR' then
13 c := 'R';
14 when 'JUMBO' then
15 c := 'J';
16 else
17 c := 'P';
18 end case;
19
20 insert into inserthere
21 ( generate_id,
22 name)
23 values
24 (c || lpad(trim(to_char(my_seq.nextval)), 9, '0')
25 , p_name )
26 returning generate_id into return_value;
27
28 return return_value;
29 end;
30 /
Function created.
SQL>
here it is in action
SQL> var n varchar2(10)
SQL> exec :n := generator ('EXTREME', 'ABC')
PL/SQL procedure successfully completed.
SQL> print n
N
--------------------------------
X000000001
SQL> exec :n := generator ('WHATEVER', 'SOMETHING')
PL/SQL procedure successfully completed.
SQL> print n
N
--------------------------------
P000000002
SQL>
You can vary the precise implementation, depending on how you want to call it. As is often the case, details matter and more information will tend to result in a more relevant answer.