Comparing two dates with an insert trigger - sql

I want to make sure that the suborder date is not before the orderdate using this trigger:::
CREATE OR REPLACE TRIGGER suborder_date before
INSERT ON suborder FOR EACH row DECLARE suborder_dd DATE;
orderdate DATE;
BEGIN
SELECT reqshipdate INTO suborder_dd FROM suborder;
SELECT orders.orddate INTO orderdate FROM orders;
IF orderdate < suborder_dd THEN
raise_application_error(-20002,('Required Ship Date is before Order Date'));
END IF;
END;
This compiles now I want to test it and insert this into the table...
insert into suborder values (1,1,'12-Jan-13','13-Jan-14', 'Office Depot', 1)
Error: Error starting at line 1 in command: insert into suborder
values (1,1,'12-Jan-13','13-Jan-14', 'Office Depot', 1) Error report:
SQL Error: ORA-01422: exact fetch returns more than requested number
of rows ORA-06512: at "BB.SUBORDER_DATE", line 4 ORA-04088: error
during execution of trigger 'BB.SUBORDER_DATE'
01422. 00000 - "exact fetch returns more than requested number of rows"
*Cause: The number specified in exact fetch is less than the rows returned.
*Action: Rewrite the query or change number of rows requested
With the help of others I finally got the output that I wanted when I edited the command for myself. Thank you!
CREATE OR REPLACE TRIGGER suborder_date BEFORE
INSERT ON suborder FOR EACH row DECLARE reqship DATE; orderdate date;
BEGIN
SELECT o.orddate
INTO orderdate
FROM orders o
WHERE o.orderno = :new.orderno;
IF orderdate > :new.reqshipdate THEN
raise_application_error(-20002, ('Required Ship Date is before Order Date'));
END IF;
END;

If you follow the comments, you should get something like this:
CREATE OR REPLACE TRIGGER suborder_date
BEFORE INSERT ON suborder
FOR EACH row
DECLARE
orderdate DATE;
BEGIN
SELECT o.orddate
INTO orderdate
FROM orders o
WHERE o.order_id = :new.order_id;
IF orderdate < :new.suborder_dd THEN
raise_application_error(-20002, ('Required Ship Date is before Order Date'));
END IF;
END;

Related

PL/SQL No data found even there should be?

I'm currently learning PL/SQL atm and I have run into an issue with one of my homework questions.
In the below code, I'm getting user input for a province and isolating select results using said province in the declaration of the cursor and trying to run the visitsandtotal procedure but all I'm getting is no data found, why?
user prompt
SET SERVEROUTPUT ON
ACCEPT prov PROMPT 'Enter Province: ';
DECLARE
customerprov VARCHAR2(4000);
customername VARCHAR2(4000);
visits NUMBER;
total FLOAT;
CURSOR prov_cursor is
Select custprovince, custname
into customerprov, customername
from si.customer
where upper(custprovince) = '&prov';
BEGIN
for c in prov_cursor loop
visitsandtotal(c.custname, visits, total);
dbms_output.put_line('Name: ' || c.custname || ' Visits: ' || visits || ' Total Labor Cost: ' || total);
end loop;
END;
Procedure
CREATE OR REPLACE PROCEDURE visitsandtotal (
userinput IN VARCHAR2
, visits OUT NUMBER
, total OUT FLOAT
) IS
BEGIN
SELECT
COUNT(*) AS visits
, SUM(s.laborcost) AS totalcost
INTO
visits
, total
FROM
si.customer c
INNER JOIN si.servinv s ON c.custname = s.custname
WHERE
s.custname = userinput
GROUP BY
c.custname
, s.custname ;
END;
Error
Error report -
ORA-01403: no data found
ORA-06512: at "S6_TRAN84.VISITSANDTOTAL", line 7
ORA-06512: at line 11
01403. 00000 - "no data found"
*Cause: No data was found from the objects.
*Action: There was no data from the objects which may be due to end of fetch.
I cannot comment due to less number of reputation.
NO_DATA_FOUND error comes from the procedure where you have where clause and group by..
and if no records with parameter "userinput" leads to the exception.
I would suggest to change the procedure as we certainly don't need the group by custname as the custname is part of where clause;
CREATE OR REPLACE PROCEDURE visitsandtotal
(
userinput IN VARCHAR2
,visits OUT NUMBER
,total OUT FLOAT
)
IS
BEGIN
SELECT COUNT(*) AS visits
,SUM(s.laborcost) AS totalcost
INTO visits
,total
FROM si.customer c
INNER JOIN si.servinv s
ON c.custname = s.custname
WHERE s.custname = userinput;
--removed group by as custname is part of where clause
END visitsandtotal;
But for whatever reason if you insists to keep the group by clause, you have to handle NO_DATA_FOUND exception explicitly in the procedure visitsandtotal
CREATE OR REPLACE PROCEDURE visitsandtotal
(
userinput IN VARCHAR2
,visits OUT NUMBER
,total OUT FLOAT
)
IS
BEGIN
SELECT COUNT(*) AS visits
,SUM(s.laborcost) AS totalcost
INTO visits
,total
FROM si.customer c
INNER JOIN si.servinv s
ON c.custname = s.custname
WHERE s.custname = userinput;
GROUP BY c.custname,s.custname;
-- you dont need to mention custname from both table as join is in place
EXCEPTION
WHEN no_data_found THEN
--HERE - write your exception code whatever you like to add
END visitsandtotal;

ERROR PLS-00103: Encountered the symbol "DECLARE"?

I have a procedure that is bringing up the error 'PL/SQL: Statement ignored'. This message is a little vague and I cant figure out why my procedure won't compile. All the procedure should do is check if the customer's delivery date is less then the SYSDATE and if it is delete and if not print 'customer can't be deleted'.
The code for the procedure is here:
CREATE PROCEDURE remove_customer (customer_id VARCHAR2) IS
declare
ordersCount pls_integer;
BEGIN
select count(*) into ordersCount
from placed_orders
where fk1_customer_id = remove_customer.customer_id
and delivery_date < sysdate;
if ordersCount = 0 then
THEN
DELETE FROM order_line
WHERE order_line.FK1_order_id in
(SELECT order_id FROM placed_order
WHERE placed_order.FK1_customer_id = remove_customer.customer_id
);
DELETE FROM placed_order
WHERE placed_order.FK1_customer_id = remove_customer.customer_id;
DELETE FROM customer
WHERE customer.customer_id = remove_customer.customer_id;
total_customers := total_customers - 1;
ELSE
DBMS_OUTPUT.PUT_LINE 'Customer currently has a order been delivered';
END IF;
END;
And the error message is specifying PLS-00103: Encountered the symbol "DECLARE"
thanks for any advice.
This line:
IF placed_order.delivery_date < SYSDATE
doesn't make much sense - you cannot use a column like this (think about it: which of the rows in placed_order should be compared to SYSDATE? One? All?).
If you want to check whether this customer has a delivery that's already been delivered, you need an additional SELECT:
CREATE PROCEDURE remove_customer (customer_id VARCHAR2) IS
ordersCount pls_integer;
begin
select count(*) into ordersCount
from placed_orders
where fk1_customer_id = remove_customer.customer_id
and delivery_date < sysdate;
if ordersCount = 0 then
-- your code for deleting the customer here
else
-- raise error, show message, ...
end if;

procedure to check if dates of new row overlaps with existing dates in the table

I am trying to write a procedure to check if parameters given (dates) lie between any of the existing dates in the table. And if not insert new row.
CREATE OR REPLACE PROCEDURE test(date1 IN DATE, date2 IN DATE) AS
ddate1 DATE;
ddate2 DATE;
quer VARCHAR2(50);
BEGIN
SELECT fdate, tdate INTO ddate1, ddate2 FROM dataHolder;
IF (ddate1 < date1) AND (ddate2 > date2) THEN
quer := 'invalid';
ELSE
INSERT INTO dataHolder VALUES (date1, date2);
quer := 'success';
END IF;
DBMS_OUTPUT.PUT_LINE(quer);
END;
/
I have tried something like this but when executed I get this error:
ORA-01422: exact fetch returns more than requested number of rows
You are getting that error because your select statement returns more than one record. To simplify the process you could use merge statement and rewrite your procedure as follows:
CREATE OR REPLACE PROCEDURE test(date1 IN DATE, date2 IN DATE) AS
BEGIN
merge into Dataholder dh
using dual
on ((date1 < dh.fdate) and (date2 < dh.tdate))
when not matched then
insert (dh.fdate, dh.tdate)
values(date1, date2);
if sql%rowcount > 0
then
dbms_output.put_line('success');
else
dbms_output.put_line('invalid');
end if;
END;
Your select statement fetches more than record whereas your code expects only one, since you're fetching into single-value variables. You could use BULK COLLECT and collect all the dates into a collection of dates, but I think you can improve on it with the code below:
CREATE OR REPLACE PROCEDURE test(date1 IN DATE, date2 IN DATE) AS
ddate1 DATE;
ddate2 DATE;
invalidRecords NUMBER := 0;
quer VARCHAR2(50);
BEGIN
SELECT COUNT(1) INTO invalidRecords FROM dataHolder WHERE fdate < date1 AND tdate > date2;
IF (invalidRecords > 0) THEN
quer := 'invalid';
ELSE
INSERT INTO dataHolder VALUES (date1, date2);
quer := 'success';
END IF;
DBMS_OUTPUT.PUT_LINE(quer);
END;
/
Since COUNT(1) will always return just one record, it will never throw an ORA-01422 error. Also, it will always return data, so you don't need to worry about NO_DATA_FOUND, as the value 0 will be fetched if there are no invalid records.
Some small optmization of Nuno Guerreiro's answer
SELECT COUNT(1) INTO invalidRecords
FROM dual
WHERE exists
(SELECT 1 FROM dataHolder WHERE fdate < date1 AND tdate > date2);
It will allow to keep out of counting.

TRIGGER AND COUNT to limit the Count value

i want to create a trigger that will
select staffid where dateread = this months date
to count the number of a staffID
if the count for that staff during that month is more than 5
a stop will be issued.
here is what i did that screws me up
I would like to know is this logically correct?
and here are my compiler log errors
This is my required outcome:
Meter readers can only read a maximum of 5 meters in any given calendar month
My Reading Table has
StaffID
MeterID
DateRead
ReadinID (PK)
Here is the error text:
Error(5,7): PL/SQL: SQL Statement ignored Error(5,27):
PL/SQL:ORA-00923: FROM keyword not found where expected
C:\Users\ChrisPin\AppData\Roaming\SQL Developer\assgn2 sat4.sql Error(5,7):
PL/SQL: SQL Statement ignored Error(5,27):
PL/SQL: ORA-00923: FROM keyword not found where expected
Here is the trigger code:
CREATE OR REPLACE TRIGGER LIMIT_5_REDINGS
BEFORE UPDATE OR INSERT ON reading
FOR EACH ROW
DECLARE
ReadingCount INTEGER; --# of depts for this employee
max_read INTEGER := 5; --max number of depts per employee.
BEGIN
select Reading COUNT(*) into ReadingCount
from (select *
from Reading
where to_char(DateRead, 'YYYY-MM') = to_char(sysdate, 'YYYY-MM'))
WHERE STAFFID = :NEW.STAFFID;
IF :OLD.STAFFID = :NEW.STAFFID THEN
RETURN;
ELSE
IF ReadingCount >= max_read THEN
RAISE_APPLICATION_ERROR (-20000,'Employees are limited to a max of two departments.');
END IF;
END IF;
END;
It's in this line
select Reading COUNT(*) into ReadingCount
should be
select COUNT(*) into ReadingCount

PROCEDURE Warning: execution completed with warning

I'm having a problem with this procedure. The error I keep receiving is:
dbms_warning.set_warning_setting_string 'ENABLE:ALL', succeeded.
PROCEDURE promo_ship_sp compiled
Warning: execution completed with warning
I have tried setting error reporting on so I would get a little bit more information, but nothing else shows up. SELECT * FROM BB_PROMOLIST; shows results in 0 rows fetched so it can't be an insert id issue can it? I have commited it, and it still fails to compile successfully.
set serveroutput on;
CALL DBMS_WARNING.set_warning_setting_string ('ENABLE:ALL', 'SESSION');
create or replace procedure promo_ship_sp
(
tmp_id in date
)
IS
v_dt date := tmp_id;
v_Promo_flag bb_promolist.promo_flag%type;
cursor cur_select is
select idshopper, dtcreated
from bb_basket
where dtcreated < v_dt
order by idshopper;
BEGIN
for rec_something in cur_select loop
insert into bb_promolist (idshopper, month, year, promo_flag, used) values
(rec_something.idshopper, 'JAN', '2010', 1, 'N');
dbms_output.put_line(rec_something.idshopper || ' ' ||rec_something.dtcreated);
end loop;
END;
/
show errors;
execute promo_ship_sp('15-FEB-07');
GOOD Catch on the stack overflow, but that wasn't it on my actual code. I disabled the constraints and then compiled without warnings and then enabled the constraints again and can execute it as many times as I please. Kinda wierd bug. Hope this helps someone.
set serveroutput on;
CALL DBMS_WARNING.set_warning_setting_string ('ENABLE:ALL', 'SESSION');
create or replace procedure promo_ship_sp
(
tmp_id in date
)
IS
v_dt date := tmp_id;
v_Promo_flag bb_promolist.promo_flag%type;
cursor cur_select is
select idshopper, dtcreated
from bb_basket
where dtcreated < v_dt
order by idshopper;
BEGIN
delete from bb_promolist;
for rec_something in cur_select loop
insert into bb_promolist (idshopper, month, year, promo_flag, used) values
(rec_something.idshopper, to_char(rec_something.dtcreated, 'MON'), to_char(rec_something.dtcreated, 'YYYY'), 1, 'N');
end loop;
END;
/
show errors;
execute promo_ship_sp('15-FEB-07');
Not an answer to your question but too long for a comment.
Please replace the cursor, the cursor for loop and the insert ... values with:
insert into bb_promolist (idshopper, month, year, promo_flag, used)
select idshopper, to_char(dtcreated, 'MON'), to_char(dtcreated, 'YYYY'), 1, 'N'
from bb_basket
where dtcreated < v_dt;
Include the order by clause only if you have a good reason, such as putting data with the same values on the same block:
insert into bb_promolist (idshopper, month, year, promo_flag, used)
select idshopper, to_char(dtcreated, 'MON'), to_char(dtcreated, 'YYYY'), 1, 'N'
from bb_basket
where dtcreated < v_dt
order by idshopper;