PLSQL Count and Procedure - oracle

Currently trying to create a PL/SQL procedure. I am a complete noob at PL/SQL, as you can tell!
We have had to create a table using SQL, and we are looking to automatically update the table with a procedure. If the customer has requested more than 4 jobs, we are looking to input their details into this table as a frequent customer.
I currently have at the moment:
CREATE TABLE PublisherDetails
(PublisherName VARCHAR2 (40),
City VARCHAR2 (20) ,
PhoneNo NUMBER (11),
jobNo NUMBER (10),
startDate DATE,
completionDate DATE)
;
SELECT Publisher.Name AS PublisherName,
Publisher.City, Publisher.PhoneNo,
COUNT (*) AS PublisherJobCount
FROM Publisher
INNER JOIN PrintJob
ON Publisher.Name = PrintJob.PublisherName
GROUP BY Publisher.Name, Publisher.City, Publisher.PhoneNo;
Create or replace procedure Task3
IS CountPublisherJobs NUMBER;
DECLARE No_data_Found EXCEPTION
BEGIN
SELECT count(*) INTO CountPublisherJobs
OPEN Task3;
LOOP
IF PublisherJobCount < 3
THEN INSERT INTO PublisherDetails (PublisherName, City, PhoneNo)
FROM Publisher
WHERE PublisherName = publisher.name
Else
Insert Into PublisherDetails (JobNo, StartDate, CompletionDate )
SELECT jobNo, startDate, completionDate
FROM PrintJob
Where PublisherName = publishers.name
FETCH Task3 INTO PublisherDetails, publishername, city, phoneNo;
EXIT WHEN c1%NOTFOUND;
INSERT INTO temp VALUES (PublisherName, City, PhoneNo, JobNo, StartDate, CompletionDate);
END IF;
COMMIT;
END LOOP;
EXCEPTION
WHEN NO_DATA_FOUND THEN
dbms_output.put_line('Sorry no data found');
END;
/
Its churning up errors and I am not sure why. Any help as always is appreciated.

There are a number of things incorrect with the syntax of your procedure.
The basic format for a stored procedure is
Create {Or Replace} Procedure PROCEDURE_NAME {(i_param IN datatype)}
Is
<<Declaration Section>>
Begin
<<code section>>
Exception
<<Exceptions>>
End PROCEDURE_NAME;
From what you have described above, you want to insert a record into a table, when a condition is met in another table.
To accomplish this, I would need to see the underlying data structure, what you have provided doesn't show the tables the data is currently in (is there a JOB table for instance? a Customer table?).
The NO_DATA_FOUND exception does not need to be declared, it is an Oracle exception
Your Select Count(*) Into CountJobs is missing a From TABLE, and any predicates you want to add, although I am not sure what you are trying to accomplish with this.
You are attempting to open a cursor on the procedure name. You have not defined a cursor with the name Task3
You have not declared the CountPublisherJobs variable
I would suggest perhaps revisiting the basic structure for a stored procedure.
Edit
Based on your response, you could achieve the result using the following:
Create Or Replace Procedure addFrequentPublisher
Is
Cursor frequentPublishers Is
Select PUBLISHER_ID
From JOB
Group By
PUBLISHER_ID
Having Count(*) >= 4;
Begin
For i In frequentPublishers
Loop
Insert Into FREQUENT_CUSTOMER ...
End Loop;
End;

Related

SYS_REFCURSOR is returning all the rows from table without considering the IN parameter

I am facing a weird problem here.
PROCEDURE USL_EMPLOYEEBYID (
EMPLOYEE_ID IN NUMBER,
EMPIDCURSOR OUT SYS_REFCURSOR
)
AS
BEGIN
OPEN EMPIDCURSOR FOR
SELECT emp.employee_id,emp.employee_name,emp.present_address,emp.permanent_address,emp.status
FROM Employee_Info emp
WHERE emp.employee_id = EMPLOYEE_ID;
END;
This procedure should give me a single employee upon entering the employee Id. But it is returning all the employees.
What am I doing wrong here?
In your query, Oracle interprets EMPLOYEE_ID as the column EMPLOYEE_ID, not the input parameter, here you find something more; in this way, your where condition is something like a=a.
Change the parameter name to distinguish it from the table column:
PROCEDURE USL_EMPLOYEEBYID (
p_EMPLOYEE_ID IN NUMBER,
po_EMPIDCURSOR OUT SYS_REFCURSOR
)
AS
BEGIN
OPEN po_EMPIDCURSOR FOR
SELECT emp.employee_id,emp.employee_name,emp.present_address,emp.permanent_address,emp.status
FROM Employee_Info emp
WHERE emp.employee_id = p_EMPLOYEE_ID;
END;
this is a good practice, to always know in your code whether you are handling an input parameter, a local variable, a column and so on

Why am I getting this error: "ORA-00922: missing or invalid option" after running a PL/SQL procedure?

I created a table Patient with some attributes, like p_name, p_surname, p_number... I would like to create a procedure to transfer a patient from this table Patient to another table (Patient_backup), case his "p_number" attribute has received an input, deleting it from the first table and remaining only in the second. The second table has the same structure of the first one. I have coded the procedure like that.
CREATE TABLE patient (
p_number VARCHAR2(10) NOT NULL,
p_name VARCHAR2(15),
p_surname VARCHAR2(15),
p_street VARCHAR2(20),
p_city VARCHAR2(15)
);
CREATE TABLE patient_backup (
p_number VARCHAR2(10) NOT NULL,
p_name VARCHAR2(15),
p_surname VARCHAR2(15),
p_street VARCHAR2(20),
p_city VARCHAR2(15)
);
CREATE [OR REPLACE] PROCEDURE transfer (p_number VARCHAR2)
AS
CURSOR k1 IS SELECT p_number FROM patient;
BEGIN
OPEN k1;
LOOP
FETCH k1 INTO p_number;
IF p_number IS NULL THEN
dbms_output.put_line('empty');
ELSE
INSERT INTO patient_backup (SELECT * FROM patient);
Execute Immediate 'Drop Table patient;';
END IF;
END LOOP;
CLOSE k1;
END transfer;
But when I run it,I get the error "ORA-00922: missing or invalid option". Could you help me with that? I wonder if the code is correct. I have read a material about PL/SQL, but the concepts were not connected to each other, so I just tried to gather everything together, and I hope it is correct. Could you help me to correct this code and make it work?
It's hard to tell where exactly the error is, but my guess is: remove the ; from inside the string for execute immediate.
But I think you want do not want to DROP the table - that removes the table completely from the database including all rows and its definition. It won't be accessible after that.
I think what you really want is to DELETE a row from that table, not remove the table completely.
Also: the whole loop is completely unnecessary (and inefficient). You can do that with two simple SQL statements:
insert into patient_backup
select *
from patient
where p_number = 42; --<< to pick one patient
delete from patient
where p_number = 42;
Putting that into a procedure:
CREATE PROCEDURE transfer (p_number_to_delete VARCHAR2)
AS
BEGIN
insert into patient_backup
select *
from patient
where p_number = p_number_to_delete;
delete from patient
where p_number = p_number_to_delete;
END transfer;
It's highly recommended to not use the name of a column as the name of a parameter. That's why I named the parameter p_number_to_delete (but p_number is a bad name for a column that isn't a number to begin with - but that's a different discussion)
I think you need to DECLARE your cursor before you define it.
In Your procedure code have some error
1.P_NUMBER input parameter cannot be used into statment
2.don't use semicolon inside the EXECUTE IMMEDIATE string
3. in loop statement you should use exit otherwise it will run
continuously
Here the code
CREATE OR REPLACE PROCEDURE TRANSFER (P_NUMBER IN VARCHAR2) AS
CURSOR K1 IS
SELECT P_NUMBER FROM PATIENT;
P_NUM PLS_INTEGER;
BEGIN
OPEN K1;
LOOP
FETCH K1 INTO P_NUM;
IF P_NUM IS NULL THEN
DBMS_OUTPUT.PUT_LINE('EMPTY');
ELSE
INSERT INTO PATIENT_BACKUP (SELECT * FROM PATIENT);
DELETE FROM PATIENT;
END IF;
EXIT WHEN P_NUM IS NULL;
END LOOP;
CLOSE K1;
END TRANSFER;

Table name, column names as argument to stored procedure

I am a newbie to stored procedure and to PL/SQL. There is an existing procedure to copy data from one table to another. I want to rewrite the stored procedure to accept table name and column names as arguments.Did googling on the solution but couldn't come up with a solid solution.
Also planning to add column names as argument so that the column names don't have to be repeatedly added in multiple stored procedures which uses the same tables and columns, helps to reduce maintenance when columns names gets added/removed. Code has been added.
Can anyone help me with this? Any sample code will be very helpful.
create or replace procedure copy_data(startDate DATE, endDate DATE,
mainTable varchar2, subTable varchar2, cpyTbl varchar2)
IS
commit_size NUMBER :=1000;
existing_columns NUMBER;
after_deletion_columns NUMBER;
removed_columns NUMBER;
TYPE order_ids IS TABLE OF subTable.id%TYPE INDEX BY PLS_INTEGER;
removable_order_ids order_ids;
bulk_errors EXCEPTION;
PRAGMA EXCEPTION_INIT (bulk_errors, -24381);
CURSOR C1 is select id FROM subTable where ord_id in (select ord_id from
mainTable where tmstmp BETWEEN startDate AND endDate);
BEGIN
open C1;
LOOP
FETCH C1 BULK COLLECT INTO removable_order_ids LIMIT commit_size;
forall indx in 1..removable_order_ids.COUNT
INSERT INTO cpyTbl (id, ord_id, name, phon_nbr)
select id, ord_id, name, phon_nbr from subTable
where ord_id = removable_order_ids(indx) LOG ERRORS INTO
ERR$_cpyTbl('INSERT') REJECT LIMIT UNLIMITED;
COMMIT;
EXIT WHEN removable_order_ids.COUNT < commit_size;
END LOOP;
COMMIT;
end;

FORALL+ EXECUTE IMMEDIATE + INSERT Into tbl SELECT

I have got stuck in below and getting syntax error - Please help.
Basically I am using a collection to store few department ids and then would like to use these department ids as a filter condition while inserting data into emp table in FORALL statement.
Below is sample code:
while compiling this code i am getting error, my requirement is to use INSERT INTO table select * from table and cannot avoid it so please suggest.
create or replace Procedure abc(dblink VARCHAR2)
CURSOR dept_id is select dept_ids from dept;
TYPE nt_dept_detail IS TABLE OF VARCHAR2(25);
l_dept_array nt_dept_detail;
Begin
OPEN dept_id;
FETCH dept_id BULK COLLECT INTO l_dept_array;
IF l_dept_array.COUNT() > 0 THEN
FORALL i IN 1..l_dept_array.COUNT SAVE EXCEPTIONS
EXECUTE IMMEDIATE 'INSERT INTO stg_emp SELECT
Dept,''DEPT_10'' FROM dept_emp'||dblink||' WHERE
dept_id = '||l_dept_array(i)||'';
COMMIT;
END IF;
CLOSE dept_id;
end abc;
Why are you bothering to use cursors, arrays etc in the first place? Why can't you just do a simple insert as select?
Problems with your procedure as listed above:
You don't declare procedures like Procedure abc () - for a standalone procedure, you would do create or replace procedure abc as, or in a package: procedure abc is
You reference a variable called "dblink" that isn't declared anywhere.
You didn't put end abc; at the end of your procedure (I hope that was just a mis-c&p?)
You're effectively doing a simple insert as select, but you're way over-complicating it, plus you're making your code less performant.
You've not listed the column names that you're trying to insert into; if stg_emp has more than two columns or ends up having columns added, your code is going to fail.
Assuming your dblink name isn't known until runtime, then here's something that would do what you're after:
create Procedure abc (dblink in varchar2)
is
begin
execute immediate 'insert into stg_emp select dept, ''DEPT_10'' from dept_emp#'||dblink||
' where dept_id in (select dept_ids from dept)';
commit;
end abc;
/
If, however, you do know the dblink name, then you'd just get rid of the execute immediate and do:
create Procedure abc (dblink in varchar2)
is
begin
insert into stg_emp -- best to list the column names you're inserting into here
select dept, 'DEPT_10'
from dept_emp#dblink
where dept_id in (select dept_ids from dept);
commit;
end abc;
/
There appears te be a lot wrong with this code.
1) why the execute immediate? Is there any explicit requirement for that? No, than don't use it
2) where is the dblink variable declared?
3) as Boneist already stated, why not a simple subselect in the insert statement?
INSERT INTO stg_emp SELECT
Dept,'DEPT_10' FROM dept_emp#dblink WHERE
dept_id in (select dept_ids from dept );
For one, it would make the code actually readable ;)

Iterate over a column in PL/SQL

I have a table Emp with EmpID, Empname, Salary and I am trying to do a calculation for each employee. But I am having problems trying to iterate over each emp to do the calculation. I cant use explicit cursors though.
So right now I am just trying to create the list of empIDs:
Declare
aRows Number;
eid emp_ID%TYPE;
Begin
Select Count(*)
Into aRows
from emp;
Select emp_ID
Into eid
From emp;
FOR days IN 1..Tot_Rows
Loop
Dbms_Output.Put_Line(eid);
eid := eid + 1;
End Loop;
END;
But I get the error:
PLS-00320: the declaration of the type of this expression is incomplete or malformed
The simplest way to iterate over the rows in a table in PL/SQL is to do something like
BEGIN
FOR employees IN (SELECT emp_id FROM emp)
LOOP
dbms_output.put_line( employees.emp_id );
END LOOP;
END;
Alternately, you could fetch all the EID values into a PL/SQL collection and iterate over the collection, as in this example
DECLARE
TYPE emp_id_tbl IS TABLE OF emp.emp_id%type;
l_emp_ids emp_id_tbl ;
BEGIN
SELECT emp_id
BULK COLLECT INTO l_emp_ids
FROM emp;
FOR i IN l_emp_ids .FIRST .. l_empnos.LAST
LOOP
dbms_output.put_line( l_emp_ids (i) );
END LOOP;
END;
If your query can return thousands of rows, however, fetching all the data into the collection may use more of the PGA memory than you'd like and you may need to fetch rows in chunks using the LIMIT clause. But that would seem to be getting ahead of ourselves at this point.
Justin Cave has explained how to do it, but to specifically look at the error you got, that was because of this:
eid emp_ID%TYPE;
When using the %TYPE you have to specify the table name as well as the column name:
eid emp.emp_ID%TYPE;
If you were selecting all the columns in the row you could also look at %ROWTYPE.
Your approach was also making two assumptions: that the initial select into eid found the lowest ID, which is by no means guaranteed; and that all the subsequent ID values are sequential. And you're declaring and populating aRows but referring to Tot_Rows.

Resources