Oracle PLS-00049: bad bind variable - oracle

I'm getting this error when I try to create this trigger. I tried everything but I don't know what seems to be the problem.
Here is the code:
CREATE OR REPLACE TRIGGER after_price_update
AFTER UPDATE
ON Item
FOR EACH ROW
DECLARE new_totalprice INT;
BEGIN
IF :OLD.price <> :new.price THEN
new_totalprice := :old.Quantity * :new.price;
INSERT INTO OrderRecord(OrderRecord_Id, Item_Id, Employee_Id, Reservation_Id, Order_Time, Quantity, TotalPrice)
VALUES(old.OrderRecord_Id, old.Item_Id, old.Employee_Id, old.Reservation_Id, old.Order_Time, old.Quantity, new_totalprice);
END IF;
END;
And the error is:
4/22 PLS-00049: bad bind variable 'OLD.QUANTITY'
The tables look like this:
CREATE TABLE Item (
Item_Id int PRIMARY KEY,
Menu_Id int,
Name varchar2(20),
Description varchar2(120),
Price int,
FOREIGN KEY(Menu_Id) REFERENCES Menu(Menu_Id)
);
CREATE TABLE OrderRecord (
OrderRecord_Id int PRIMARY KEY,
Item_Id int,
Employee_Id int,
Reservation_Id int,
Order_Time date,
Quantity int,
TotalPrice int,
FOREIGN KEY(Item_Id)References Item(Item_Id),
FOREIGN KEY(Employee_Id)References Employee(Employee_Id),
FOREIGN KEY(Reservation_Id)References Reservation(Reservation_Id)
);

Table OrderRecord is not the table being updated: you cannot reference an old value for this table; you need to read this value with some SELECT statement.
IF :OLD.price <> :new.price THEN
new_totalprice := <Quantity> * :new.price;

There is no column Quantity in the table ITEM on which you are applying trigger.

If I understand what you are wanting to do correctly, it looks like you want to update the calculated total price of all existing OrderRecord entries when an Item entry has a price change.
CREATE OR REPLACE TRIGGER after_price_update
AFTER UPDATE
ON Item
FOR EACH ROW
BEGIN
IF NVL(:OLD.Price, 0) <> NVL(:new.Price, 0) THEN
-- Update the child table "OrderRecord" for this item using new price
UPDATE OrderRecord SET TotalPrice = Quantity * :new.Price WHERE Item_Id = :new.Item_id;
END IF;
END;
Note that this trigger is on the parent table, Item, and references two values from the modified record: price and Item_Id, to update the child OrderRecord table.
Also note the use of NVL to watch for nulls because the comparison will not succeed if a null is on either side. Using zero for null is debatable; one could use a value that will never occur such as a negative number.

Related

How to insert in a table in which trigger is also present?

For the past few days, I am working on my SQL project 'Supermarket Billing Management System', here I am getting a lot of obstacles while creating this project. Since I am a beginner I am not able to solve my all issues, So please help me!!
Here is my doubt:
I have created a table called 'Payments' and inside it, I have even created a trigger and a procedure, Now I don't know how to insert the values in the Payments table because of trigger and procedure. And I want such a procedure that can add the total of the product's price of a single person and it will store into Final Total, I am not sure that my procedure code is right or not, but it was created successfully. So if there is any problem with my Procedure code then please let me know and please also tell me how can I insert it by giving an example
Paymnets table :
create table Paymnets
(
PayId int primary key, PayDate date,
ProdTotal int,
FinalTotal int,
PayOrdId int references orders,
PayProdId int references Products,
PayCustId int references Customers
);
Products table:
create table Products (
ProdId number primary key,
ProdNum number not null unique,
ProdName varchar2(15),
ProdPrice int,
ProdQuantity int,
ProdCustId int references Customers,
ProdOrdId int references Orders,
ProdStoreId int references Stores
);
Procedure :
create procedure FINAL_TOTAL(C IN NUMBER, T OUT NUMBER) IS BEGIN
UPDATE
Payments
SET
FinalTotal = FinalTotal + ProdTotal
WHERE
PayCustId = C;
Commit;
SELECT
FinalTotal into T
FROM
Payments
WHERE
PayCustId = C;
END;
/
Trigger:
create trigger PROD_TOTAL
AFTER INSERT ON Products
BEGIN
UPDATE Paymnets
SET ProdTotal = (SELECT Products.ProdPrice * Products.ProdQuantity FROM Products);
END;
/
insert statement:
insert into Payments values(1,'2020-10-07',1,1,1);
Well, after inserting this many values I knew that I'll get an error and so I got:
error:not enough values
Here I want to know, how can I insert the values in the Payment table and If my procedure code is wrong then what should I write? So please help me in solving these issues!!

PL/SQL Stored Procedure to Populate Fact Table

I need to populate this fact table using a PL/SQL stored procedure:
CREATE TABLE SALES_FACTS
(saleDay DATE,
vehicleCode INT,
planID INT,
dealerID INT,
vehiclesSold INT,
grossSalesAmt NUMBER(10),
CONSTRAINT SALE_DAY_FK FOREIGN KEY (saleDay) REFERENCES TIMES(saleDay) ON DELETE CASCADE,
CONSTRAINT VEHICLE_CODE_FK FOREIGN KEY (vehicleCode) REFERENCES VEHICLES(vehicleCode) ON DELETE CASCADE,
CONSTRAINT PLAN_ID_FK FOREIGN KEY (planID) REFERENCES FINANCING_PLANS(planID) ON DELETE CASCADE,
CONSTRAINT DEALER_FK FOREIGN KEY (dealerID) REFERENCES DEALERSHIPS(dealerID) ON DELETE CASCADE,
CONSTRAINT SALES_FACTS_PK PRIMARY KEY (saleDay, vehicleCode, planID, dealerID));
I have been asked to do this by using four nested cursor loops to get every possible combination of the dimension tables' primary keys, along with the total vehicles sold and gross sales amount for each combination.
Also, if the values for vehiclesSold and grossSalesAmount are zero, then a row SHOULD NOT be inserted into the SALES_FACTS table.
Only rows for combinations of the four foreign key columns where there were some vehicles sold should be inserted.
I have created the following code that I hoped would accomplish this:
CURSOR factData IS
SELECT vehicleVin,saleDate,sf.planID,sp.dealerID
COUNT (*) AS vehiclesSold
SUM (s.grossSalePrice) AS grossSalesAmount
FROM SALES s, SALES_FINANCINGS sf, SALESPERSONS sp
WHERE s.saleID = sf.saleID
AND s.salespersonID = sp.salespersonID
GROUP BY vehicleVIN, saleDate, sf.planID, sp.dealerID
HAVING COUNT(*) > 0;
BEGIN
FOR record IN factData
LOOP
INSERT INTO SALES_FACTS (saleDay,vehicleCode,planID,dealerID,vehiclesSold, grossSalesAmount
VALUES (record.saleDate,record.vehicleVin,record.planID,record.dealerID,record.vehiclesSold,record.grossSalesAmount);
END LOOP;
END;
/
However the code executes fine, but I do not get any results when I run a
SELECT COUNT(*) FROM SALES_FACTS;
I have created an SQL Fiddle link here http://sqlfiddle.com/#!4/9708d6/1 since the code for the tables and table population was too much to post on this question. Keep in mind that I only INSERTed about 2-3 rows of data for each table to keep the code somewhat short, however the data that has been inserted should suffice to get this working.
Please let me know where I'm going wrong and what the best way to fix it is! Thanks in advance!
This Ended up doing the trick. Thanks for all of the help to those who commented.
DECLARE
CURSOR sales_data
IS
SELECT vehicleVIN, saleDate, SF.planID, SP.dealerID,
COUNT(*) AS vehiclesSold, SUM(S.grossSalePrice) AS grossSalesAmt
FROM SALES S, SALES_FINANCINGS SF, SALESPERSONS SP, VEHICLES V
WHERE S.saleID = SF.saleID AND S.vehicleVIN = V.vehicleCode AND S.salespersonID = SP.salespersonID
GROUP BY vehicleVIN, saleDate, SF.planID, SP.dealerID
HAVING COUNT(*) > 0;
BEGIN
FOR record IN sales_data
LOOP
INSERT INTO SALES_FACTS (saleDay,vehicleCode,planID,dealerID,vehiclesSold, grossSalesAmt)
VALUES (record.saleDate,record.vehicleVIN,record.planID,record.dealerID,record.vehiclesSold,record.grossSalesAmt);
END LOOP;
END;
/

Returning multiple values after insert

I got this 'Message' table.
CREATE TABLE message (
id INT PRIMARY KEY,
user_id INT NOT NULL REFERENCES users (id) ON DELETE CASCADE,
category_id INT NOT NULL REFERENCES category (id) ON DELETE CASCADE,
text VARCHAR2(4000),
media VARCHAR2(500),
creation_date DATE DEFAULT SYSDATE
);
CREATE SEQUENCE message_seq;
CREATE OR REPLACE TRIGGER message_bir
BEFORE INSERT ON message
FOR EACH ROW
BEGIN
SELECT message_seq.NEXTVAL
INTO :new.id
FROM dual;
END;
After i insert something i need the last inserted id and the date.
INSERT INTO message (user_id, category_id, media)
VALUES (1, 1, 'fdsfsd')
RETURNING id INTO :last_insert_id
The above gives me the last inserted id, but like i said i also need the creation_date. I dont want to do a select query after...
Is there a way to get 2 values back after run an insert?
You can write:
RETURNING id, creation_date INTO :last_insert_id, :last_creation_date.
See http://docs.oracle.com/cd/B28359_01/appdev.111/b28370/returninginto_clause.htm

oracle triggers :new context variable

Question:
A Trigger which automatically stores in a separate table called ‘ExcellentSale’ the Sales Agent
name, car model and manufacturer name, each time the agreed price of a
SalesTransaction is more than 20% of the car’s asking price. (Note: You need to create
the ‘ExcellentSale’ table before implementing this trigger. To create the primary key, use a
sequence that starts at 1 and increments by 1).
I am using these tables
Manufacturer(manufacturerID, name, region)
Model(modelNo, name, type, previousModel, manufacturerID)
Car(VIN, dateAcquired, yearBuilt, purchasedPrice, askingPrice,
currentMileage, modelNo)
SalesAgent(agentID, name, DOB)
SalesTransaction(VIN, custID, agentID, dateOfSale, agreedPrice)
Here is my attempt
create sequence generateKey
start with 1
increment by 1;
CREATE TABLE ExcellentSale(
recordNo NUMBER,
agentName VARCHAR2(20) NOT NULL,
modelName VARCHAR2(20) NOT NULL,
manufacturerName VARCHAR2(20) NOT NULL,
PRIMARY KEY(recordNo));
create or replace trigger AutoStore
before insert on SalesTransaction
for each row
declare
SAname varchar2(50);
carModel varchar2(50);
manufacturerName varchar2(50);
askingprice number;
agreedprice number;
begin
select sa.name, mo.name, mu.name, c.askingprice, st.agreedprice
into SAname, CarModel, manufacturerName, askingprice, agreedprice
from manufacturer MU, Model MO, Car C, SalesAgent SA, SalesTransaction ST
where mu.manufacturerid = mo.manufacturerid
and st.vin = c.vin
AND c.vin = :new.vin
AND sa.agentID = :new.agentID;
IF :new.agreedPrice > (1.2 * askingPrice) THEN
INSERT INTO ExcellentSale
VALUES
(generateKey.nextval, agentName, modelName, manufacturerName);
END IF;
end AutoStore;
/
and the error I am getting is
PL/SQL: ORA -00984: Column not allowed here
please help, thank you in advanced.
In the VALUES clause of the insert statement, you have go use the variables that hold the values you need instead of the column names.
INSERT INTO ExcellentSale
VALUES
(generateKey.nextval, SAname, CarModel, manufacturerName);

How to write an Oracle trigger based on Auto incremented value?

I have an Oracle Table with the following keys: ID, Name, DoB, Dept & FileNo. The ID field is the primary Key with an Auto Incremented value.
I wish to write a trigger, so that when a row is added with the Name, DoB & Dept , the FileNo field should get the value yyyy/xxxx where 'yyyy' is a predefined string & 'xxxx' is the value in ID field.
How do I do this?
If it will always be the ID with some prefix, then it probably shouldn't be a column. If that is the default, then a trigger that sets :new.fileno := 'string'||:new.id should suffice.
Oracle doesn't have auto increment, so you probably mean a sequence. If you have a trigger populating that, then this can go in the same trigger.
You need a sequence to implement an Auto Incremented value:
create sequence seq_file_id start with 1 increment by 1;
and a trigger on a table
CREATE TRIGGER file_trg
BEFORE insert
ON file_table
FOR EACH ROW
BEGIN
SELECT seq_file_id.NEXTVAL INTO :new.id FROM dual;
:NEW.fileno := 'yyyy' || :new.id;
END;
/

Resources