table column containing list of other table column - oracle

create table cdi(comp_id varchar2(3),pk_key varchar2(2000));
insert into cdi values('abc','empno,ename,job');
insert into cdi values('pqr','empno,job,mgr');
insert into cdi values('cde','empno,mgr,sal');
commit;
create table emp_test(empno integer,ename varchar2(200),job varchar2(200),mrg integer,sal integer);
insert into emp_test values(1,'Gaurav Soni','DB',12,12000);
insert into emp_test values(2,'Niharika Saraf','Law',13,12000);
insert into emp_test values(2,'Niharika Saraf','Law',13,12000);
insert into emp_test values(3,'Saurabh Soni',null,12,12000);
commit;
In cdi table comp_id is primary key
create or replace procedure test(p_comp_id IN cdi.comp_id%TYPE
,p_empno IN emp_test.empno%TYPE
)
IS
TYPE ref_cur is ref cursor;
v_cur ref_cur;
v_pk_key cdi.pk_key%TYPE;
BEGIN
OPEN v_cur is select pk_key from cdi where comp_id =p_comp_id;
fetch v_cur into v_pk_key;
--now this list v_pk_key is primary key for that comp_id
--so following things need to be done
--1.check the emp_test table with this column list (v_pk_key )
--2. whether for that emp_no the primary key is null eg.
-- incase of comp_id cde ...empno,mgr,sal value should be not null
--if any of the value is null raise an error
--3.If there are two rows for that primary also raise an error.
-- for eg comp_id=abc two rows are fetched from emp_test
close v_cur;
END;
I am not sure of the approach what should i do to,first i think of concatenating the v_pk_key like empno||ename||job and then used this in select query ,but not able to check for null values ,i am confused what to do .
EDIT
what i have tried was to convert the list v_pk_key to
NVL(empno,'$')||NVL(ename,'$')||NVL(job,'$') and then
select v_pk_list from emp_test where empno=p_empno;
and then check for $ in the result if there is no $ in the result i ll check for more than one row,but i am not finding this as an efficient solution
if anyone give me a jist of it ,i will solve this .

I would split out that list of values, which really represents 3 columns ('empno, ename, job'). Use instr function, or create a separate function to split and return a pl/sql table, but either way it would be much more clear what is intended in the code.
See here for a SO link to some examples using instr to split csv fields.
Once you have 3 separate local variables with these values (l_empno, l_ename, l_job), then you can use much easier in your various SQL statements (where l_empno = blah and l_ename not in (blahblah)), etc...

Related

ORA-01406 when copying data from one Oracle database to another with different Unicode

I have two identical tables: original_table, destination table in two different Oracle database.
-- Oracle 1
create table original_table
(
my_id NUMBER(11) not null,
my_fld CHAR(15),
)
-- Oracle 2
create table destination_table
(
my_id NUMBER(11) not null,
my_fld CHAR(15),
)
I'm copying data from original_table to destination_table using the procedure and a database link. Here is a pseudocode version.
PROCEDURE COPY_DATA AS
BEGIN
FOR c_cursor IN (SELECT my_id ,my_fld FROM original_table#dblink)
LOOP
INSERT INTO destination_table
VALUES (c_cursor.my_id, c_cursor.my_fld);
END LOOP;
END;
Sometimes Oracle throws ERROR, when special character is inserted in original_table.my_fld column.
ORA-01406: fetched column value was truncated
This is because those two databases have different Unicode and I'm selecting data in LOOP. I tried to write select-insert statement outside of LOOP and it worked fine.
Can you tell me how to fix this problem?
If you just want to copy all data from one table to another u don t need cursor u can do it with sql inside a procedure. Try it hope it helps...
PROCEDURE COPY_DATA AS
BEGIN
INSERT INTO [database].[schema].destination_table (column_list)
SELECT column_list
FROM [database].[schema].original_table
WHERE condition;
END;
Select and insert the data row-by-row is basically the slowest way you can do it. Use this one:
INSERT INTO destination_table (my_id ,my_fld)
SELECT my_id ,my_fld
FROM original_table#dblink;
I used UNISTR function for my string field.
FOR c_cursor IN (SELECT my_id ,UNISTR(my_fld) FROM original_table#dblink)
LOOP
INSERT INTO destination_table
VALUES (c_cursor.my_id, c_cursor.my_fld);
END LOOP;
It fixed the problem.

how to use parametrized cursor in another cursor using bulk collect and forall

The existed logic is taking data from CUST_MSTR table and inserting in customer_mac_feed using BULK COLLECT and FORALL.
Now the requirement in addition to the existed logic data needs to fetched from CUST_RELT table based on cust_gpid and cust_confid of CUST_MSTR table . i.e. c_cust_mstr cursor.
CREATE OR REPLACE PROCEDURE proc_parameterized_cur_bulk
AS
CURSOR c_cust_mstr IS
SELECT
a.cust_modi_date,
a.cust_id,
a.cust_gpid,
a.cust_confid,
b.sch_id,
b.sch_date,
c.c_name
FROM CUST_MSTR a,
CUST_SCH b,
CUST_REPO c
WHERE a.cust_gpid=b.cust_gpid
and b.B_flag=c.C_flag;
p_cust_gpid VARCHAR2(8);
p_cust_confid NUMBER;
**CURSOR c_CUST_RELT(p_cust_gpid VARCHAR2,p_cust_confid NUMBER) IS
SELECT relt_custid FROM CUST_RELT
WHERE relt_cust_gpid=p_cust_gpid and relt_cust_confid=p_cust_confid;**
TYPE v_cust_mstr IS TABLE OF c_cust_mstr%ROWTYPE;
v_cust_mstr_TYPE v_cust_mstr;
**TYPE v_cust_relt IS TABLE OF c_CUST_RELT%ROWTYPE;
v_cust_relt_TYPE v_cust_relt;**
BEGIN
v_cust_mstr_TYPE :=v_cust_mstr();
v_cust_relt_TYPE :=v_cust_relt();
OPEN c_cust_mstr;
LOOP
FETCH c_cust_mstr bulk collect INTO v_cust_mstr_TYPE limit p_cccnt;
V_RECORDCNT :=V_RECORDCNT + v_cust_mstr_TYPE.COUNT;
BEGIN
FORALL REC IN 1..v_cust_mstr_TYPE.COUNT SAVE EXCEPTIONS
INSERT
INTO customer_mac_feed
(
MODIDATE,
cust_id,
gpid,
CONFIGID,
schid,
schdate,
cname
)
VALUES
(
v_cust_mstr_TYPE(REC).cust_modi_date,
v_cust_mstr_TYPE(REC).cust_id,
v_cust_mstr_TYPE(REC).cust_gpid,
v_cust_mstr_TYPE(REC).cust_confid,
v_cust_mstr_TYPE(REC).sch_id,
v_cust_mstr_TYPE(REC).sch_date,
v_cust_mstr_TYPE(REC).c_name
);
COMMIT;
**OPEN c_CUST_RELT(v_cust_mstr_TYPE(REC).cust_gpid,v_cust_mstr_TYPE(REC).cust_confid);
LOOP
FETCH c_CUST_RELT bulk collect INTO v_cust_relt_TYPE limit 100;
FORALL i IN 1..v_cust_relt_TYPE.COUNT SAVE EXCEPTIONS
INSERT
INTO customer_mac_feed
(
MODIDATE,
cust_id,
gpid,
CONFIGID,
schid,
schdate,
cname
)
VALUES
(
v_cust_mstr_TYPE(REC).cust_modi_date,
v_cust_relt_TYPE(i).relt_custid,
v_cust_mstr_TYPE(REC).cust_gpid,
v_cust_mstr_TYPE(REC).cust_confid,
v_cust_mstr_TYPE(REC).sch_id,
v_cust_mstr_TYPE(REC).sch_date,
v_cust_mstr_TYPE(REC).c_name
);
EXIT WHEN c_CUST_RELT%NOTFOUND;
END LOOP;
CLOSE c_CUST_RELT;**
EXIT WHEN c_cust_mstr%NOTFOUND;
END LOOP;
CLOSE c_cust_mstr;
EXCEPTION
WHEN OTHERS THEN
RAISE;
END;
END proc_parameterized_cur_bulk;
Now the requirement in addition to the existed logic data needs to fetched from CUST_RELT table based on cust_gpid and cust_confid of CUST_MSTR table.
If data is existed for these two combination of columns in CUST_RELT table then take only cust_id from CUST_RELT table
and take remaining columns from CUST_MSTR table and insert in CUSTOMER_MAC_FEED table.
Source Tables:
CREATE TABLE CUST_MSTR
(
cust_gpid VARCHAR2(10),
cust_confid NUMBER,
cust_id VARCHAR2(10), cust_modi_date DATE,
cust_account varchar2(11)
);
CREATE TABLE CUST_SCH
(
cust_gpid VARCHAR2(10),
sch_id NUMBER,
sch_date DATE,
B_flag VARCHAR2(1)
);
CREATE TABLE CUST_REPO
(
c_name VARCHAR2(20),
C_flag VARCHAR2(1)
);
CREATE TABLE CUST_RELT
(
relt_cust_gpid VARCHAR2(10),
relt_cust_confid NUMBER,
relt_custid VARCHAR2(10),
relt_ownername VARCHAR2(20),
relt_ownerloc VARCHAR2(10)
);
Target Table:
CREATE TABLE CUSTOMER_MAC_FEED
(
modidate DATE,
cust_id VARCHAR2(10),
gpid VARCHAR2(10),
configid NUMBER,
schid NUMBER,
schdate DATE,
cname VARCHAR2(20)
);
Insert Statements:
INSERT INTO CUST_MSTR VALUES('AP1122',572072,'A1',TO_DATE('12-MAR-2021','DD-MON-YYYY'),334455671);
INSERT INTO CUST_MSTR VALUES('AP1122',571010,'B1',TO_DATE('18-MAR-2021','DD-MON-YYYY'),334455672);
INSERT INTO CUST_MSTR VALUES('AP1122',674521,'B1',TO_DATE('15-MAR-2021','DD-MON-YYYY'),334455673);
INSERT INTO CUST_MSTR VALUES('AP1122',457534,'C1',TO_DATE('01-MAR-2021','DD-MON-YYYY'),334455674);
INSERT INTO CUST_MSTR VALUES('ST5577',271956,'C1',TO_DATE('14-MAR-2021','DD-MON-YYYY'),334455860);
INSERT INTO CUST_MSTR VALUES('ST5577',271957,'B1',TO_DATE('08-MAR-2021','DD-MON-YYYY'),334455861);
INSERT INTO CUST_MSTR VALUES('KK6688',234981,'A1',TO_DATE('11-MAR-2021','DD-MON-YYYY'),334455771);
INSERT INTO CUST_MSTR VALUES('MR7700',360787,'C1',TO_DATE('10-MAR-2021','DD-MON-YYYY'),334455571);
INSERT INTO CUST_SCH VALUES('AP1122',111,TO_DATE('12-FEB-2021','DD-MON-YYYY'),'Y');
INSERT INTO CUST_SCH VALUES('ST5577',222,TO_DATE('14-FEB-2021','DD-MON-YYYY'),'Y');
INSERT INTO CUST_SCH VALUES('KK6688',444,TO_DATE('11-FEB-2021','DD-MON-YYYY'),'Y');
INSERT INTO CUST_SCH VALUES('MR7700',888,TO_DATE('10-FEB-2021','DD-MON-YYYY'),'Y');
INSERT INTO CUST_REPO VALUES('C_COMMON','Y');
INSERT INTO CUST_RELT VALUES('AP1122',457534,'B2','ALEN','USA');
INSERT INTO CUST_RELT VALUES('ST5577',271956,'C2','KHAN','UK');
INSERT INTO CUST_RELT VALUES('ST5577',271957,'C2','BEN','USA');
INSERT INTO CUST_RELT VALUES('KK6688',437692,'B2','JACK','AUS');
INSERT INTO CUST_RELT VALUES('MR7700',360787,'B2','MUSK','AUS');
Expected output in the target table CUSTOMER_MAC_FEED as below.
Note : Showing only few columns
AP1122 572072 A1 12-MAR-2021
AP1122 571010 B1 18-MAR-2021
AP1122 674521 B1 15-MAR-2021
AP1122 457534 C1 01-MAR-2021
AP1122 457534 B2 01-MAR-2021
ST5577 271956 C1 14-MAR-2021
ST5577 271957 B1 08-MAR-2021
ST5577 271956 C2 14-MAR-2021
ST5577 271957 C2 08-MAR-2021
KK6688 234981 A1 11-MAR-2021
MR7700 360787 C1 10-MAR-2021
MR7700 360787 B2 10-MAR-2021
Please help me.
Thanks in advance.
Your procedure has several issues, some minor but a couple quite serious. In the section below I break the procedure and where there are issues.
create or replace procedure proc_parameterized_cur_bulk as
cursor c_cust_mstr is
select
a.cust_modi_date,
a.cust_id
from cust_mstr a
join cust_sch b on a.cust_gpid=b.cust_gpid
join cust_repo c on b.b_flag=c.c_flag;
p_cust_gpid varchar2(8);
p_cust_confid number;
cursor c_cust_relt(p_cust_gpid varchar2,p_cust_confid number) is
select relt_custid from cust_relt
where relt_cust_gpid=p_cust_gpid and relt_cust_confid=p_cust_confid;
Minor Issue. The above variables, p_cust_gpid and p_cust_confid, are unnecessary. The names are the same as those in the in the cursor following then. But they are not the same variable as they are in a different scope. Formal cursor parameters defined are local to the that cursor. According to cursor scoping rules:
The scope of cursor parameters is local to the cursor, meaning that
they can be referenced only within the query used in the cursor
declaration. The values of cursor parameters are used by the
associated query when the cursor is opened.
(Emphasis mine)
Since formal cursor variable override the other declaration they are not used else where the are not needed.
IMHO: Parameters do not have to be p_ that is just a convension. My convention, cursor parameters are indicated as c_. Staying with p_ is not incorrect I just find c_ more descriptive. Continuing:
type v_cust_mstr is table of c_cust_mstr%rowtype; --<IMHO keep type definition based on cursor close to cursor.
v_cust_mstr_type v_cust_mstr;
type v_cust_relt is table of c_cust_relt%rowtype; --<IMHO keep type definition based on cursor close to cursor.
v_cust_relt_type v_cust_relt;
begin
v_cust_mstr_type :=v_cust_mstr();
v_cust_relt_type :=v_cust_relt();
open c_cust_mstr;
loop --- outer loop
fetch c_cust_mstr bulk collect into v_cust_mstr_type limit p_cccnt; --<<< p_cccnt undefined
v_recordcnt :=v_recordcnt + v_cust_mstr_type.count;
--<<< what's the point? v_recordcnt is undefined and not used elsewhere
begin -- inner block
forall rec in 1..v_cust_mstr_type.count save exceptions
insert
into customer_mac_feed
(
modidate,
cust_id
)
values
(
v_cust_mstr_type(rec).cust_modi_date,
v_cust_mstr_type(rec).cust_id
);
commit;
DO NOT COMMIT here.
You are in the middle of your transaction. What happens when an error occurs later in the code. How do you restart/recover? Further in this you does not commit anywhere. BASIC rule to follow: 1 commit at conclusion of procedure, and only 1 commit. There are time this needs to be set aside, but it should be the rare exception as a last resort. And make sure restart/recover process is well documented. Continuing:
open c_cust_relt(v_cust_mstr_type(rec).cust_gpid,v_cust_mstr_type(rec).cust_confid);
Invalid reference to REC. It is only defined as the index variable of the forall. According to the forall scoping rules
Name for the implicitly declared integer variable that is local to the
FORALL statement. Statements outside the FORALL statement cannot
reference index. Statements inside the FORALL statement can reference
index as an index variable, but cannot use it in expressions or change
its value. After the FORALL statement runs, index is undefined.
Containing:
loop
fetch c_cust_relt bulk collect into v_cust_relt_type limit 100;
forall i in 1..v_cust_relt_type.count save exceptions
insert
into customer_mac_feed
(
modidate,
cust_id
)
values
(
v_cust_mstr_type(rec).cust_modi_date,
v_cust_relt_type(i).relt_custid
);
exit when c_cust_relt%notfound;
end loop;
close c_cust_relt;
exit when c_cust_mstr%notfound;
Your control (following) flow completely breaks down at this point.
You have an outer loop and inside that loop an inner block (begin) However, you attempt to close (end loop) that loop before closing the block. You must either close the inner block first or remove it.
Your exception, as it stands is useless. The only thing it accomplishes is distort the line number that an error actually occurred. It will report the line number of of RAISE. Making finding the problem more difficult. Perhaps this is just a simplification gone wrong.
Further more you use "forall ... save exceptions" twice, but never attempt to do anything with any exceptions saved.
Continuing:
end loop; -- outer loop
close c_cust_mstr;
exception
when others then
raise;
end; --inner block
end proc_parameterized_cur_bulk;
I have put together a fiddle showing 3 versions.
This is what I believe is a corrected version but essentially
maintaining the format of your orig. I did add labels to indicate
code start/end segments. Labels are never required but sometimes
very useful.
This is a consolidation and simplification of the first. By JOINing
the cust_relt table you can get the relt_custid in the mast_cust
cursor. Then use forall twice using the same collection. No iterating
the collection and selecting each time through just to get 1 column
of additional data.
Finally, if your error rate is low, a version that consists of just 2
insert statements - that all. However it is 1 error and out.

Create insert record dynamically by changing pk of existing record for passed in table

I want to pass a table name and schema into a procedure, and have it generate insert, update and delete statements for the particular table. This is part of an automated testing solution (in a development environment) in which I need to test some change data capture. I want to make this dynamic as it is going to be need to be done for lots of different tables over a long period of time, and I need to call it via a REST request through ORDS, so don't want to have to make an endpoint for every table.
Update and delete are fairly easy, however I am struggling with the insert statement. Some of the tables being passed in have hundreds of columns with various constraints, fks etc. so I think it makes sense to just manipulate an existing record by changing only the primary key. I need to be able to modify the primary key to a new value known to me beforehand (e.g. '-1').
Ideally I would create a dynamic rowtype, and select into where rownum = 1, then loop round the primary keys found from all_constraints, and update the rowtype.pk with my new value, before inserting this into the table. Essentially the same as this but without knowing the table in advance.
e.g. rough idea
PROCEDURE manipulate_records(p_owner in varchar2, p_table in varchar2)
IS
cursor c_pk is
select column_name
from all_cons_columns
where owner = p_owner
and constraint_name in (select constraint_name
from all_constraints
where table_name = p_table
and constraint_type = 'P');
l_row tbl_passed_in%ROWTYPE --(I know this isn't possible but ideally)
BEGIN
-- dynamic sql or refcursor to collect a record
select * into tbl_passed_in from tablename where rownum = 1;
-- now loop through pks and reassign their values to my known value
for i in c_pk loop
...if matches then reassign;
...
end loop;
-- now insert the record into the table passed in
END manipulate_records;
I have searched around but haven't found any examples which fit this exact use case, where an unknown column needs to be modified and insert into a table.
Depending on how complex your procedure is, you might be able to store it as a template in a CLOB. Then pull it in, replace table and owner, then compile it.
DECLARE
prc_Template VARCHAR2(4000);
vc_Owner VARCHAR2(0008);
vc_Table VARCHAR2(0008);
BEGIN
vc_Table := 'DUAL';
vc_Owner := 'SYS';
-- Pull code into prc_Template from CLOB, but this demonstrates the concept
prc_Template := 'CREATE OR REPLACE PROCEDURE xyz AS r_Dual <Owner>.<Table>%ROWTYPE; BEGIN NULL; END;';
prc_Template := REPLACE(prc_Template,'<Owner>',vc_Owner);
prc_Template := REPLACE(prc_Template,'<Table>',vc_Table);
-- Create the procedure
EXECUTE IMMEDIATE prc_Template;
END;
Then you have the appropriate ROWTYPE available:
CREATE OR REPLACE PROCEDURE xyz AS r_Dual SYS.DUAL%ROWTYPE; BEGIN NULL; END;
But you can't create the procedure and run it in the same code block.

Create a procedure to copy some records from a table to another

I'm trying to create a simple procedure to copy some records from Table1 to Table2.
Table1:
id number PK
operation varchar2(50)
position varchar2(50)
code_operation varchar2(50) FK
Table2:
code_operation varchar2(50) PK
operation varchar2(50)
position varchar2(50)
client_number varchar2(50)
Starting from the client_number I have to copy the associated operation and position from Table1 and insert into operation and position of Table2.
I've tried this code but it doesn't work:
CREATE OR REPLACE PROCEDURE COPY_DATA(
BEGIN
DECLARE P_CLIENT_NUMBER VARCHAR2(50);
DECLARE P_CODE_OPERATION VARCHAR2(50);
DECLARE P_DIVISION VARCHAR2(50);
DECLARE P_POSITION VARCHAR2(50)
SELECT CODE_OPERATION INTO P_CODE_OPERATION FROM TABLE2;
SELECT CLIENT_NUMBER INTO P_CLIENT_NUMBER FROM TABLE2;
SELECT DIVISION INTO P_DIVISION FROM TABLE1;
SELECT POSITION INTO P_POSITION FROM TABLE1;
INSERT INTO TABLE2(DIVISION,POSITION)
WHERE CODE_OPERATION=P_COD_OPERATION;
END
);
I've got this error ERROR PLS-00103: Encountered the symbol “DECLARE” but I don't understand why, plus with this error I don't know if my code is correct or not.
Error you got is easy to fix. PL/SQL block has declare-begin-exception-end structure, which means that you first declare variables (in a stored procedure, you don't use declare keyword; strange enough, for triggers - which as kind of stored as well - you do use it). For example:
create or replace procedure copy_data as
p_client_number table2.client_number%type;
begin
select client_number
into p_client_number
from table2;
exception
when too_many_rows then
raise;
end;
However, without WHERE clause (or an aggregate, such as MIN or MAX), this will raise too_many_rows exception because it'll try to fetch all client numbers into a scalar variable, and that won't work. I included the way which shows how to handle it. What will you really do? Restrict number of rows to 1. Similarly, you'd handle no_data_found or other exceptions.
What you described looks strange to me.
you want to insert into table2 - which is a master table
values you'd insert are in table1 - which is a detail table
Looking at tables, I'd say that it should be vice versa.
insert into table1 (id, operation, position, code_operation)
select seq.nextval,
b.operation,
b.position,
b.code_operation
from table2 b
where b.client_number = 'ABC';
(I presumed that primary key is populated via a sequence.)
It also means that none of variables you declared is necessary. But, the procedure would accept client number as a parameter.
The whole procedure would then be:
create or replace procedure copy_data
(par_client_number in table2.client_number%type)
is
begin
insert into table1 (id, operation, position, code_operation)
select seq.nextval,
b.operation,
b.position,
b.code_operation
from table2 b
where b.client_number = par_client_number;
end;
You'd call it as
begin
copy_data('ABC');
end;
/

Is it possible to return the Primary Key on an Insert as select statement - Oracle?

So I usually get the Primary Key of a newly inserted record as the following while using a trigger.
insert into table1 (pk1, notes) values (null, "Tester") returning pk1
into v_item;
I am trying to use the same concept but with an insert using a select statement. So for example:
insert into table1 (pk1, notes) select null, description from table2 where pk2 = 2 returning pk1
into v_item;
Note:
1. There is a trigger on table1 which automatically creates a pk1 on insert.
2. I need to use a select insert because of the size of the table that is being inserted into.
3. The insert is basically a copy of the record, so there is only 1 record being inserted at a time.
Let me know if I can provide more information.
I don't believe you can do this with insert/select directly. However, you can do it with PL/SQL and FORALL. Given the constraint about the table size, you'll have to balance memory usage with performance using l_limit. Here's an example...
Given this table with 100 rows:
create table t (
c number generated by default as identity,
c2 number
);
insert into t (c2)
select rownum
from dual
connect by rownum <= 100;
You can do this:
declare
cursor t_cur
is
select c2
from t;
type t_ntt is table of number;
l_c2_vals_in t_ntt;
l_c_vals_out t_ntt;
l_limit number := 10;
begin
open t_cur;
loop
fetch t_cur bulk collect into l_c2_vals_in limit l_limit;
forall i in indices of l_c2_vals_in
insert into t (c2) values (l_c2_vals_in(i))
returning c bulk collect into l_c_vals_out;
-- You have access to the new ids here
dbms_output.put_line(l_c_vals_out.count);
exit when l_c2_vals_in.count < l_limit;
end loop;
close t_cur;
end;
You can't use that mechanism; as shown in the documentation railroad diagram:
the returning clause is only allowed with the values version, not with the subquery version.
I'm interpreting your second restriction (about 'table size') as being about the number of columns you would have to handle, possibly as individual variables, rather than about the number of rows - I don't see how that would be relevant here. There are ways to avoid having lots of per-column local variables though; you could select into a row-type variable first:
declare
v_item number;
v_row table1%rowtype;
begin
...
select null, description
into v_row
from table2 where pk2 = 2;
insert into table1 values v_row returning pk1 into v_item;
dbms_output.put_line(v_item);
...
or with a loop, which might make things look more complicated than necessary if you really only ever have a single row:
declare
v_item number;
begin
...
for r in (
select description
from table2 where pk2 = 2
)
loop
insert into table1 (notes) values (r.description) returning pk1 into v_item;
dbms_output.put_line(v_item);
...
end loop;
...
or with a collection... as #Dan has posted while I was answering this so I won't repeat! - though again that might be overkill or overly complicated for a single row.

Resources