oracle pl/sql issue on procedure way of execution - oracle

I have a scenario I have a table t1 which has two table names they are san and man. And now the two tables san, man each table has multiple table filenames like table san has two file names (audi.txt ,mercedes.txt) and the second table man has one file name (hundai.txt). I wrote a procedure where it can return number of rows in the which are present in the respective table with the respective file name. Here is the procedure:
:sql queries
-- for creating t1 table--
CREATE TABLE HR.T1
(
NAMES VARCHAR2(20 BYTE),
MAPPING_ID VARCHAR2(10 BYTE)
);
SET DEFINE OFF;
Insert into HR.T1
(NAMES, MAPPING_ID)
Values
('san', '1');
Insert into HR.T1
(NAMES, MAPPING_ID)
Values
('man', '1');
COMMIT;
-----------sql query for 'san' table----
CREATE TABLE HR.SAN
(
SRC_FILENAME VARCHAR2(20 BYTE)
);
SET DEFINE OFF;
Insert into HR.SAN
(SRC_FILENAME)
Values
('audi.txt');
Insert into HR.SAN
(SRC_FILENAME)
Values
('mercedes.txt');
COMMIT;
------sql query for man table ----
CREATE TABLE HR.MAN
(
SRC_FILENAME VARCHAR2(20 BYTE)
);
SET DEFINE OFF;
Insert into HR.MAN
(SRC_FILENAME)
Values
('hundai.txt');
COMMIT;
-------package spec -----
CREATE OR REPLACE PACKAGE HR.file_entry
AS
PROCEDURE PKG_PROC_FILES(L_MAPPING_ID NUMBER);
procedure insert_proc (l_object_name VARCHAR2);
END;
-----package body -----
CREATE OR REPLACE PACKAGE BODY HR.file_entry
AS
PROCEDURE PKG_PROC_FILES (L_MAPPING_ID NUMBER)
AS
V_TABLE_NAME VARCHAR2 (50);
V_SCHEMA_NAME VARCHAR2 (50);
TYPE CURTYPE IS REF CURSOR;
V_SCHEMA_NAME VARCHAR2 (50);
----
CURSOR TARGET_OBJ_CUR
IS
SELECT DISTINCT names
FROM t1
WHERE MAPPING_ID = L_MAPPING_ID;
BEGIN
FOR I IN TARGET_OBJ_CUR
LOOP
INSERT_PROC (I.names);
DBMS_OUTPUT.PUT_LINE ('TARGET_TABLE_NAME= ' || I.names);
END LOOP;
END;
PROCEDURE INSERT_PROC (L_OBJECT_NAME VARCHAR2)
AS
V_TABLE_NAME VARCHAR2 (50);
V_SCHEMA_NAME VARCHAR2 (50);
V_QUERY VARCHAR2 (50);
TYPE CURTYPE IS REF CURSOR;
V_SRC_FILE_NAMES VARCHAR2 (200);
CUR CURTYPE;
BEGIN
V_QUERY := 'select distinct src_filename from ' || L_OBJECT_NAME;
OPEN CUR FOR V_QUERY;
LOOP
FETCH CUR INTO V_SRC_FILE_NAMES;
DBMS_OUTPUT.PUT_LINE ('SOURCE FILE NAMES 1 = ' || V_SRC_FILE_NAMES);
COMMIT;
EXIT WHEN CUR%NOTFOUND;
END LOOP;
CLOSE CUR;
END;
END;
/
After executing the procedure I have multiple names from DB:
O/P
SOURCE FILE NAMES = mercedes.txt
SOURCE FILE NAMES = audi.txt
SOURCE FILE NAMES = audi.txt
TARGET_TABLE_NAME= san
SOURCE FILE NAMES = hundai.txt
SOURCE FILE NAMES = hundai.txt
TARGET_TABLE_NAME= man
In the below O/P I need to get only distinct source file names from table butI am unable to understand why am I getting the audi.txt and hundai.txt multiple times. Can anyone help me out to solve this issue? I need a file name once to be printed in the output like mercedes.txt which has been printed only once in the output.

EXIT WHEN CUR%NOTFOUND; exit statement needs to be checked after fetch in the INSERT_PROC procedure, not before end loop . And why you use commit after dbms_output.put_line ?

Related

How can I make table name from two string column?

I need to make a PL/SQL script.
The inputs are a schema name and a table name. How can I make it to a table name?
So e.g. I'd like to do this:
create or replace procedure proc(schema in varchar2, table in varchar2) is
begin
select * from 'schema.table';
end;
begin
proc('db', 'items');
end;
So I'd like to get everything from db.items.
I've tried concat, ( 'schema' || '.' || 'table'), put it in a variable, but non of these has worked.
What you need is dynamic sql. Example that will return and print the count of rows (you can change it accordingly to your needs):
SQL> set serveroutput on -- to be able to see the printed results.
SQL> create or replace procedure proc(p_schema in varchar2, p_table in varchar2) is
v_sql varchar2(100);
v_result number;
begin
v_sql := 'select count(*) from :1' || '.' || ':2';
EXECUTE IMMEDIATE v_sql into v_result USING p_schema, p_table;
DBMS_OUTPUT.PUT_LINE ('Total rows in table: '|| v_result );
end;

Using count(*) to fetch more than one row in a SQL Procedure

I'm trying to return the number of rows per invoice_id using a function and procedure. Some invoice_id's have more than one row and I'm not sure how to fetch the count when I execute my procedure. As an example invoice_id(7) has just one row, but invoice_id(100) has four rows of information.
Create or replace function return_num_rows_function(invoice_id_text in varchar2)
Return varchar2
Is inv_id varchar2(20);
Begin
Select count(*)invoice_id into inv_id from invoice_line_items where invoice_id=invoice_id_text;
Return inv_id;
End;
Create or replace procedure return_num_rows (invoice_id_text in varchar2)
Is inv_id varchar(20);
line_item_desc invoice_line_items.line_item_description%type;
Begin
inv_id := return_num_rows_function(invoice_id_text);
If inv_id is not null then
Select count(*)invoice_id, line_item_description into inv_id,line_item_desc
From invoice_line_items where invoice_id = inv_id;
dbms_output.put_line('The number of rows returned:'|| inv_id);
dbms_output.put_line('Item description(s):'|| line_item_desc);
End if;
End;
set serveroutput on;
execute return_num_rows(7);
First of all do not use a string type variable for a numeric one
(invoice_id_text).
For your case it's better to use a procedure instead of called
function ( return_num_rows_function ), since you need two out
arguments returned.
A SQL Select statement cannot be used without Group By with aggegated and non-aggregated columns together ( i.e. don't use this one :
Select count(*) invoice_id, line_item_description
into inv_id,line_item_desc
From invoice_line_items
Where invoice_id = inv_id;
)
So, Try to create below procedures :
SQL> CREATE OR REPLACE Procedure
return_num_rows_proc(
i_invoice_id invoice_line_items.invoice_id%type,
inv_id out pls_integer,
line_item_desc out invoice_line_items.line_item_description%type
) Is
Begin
for c in
(
Select line_item_description
into line_item_desc
From invoice_line_items
Where invoice_id = i_invoice_id
)
loop
line_item_desc := line_item_desc||' '||c.line_item_description;
inv_id := nvl(inv_id,0) + 1;
end loop;
End;
/
SQL> CREATE OR REPLACE Procedure
return_num_rows(
i_invoice_id pls_integer
) Is
inv_id pls_integer;
line_item_desc invoice_line_items.line_item_description%type;
Begin
return_num_rows_proc(i_invoice_id,inv_id,line_item_desc);
If inv_id is not null then
dbms_output.put_line('The number of rows returned:' || inv_id);
dbms_output.put_line('Item description(s):' || line_item_desc);
End if;
End;
/
and call as in your case :
SQL> set serveroutput on;
SQL> execute return_num_rows(7);
Replace inv_id varchar2(20) with inv_id number;
and also if you want to get two outputs from procedure better to use refcursor.

ORACLE PL/SQL for each passing tablename to procedure

I need to do a FOR EACH loop in a procedure, but I need to pass the table name dynamically.
This is the declaration
CREATE OR REPLACE PROCEDURE MIGRATE_PRIMITIVES_PROPS
(
FromTable IN VARCHAR2,
ToTable IN VARCHAR2
)
When I try and do this
FOR EachRow IN (SELECT * FROM FromTable) It says the table isn't valid
The table coming into the procedure is dynamic, columns are added and deleted all the time so I can't spell out the columns and use a cursor to populate them.
You have to use dynamic SQL to query a table whose name you don't know at compile time. You can do that with a dynamic cursor:
as
l_cursor sys_refcursor;
begin
open l_cursor for 'select * from ' || fromtable;
loop
fetch l_cursor into ...
... but then it breaks down because you can't define a record type to fetch into based on a weak ref cursor; and you don't know the column names or types you're actually interested in - you're using select * and have specific names to exclude, not include. You mentioned an inner loop that works and gets the column names, but there is no way to refer to a field in that cursor variable dynamically either.
So you have to work a bit harder and use the dbms_sql package instead of native dynamic SQL.
Here's a basic version:
create or replace procedure migrate_primitives_props
(
fromtable in varchar2,
totable in varchar2
)
as
l_cursor pls_integer;
l_desc_tab dbms_sql.desc_tab;
l_columns pls_integer;
l_value varchar2(4000);
l_status pls_integer;
begin
l_cursor := dbms_sql.open_cursor;
-- parse the query using the parameter table name
dbms_sql.parse(l_cursor, 'select * from ' || fromtable, dbms_sql.native);
dbms_sql.describe_columns(l_cursor, l_columns, l_desc_tab);
-- define all of the columns
for i in 1..l_columns loop
dbms_sql.define_column(l_cursor, i, l_value, 4000);
end loop;
-- execute the cursor query
l_status := dbms_sql.execute(l_cursor);
-- loop over the rows in the result set
while (dbms_sql.fetch_rows(l_cursor) > 0) loop
-- loop over the columns in each row
for i in 1..l_columns loop
-- skip the columns you aren't interested in
if l_desc_tab(i).col_name in ('COL_NAME', 'LIB_NAME', 'PARTNAME',
'PRIMITIVE', 'PART_ROW')
then
continue;
end if;
-- get the column value for this row
dbms_sql.column_value(l_cursor, i, l_value);
-- insert the key-value pair for this row
execute immediate 'insert into ' || totable
|| '(key, value) values (:key, :value)'
using l_desc_tab(i).col_name, l_value;
end loop;
end loop;
end;
/
I've assumed you know the column names in your ToTable but still used a dynamic insert statement since that table name is unknown. (Which seems strange, but...)
Creating and populating sample tables, and then calling the procedure with their names:
create table source_table (col_name varchar2(30), lib_name varchar2(30),
partname varchar2(30), primitive number, part_row number,
col1 varchar2(10), col2 number, col3 date);
create table target_table (key varchar2(30), value varchar2(30));
insert into source_table (col_name, lib_name, partname, primitive, part_row,
col1, col2, col3)
values ('A', 'B', 'C', 0, 1, 'Test', 42, sysdate);
exec migrate_primitives_props('source_table', 'target_table');
End up with the target table containing:
select * from target_table;
KEY VALUE
------------------------------ ------------------------------
COL1 Test
COL2 42
COL3 2015-05-22 15:29:31
It's basic because it isn't sanitising the inputs (look up the dbms_assert package), and isn't doing any special handling for different data types. In my example my source table had a date column; the target table gets a string representation of that date value based on the calling session's NLS_DATE_FORMAT setting, which isn't ideal. There's a simple but slightly hacky way to get a consistent date format, and a better but more complicated way; but you may not have date values so this might be good enough as it is.

Procedure to input number and output varchar2

I need to write a procedure to input let's say a rep_id and then output the rep_name that corresponds to the rep_id.
After that, I need to use another procedure to call the above procedure.
Here is what I have for the first procedure.
create or replace procedure p_inout
(v_rep_id in number)
As
v_first_name varchar2(20);
v_last_name varchar2(20);
begin
select first_name,last_name into v_first_name, v_last_name
from rep
where rep_id = v_rep_id;
dbms_output.put_line(v_first_name||' '||v_last_name);
end p_inout;
/
Execute p_inout(100);
And here is my procedure to call the above procedure
create or replace procedure p_call
is
v_first_name varchar2(20);
v_last_name varchar2(20);
begin
p_inout(100);
dbms_output.put_line(v_first_name||' '||v_last_name);
end p_call;
/
execute p_call
I was able to get the result but one guy told me that my call procedure should be like this
Create or replace procedure p_call
Is
V_name varchar2(20);
Begin
P_inout(100,v_name); --100 is a rep id
Dbms_output.Put_line(v_name);
End;
/
Execute p_call
Doesn't my procedure to call and his call procedure produce the same result?
CREATE TABLE minions (
rep_id DATE CONSTRAINT minions__rep_id__pk PRIMARY KEY,
rep_name NUMBER CONSTRAINT minions__rep_name__nn NOT NULL
CONSTRAINT minions__rep_name__u UNIQUE
)
/
CREATE OR REPLACE PROCEDURE myCatWasSick (
p_rep_id IN minions.rep_id%TYPE,
p_rep_name OUT minions.rep_name%TYPE
)
IS
BEGIN
SELECT rep_name
INTO p_rep_name
FROM minions
WHERE rep_id = p_rep_id;
EXCEPTION
WHEN NO_DATA_FOUND THEN
p_rep_name := NULL;
END myCatWasSick;
/
CREATE OR REPLACE PROCEDURE releaseTheBadgers
IS
the_badger NUMBER(10);
BEGIN
myCatWasSick( SYSDATE + 1, the_badger );
// Do something with the_badger.
END releaseTheBadgers;
/

Create nested procedure in Oracle 10g

I want to create a nested procedure where in the 1st procedure, I want to create a table dynamically with 2 columns and in the 2nd procedure, I want to insert values into that table.
Below is the code I am trying to use; what am I doing wrong?
CREATE or replace PROCEDURE mytable (tname varchar2)
is
stmt varchar2(1000);
begin
stmt := 'CREATE TABLE '||tname || '(sname varchar2(20) ,sage number (4))';
execute immediate stmt;
end;
create PROCEDURE mytable1 (emp_name varchar2,emp_age number,tname varchar2)
is
stmt1 varchar2(1000);
begin
stmt1 := 'insert into '||tname||' values ('Gaurav' ,27)';
execute immediate stmt1;
end;
There's no need to create a nested procedure here. You can do everything in a single procedure.
Note my use of bind variables in the execute immediate statement
create or replace procedure mytable (
Ptable_name in varchar2
, Pemp_name in varchar2
, Pemp_age in number
) is
begin
execute immediate 'create table ' || Ptable_name
|| ' (sname varchar2(20), sage number (4))';
execute immediate 'insert into ' || Ptable_name
|| ' values (:emp_name, :emp_age)'
using Pemp_name, Pemp_age;
end;
More generally, there's no need to use execute immediate at all; creating tables on the fly is indicative of a poorly designed database. If at all possible do not do this; create the table in advance and have a simple procedure to insert data, should you need it:
create or replace procedure mytable (
, Pemp_name in varchar2
, Pemp_age in number
) is
begin
insert into my_table
values (Pemp_name, Pemp_age);
end;
I would highly recommend reading Oracle's chapter on Guarding Against SQL Injection.
If you really feel like you have to do this as a nested procedure it would look like this; don't forget to call the nested procedure in the main one as the nested procedure isn't visible outside the scope of the first.
create or replace procedure mytable (
Ptable_name in varchar2
, Pemp_name in varchar2
, Pemp_age in number
) is
procedure myvalues (
Pemp_name in varchar2
, Pemp_age in number
) is
begin
execute immediate 'insert into ' || Ptable_name
|| ' values (:emp_name, :emp_age)'
using Pemp_name, Pemp_age;
end;
begin
execute immediate 'create table ' || Ptable_name
|| ' (sname varchar2(20), sage number (4))';
myvalues ( Pemp_name, Pemp_age);
end;
Please see Oracle's documentation on PL/SQL subprograms

Resources