PL/SQL procedure for inner join and where clause - oracle

I want to create PL/SQL stored procedure for following query:
SELECT order_id, order_date, customer_id
FROM Orders
INNER JOIN Customers ON Orders.customer_id = Customers.customer_id
WHERE order_id = xyz;
I want to pass order_id as input parameter in stored procedure.
Can someone please share the PL/SQL code for this?

Try this function
CREATE OR REPLACE FUNCTION fn_test(p1 IN NUMBER)
RETURN VARCHAR2
IS
s_query_stmt VARCHAR2(1000 CHAR);
r1 VARCHAR2(100 CHAR);
r2 VARCHAR2(100 CHAR);
r3 VARCHAR2(100 CHAR);
BEGIN
s_query_stmt := 'Select order_id, order_date, customer_id
From Orders inner join Customers
On Orders.customer_id = Customers.customer_id
WHERE order_id = :x )';
EXECUTE IMMEDIATE s_query_stmt INTO r1, r2, r3 USING p1;
return 'x';
END;

The most simple form of a procedure that would return those values is like this (:
create or replace procedure retrieve_order_values(
p_order_id IN Orders.order_id%type
, p_order_date OUT Orders.order_date%type
, p_customer_id OUT Orders.customer_id%type
)
is
begin
select order_date, customer_id
into p_order_date, p_customer_id
from orders
where order_id = p_order_id;
end retrieve_order_values;
Please note that you do not need the join to the Customers table to retrieve the customer_id.

Related

Oracle SP to move data from table to another

In my Storedprocedure , I want to move data from TableA, TableB and return a message that it is successfully copied.
Also I want to truncate TableB before moving data.
Following the SP I wrote, it gives me an error while truncating the table, how can I modify this to achieve this.
CREATE OR REPLACE PROCEDURE SP_MOVE_DATA
(
name IN VARCHAR2,
gender IN VARCHAR2,
city IN VARCHAR2,
OU_MSG OUT VARCHAR2
)
AS
BEGIN
DECLARE
BEGIN
execute immediate 'TRUNCATE table TableB';
INSERT INTO tableB(name, gender, city)
SELECT a.name, a.gender, a.city
from TableA a
where a.city in (select distinct city from ADDRESS where AREATYPE not in (4))
OU_MSG :='Successfully moved data';
COMMIT;
END;
END SP_MOVE_DATA ;

Need to populate table with Foreign and Primary key

I need to create three tables from all_tab_col system table such that schema details are in one schema_detail table, table details are in table_detail table and column details are in col_table. These three tables are to be populated simultaneously through a stored procedure, with PK(generated using SEQUENCE) in schema_detail is FK in table_detail table and PK(generated using SEQUENCE) in table_detail is FK in col_detail table.
SQL is a set based language, so I would be tempted to solve your task with three set bases steps.
Some mock up tables (just add columns for the details you are interested in):
CREATE TABLE schema_detail (
schema_id NUMBER GENERATED ALWAYS AS IDENTITY NOT NULL,
schema_name VARCHAR2(128 BYTE) NOT NULL,
CONSTRAINT schema_detail_pk PRIMARY KEY (schema_id)
);
CREATE TABLE table_detail (
schema_id NUMBER,
table_id NUMBER GENERATED ALWAYS AS IDENTITY NOT NULL,
table_name VARCHAR2(128 BYTE) NOT NULL,
CONSTRAINT table_detail_pk PRIMARY KEY (table_id),
CONSTRAINT table_detail_fk FOREIGN KEY (schema_id)
REFERENCES schema_detail(schema_id)
ON DELETE CASCADE
);
CREATE INDEX table_detail_schema_idx ON table_detail(schema_id);
CREATE INDEX table_detail_name_idx ON table_detail(table_name);
CREATE TABLE col_detail (
table_id NUMBER,
col_id NUMBER GENERATED ALWAYS AS IDENTITY NOT NULL,
col_name VARCHAR2(128 BYTE) NOT NULL,
CONSTRAINT col_detail_pk PRIMARY KEY (col_id),
CONSTRAINT col_detail_fk FOREIGN KEY (table_id)
REFERENCES table_detail(table_id)
ON DELETE CASCADE
);
CREATE INDEX col_detail ON col_detail(table_id);
I'd fill the table schema_detail first. PK is generated automatically:
INSERT INTO schema_detail(schema_name)
SELECT DISTINCT c.owner FROM all_tab_columns c ORDER BY owner;
SCHEMA_ID SCHEMA_NAME
1 APPQOSSYS
2 AUDSYS
3 CTXSYS
...
Next, I'd fill the tables. The schema_id needs to be looked up the the schema_detail table. Again, we let the PKs be generated automatically:
INSERT INTO table_detail(schema_id, table_name)
SELECT DISTINCT s.schema_id, c.table_name
FROM all_tab_columns c
JOIN schema_detail s ON c.owner = s.schema_name
ORDER BY table_name;
SCHEMA_ID TABLE_ID TABLE_NAME
1 8403 WLM_CLASSIFIER_PLAN
1 8404 WLM_FEATURE_USAGE
1 8405 WLM_METRICS_STREAM
...
And last, I'd fill the columns:
INSERT INTO col_detail(table_id, col_name)
SELECT DISTINCT t.table_id, c.column_name
FROM all_tab_columns c
JOIN table_detail t ON c.table_name = t.table_name
JOIN schema_detail s ON c.owner = s.schema_name
ORDER BY s.schema_id, t.table_id, c.column_name;
Does this solve your question or do you need a PL/SQL procedure?
In case you insist on a PL/SQL stored procedure, I would code it along the lines of:
CREATE OR REPLACE PROCEDURE myproc IS
schema_id schema_detail.schema_id%type;
table_id table_detail.table_id%type;
FUNCTION lookup_insert_schema (p_schema_name VARCHAR2)
RETURN NUMBER
IS
sid schema_detail.schema_id%type;
BEGIN
BEGIN
SELECT schema_id INTO sid
FROM schema_detail
WHERE schema_name = p_schema_name;
EXCEPTION WHEN NO_DATA_FOUND THEN
INSERT INTO schema_detail (schema_name)
VALUES (p_schema_name)
RETURNING schema_id INTO sid;
END;
RETURN sid;
END lookup_insert_schema;
-- lookup p_table_name in table table_detail, if not found, insert it
FUNCTION lookup_insert_table (p_schema_id NUMBER, p_table_name VARCHAR)
RETURN NUMBER
IS
tid table_detail.table_id%type;
BEGIN
BEGIN
SELECT table_id INTO tid
FROM table_detail
WHERE schema_id = p_schema_id
AND table_name = p_table_name;
EXCEPTION WHEN NO_DATA_FOUND THEN
INSERT INTO table_detail (schema_id, table_name)
VALUES (p_schema_id, p_table_name)
RETURNING table_id INTO tid;
END;
RETURN tid;
END lookup_insert_table;
BEGIN
FOR r IN (SELECT * FROM all_tab_columns)
LOOP
schema_id := lookup_insert_schema(r.owner);
table_id := lookup_insert_table(schema_id, r.table_name);
INSERT INTO col_detail (table_id, col_name)
VALUES (table_id, r.column_name);
END LOOP;
END myproc;
/

FORALL INSERT Not Inserting Rows

I have the following code snippet to insert rows into t1 table, however when I execute the procedure no rows are getting populated in t1 table.
CREATE OR REPLACE PROCEDURE my_proc
IS
TYPE rt_t1 IS TABLE OF t1%ROWTYPE
INDEX BY BINARY_INTEGER;
vrt_t1 rt_t1;
TYPE t_emp_no_list IS TABLE OF t1.emp_no%TYPE
INDEX BY BINARY_INTEGER;
TYPE t_emp_name_list IS TABLE OF t1.emp_name%TYPE
INDEX BY BINARY_INTEGER;
TYPE t_loc_name_list IS TABLE OF t1.loc_name%TYPE
INDEX BY BINARY_INTEGER;
TYPE t_hire_date_list IS TABLE OF t1.hire_date%TYPE
INDEX BY BINARY_INTEGER;
l_emp_no t_emp_no_list;
l_emp_name t_emp_name_list;
l_loc_name t_loc_name_list;
l_hire_date t_hire_date_list;
BEGIN
SELECT empno,
ename,
loc,
hiredate
BULK COLLECT INTO l_emp_no,
l_emp_name,
l_loc_name,
l_hire_date
FROM (SELECT empno,
ename,
dept.loc,
emp.hiredate
FROM emp JOIN dept ON emp.deptno = dept.deptno);
FORALL i IN vrt_t1.FIRST .. vrt_t1.LAST
INSERT INTO t1 (emp_no,
emp_name,
loc_name,
hire_date)
VALUES (l_emp_no (i),
l_emp_name (i),
l_loc_name (i),
l_hire_date (i));
COMMIT;
EXCEPTION
WHEN OTHERS
THEN
RAISE;
END;
You should use cursor to achieve your requirement. See below:
CREATE OR REPLACE PROCEDURE my_proc
IS
TYPE rt_t1 IS TABLE OF t1%ROWTYPE
INDEX BY BINARY_INTEGER;
vrt_t1 rt_t1;
cursor cur is
SELECT empno,
ename,
dept.loc,
emp.hiredate
FROM emp JOIN dept ON emp.deptno = dept.deptno;
BEGIN
OPEN cur;
fetch cur BULK COLLECT INTO vrt_t1;
close cur;
FORALL i IN 1 .. vrt_t1.count
INSERT INTO t1
VALUES vrt_t1(i);
COMMIT;
EXCEPTION
WHEN OTHERS
THEN
RAISE;
END;
PS: the above code will work if the number of columns of both the tables are same;
EDIT..In case you are inserting specific rows to a table then you need to create a RECORD to do so. See below example. Your code is failing coz you are trying to inserting multiple collection in one go. In your case first colelction would be an insert and others should be an update.
CREATE TABLE t1
(
emp_no NUMBER,
emp_name VARCHAR2 (30),
loc_name VARCHAR2 (30),
hire_date DATE
);
--------------
SQL> select * from t1;
no rows selected
CREATE OR REPLACE PROCEDURE my_proc
IS
TYPE TBL IS RECORD
(
emp_no number,
emp_name varchar2(100),
loc_name varchar2(100),
hire_date date
);
TYPE t_emp IS TABLE OF TBL INDEX BY PLS_INTEGER;
var_emp_det t_emp;
BEGIN
SELECT ENO,
ENAME,
JOB,
HIREDATE
BULK COLLECT INTO var_emp_det
from emp_sal;
FORALL i IN 1..var_emp_det.count
INSERT INTO t1 (emp_no,
emp_name,
loc_name,
hire_date)
values (var_emp_det(i).emp_no,
var_emp_det(i).emp_name,
var_emp_det(i).loc_name,
var_emp_det(i).hire_date);
COMMIT;
EXCEPTION
WHEN OTHERS
THEN
RAISE;
END;
Output:
SQL> execute my_proc;
PL/SQL procedure successfully completed.
SQL> select * from t1;
EMP_NO EMP_NAME LOC_NAME
---------- ------------------------------ ------------------------------
HIRE_DATE
---------
2 Thomas IT
03-JAN-14

can plsql function return object

i have encountered a problem while writing the following code:
create or replace function getashish(dept varchar2) return emp3 as
emp5 emp3;
str varchar2(300);
begin
str := 'select e.last_name,l.city,e.salary from employees e join departments d
on e.department_id = d.department_id join locations l on d.location_id=l.location_id where
d.department_name = :dept';
execute immediate str bulk collect into emp5 using dept;
end;
emp 3 is table of an object as defined below:
create or replace type emp1 as object (lname varchar2(10),city varchar2(10),sal number(10));
create or replace type emp3 as table of emp1;
i am getting the following error while executing the function: SQL Error:
ORA-00932: inconsistent datatypes: expected - got -
thank you for your anticipated help and support.
try declaring emp5 as a datatype of emp1.
emp5 emp1 := emp1();
Please try the below, (added emp1() to your select query inside function and returned emp5)
CREATE OR REPLACE FUNCTION getashish(
dept VARCHAR2)
RETURN emp3
AS
emp5 emp3 := emp3();
str VARCHAR2(300);
BEGIN
str := 'select emp1(e.last_name,l.city,e.salary) from employees e join departments d
on e.department_id = d.department_id join locations l on d.location_id=l.location_id where
d.department_name = :dept';
EXECUTE immediate str bulk collect INTO emp5 USING dept;
RETURN emp5;
END;
/
and the caller block
SELECT * FROM TABLE( CAST(getashish('IT') AS emp3))
UNION
SELECT * FROM TABLE( CAST(getashish('FINANCE') AS emp3));
the function returns a Table, and hence cant be used in SELECT clause, since it is supposed to return one row only, if have to be used in SELECT. Hope you got the concept!
EDIT: Whatever I did!
create table employees
(last_name varchar2(10),salary number,department_id varchar2(10));
create table locations
(location_id varchar2(10),city varchar2(10));
drop table employees;
create table departments
(department_id varchar2(10),location_id varchar2(10),department_name varchar2(10));
insert into employees values ('ASHISH',6000000,'D1');
insert into employees values ('MAHESH',5000000,'D2');
insert into departments values('D1','L1','IT');
insert into departments values('D2','L2','FINANCE');
insert into locations values('L1','Gurgoan');
insert into locations values('L2','Chennai');
commit;
create or replace type emp1 as object (lname varchar2(10),city varchar2(10),sal number(10));
/
create or replace type emp3 as table of emp1;
/
CREATE OR REPLACE FUNCTION getashish(
dept VARCHAR2)
RETURN emp3
AS
emp5 emp3 := emp3();
str VARCHAR2(300);
BEGIN
str := 'select emp1(e.last_name,l.city,e.salary) from employees e join departments d
on e.department_id = d.department_id join locations l on d.location_id=l.location_id where
d.department_name = :dept';
EXECUTE immediate str bulk collect INTO emp5 USING dept;
RETURN emp5;
END;
/
SELECT * FROM TABLE( CAST(getashish('IT') AS emp3))
UNION
SELECT * FROM TABLE( CAST(getashish('FINANCE') AS emp3));
SQL> SELECT * FROM TABLE( CAST(getashish('IT') AS emp3))
2 UNION
3 SELECT * FROM TABLE( CAST(getashish('FINANCE') AS emp3));
LNAME CITY SAL
---------- ---------- ----------
ASHISH Gurgoan 6000000
MAHESH Chennai 5000000

Assigning data in a new table to an existing foreign key in a for loop

I was wondering if there was a way to assign new data in a table to an existing foreign key.
For example if I use the following loop to assign randomly generated numbers to columns in the customer table, how would I be able to link cust_id, which I have assigned as a foreign key in the Sales table, with new data created each time a new sale is made?
CUSTOMER:
DECLARE
v_cust_id NUMBER(4) NOT NULL := 0000;
v_cust_name VARCHAR2(30);
v_cust_add VARCHAR2(30);
v_phone VARCHAR2(10);
BEGIN
FOR v IN 1 .. 2000 --Loop 2000 times to create data for the 2000 customers in the database.
LOOP
v_cust_id := v_cust_id + 1;
v_cust_name := dbms_random.string('U',5);
v_cust_add := dbms_random.string('A',15);
v_phone := dbms_random.value(1000000,9999999);
INSERT INTO customer (cust_id, cust_name, cust_add, phone)
VALUES (v_cust_id, v_cust_name, v_cust_add, v_phone);
END LOOP;
END;
/
SALES:
DECLARE
v_sale_id NUMBER(4) NOT NULL := ;
v_sale_price NUMBER(8,2);
v_sale_date DATE;
v_no_of_prods NUMBER(4);
v_prod_id NUMBER(4);
v_desp_id NUMBER(4);
v_cust_id NUMBER(4);
BEGIN
FOR v IN 1 .. 10
LOOP
v_sale_id :=
v_sale_price
v_sale_date :=
v_no_of_products :=
v_prod_id :=
v_desp_id :=
v_cust_id :=
INSERT INTO sales (sale_id, sale_price, sale_date, no_of_prods, prod_id, desp_id, cust_id)
VALUES (v_sale_id, v_sale_price, v_sale_date, v_no_of_prods, v_prod_id, v_desp_id, v_cust_id);
END LOOP;
END;
\
You are generating test data to do some kind of performance test?
Let's first generate 2000 customers.
(untested)
insert into customer
(cust_id, cust_name, cust_add, phone)
select
level l,
dbms_random.string('U',5),
dbms_random.string('A',15),
dbms_random.value(1000000,9999999)
from dual
connect by level <= 2000;
Now you can genereate sales data by selecting from the customer table:
insert into sales
(sale_id, sale_price, sale_date, no_of_prods, prod_id, desp_id, cust_id)
select sale_id_sequence.nextval , dbms_random. ...., cust_id
from customer;
insert into sales
(sale_id, sale_price, sale_date, no_of_prods, prod_id, desp_id, cust_id)
select sale_id_sequence.nextval , dbms_random. ...., cust_id
from customer
where mod(cust_id,2) =0;
insert into sales
(sale_id, sale_price, sale_date, no_of_prods, prod_id, desp_id, cust_id)
select sale_id_sequence.nextval , dbms_radom. ...., cust_id
from customer
where mod(cust_id,7) =0;
insert into sales
(sale_id, sale_price, sale_date, no_of_prods, prod_id, desp_id, cust_id)
select sale_id_sequence.nextval , dbms_random. ...., cust_id
from customer
where mod(cust_id,13) =0;
commit;
I assume there is a sequence to create a sale id.
edit1 improvement:
create table customer
( cust_id number(10)
, cust_name varchar2(50)
, cust_add varchar2(30)
, cust_phone varchar2(10)
);
create sequence cust_id_seq;
create table sales
( sale_id number(10)
, prod_no number(10)
, cust_id number(10)
);
create sequence sale_id_seq;
begin
insert into customer
select cust_id_seq.nextval
, dbms_random.string('U',5)
, dbms_random.string('A',15)
, trunc(dbms_random.value(1000000,9999999))
from dual
connect by level < 2000;
for i in 1..10 loop
insert into sales
select sale_id_seq.nextval
, trunc(dbms_random.value(1,100))
, cust_id
from customer;
insert into sales
select sale_id_seq.nextval
, trunc(dbms_random.value(1,100))
, cust_id
from customer
where mod(cust_id+i,2)=0;
insert into sales
select sale_id_seq.nextval
, trunc(dbms_random.value(1,100))
, cust_id
from customer
where mod(cust_id+i,7)=0;
end loop;
end;
/
commit;
select count(*) from customer;
select count(*) from sales;

Resources