Insert into nested table plsql - oracle

I'm a newbie with pl sql and I'm facing some problems with inserting into nested tables (I'm using these just to test a procedure).
So my code is:
insert into t_prenotazioni
(nro_cliente, data_disponibilita)
values
(righe.nro_cliente, v_data_disponibilita);
where t_prenotazioni is a table of a type defined by me, righe.nro_cliente is a value that I get from a cursor and v_data_disponibilita is a variable.
The error that I get is:
PLS-00330 invalid use of type name or subtype

You are probably trying to do something like:
declare
type type_prenotazioni is record(nro_cliente number, data_disponibilita date);
type prenotazioni is table of type_prenotazioni;
vPrenotazioni prenotazioni;
begin
vPrenotazioni := new prenotazioni();
vPrenotazioni.extend(1);
vPrenotazioni(1).nro_cliente := 10;
vPrenotazioni(1).data_disponibilita := sysdate;
--
for i in vPrenotazioni.first .. vPrenotazioni.last loop
dbms_output.put_line(vPrenotazioni(i).nro_cliente || ' - ' ||
to_char(vPrenotazioni(i).data_disponibilita, 'dd/mm/yyyy')
);
end loop;
end;
I would stronlgy recommend having a look at the Oracle documentation to improve your knowledge; this is only a simple, small example, but there are many many different things you may want to do.

Related

How can I test my package with one function?

I made a package that compiles fine but when I try to test it it gives me "invalid data type".
I've tried two different ways, first one like this
select pkg_contabilidad.f_totalizar_Detalle(100) FROM DUAL;
It gives me the ORA-00902 'invalid data type'
Also I've tried this
DECLARE
TYPE r_registro IS RECORD
(rubro_contable CN_RUBROS_CONTABLES.COD_RUBRO%TYPE,
tipo VARCHAR2(1),
monto NUMBER(16));
resultao r_registro;
numero NUMBER :=100;
BEGIN
resultao := pkg_contabilidad.f_totalizar_detalle(numero);
END;
It gives me another error PLS-00382 'expression is of wrong type'
I don't know what am I doing wrong, cause my function receives just one parameter and is of type NUMBER, so I dont know where's my mistake. I'll leave the code of my package just in case
CREATE OR REPLACE PACKAGE pkg_contabilidad AS
TYPE r_registro IS RECORD
(rubro_contable CN_RUBROS_CONTABLES.COD_RUBRO%TYPE,
tipo VARCHAR2(1),
monto NUMBER(16));
TYPE t_detalle IS TABLE OF
r_registro INDEX BY BINARY_INTEGER;
FUNCTION f_totalizar_detalle(p_clave NUMBER)RETURN t_detalle;
END pkg_contabilidad;
/
CREATE OR REPLACE PACKAGE BODY pkg_contabilidad AS
B_detalle t_detalle;
i integer :=1;
FUNCTION f_totalizar_detalle(p_clave NUMBER) RETURN t_detalle IS
v_detalle t_detalle;
CURSOR c_facturado IS
SELECT c.cod_rubro, 'H', CASE WHEN SUM(d.gravada)=0 THEN SUM(d.iva) ELSE SUM(d.gravada) END
FROM fn_documentos_det d JOIN fn_conceptos c ON d.cod_concepto = c.cod_concepto
WHERE d.clave_doc=p_clave
GROUP BY c.cod_rubro;
CURSOR c_datos IS
SELECT SUM(d.total_doc), 'D',r.cod_rubro
FROM fn_documentos d JOIN fn_cajas_ctas r ON d.num_caja_cta = r.num_caja_cta
WHERE d.clave_doc = p_clave
GROUP BY r.cod_rubro;
BEGIN
open c_datos;
LOOP
FETCH c_datos INTO v_detalle(1);
END LOOP;
CLOSE c_datos;
FOR fila IN c_facturado LOOP
i := i + 1;
v_detalle(i) := fila;
END LOOP;
END;
END PKG_CONTABILIDAD;
The function returns a pkg_contabilidad.t_detalle, so the test needs to be:
declare
resultao pkg_contabilidad.t_detalle;
numero number := 100;
begin
resultao := pkg_contabilidad.f_totalizar_detalle(numero);
end;
It doesn't work in SQL because pkg_contabilidad.t_detalle is a PL/SQL type, not a SQL type (create or replace type). The database can perform some automatic conversions, but there are still limitations.
By the way, this loop will never complete because it lacks an exit condition:
open c_datos;
loop
fetch c_datos into v_detalle(1);
end loop;
close c_datos;
Your function returns a PL/SQL table type, with a table of a PL/SQL record type, which is defined in your package, which plain SQL doesn't know about and can't display - hence your invalid datatype error. If you need to call the function and access the data from SQL you can create schema-level object and collection types instead.
In your anonymous block you are a declaring a new record type. That looks the same to you because the structure is the same, but Oracle expects the exact type the function returns. That makes your test code shorter and simpler though. But you are also trying to return the whole collection into a single record.
DECLARE
l_detalle pkg_contabilidad.t_detalle;
l_registro pkg_contabilidad.r_registro;
l_idx pls_integer;
numero NUMBER :=100;
BEGIN
l_detalle := pkg_contabilidad.f_totalizar_detalle(numero);
l_idx := l_detalle.FIRST;
WHILE l_idx is not null LOOP
l_registro := l_detalle(l_idx);
-- do something with this record
dbms_output.put_line(l_registro.tipo);
l_idx := l_detalle.NEXT(l_idx);
END LOOP;
END;
db<>fiddle with dummy cursors.
Your function is a bit strange and probably isn't doing quite what you want; but also has two fatal problems: it isn't returning anything, and it has an infinite loop. I've fixed those for the fiddle but not anything else, as this seems to be an exercise.

Bulk collect sql

i made a data_object by myself :
CREATE OR REPLACE TYPE my_object AS OBJECT(
number_type NUMBER,
varchar_type VARCHAR2(20)
)
and then i create type
CREATE OR REPLACE TYPE my_nt IS TABLE OF my_object;
And I want with nested table and this object make a procedure, that will be return number of employyes of some departments. I ve got Two tables: employees and department a this is my code:
DECLARE
enum_dname my_nt := my_nt();
PROCEDURE print_l IS
BEGIN
DBMS_OUTPUT.put_line('---------------------------------------------------------');
FOR i IN 1..enum_dname.COUNT
LOOP
DBMS_OUTPUT.PUT_LINE(enum_dname(i));
END LOOP;
END;
BEGIN
SELECT COUNT(emp_id) as number_of, department_name
BULK COLLECT INTO enum_dname
FROM employees e, department d
WHERE e.department_id = d.department_id
GROUP BY department_name;
print_l;
END;
And it show me errors : PLS - 00306: Wrong numbers of argument in call type: PUT_LINE
and PL\SQL : ORA - 00947:not enough values
THANK YOU!
You have two errors. As #SudiptaMondal pointed out (and as here) you can't pass an object to put_line(), you have to pass a single string value - or something which evaluates to a string, which can be concatenated or implicit converted or whatever. So here you might do:
DBMS_OUTPUT.PUT_LINE(enum_dname(i).varchar_type || ': ' || enum_dname(i).number_type);
or however you want to format that output. Using dbms_output for anything except debugging generally not a good idea as you have no control over whether someone using your code has output enabled. But this may be enough for this exercise.
The second problem, causing the ORA-00947, is because your query is trying to bulk collect two scalar variables into a collection of objects. You need to include the object constructor:
SELECT my_object(COUNT(emp_id), department_name)
BULK COLLECT INTO enum_dname
...

Creating a dynamic script from dynamic SQL

I've searched a lot of Q-A's but I haven't found quite what I'm looking for. I'm looking to create a procedure that generates a transport script (but does NOT execute it) for two different lookup tables in a dynamic way.
I've been able to create the sql select statement using:
DECLARE
testertable VARCHAR2(320) := 'LOOKUP_TABLE';
testerid VARCHAR2(320) := '999';
testerfield VARCHAR2(320) := 'id_no';
type nameArray IS TABLE OF VARCHAR2(60);
type typeArray IS TABLE OF VARCHAR2(60);
data_nameA nameArray := nameArray();
data_typeA typeArray := typeArray();
i NUMBER;
CURSOR c_curse IS SELECT column_name, data_type
FROM user_tab_columns
WHERE table_name = testertable;
i := 0;
FOR n IN c_curse LOOP
i := i + 1;
data_nameA.EXTEND(1);
data_typeA.EXTEND(1);
data_nameA(i) := n.column_name;
data_typeA(i) := n.data_type;
END LOOP;
From there, I'm able to create the select statement. Then I want to use that statement to open a query, and populate insert statements, using data_typeA(i) as a reference to make sure quotes are input as needed.
This is part of a larger transport function that I'm not allowed to modify, my task is to expand it to include these two lookup tables, which may or may not exist for each transported system.
Hopeful output would be a CLOB or other text field like this:
-- transport for lookup 'LOOKUP_TABLE'
Insert into LOOKUP_TABLE VALUES (value1, 'value2', value3), (value4, 'value5', value6), (value7, 'value8', value9);
Thank you.

how to insert into nested table collection using cursor plsql

I'm writing a function that needs to manipulate multiple rows at the same time and they need to be indexed. After several hours of reading about Oracle pl/sql I figured I could create a nested table kind of collection. Since I couldn't find a definitive answer and trial/error method takes way to long.
Here is question part:
QUESTION: What is the best practice to populate a nested table collection? Oracle PL/SQL
type partsTable is table of Parts_north_wing%rowtype;
pt PartsTable;
index number;
cursor pCursor is select * from Parts_north_wing;
begin
index := 1;
open pCursor;
loop
fetch pCursor into tempRow;
pt(index) := tempRow;
index := index + 1;
exit when pCursor%notfound;
end loop;
close pCursor;
A cursor FOR LOOP is almost always the best way to process rows in PL/SQL. It's simpler than the OPEN/FETCH/CLOSE method - no need to declare variables and manipulate cursors. It's also faster since it automatically bulk collects the results.
begin
for pt in
(
select parts_north_wing.*, rownum row_index
from parts_north_wing
) loop
--Do something here
null;
end loop;
end;
/
Try this. Hope this helps you to clear some of your concepts.
--Create a dummy object tyep
CREATE OR REPLACE TYPE av_obj
IS
OBJECT
(
ADD1 VARCHAR2(100),
ADD2 VARCHAR2(100) );
--Create a nested tale type
CREATE OR REPLACE TYPE AV_TT
IS
TABLE OF AV_OBJ;
--Bulk collect into nested table type
DECLARE
av_nested_tab AVROY.AV_TT;
BEGIN
SELECT avroy.av_obj(LEVEL
||'add1',LEVEL
||'add2') BULK COLLECT
INTO av_nested_tab
FROM DUAL
CONNECT BY LEVEL < 10;
END;

PL/SQL - How to use an array in an IN Clause

I'm trying to use an array of input values to my procedure in an IN Clause as part of the where clause of a cursor. I know that this has been asked before, but I haven't seen how to make my syntax compile correctly.
In the package specification, the type is
TYPE t_brth_dt IS TABLE OF sourceTable.stdt_brth_dt%TYPE INDEX BY PLS_INTEGER;
sourceTable.std_brth_dt is a date column in the table.
Simplified version of my cursor is in the package body is -
cursor DataCursor_Sort( p_brth_dt in t_brth_dt) is
SELECT *
FROM sourceTable
WHERE a.brth_dt IN (select column_value
from table(p_brth_dt))
When I try to compile this, I'm getting the following errors.
[1]:(Error): PLS-00382: expression is of wrong type
[2]:(Error): PL/SQL: ORA-22905: cannot access rows from a non-nested table item
I know this looks similar to other questions, but I don't understand what the syntax error is.
In order to use collection defined as a nested table or an associative array in the from clause of a query you either should, as #Alex Poole correctly pointed out, create a schema level (SQL) type or use one, that is available to you trough ODCIConst package - odcidatelist as you intend to use a list of dates. For example, your cursor definition might look like this:
cursor DataCursor_Sort(p_brth_dt in sys.odcidatelist) is
select *
from sourceTable
where a.brth_dt IN (select column_value
from table(p_brth_dt))
OR
cursor DataCursor_Sort(p_brth_dt in sys.odcidatelist) is
select s.*
from sourceTable s
join table(p_brth_dt) t
on (s.brth_dt = t.column_value)
Note: You should take into consideration the time part of a date when performing a date comparison. If you want to compare date part only it probably would be useful to get rid of time part by using trunc() function.
It is possible to use a PL/SQL-defined nested table type (as opposed to a SQL-defined nested table type) indirectly in an IN clause of a SELECT statement in a PL/SQL package. You must use a PIPELINED function as an intermediary. It felt kind of clever to write, but I don't believe in its fundamental usefulness.
CREATE OR REPLACE PACKAGE so18989249 IS
TYPE date_plsql_nested_table_type IS TABLE OF DATE;
dates date_plsql_nested_table_type;
FUNCTION dates_pipelined RETURN date_plsql_nested_table_type PIPELINED;
PROCEDURE use_plsql_nested_table_type;
END so18989249;
/
CREATE OR REPLACE PACKAGE BODY so18989249 IS
FUNCTION dates_pipelined RETURN date_plsql_nested_table_type
PIPELINED IS
BEGIN
IF (dates.count > 0)
THEN
FOR i IN dates.first .. dates.last
LOOP
IF (dates.exists(i))
THEN
PIPE ROW(dates(i));
END IF;
END LOOP;
END IF;
END;
PROCEDURE use_plsql_nested_table_type IS
BEGIN
dates := NEW date_plsql_nested_table_type();
-- tweak these values as you see fit to produce the dbms_output results you want
dates.extend(5);
dates(1) := DATE '2013-12-25';
dates(2) := DATE '2013-01-01';
dates(3) := DATE '2013-07-01';
dates(4) := DATE '2013-09-03';
dates(5) := DATE '2008-11-18';
FOR i IN (SELECT o.owner,
o.object_name,
o.object_type,
to_char(o.last_ddl_time, 'YYYY-MM-DD') AS last_ddl
FROM all_objects o
WHERE trunc(o.last_ddl_time) IN
(SELECT column_value FROM TABLE(dates_pipelined))
--uses pipeline function which uses pl/sql-defined nested table
)
LOOP
dbms_output.put_line('"' || i.owner || '"."' || i.object_name || '" ("' || i.object_type || ') on ' || i.last_ddl);
END LOOP;
END;
END so18989249;
/
begin so18989249.use_plsql_nested_table_type; end;
/
The type has to be created at SQL level, not in a package. An SQL query doesn't know how to use any types defined in PL/SQL. So you'd have to do:
CREATE OR REPLACE TYPE t_brth_dt IS TABLE OF date;
/
... and remove the type from your package specification. (Or give them different names, at least, and they won't be interchangeable in use). Because it's at SQL level, you also can't use sourceTable.stdt_brth_dt%TYPE in the declaration, unfortunately.

Resources