📘 PL/SQL Trigger Lab Exam – 30 Questions
Practice these 30 trigger questions based on the sample database schema for Product, Customer, Orders, Invoice, and InvoiceDetails.
🔹 Sample Database Schema
-- Product Table CREATE TABLE Product ( ProductID NUMBER PRIMARY KEY, ProductName VARCHAR2(50), CategoryID NUMBER, Price NUMBER ); -- Customer Table CREATE TABLE Customer ( CustomerID NUMBER PRIMARY KEY, CustomerName VARCHAR2(50), CategoryID NUMBER ); -- Orders Table CREATE TABLE Orders ( OrderID NUMBER PRIMARY KEY, CustomerID NUMBER REFERENCES Customer(CustomerID), OrderDate DATE, OrderType VARCHAR2(20), Status VARCHAR2(20) ); -- Invoice Table CREATE TABLE Invoice ( InvoiceID NUMBER PRIMARY KEY, OrderID NUMBER REFERENCES Orders(OrderID), TotalAmount NUMBER, PaymentStatus VARCHAR2(20), Discount NUMBER ); -- InvoiceDetails Table CREATE TABLE InvoiceDetails ( InvoiceID NUMBER REFERENCES Invoice(InvoiceID), ProductID NUMBER REFERENCES Product(ProductID), Quantity NUMBER );
🔹 PL/SQL Trigger Questions (Q1–Q30)
- Create a BEFORE INSERT trigger on Product to automatically set ProductID using a sequence.
- Create an AFTER INSERT trigger on Customer to send a welcome message using DBMS_OUTPUT.
- Create a BEFORE UPDATE trigger on Product to prevent price from being set below zero.
- Create an AFTER DELETE trigger on Orders to log deleted order details into a separate table.
- Create a BEFORE INSERT trigger on Orders to check if CustomerID exists in Customer table.
- Create a BEFORE UPDATE trigger on Orders to prevent changing status to 'Completed' if TotalAmount = 0.
- Create a BEFORE DELETE trigger on Product to prevent deletion if the product exists in InvoiceDetails.
- Create an AFTER INSERT trigger on Invoice to automatically calculate TotalAmount based on InvoiceDetails.
- Create a BEFORE INSERT trigger on InvoiceDetails to ensure Quantity is greater than zero.
- Create an AFTER UPDATE trigger on Product to record old and new prices in a PriceLog table.
- Create a BEFORE INSERT trigger on Customer to prevent duplicate customer names.
- Create an AFTER DELETE trigger on Customer to delete all associated Orders.
- Create a BEFORE UPDATE trigger on Invoice to prevent discount from exceeding 50% of TotalAmount.
- Create an AFTER INSERT trigger on Orders to generate an initial Invoice with zero total.
- Create a BEFORE INSERT trigger on Orders to restrict OrderType to specific values ('Online', 'Offline').
- Create an AFTER UPDATE trigger on Invoice to update PaymentStatus to 'Paid' if TotalAmount = 0.
- Create a BEFORE DELETE trigger on Invoice to prevent deletion if PaymentStatus='Paid'.
- Create an AFTER INSERT trigger on InvoiceDetails to update TotalAmount in Invoice table.
- Create a BEFORE UPDATE trigger on Orders to record changes in StatusHistory table.
- Create a BEFORE INSERT trigger on Product to ensure ProductName is not null or empty.
- Create an AFTER DELETE trigger on InvoiceDetails to recalculate Invoice TotalAmount.
- Create a BEFORE UPDATE trigger on Customer to prevent changing CategoryID if TotalOrders > 10.
- Create an AFTER INSERT trigger on Product to insert initial stock quantity into a Stock table.
- Create a BEFORE DELETE trigger on Orders to log details into DeletedOrders table for auditing.
- Create an AFTER UPDATE trigger on Invoice to record old TotalAmount and new TotalAmount into InvoiceHistory.
- Create a BEFORE INSERT trigger on Invoice to ensure OrderID exists in Orders table.
- Create a BEFORE UPDATE trigger on Product to prevent changing CategoryID if sales exist for that product.
- Create an AFTER INSERT trigger on Customer to assign default CategoryID if not provided.
- Create a BEFORE DELETE trigger on Customer to prevent deletion if pending Orders exist.
- Create an AFTER UPDATE trigger on Orders to automatically update Invoice TotalAmount if quantities in InvoiceDetails change.
🔹 PL/SQL Trigger Solutions (Q1–Q30)
-- Q1: BEFORE INSERT on Product to set ProductID using sequence
CREATE OR REPLACE TRIGGER trg_product_bi
BEFORE INSERT ON Product
FOR EACH ROW
BEGIN
:NEW.ProductID := Product_seq.NEXTVAL;
END;
/
-- Explanation: Automatically assigns ProductID from a sequence before inserting.
-- Q2: AFTER INSERT on Customer to send welcome message
CREATE OR REPLACE TRIGGER trg_customer_ai
AFTER INSERT ON Customer
FOR EACH ROW
BEGIN
DBMS_OUTPUT.PUT_LINE('Welcome, ' || :NEW.CustomerName || '!');
END;
/
-- Explanation: Outputs a welcome message whenever a new customer is inserted.
-- Q3: BEFORE UPDATE on Product to prevent negative price
CREATE OR REPLACE TRIGGER trg_product_bu
BEFORE UPDATE ON Product
FOR EACH ROW
BEGIN
IF :NEW.Price < 0 THEN
RAISE_APPLICATION_ERROR(-20001, 'Price cannot be negative.');
END IF;
END;
/
-- Explanation: Checks the new price before update and prevents negative values.
-- Q4: AFTER DELETE on Orders to log deleted order
CREATE OR REPLACE TRIGGER trg_orders_ad
AFTER DELETE ON Orders
FOR EACH ROW
BEGIN
INSERT INTO DeletedOrders(OrderID, CustomerID, OrderDate)
VALUES(:OLD.OrderID, :OLD.CustomerID, :OLD.OrderDate);
END;
/
-- Explanation: Logs deleted orders into a separate table for audit.
-- Q5: BEFORE INSERT on Orders to check CustomerID exists
CREATE OR REPLACE TRIGGER trg_orders_bi
BEFORE INSERT ON Orders
FOR EACH ROW
DECLARE
v_count NUMBER;
BEGIN
SELECT COUNT(*) INTO v_count FROM Customer WHERE CustomerID = :NEW.CustomerID;
IF v_count = 0 THEN
RAISE_APPLICATION_ERROR(-20002, 'CustomerID does not exist.');
END IF;
END;
/
-- Explanation: Ensures foreign key constraint at trigger level.
-- Q6: BEFORE UPDATE on Orders to prevent completing zero-total orders
CREATE OR REPLACE TRIGGER trg_orders_bu
BEFORE UPDATE ON Orders
FOR EACH ROW
BEGIN
DECLARE
v_total NUMBER;
BEGIN
SELECT SUM(TotalAmount) INTO v_total FROM Invoice WHERE OrderID = :NEW.OrderID;
IF v_total = 0 AND :NEW.Status='Completed' THEN
RAISE_APPLICATION_ERROR(-20003,'Cannot complete order with zero total.');
END IF;
END;
END;
/
-- Explanation: Prevents status change to 'Completed' if total invoice is zero.
-- Q7: BEFORE DELETE on Product to prevent deletion if referenced
CREATE OR REPLACE TRIGGER trg_product_bd
BEFORE DELETE ON Product
FOR EACH ROW
DECLARE
v_count NUMBER;
BEGIN
SELECT COUNT(*) INTO v_count FROM InvoiceDetails WHERE ProductID = :OLD.ProductID;
IF v_count > 0 THEN
RAISE_APPLICATION_ERROR(-20004,'Cannot delete product with related orders.');
END IF;
END;
/
-- Explanation: Checks foreign key references before deletion.
-- Q8: AFTER INSERT on Invoice to calculate TotalAmount
CREATE OR REPLACE TRIGGER trg_invoice_ai
AFTER INSERT ON Invoice
FOR EACH ROW
BEGIN
UPDATE Invoice i
SET TotalAmount = (SELECT SUM(Quantity * p.Price)
FROM InvoiceDetails d
JOIN Product p ON d.ProductID = p.ProductID
WHERE d.InvoiceID = :NEW.InvoiceID)
WHERE i.InvoiceID = :NEW.InvoiceID;
END;
/
-- Explanation: Automatically calculates total invoice amount after insertion.
-- Q9: BEFORE INSERT on InvoiceDetails to ensure Quantity > 0
CREATE OR REPLACE TRIGGER trg_invoice_details_bi
BEFORE INSERT ON InvoiceDetails
FOR EACH ROW
BEGIN
IF :NEW.Quantity <= 0 THEN
RAISE_APPLICATION_ERROR(-20005,'Quantity must be greater than zero.');
END IF;
END;
/
-- Explanation: Validates quantity before inserting invoice details.
-- Q10: AFTER UPDATE on Product to log price changes
CREATE OR REPLACE TRIGGER trg_product_au
AFTER UPDATE ON Product
FOR EACH ROW
BEGIN
IF :OLD.Price <> :NEW.Price THEN
INSERT INTO PriceLog(ProductID, OldPrice, NewPrice, ChangeDate)
VALUES(:NEW.ProductID, :OLD.Price, :NEW.Price, SYSDATE);
END IF;
END;
/
-- Explanation: Logs any price changes for auditing.
-- Q11: BEFORE INSERT on Customer to prevent duplicate names
CREATE OR REPLACE TRIGGER trg_customer_bi
BEFORE INSERT ON Customer
FOR EACH ROW
DECLARE
v_count NUMBER;
BEGIN
SELECT COUNT(*) INTO v_count FROM Customer WHERE CustomerName = :NEW.CustomerName;
IF v_count > 0 THEN
RAISE_APPLICATION_ERROR(-20006,'Customer name already exists.');
END IF;
END;
/
-- Explanation: Prevents inserting duplicate customer names.
-- Q12: AFTER DELETE on Customer to remove associated orders
CREATE OR REPLACE TRIGGER trg_customer_ad
AFTER DELETE ON Customer
FOR EACH ROW
BEGIN
DELETE FROM Orders WHERE CustomerID = :OLD.CustomerID;
END;
/
-- Explanation: Automatically deletes orders when a customer is deleted.
-- Q13: BEFORE UPDATE on Invoice to limit discount to 50%
CREATE OR REPLACE TRIGGER trg_invoice_bu
BEFORE UPDATE ON Invoice
FOR EACH ROW
BEGIN
IF :NEW.Discount > (:OLD.TotalAmount * 0.5) THEN
RAISE_APPLICATION_ERROR(-20007,'Discount cannot exceed 50% of total.');
END IF;
END;
/
-- Explanation: Validates discount before updating.
-- Q14: AFTER INSERT on Orders to generate initial Invoice
CREATE OR REPLACE TRIGGER trg_orders_ai
AFTER INSERT ON Orders
FOR EACH ROW
BEGIN
INSERT INTO Invoice(InvoiceID, OrderID, TotalAmount, PaymentStatus, Discount)
VALUES(Invoice_seq.NEXTVAL, :NEW.OrderID, 0, 'Pending', 0);
END;
/
-- Explanation: Creates a new invoice automatically when a new order is added.
-- Q15: BEFORE INSERT on Orders to restrict OrderType
CREATE OR REPLACE TRIGGER trg_orders_type
BEFORE INSERT ON Orders
FOR EACH ROW
BEGIN
IF :NEW.OrderType NOT IN ('Online','Offline') THEN
RAISE_APPLICATION_ERROR(-20008,'Invalid OrderType.');
END IF;
END;
/
-- Explanation: Ensures only valid order types are inserted.
-- Q16: AFTER UPDATE on Invoice to mark Paid if TotalAmount=0
CREATE OR REPLACE TRIGGER trg_invoice_au
AFTER UPDATE ON Invoice
FOR EACH ROW
BEGIN
IF :NEW.TotalAmount = 0 THEN
UPDATE Invoice SET PaymentStatus='Paid' WHERE InvoiceID = :NEW.InvoiceID;
END IF;
END;
/
-- Explanation: Automatically marks invoices as paid if total is zero.
-- Q17: BEFORE DELETE on Invoice to prevent deletion if Paid
CREATE OR REPLACE TRIGGER trg_invoice_bd
BEFORE DELETE ON Invoice
FOR EACH ROW
BEGIN
IF :OLD.PaymentStatus='Paid' THEN
RAISE_APPLICATION_ERROR(-20009,'Cannot delete paid invoice.');
END IF;
END;
/
-- Explanation: Prevents deletion of paid invoices.
-- Q18: AFTER INSERT on InvoiceDetails to update Invoice TotalAmount
CREATE OR REPLACE TRIGGER trg_invoice_details_ai
AFTER INSERT ON InvoiceDetails
FOR EACH ROW
BEGIN
UPDATE Invoice i
SET TotalAmount = TotalAmount + (:NEW.Quantity * (SELECT Price FROM Product WHERE ProductID = :NEW.ProductID))
WHERE i.InvoiceID = :NEW.InvoiceID;
END;
/
-- Explanation: Updates total invoice after adding invoice details.
-- Q19: BEFORE UPDATE on Orders to record status changes
CREATE OR REPLACE TRIGGER trg_orders_status
BEFORE UPDATE ON Orders
FOR EACH ROW
BEGIN
IF :OLD.Status <> :NEW.Status THEN
INSERT INTO StatusHistory(OrderID, OldStatus, NewStatus, ChangeDate)
VALUES(:NEW.OrderID, :OLD.Status, :NEW.Status, SYSDATE);
END IF;
END;
/
-- Explanation: Logs order status changes for tracking.
-- Q20: BEFORE INSERT on Product to ensure name is not null
CREATE OR REPLACE TRIGGER trg_product_name
BEFORE INSERT ON Product
FOR EACH ROW
BEGIN
IF :NEW.ProductName IS NULL THEN
RAISE_APPLICATION_ERROR(-20010,'ProductName cannot be null.');
END IF;
END;
/
-- Explanation: Validates product name before insertion.
-- Q21: AFTER DELETE on InvoiceDetails to recalc TotalAmount
CREATE OR REPLACE TRIGGER trg_invoice_details_ad
AFTER DELETE ON InvoiceDetails
FOR EACH ROW
BEGIN
UPDATE Invoice i
SET TotalAmount = TotalAmount - (:OLD.Quantity * (SELECT Price FROM Product WHERE ProductID = :OLD.ProductID))
WHERE i.InvoiceID = :OLD.InvoiceID;
END;
/
-- Explanation: Adjusts invoice total when details are deleted.
-- Q22: BEFORE UPDATE on Customer to prevent changing CategoryID if TotalOrders>10
CREATE OR REPLACE TRIGGER trg_customer_bu
BEFORE UPDATE ON Customer
FOR EACH ROW
DECLARE
v_count NUMBER;
BEGIN
SELECT COUNT(*) INTO v_count FROM Orders WHERE CustomerID = :OLD.CustomerID;
IF v_count > 10 AND :NEW.CategoryID <> :OLD.CategoryID THEN
RAISE_APPLICATION_ERROR(-20011,'Cannot change CategoryID for active customer.');
END IF;
END;
/
-- Explanation: Protects category changes for active customers.
-- Q23: AFTER INSERT on Product to add stock entry
CREATE OR REPLACE TRIGGER trg_product_stock
AFTER INSERT ON Product
FOR EACH ROW
BEGIN
INSERT INTO Stock(ProductID, Quantity) VALUES(:NEW.ProductID, 0);
END;
/
-- Explanation: Adds initial stock record for new products.
-- Q24: BEFORE DELETE on Orders to log details
CREATE OR REPLACE TRIGGER trg_orders_bd
BEFORE DELETE ON Orders
FOR EACH ROW
BEGIN
INSERT INTO DeletedOrders(OrderID, CustomerID, OrderDate)
VALUES(:OLD.OrderID, :OLD.CustomerID, :OLD.OrderDate);
END;
/
-- Explanation: Logs order details before deletion for auditing.
-- Q25: AFTER UPDATE on Invoice to log total changes
CREATE OR REPLACE TRIGGER trg_invoice_au_log
AFTER UPDATE ON Invoice
FOR EACH ROW
BEGIN
IF :OLD.TotalAmount <> :NEW.TotalAmount THEN
INSERT INTO InvoiceHistory(InvoiceID, OldTotal, NewTotal, ChangeDate)
VALUES(:NEW.InvoiceID, :OLD.TotalAmount, :NEW.TotalAmount, SYSDATE);
END IF;
END;
/
-- Explanation: Tracks changes in invoice total for auditing.
-- Q26: BEFORE INSERT on Invoice to ensure OrderID exists
CREATE OR REPLACE TRIGGER trg_invoice_bi
BEFORE INSERT ON Invoice
FOR EACH ROW
DECLARE
v_count NUMBER;
BEGIN
SELECT COUNT(*) INTO v_count FROM Orders WHERE OrderID = :NEW.OrderID;
IF v_count = 0 THEN
RAISE_APPLICATION_ERROR(-20012,'OrderID does not exist.');
END IF;
END;
/
-- Explanation: Validates foreign key before inserting invoice.
-- Q27: BEFORE UPDATE on Product to prevent changing CategoryID if sales exist
CREATE OR REPLACE TRIGGER trg_product_bu_category
BEFORE UPDATE ON Product
FOR EACH ROW
DECLARE
v_count NUMBER;
BEGIN
SELECT COUNT(*) INTO v_count FROM InvoiceDetails WHERE ProductID = :OLD.ProductID;
IF v_count > 0 AND :NEW.CategoryID <> :OLD.CategoryID THEN
RAISE_APPLICATION_ERROR(-20013,'Cannot change category for products with sales.');
END IF;
END;
/
-- Explanation: Prevents category update for sold products.
-- Q28: AFTER INSERT on Customer to assign default CategoryID
CREATE OR REPLACE TRIGGER trg_customer_ai_default
AFTER INSERT ON Customer
FOR EACH ROW
BEGIN
IF :NEW.CategoryID IS NULL THEN
UPDATE Customer SET CategoryID=1 WHERE CustomerID=:NEW.CustomerID;
END IF;
END;
/
-- Explanation: Assigns a default category for new customers if none is provided.
-- Q29: BEFORE DELETE on Customer to prevent deletion if pending orders exist
CREATE OR REPLACE TRIGGER trg_customer_bd
BEFORE DELETE ON Customer
FOR EACH ROW
DECLARE
v_count NUMBER;
BEGIN
SELECT COUNT(*) INTO v_count FROM Orders WHERE CustomerID = :OLD.CustomerID AND Status='Pending';
IF v_count > 0 THEN
RAISE_APPLICATION_ERROR(-20014,'Cannot delete customer with pending orders.');
END IF;
END;
/
-- Explanation: Protects customers with pending orders from deletion.
-- Q30: AFTER UPDATE on Orders to update Invoice TotalAmount if InvoiceDetails changed
CREATE OR REPLACE TRIGGER trg_orders_au
AFTER UPDATE ON Orders
FOR EACH ROW
BEGIN
UPDATE Invoice i
SET TotalAmount = (SELECT SUM(d.Quantity * p.Price)
FROM InvoiceDetails d
JOIN Product p ON d.ProductID = p.ProductID
WHERE d.InvoiceID = i.InvoiceID)
WHERE i.OrderID = :NEW.OrderID;
END;
/
-- Explanation: Recalculates invoice totals when orders are updated.
কোন মন্তব্য নেই:
একটি মন্তব্য পোস্ট করুন