How to insert into oracle custom object from select result? - oracle

I have a obj like below
CREATE TYPE SOME_CUSTOM_OBJ FORCE AS OBJECT(
ID number,
Name varchar(30)
)
and want to insert value in that, I read the document for oracle database, but only way that I found is use initial function.
DECLARE
SOME_OBJ SOME_CUSTOM_OBJ;
BEGIN
SOME_OBJ := SOME_CUSTOM_OBJ(1,'PETER');
DBMS_OUTPUT.PUT_LINE(SOME_OBJECT.ID);
END;
// OUTPUT : 1
I want to insert those value from another table, the imagine like below
DECLARE
SOME_OBJECT SOME_CUSTOM_OBJECT;
BEGIN
SOME_OBJECT := SOME_CUSTOM_OBJECT(
SELECT ID,NAME FROM ANTHER_TABLE WHERE {SOME CONDITIONS} AND rownum = 1
);
DBMS_OUTPUT.PUT_LINE(SOME_OBJECT.ID);
END;
// OUTPUT : 1
The value who select from table would be only 1 row.
If use Object_table could be easy to solve my question, but I don't want to add too much things in the database.

You can use the object constructor in the SELECT clause:
DECLARE
SOME_OBJECT SOME_CUSTOM_OBJ;
BEGIN
SELECT some_custom_obj(ID,NAME)
INTO some_object
FROM another_table
WHERE rownum = 1;
DBMS_OUTPUT.PUT_LINE(SOME_OBJECT.ID);
END;
/
Which, for the sample data:
CREATE TABLE another_table (id, name) AS
SELECT 1, 'Alice' FROM DUAL;
Outputs:
1
db<>fiddle here

Related

pl sql insert into within a procedure and dynamic variables

I need some help with PL SQL. I have to insert some data into table. Another application is calling my procedure and caller is passing few details which I also need to insert into my table.
here is the syntax I am struggling with:
PROCEDURE invform_last2orders_item_insert( p_userId IN NUMBER
,p_accountId IN NUMBER
,p_site_Id IN NUMBER
,p_return_message OUT VARCHAR2) IS
Begin
insert into mytable
(p_userId , p_accountId , p_site_Id , sku, description, 'Cart', 1, unitId)
as
select sku, description, unitId
from mycatalogtable where site_id= p_site_Id ) ;
End;
Can you help me with syntax? I need to pass three parameters from called in parameter and some values returned from select query. How can I achieve this?
thank you for your help.
That would be something like this; see comments within code:
PROCEDURE invform_last2orders_item_insert
( p_userId IN NUMBER
,p_accountId IN NUMBER
,p_site_Id IN NUMBER
,p_return_message OUT VARCHAR2)
IS
Begin
insert into mytable
-- first name all columns you'll be inserting into; I don't know their
-- names so I just guessed
(userid,
accountid,
siteid,
sku,
description,
col1,
col2,
unitid
)
-- if you were to insert only values you got via parameters, you'd use the
-- VALUE keyword and insert those values separately.
-- As some of them belong to a table, use SELECT statement
(select p_userid,
p_accountid,
p_siteid,
c.sku,
c.description,
'Cart',
1,
c.unitid
from mycatalogtable c
where c.site_id = p_site_Id
);
-- I don't know what you are supposed to return; this is just an example
p_return_message := sql%rowcount || ' row(s) inserted';
End;
in your select statement you should have the same number of columns as you are inserting into the table, your code should be something like this example,
DECLARE
userid varchar2(20) := 'Jack';
Begin
INSERT INTO mytable (SELECT userid, SPORT from OLYM.OLYM_SPORTS);
commit;
end;

Variables in PLSQL

I want to get the value defined in my Procedure as mentioned below.
declare
city varchar2(50) := 'XYZ';
TYPE table_type is table of table_name%rowtype;
var table_type;
begin
select * bulk collect into var from table_name;
DBMS_OUTPUT.PUT_LINE(var(1).field); -- Output is City
I want the output of
DBMS_OUTPUT.PUT_LINE(var(1).field); -- Output is XYZ
How can I achieve this?
First of all, your code does not work. Second, if you are going to recover a variable, you don't need bulk collect
Example
create table mytest ( c1 varchar2(50) ;
insert into mytest values ( 'XYZ' );
commit;
Then
SQL> set serveroutput on
declare
vcity mytest.c1%type := 'XYZ';
var mytest.c1%type;
begin
select c1 into var from mytest where c1=vcity;
DBMS_OUTPUT.PUT_LINE('vcity is '||vcity||' ');
DBMS_OUTPUT.PUT_LINE('var is '||var||' ');
end;
/SQL> 2 3 4 5 6 7 8 9
vcity is XYZ
var is XYZ
PL/SQL procedure successfully completed.
SQL>
vcity is just a variable defined with the type of the column c1, but
assigned to a constant, in this case 'XYZ'
var is the variable which I use to recover the value of c1 in the
table, whatever that value is.
You don't need bulk collect here at all.
Assuming you have several cities to list, you need to define a collection to house them once selected, then a variable of that collection. So for example:
--setup
create table table_name ( id integer
, city varchar2(10)
);
insert into table_name(id, city)
select level, 'City #' || trunc(dbms_random.value( 10, 75 ))
from dual connect by level <= 10;
-- process
declare
type city_list is table of table_name%rowtype;
var city_list;
begin
select *
bulk collect
into var
from table_name;
for r in 1 .. var.count
loop
dbms_output.put_line(var(r).city); -- Output is City
end loop;
end;
Let's keep remain mystery about why you want like this,
If I understood you , to achieve what you want you have to specify columns instead of '*' and replace the variable city with column field.
Dbfiddle link for your reference (unable to provide via link through mobile device)
https://dbfiddle.uk/?rdbms=oracle_18&fiddle=20f1a1fff11d4acda7acc701ad120d32
DECLARE
city varchar2(50) := 'XYZ';
TYPE table1_type is table of table1%rowtype;
var table1_type;
BEGIN
select city,field2,field3,field4
bulk collect into var
from table1;
DBMS_OUTPUT.PUT_LINE(var(1).field);
END;
/

How does Oracle Insert Into work when order of values is not defined?

I came across some code that looks like this. I understand that it will return the auto-generated id, but what I don't understand is when I pass cursor data when I call this function, how does it identify what values are to be inserted in which columns when the column order is not defined?
FUNCTION INSERT_ROW(DATA IN OWNER.TABLE%ROWTYPE)
RETURN OWNER.TABLE.ID%TYPE
IS
l_ID OWNER.MY_TABLE.ID%TYPE;
l_Data OWNER.MY_TABLE%ROWTYPE := DATA;
BEGIN
INSERT INTO OWNER.MY_TABLE
VALUES l_Data
RETURNING ID INTO l_ID;
I tried to look up many examples and I only come across ones where the values are defined in order like this
INSERT INTO my_table (val2, val3, val4) VALUES (2, 3, 4) RETURNING val1
INTO val1;
The order of columns in a table in Oracle IS defined. Take a look at the ALL_TAB_COLUMNS view - there's a COLUMN_ID column which defines the order of columns within the table. If a field list is not given in a SELECT (i.e. SELECT * FROM MY_TABLE) the columns from MY_TABLE will be returned in ALL_TAB_COLUMNS.COLUMN_ID order. This is also the same way columns are ordered in a %ROWTYPE variable, and it's the way that an INSERT which doesn't have a field list specified expects fields to be ordered.
The insert values statement in your code is a PL/SQL extension to the standard insert values clause that has parentheses. This is a page from the 12.2 manual about this topic:
https://docs.oracle.com/en/database/oracle/oracle-database/12.2/lnpls/INSERT-statement-extension.html#GUID-D81224C4-06DE-4635-A850-41D29D4A8E1B
The OWNER.TABLE%ROWTYPE data type defines a record with the same columns as the table and in the same order. You are just passing the data into the function in that format and passing it into a variable and then into the insert statement.
The main purpose of the RETURNING clause is to obtain the value of a derived column, a value which is generated during the insert process. Usually this is a technical primary key derived from a sequence, or since 12c an IDENTITY column.
So for instance:
create table my_table (
val1 number generated as identity primary key
, val2 varchar2(16)
, val3 varchar2(16)
, val4 date)
/
declare
id number;
begin
INSERT INTO my_table (val2, val3, val4)
VALUES ('one', 'test', sysdate)
RETURNING val1 INTO id;
dbms_output.put_line('new id = ' || id);
end;
/
This is why the examples you found specify columns in the INSERT projection: the value of the primary key is generated automatically, so there's no point in us assigning it a value in our code.
Now your function uses a record type in its insert statement. We can't do that with IDENTITY columns. This variant ...
declare
lrec my_table%rowtype;
id number;
begin
lrec.val2 := 'two';
lrec.val3 := 'test again';
lrec.val4 := sysdate;
INSERT INTO my_table
VALUES lrec
RETURNING val1 INTO id;
dbms_output.put_line('new id = ' || id);
end;
/
... will hurl
ORA-32795: cannot insert into a generated always identity column
But we can use a %rowtype with the old-fashioned sequence and trigger combo:
create table my_table (
val1 number primary key
, val2 varchar2(16)
, val3 varchar2(16)
, val4 date)
/
create sequence my_seq start with 42;
create or replace trigger my_trg
before insert on my_table for each row
begin
:new.val1 := my_seq.nextval;
end;
/
declare
lrec my_table%rowtype;
id number;
begin
lrec.val1 := 1;
lrec.val2 := 'three';
lrec.val3 := 'test again';
lrec.val4 := sysdate;
INSERT INTO my_table
VALUES lrec
RETURNING val1 INTO id;
dbms_output.put_line('new id = ' || id);
end;
/
Here is a LiveSQL demo (free Oracle OTN account required, alas). If you run it you will see that the trigger overrides the assigned value and the val1 column has the value from the sequence.

oracle - multiple insert into type table collection

I have created the following object in oracle 11g.
CREATE OR REPLACE TYPE myObject as object(
fieldOne number,
fieldTwo number
);
And created a new table type of myObject;
CREATE OR REPLACE TYPE myTable IS TABLE OF myObject;
I would now like to create a new instance of myTable and add several hard-coded rows to myTable on the SQL Plus command line then pass the object to myProcedure as a parameter.
I have tried the following;
declare newTable myTable;
begin
select myObject(50,5) bulk collect into newTable from dual;
select myObject(40,7) bulk collect into newTable from dual;
myProcedure(newTable);
commit;
end;
Which sort-of works although the second select into statement overwrites the first.
My question is; how can I add multiple rows to newTable?
Many Thanks in Advance :)
declare
newTable myTable;
begin
newTable := myTable();
newTable.extend(2); -- The desired size of the collection
-- Oracle collections begin at index 1, not 0
newTable(1) := myObject(50, 5);
newTable(2) := myObject(40, 7);
myProcedure(newTable);
end;

How to return a Cursor for pl/sql table

I select data from several tables. Then i need to edit the data returned from the cursor before returning. The cursor will then be passed to a perl script to display the rows.
To that i build a pl/sql table as in the following code. What i need to know is how to return the to that table ?
At present i get the error "table or view doesn't exist". Test code i use for a simple table is attached here.
CREATE OR REPLACE FUNCTION test_rep
RETURN SYS_REFCURSOR
AS
CURSOR rec_Cur IS
SELECT table1.NAME,
table1.ID
FROM TESTREPORT table1;
TYPE rec_Table IS TABLE OF rec_Cur%ROWTYPE INDEX BY PLS_INTEGER;
working_Rec_Table rec_Table;
TYPE n_trade_rec IS RECORD
(
NAME VARCHAR2(15),
ID NUMBER
);
TYPE ga_novated_trades IS TABLE OF n_trade_rec index by VARCHAR2(15);
va_novated_trades ga_novated_trades;
v_unique_key VARCHAR2(15);
TYPE db_cursor IS REF CURSOR;
db_cursor2 db_cursor;
BEGIN
OPEN rec_Cur;
FETCH rec_Cur BULK COLLECT INTO working_Rec_Table;
FOR I IN 1..working_Rec_Table.COUNT LOOP
v_unique_key := working_Rec_Table(I).NAME;
va_novated_trades(v_unique_key).NAME := working_Rec_Table(I).NAME;
va_novated_trades(v_unique_key).ID := working_Rec_Table(I).ID;
END LOOP; --FOR LOOP
OPEN db_cursor2 FOR SELECT * FROM va_novated_trades; --ERROR LINE
CLOSE rec_Cur;
RETURN db_cursor2;
END test_rep;
/
Basically there is a way to select from a table type in oracle using the TABLE() function
SELECT * FROM table(va_novated_trades);
But this works only for schema table types and on plsql tables (table types defined in the SCHEMA and not in a plsql package):
CREATE TYPE n_trade_rec AS OBJECT
(
NAME VARCHAR2(15),
ID NUMBER
);
CREATE TYPE ga_novated_trades AS TABLE OF n_trade_rec;
But I still think you should try to do it all in a query (and/or in the perl script),
For example, there is one field where i have to analyse the 4th
character and then edit other fields accordingly
This can be achieved in the query, could be something like:
select case when substr(one_field, 4, 1) = 'A' then 'A.' || sec_field
when substr(one_field, 4, 1) = 'B' then 'B.' || sec_field
else sec_field
end as new_sec_field,
case when substr(one_field, 4, 1) = 'A' then 100 * trd_field
when substr(one_field, 4, 1) = 'B' then 1000 * trd_field
else trd_field
end as new_trd_field,
-- and so on
from TESTREPORT

Resources