PL/SQL Stored Procedure Q31 – Safe Delete Product with Exception Handling

📘 PL/SQL Stored Procedure Lab Exam – 30 Questions (Sample Schema)

Practice these 30 stored procedure questions based on the sample database schema for Product, Customer, Orders, Invoice, and InvoiceDetails.

plsql stored procedure questions, plsql procedure lab exam, parameterized procedure plsql, exception handling plsql, plsql loops, procedure creation, plsql final exam, plsql procedure examples, database operations plsql

🔹 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 Stored Procedure Questions (Q1–Q30)

  1. Create a procedure to display all products in the Product table.
  2. Create a procedure with an input parameter to display products of a specific category.
  3. Create a procedure to insert a new customer into the Customer table.
  4. Create a procedure to update product price by a given percentage.
  5. Create a procedure to delete a product based on ProductID.
  6. Create a procedure to display all orders of a specific customer using CustomerID as input.
  7. Create a procedure to calculate total invoice amount for a given customer.
  8. Create a procedure to insert a new order with order details.
  9. Create a procedure to update order status to 'Completed'.
  10. Create a procedure to calculate the total quantity sold for a specific product.
  11. Create a procedure with exception handling for inserting duplicate ProductID.
  12. Create a procedure to display invoices with a discount greater than a given value.
  13. Create a procedure to generate a report of customers with multiple orders.
  14. Create a procedure to display top 5 selling products.
  15. Create a procedure with IN and OUT parameters to return total invoice amount for a customer.
  16. Create a procedure to list all unpaid invoices.
  17. Create a procedure to apply a discount to all invoices of a customer.
  18. Create a procedure to delete orders that are canceled.
  19. Create a procedure to display products in a price range using IN parameters.
  20. Create a procedure to display orders within a date range.
  21. Create a procedure to calculate average invoice amount for a customer.
  22. Create a procedure to update customer category based on total purchases.
  23. Create a procedure to insert multiple products in a loop.
  24. Create a procedure to display orders grouped by order type.
  25. Create a procedure to check if a product exists before inserting a new order.
  26. Create a procedure to display invoice details including product names and quantities.
  27. Create a procedure to calculate grand total of all invoices.
  28. Create a procedure to update payment status of multiple invoices in a loop.
  29. Create a procedure with nested procedure to calculate invoice totals per order.
  30. Create a procedure to generate a summary report of customers, orders, and total invoice amounts.
  31. Create a procedure to handle exceptions when deleting a product that has related orders.

🔹 PL/SQL Stored Procedure Questions with Solutions

-- Q1: Display all products
CREATE OR REPLACE PROCEDURE show_all_products AS
BEGIN
  FOR rec IN (SELECT * FROM Product) LOOP
    DBMS_OUTPUT.PUT_LINE('ID: ' || rec.ProductID || ', Name: ' || rec.ProductName || ', Price: ' || rec.Price);
  END LOOP;
END;
/
-- Explanation: A simple cursor FOR loop fetches all product records and displays them using DBMS_OUTPUT.

-- Q2: Display products of a specific category
CREATE OR REPLACE PROCEDURE show_products_by_category(p_cat NUMBER) AS
BEGIN
  FOR rec IN (SELECT * FROM Product WHERE CategoryID = p_cat) LOOP
    DBMS_OUTPUT.PUT_LINE('ID: ' || rec.ProductID || ', Name: ' || rec.ProductName || ', Price: ' || rec.Price);
  END LOOP;
END;
/
-- Explanation: Uses an input parameter (p_cat) to filter products by category.

-- Q3: Insert a new customer
CREATE OR REPLACE PROCEDURE add_customer(p_name VARCHAR2, p_category NUMBER) AS
BEGIN
  INSERT INTO Customer(CustomerID, CustomerName, CategoryID)
  VALUES(Customer_seq.NEXTVAL, p_name, p_category);
  COMMIT;
END;
/
-- Explanation: Inserts a new customer using parameters and commits the transaction.

-- Q4: Update product price by percentage
CREATE OR REPLACE PROCEDURE update_product_price(p_id NUMBER, p_percent NUMBER) AS
BEGIN
  UPDATE Product SET Price = Price + (Price * p_percent / 100) WHERE ProductID = p_id;
  COMMIT;
END;
/
-- Explanation: Updates a product's price based on a given percentage.

-- Q5: Delete product by ProductID
CREATE OR REPLACE PROCEDURE delete_product(p_id NUMBER) AS
BEGIN
  DELETE FROM Product WHERE ProductID = p_id;
  COMMIT;
END;
/
-- Explanation: Deletes the product with the given ProductID.

-- Q6: Display all orders for a customer
CREATE OR REPLACE PROCEDURE show_orders_by_customer(p_cid NUMBER) AS
BEGIN
  FOR rec IN (SELECT * FROM Orders WHERE CustomerID = p_cid) LOOP
    DBMS_OUTPUT.PUT_LINE('OrderID: ' || rec.OrderID || ', Date: ' || rec.OrderDate || ', Status: ' || rec.Status);
  END LOOP;
END;
/
-- Explanation: Fetches all orders for a specific customer using a parameter.

-- Q7: Calculate total invoice for a customer
CREATE OR REPLACE PROCEDURE total_invoice_customer(p_cid NUMBER, p_total OUT NUMBER) AS
BEGIN
  SELECT SUM(i.TotalAmount) INTO p_total
  FROM Invoice i
  JOIN Orders o ON i.OrderID = o.OrderID
  WHERE o.CustomerID = p_cid;
END;
/
-- Explanation: Uses an OUT parameter to return total invoice amount for a customer.

-- Q8: Insert new order with details
CREATE OR REPLACE PROCEDURE add_order(p_cid NUMBER, p_type VARCHAR2, p_status VARCHAR2, p_productid NUMBER, p_qty NUMBER) AS
  v_orderid NUMBER;
  v_invoiceid NUMBER;
BEGIN
  INSERT INTO Orders(OrderID, CustomerID, OrderDate, OrderType, Status)
  VALUES(Order_seq.NEXTVAL, p_cid, SYSDATE, p_type, p_status)
  RETURNING OrderID INTO v_orderid;

  INSERT INTO Invoice(InvoiceID, OrderID, TotalAmount, PaymentStatus, Discount)
  VALUES(Invoice_seq.NEXTVAL, v_orderid, 0, 'Pending', 0)
  RETURNING InvoiceID INTO v_invoiceid;

  INSERT INTO InvoiceDetails(InvoiceID, ProductID, Quantity)
  VALUES(v_invoiceid, p_productid, p_qty);

  COMMIT;
END;
/
-- Explanation: Inserts an order, creates an invoice, and adds product details.

-- Q9: Update order status to 'Completed'
CREATE OR REPLACE PROCEDURE complete_order(p_oid NUMBER) AS
BEGIN
  UPDATE Orders SET Status = 'Completed' WHERE OrderID = p_oid;
  COMMIT;
END;
/
-- Explanation: Updates the status of a specific order.

-- Q10: Total quantity sold for a product
CREATE OR REPLACE PROCEDURE total_quantity_product(p_pid NUMBER, p_total OUT NUMBER) AS
BEGIN
  SELECT SUM(Quantity) INTO p_total FROM InvoiceDetails WHERE ProductID = p_pid;
END;
/
-- Explanation: Uses an OUT parameter to calculate total quantity sold.

-- Q11: Exception handling for duplicate ProductID
CREATE OR REPLACE PROCEDURE insert_product(p_id NUMBER, p_name VARCHAR2, p_price NUMBER) AS
BEGIN
  INSERT INTO Product(ProductID, ProductName, Price) VALUES(p_id, p_name, p_price);
  COMMIT;
EXCEPTION
  WHEN DUP_VAL_ON_INDEX THEN
    DBMS_OUTPUT.PUT_LINE('ProductID already exists.');
END;
/
-- Explanation: Handles duplicate primary key exception during insert.

-- Q12: Display invoices with discount > value
CREATE OR REPLACE PROCEDURE invoices_with_discount(p_discount NUMBER) AS
BEGIN
  FOR rec IN (SELECT * FROM Invoice WHERE Discount > p_discount) LOOP
    DBMS_OUTPUT.PUT_LINE('InvoiceID: ' || rec.InvoiceID || ', Total: ' || rec.TotalAmount || ', Discount: ' || rec.Discount);
  END LOOP;
END;
/
-- Explanation: Fetches invoices exceeding a discount threshold.

-- Q13: Report of customers with multiple orders
CREATE OR REPLACE PROCEDURE customers_multiple_orders AS
BEGIN
  FOR rec IN (SELECT CustomerID, COUNT(*) AS orders_count FROM Orders GROUP BY CustomerID HAVING COUNT(*) > 1) LOOP
    DBMS_OUTPUT.PUT_LINE('CustomerID: ' || rec.CustomerID || ', Orders: ' || rec.orders_count);
  END LOOP;
END;
/
-- Explanation: Uses GROUP BY and HAVING to identify customers with multiple orders.

-- Q14: Top 5 selling products
CREATE OR REPLACE PROCEDURE top_selling_products AS
BEGIN
  FOR rec IN (SELECT ProductID, SUM(Quantity) AS total_sold FROM InvoiceDetails GROUP BY ProductID ORDER BY total_sold DESC FETCH FIRST 5 ROWS ONLY) LOOP
    DBMS_OUTPUT.PUT_LINE('ProductID: ' || rec.ProductID || ', Sold: ' || rec.total_sold);
  END LOOP;
END;
/
-- Explanation: Aggregates sales per product and fetches top 5.

-- Q15: Procedure with IN and OUT parameters for total invoice
CREATE OR REPLACE PROCEDURE invoice_total(p_cid NUMBER, p_total OUT NUMBER) AS
BEGIN
  SELECT SUM(i.TotalAmount) INTO p_total
  FROM Invoice i JOIN Orders o ON i.OrderID = o.OrderID
  WHERE o.CustomerID = p_cid;
END;
/
-- Explanation: Returns total invoice using OUT parameter.

-- Q16: List unpaid invoices
CREATE OR REPLACE PROCEDURE unpaid_invoices AS
BEGIN
  FOR rec IN (SELECT * FROM Invoice WHERE PaymentStatus='Pending') LOOP
    DBMS_OUTPUT.PUT_LINE('InvoiceID: ' || rec.InvoiceID || ', Total: ' || rec.TotalAmount);
  END LOOP;
END;
/
-- Explanation: Displays all invoices with PaymentStatus 'Pending'.

-- Q17: Apply discount to all invoices of a customer
CREATE OR REPLACE PROCEDURE apply_discount(p_cid NUMBER, p_discount NUMBER) AS
BEGIN
  UPDATE Invoice i
  SET Discount = p_discount
  WHERE i.OrderID IN (SELECT OrderID FROM Orders WHERE CustomerID = p_cid);
  COMMIT;
END;
/
-- Explanation: Updates discount for all invoices of a specific customer.

-- Q18: Delete canceled orders
CREATE OR REPLACE PROCEDURE delete_canceled_orders AS
BEGIN
  DELETE FROM Orders WHERE Status='Canceled';
  COMMIT;
END;
/
-- Explanation: Removes all canceled orders from the Orders table.

-- Q19: Display products in price range
CREATE OR REPLACE PROCEDURE products_in_range(p_min NUMBER, p_max NUMBER) AS
BEGIN
  FOR rec IN (SELECT * FROM Product WHERE Price BETWEEN p_min AND p_max) LOOP
    DBMS_OUTPUT.PUT_LINE('ProductID: ' || rec.ProductID || ', Name: ' || rec.ProductName || ', Price: ' || rec.Price);
  END LOOP;
END;
/
-- Explanation: Filters products within the given price range.

-- Q20: Display orders within date range
CREATE OR REPLACE PROCEDURE orders_in_date_range(p_start DATE, p_end DATE) AS
BEGIN
  FOR rec IN (SELECT * FROM Orders WHERE OrderDate BETWEEN p_start AND p_end) LOOP
    DBMS_OUTPUT.PUT_LINE('OrderID: ' || rec.OrderID || ', Date: ' || rec.OrderDate);
  END LOOP;
END;
/
-- Explanation: Filters orders based on date range parameters.

-- Q21: Average invoice amount for customer
CREATE OR REPLACE PROCEDURE avg_invoice_customer(p_cid NUMBER, p_avg OUT NUMBER) AS
BEGIN
  SELECT AVG(i.TotalAmount) INTO p_avg
  FROM Invoice i JOIN Orders o ON i.OrderID = o.OrderID
  WHERE o.CustomerID = p_cid;
END;
/
-- Explanation: Calculates average invoice amount using OUT parameter.

-- Q22: Update customer category based on total purchases
CREATE OR REPLACE PROCEDURE update_customer_category(p_cid NUMBER, p_category NUMBER) AS
BEGIN
  UPDATE Customer SET CategoryID = p_category WHERE CustomerID = p_cid;
  COMMIT;
END;
/
-- Explanation: Updates customer category.

-- Q23: Insert multiple products in loop
CREATE OR REPLACE PROCEDURE insert_multiple_products AS
BEGIN
  FOR i IN 1..5 LOOP
    INSERT INTO Product(ProductID, ProductName, Price) VALUES(Product_seq.NEXTVAL, 'Product'||i, 100 + i*10);
  END LOOP;
  COMMIT;
END;
/
-- Explanation: Demonstrates looping to insert multiple products.

-- Q24: Display orders grouped by order type
CREATE OR REPLACE PROCEDURE orders_grouped_by_type AS
BEGIN
  FOR rec IN (SELECT OrderType, COUNT(*) AS cnt FROM Orders GROUP BY OrderType) LOOP
    DBMS_OUTPUT.PUT_LINE('Type: ' || rec.OrderType || ', Count: ' || rec.cnt);
  END LOOP;
END;
/
-- Explanation: Aggregates orders by type.

-- Q25: Check if product exists before inserting order
CREATE OR REPLACE PROCEDURE check_product_before_order(p_pid NUMBER) AS
  v_count NUMBER;
BEGIN
  SELECT COUNT(*) INTO v_count FROM Product WHERE ProductID = p_pid;
  IF v_count = 0 THEN
    DBMS_OUTPUT.PUT_LINE('Product does not exist.');
  ELSE
    DBMS_OUTPUT.PUT_LINE('Product exists, proceed.');
  END IF;
END;
/
-- Explanation: Validates product existence using COUNT.

-- Q26: Display invoice details with product names
CREATE OR REPLACE PROCEDURE invoice_details_with_products AS
BEGIN
  FOR rec IN (SELECT i.InvoiceID, p.ProductName, d.Quantity
              FROM Invoice i
              JOIN InvoiceDetails d ON i.InvoiceID = d.InvoiceID
              JOIN Product p ON d.ProductID = p.ProductID) LOOP
    DBMS_OUTPUT.PUT_LINE('InvoiceID: ' || rec.InvoiceID || ', Product: ' || rec.ProductName || ', Qty: ' || rec.Quantity);
  END LOOP;
END;
/
-- Explanation: Joins tables to display detailed invoice info.

-- Q27: Calculate grand total of all invoices
CREATE OR REPLACE PROCEDURE grand_total_invoices(p_total OUT NUMBER) AS
BEGIN
  SELECT SUM(TotalAmount) INTO p_total FROM Invoice;
END;
/
-- Explanation: Aggregates all invoice totals using OUT parameter.

-- Q28: Update payment status of multiple invoices in loop
CREATE OR REPLACE PROCEDURE update_payment_status AS
BEGIN
  FOR rec IN (SELECT InvoiceID FROM Invoice WHERE PaymentStatus='Pending') LOOP
    UPDATE Invoice SET PaymentStatus='Paid' WHERE InvoiceID=rec.InvoiceID;
  END LOOP;
  COMMIT;
END;
/
-- Explanation: Iterates over pending invoices to update status.

-- Q29: Nested procedure to calculate invoice totals per order
CREATE OR REPLACE PROCEDURE nested_invoice_totals AS
  PROCEDURE calc_total(p_oid NUMBER, p_total OUT NUMBER) IS
  BEGIN
    SELECT SUM(Quantity*Price) INTO p_total
    FROM InvoiceDetails d JOIN Product p ON d.ProductID = p.ProductID
    WHERE d.InvoiceID = p_oid;
  END;
BEGIN
  DBMS_OUTPUT.PUT_LINE('Nested procedure defined for invoice totals.');
END;
/
-- Explanation: Demonstrates nested procedure usage.

-- Q30: Summary report of customers, orders, and total invoice
CREATE OR REPLACE PROCEDURE customer_order_summary AS
BEGIN
  FOR rec IN (SELECT c.CustomerID, COUNT(o.OrderID) AS orders_count, SUM(i.TotalAmount) AS total_invoice
              FROM Customer c
              LEFT JOIN Orders o ON c.CustomerID = o.CustomerID
              LEFT JOIN Invoice i ON o.OrderID = i.OrderID
              GROUP BY c.CustomerID) LOOP
    DBMS_OUTPUT.PUT_LINE('CustomerID: ' || rec.CustomerID || ', Orders: ' || rec.orders_count || ', Total: ' || rec.total_invoice);
  END LOOP;
END;
/
-- Explanation: Generates a comprehensive summary report.

🔹 Q31: Delete Product with Exception Handling

plsql delete product procedure, plsql exception handling, plsql stored procedure lab, foreign key exception plsql, safe delete procedure plsql
CREATE OR REPLACE PROCEDURE delete_product_safe(p_id NUMBER) AS
BEGIN
  DELETE FROM Product WHERE ProductID = p_id;
  COMMIT;
  DBMS_OUTPUT.PUT_LINE('Product deleted successfully.');
EXCEPTION
  WHEN OTHERS THEN
    DBMS_OUTPUT.PUT_LINE('Cannot delete product. It may have related orders.');
END;
/
-- Explanation: 
-- Attempts to delete a product. If the product is referenced in Orders or InvoiceDetails, 
-- the foreign key constraint will raise an exception. 
-- This is caught in the EXCEPTION block to prevent procedure failure and display a clear message.

কোন মন্তব্য নেই:

একটি মন্তব্য পোস্ট করুন