0% found this document useful (0 votes)
39 views

235 Lab Exam Cheatsheet - Joshua Edition

Uploaded by

Naaveed Aslam
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
39 views

235 Lab Exam Cheatsheet - Joshua Edition

Uploaded by

Naaveed Aslam
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 4

235 Lab Exam

MongoDB Lab 7: 1l) Show Employee Name, department and project list of employees working 17.Set the stock quantity of a product to 0 when it is out of stock.
insertOne() inserts a single object into the database. on projects with budget between 100000 and 500000 (you must use db.products.updateMany({ stock_quantity: 0 }, { $set: { stock_quantity: 0 } });
$elemMatch) 18.Update a product’s price by increasing it by 10% if the current price is less
insertMany() inserts an array of objects into the database.
db.Employees.find({ "Projects": { $elemMatch: { "Budget": { $gte: 100000, than 100.
find() This method accepts a query object. If left empty, $lte: 500000 } } } }, { "Name": 1, "Department": 1, "Projects": 1 }) 1m) db.products.updateMany({ price: { $lt: 100 } },{ $mul: { price: 1.10 } }); 19.Add
all documents will be returned Change above query to show only the projects with matching budget a new tag ("wireless") to a product's tags array.
findOne() This method accepts a query object. If left empty, it range db.products.updateMany( { tags: { $exists: true } }, { $addToSet: { tags:
will return the first document it finds db.Employees.find({ "Projects.Budget": { $gte: 100000, $lte: 500000 } }, { "wireless" } } );
updateOne() update the first document that is found matching "Projects.$": 1 }) 20.Update the order status to "delivered" for an order that was shipped more
the provided query 2a) Project only name and price fields of all documents than 7 days ago.
updateMany() update all documents that match the provided db.PizzaOrders.find({}, { "name": 1, "price": 1 }) 2b) Project all fields except db.orders.updateMany({ status: "shipped", order_date: { $lte: new Date(new
query _id and date db.PizzaOrders.find({}, { "_id": 0, "date": 0 }) 2c) Project only Date().setDate(new Date().getDate() - 7)) } },{ $set: { status: "delivered" } });
the documents with a price greater than 15, including only name and 21.Change the status of an order to "canceled" if the total amount is less than
deleteOne() delete the first document that matches the query
price fields db.PizzaOrders.find({ "price": { $gt: 15 } }, { "name": 1, "price": 10. db.orders.updateMany({ total_amount: { $lt: 10 } }, { $set: { status:
provided
1 }) 2d) Project only the documents ordered in the first quarter of year "canceled"
deleteMany() delete all documents that match the query 2021, including the name, size, and date fields db.PizzaOrders.find({ } });
provided "date": { $gte: ISODate("2021-01-01"), $lt: Delete Queries:
upsert to insert the document if it is not found ISODate("2021-04-01") } }, { "name": 1, "size": 1, "date": 1 }) 22. Delete all products that have never been reviewed.
1a) Create a database use 2e) Find all pizzas ordered between March 13th and March 17th , 2021 and db.products.deleteMany({name: { $nin: db.reviews.distinct("product_name")
employee project only name, size and price. db.PizzaOrders.find({ "date": { $gte: }});
1b) Create a new document with the name ‘Employees’ ISODate("2021-03-13"), $lte: ISODate("2021-03-17") } }, { "name": 1, "size": 1, 23. Remove a review for a product based on the review’s review_date.
db.createCollection("Employees") 1c) Insert the above collections into "price": 1 }) 2f) Find all Pepperoni pizzas with a price greater than 19 and db.reviews.deleteOne({ review_date: ISODate("2024-10-15T10:00:00Z")
‘Employees’ document db.Employees.insertMany([{"E_id": 1234,"Name": project their size and quantity. db.PizzaOrders.find({ "name": "Pepperoni", }); 24. Delete an order if the order's total amount is greater than $200 and
{"Fname":"Asma","Lname":"Damankesh"},…}]) 1d) Show name, gender, and "price": { $gt: 19 } }, { "size": 1, the status is still "processing".
position only db.Employees.find({}, {"Name": 1, "Gender": 1, "Position": 1, "quantity": 1 }) db.orders.deleteMany({ total_amount: { $gt: 200 }, status: "processing" });
"_id": 0}) 1e) Search a record, if not found then make sure the record gets 2g) Find all pizza names that start with ‘P’ and project their name and price. 25. Delete a user and all their orders, reviews, and associated data.
inserted into the table db.Employees.updateOne({name: "Joshua Barretto"}, Hint: use /^P/ in name field during project to search for name starts with const username = "John Doe"; db.users.deleteOne({
{$setOnInsert: {name: "Joshua Barretto", name: "Alice Williams", age: 21, letter ‘P’. db.PizzaOrders.find({ "name": /^P/ }, { "name": 1, "price": 1 }) name: username }); db.orders.deleteMany({
gender: "Male", position: MongoDB Lab 9: user_name: username }); db.reviews.deleteMany({
"HR Specialist", salary: 55000}}, {upsert: true}) 2c) $match stage filters those documents we need to work with, user_name: username }); 26. Delete all out-of-stock
Retrieve all documents from the “films” collection those that fit our needs products from the catalog
db.films.find() 2d) Update the Casablanca movie rating to 5.5 db.products.deleteMany({ stock_quantity: 0 });
$group stage does the aggregation job
db.films.updateOne({"title": "Casablanca"}, {$set: {"rating": 5.5}}) Other advanced queries:
2e) Change director name and genre of City Lights film. $sort stage sorts the resulting documents the way we require
27. Find the most recent order for a user (user_id).
db.films.updateOne({"title": "City Lights"}, {$set: {"director": "John Doe", (ascending or descending)
db.orders.find({ user_name: "John Doe" }).sort({ order_date: -1 }).limit(1); 28.
"genre": "Comedy"}}) 2f) Delete a $project pass only specified fields or change fields labels in Find the product with the highest price in each category.
movie released in 2003 stage the output db.products.aggregate([ { $group: { _id: "$category", max_price: { $max:
db.films.deleteOne({"year": 2003}) $match stage Will filter documents that match the query "$price" } } } ]);
Mongo Lab 8: provided 29. Find users who have not placed an order in the last year.
MongoDB Query Operators $skip stage Skip the number of specified documents starting db.users.find({ name: { $nin: db.orders.distinct("user_name", { order_date: {
Comparison from the first document $gte: new Date(new Date().setFullYear(new Date().getFullYear() - 1)) } }) } });
$limit stage limits the number of documents passed to the 30. Retrieve all products that have been ordered at least 10 times.
next stage db.orders.aggregate([ { $unwind: "$items" }, { $group: { _id:
$eq Values are equal
$count stage counts the total amount of documents passed "$items.product_name", total_ordered: { $sum: "$items.quantity" } } }, {
$ne Values are not equal $match: { total_ordered: { $gte: 10 } } } ]);
from the previous stage
$gt Value is greater than another value 31. Find all orders where a product has a quantity greater than 3.
$sort stage groups all documents in the specified sort order
db.orders.find({ "items.quantity": { $gt: 3 } });
$gte Value is greater than or equal to another value
$unwind Expands (unnest) an array, generating one output 32. Add a new field last_order_date to each user document with the date of
$lt Value is less than another value stage document for each array entry their most recent order.
$lte Value is less than or equal to another value $out stage Create a new collection and saves the results from db.users.aggregate([ { $lookup: { from: "orders", localField: "name",
$in Value is matched within an array a pipeline to this collection foreignField: "user_name", as: "user_orders" } }, { $addFields: {
a) Use the sales collection and calculate the total revenue per order. last_order_date: { $max: "$user_orders.order_date" } } } ]);
Logical
db.sales.aggregate([{ $addFields: { revenue: { $multiply: ["$price", 33. Find users who live in the same city ("Los Angeles") and have purchased a
"$quantity"] Wireless Mouse.
$and Returns documents where both queries match } } }, { $group: { _id: "$order_id", totalRevenue: { $sum: "$revenue" } } }]); db.users.aggregate([ { $match: { "address.city": "Los Angeles" } }, { $lookup: {
$or Returns documents where either query b. Use the sales collection to identify the top selling from: "orders", localField: "name", foreignField: "user_name", as:
matches products based on the quantity sold. db.sales.aggregate([{ $group: { _id: "user_orders" } }, { $match: { "user_orders.items.product_name": "Wireless
"$product", totalQuantity: { $sum: Mouse" } } ]); Postgres
$nor Returns documents where both queries fail to
"$quantity" } } }, { $sort: { totalQuantity: -1 } }]); Practice Questions
match
c. Use the orders collection and find the total revenue PL/PGSQL Block Questions:
$not Returns documents where the query does not generated by each pizza in the year 2021. Hint: Multiply price and quantity to 1. Write a PL/pgSQL anonymous block that loops through all books in the
match compute revenue. db.orders.aggregate([{ $match: { year: 2021 } }, { $addFields: books table and prints the title and price for each book.
Evaluation { revenue: { $multiply: ["$price", "$quantity"] } } }, { $group: { _id: "$pizza", DO $$ DECLARE
totalRevenue: { book_rec RECORD;
$sum: "$revenue" } } }]); BEGIN
$regex Allows the use of regular expressions when
d. Count the number of documents (orders) for each pizza FOR book_rec IN SELECT title, price FROM books LOOP
evaluating field values
type. db.orders.aggregate([{ $group: { _id: "$pizza", orderCount: { $sum: 1 } } RAISE NOTICE 'Book: %, Price: $%', book_rec.title, book_rec.price; END LOOP;
$text Performs a text search }]); END; $$;
$where Uses a JavaScript expression to match e. Find all Pepperoni pizza orders placed after 15th March 2. Write a PL/pgSQL block that calculates the total value of stock (stock
documents 2021 and project only name and quantity. db.orders.find({ pizza: "Pepperoni", quantity * price) for all books and prints the result.
Fields date: { $gt: new Date("2021-03-15") } }, { name: 1, quantity: 1, _id: 0 }); DO $$ DECLARE total_value
f. Use inventory collection to group items by size and count DECIMAL(10,2);
the number of items. BEGIN
$currentDate Sets the field value to the current date db.inventory.aggregate([{ $group: { _id: "$size", itemCount: { $sum: 1 } } SELECT SUM(price * stock_quantity) INTO total_value FROM books;
$inc Increments the field value }]); g. Find items from inventory collection without sizes or sizes field is RAISE NOTICE 'Total stock value: $%', total_value;
$rename Renames the field null. db.inventory.find({ $or: [{ size: { $exists: false } }, { size: null }] }); h. END; $$;
Find items from the inventory collection with multiple sizes. 3. Write a PL/pgSQL block that checks if a customer’s email exists in the
$unset Removes the field from the document
db.inventory.find({ size: { $type: "array" } }); MongoDB customers table, and if so, prints their name. If not, prints "Customer not
$set Sets the value of a field Lab Practice: found".
Array Find Queries: DO $$ DECLARE
1. Find all active users (is_active: true) db.users.find({ is_active: v_email VARCHAR := 'alice@example.com'; v_name VARCHAR;
true }); BEGIN
$addToSet Adds distinct elements to an array 2. Find all products in the Electronics category with a price greater SELECT name INTO v_name FROM customers WHERE email = v_email;
$pop Removes the first or last element of an array than 50. IF FOUND THEN
$pull Removes all elements from an array that match db.products.find({ category: "Electronics", price: { $gt: 50 } }); 3. RAISE NOTICE 'Customer found: %', v_name;
the query Find a user by their email address ("john@example.com") ELSE
db.users.find({ email: "john@example.com" }); RAISE NOTICE 'Customer not found';
$push Adds an element to an array
4. Find products that are currently out of stock (stock_quantity: 0).
1a) How many female employees are working in the company? END IF; END; $$;
db.products.find({ stock_quantity: 0 }); 5.
db.Employee.countDocuments({"Gender": "F"}) 1b) How many female 4. Write a PL/pgSQL block that updates the price of a book in the books table,
Find all orders placed in the last 30 days.
employees are working in IT Department? but only if the new price is greater than the current price. If the price is not
db.orders.find({ order_date: { $gte: new Date(new Date().setDate(new updated, print "Price not increased".
db.Employee.countDocuments({"Gender": "F", "Department":"IT"}) 1c) How
Date().getDate() - 30)) } }); 6. Find all DO $$ DECLARE
many female employees are working in any department except IT, HR or
reviews with a rating of 5. v_book_id INT := 1; v_new_price DECIMAL := 15.99; v_current_price
Marketing? db.Employee.countDocuments({"Gender": "F",
db.reviews.find({ rating: 5 });
"Department":{$nin: ["IT", DECIMAL;
7. Find all users who have made at least one order in the
"HR","Marketing"]}}) BEGIN
last 6 months. db.orders.aggregate([{ $match: { order_date: { $gte: new
1d) show name, gender, position and department of non-teaching staff SELECT price INTO v_current_price FROM books WHERE book_id = v_book_id;
Date(new Date().setMonth(new Date().getMonth() - 6)) } } },{ $group: { _id:
db.Employee.find({"Position":{$ne: "Teacher"}}, {"Name": 1, "Gender": 1, IF v_new_price > v_current_price THEN
"$user_name" } }]);
"Position":1, "Department":1}) UPDATE books
8. Find products that belong to more than one category.
1e) show all details except salary, date of birth, height and default id of SET price = v_new_price
nonteaching staffs db.products.find({ categories: { $exists: true, $size: { $gt: 1 } } }); Aggregation
WHERE book_id = v_book_id;
Queries:
db.Employee.find({"Position": {$ne:"Teacher"}},{"Salary": 0, "DOB": 0, RAISE NOTICE 'Price updated to: $%', v_new_price;
9.Find the total number of orders for each user (group by user_id)
"Height":0, "_id":0}) ELSE
db.orders.aggregate([{ $group: { _id: "$user_name", total_orders: { $sum: 1 } }
1f) Show all details of all employees who are working in IT and are responsible RAISE NOTICE 'Price not increased';
}]);
for Daily Support END IF; END; $$;
10. Calculate the total amount spent by each user (sum of total_amount
db.Employee.find({"Department": "IT", "Job List": "Daily Support"}) 1g) 5. Write a PL/pgSQL block that raises an exception when an attempt is made to
in orders) db.orders.aggregate([ { $group: { _id: "$user_name",
Show ONLY department and salary details of all employees who are working delete a book that has more than 10 items in stock.
total_spent: { $sum:
in IT or are responsible for Daily Support db.Employee.find({$or: DO $$ DECLARE
"$total_amount" } } } ]); 11.Find the average rating for each product
[{"Department": "IT"}, {"Job List": "Daily Support"}]}, {"Department": 1, v_book_id INT := 1; v_stock INT;
db.reviews.aggregate([{ $group: { _id: "$product_name", average_rating:
"Salary": 1}); BEGIN
{
1h) Show ONLY department and salary details of all employees who are SELECT stock_quantity INTO v_stock FROM books WHERE book_id =
$avg: "$rating" } } }]);
working in IT or are responsible for Daily Support or Accounting v_book_id;
12.Group products by brand and count how many products are in each brand.
db.Employee.find({$or:[{"Department": "IT"},{"Joblist":"Daily IF v_stock > 10 THEN
Support"}]},{"Department":1, "Salary":1}) 1i) Show hourly employees who are db.products.aggregate([{ $group: { _id: "$brand", product_count: { $sum: 1 } }
}]); RAISE EXCEPTION 'Cannot delete book with more than 10 items in stock';
paid between 100 and 200 (including 100 and 200)
13.Find the most popular product based on the number of times it has been ELSE
db.Employee.find({ "Salary.Salary Type": "Hourly", "Salary.Rate": { $gte: 100,
ordered DELETE FROM books WHERE book_id = v_book_id;
$lte: 200 } })
db.orders.aggregate([ { $unwind: "$items" }, { $group: { RAISE NOTICE 'Book deleted successfully';
1j) Show hourly employees who are paid between 50 and 200 (including 50
_id:"$items.product_name", total_ordered: { $sum: "$items.quantity" } } }, END IF; END; $$; PL/PGSQL Function
and 200) and has a job list
{$sort: { total_ordered: -1 } }, { $limit: 1 } ]); 14.Find the top 5 questions:
db.Employees.find({ "Salary.Salary Type": "Hourly", "Salary.Rate": { $gte: 50,
most expensive products db.products.find().sort({ price: -1 1. Write a function get_total_order_amount(order_id INT) that returns the
$lte: 200 }, "Job List": { $exists: true } })
}).limit(5); 15.Calculate the total stock available for each product total amount of an order by summing the price * quantity for each item in
1k) Show hourly employees who are paid between 50 and 200 (including 20 the order_items table.
category db.products.aggregate([ { $group: { _id: "$category",
and 200) and has at least 2 jobs in their job lists db.Employees.find({ CREATE OR REPLACE FUNCTION get_total_order_amount(p_order_id INT)
total_stock: { $sum:"$stock_quantity" } } } ]); Update Queries:
"Salary.Salary Type": "Hourly", "Salary.Rate": { $gte: 50,
16.Update a user’s address by changing their city to "San Diego". RETURNS DECIMAL(10,2) AS $$ BEGIN
$lte: 200 }, "Job List": { $size: 2 } })
db.users.updateOne( { "address.city": "Los Angeles" }, { $set: { "address.city": RETURN (
"San Diego" } } );
235 Lab Exam

SELECT SUM(quantity * price) FROM order_items WHERE order_id = BEGIN AFTER INSERT OR UPDATE OR DELETE ON books FOR EACH ROW
p_order_id IF p_new_price < 0 THEN RAISE EXCEPTION 'Price cannot be negative'; EXECUTE FUNCTION log_book_changes();
); END; END IF; 5. Write a trigger that sends a warning (using RAISE NOTICE) if a book's stock
$$ LANGUAGE plpgsql; UPDATE books quantity drops below 5 whenever an order is placed for that book. CREATE
2. Write a function get_book_stock(book_id INT) that returns the stock SET price = p_new_price WHERE book_id = p_book_id; TABLE book_changes_log ( log_id SERIAL PRIMARY KEY, action_type
quantity of a book in the books table. END; VARCHAR(10),
CREATE OR REPLACE FUNCTION get_book_stock(p_book_id INT) $$ LANGUAGE plpgsql; book_id INT, change_timestamp TIMESTAMP DEFAULT
RETURNS INT AS $$ 2. Write a PL/pgSQL block that tries to insert a new order. If the customer_id CURRENT_TIMESTAMP
BEGIN does not exist, raise an exception saying "Customer not found". );
RETURN ( DO $$ DECLARE CREATE OR REPLACE FUNCTION log_book_changes() RETURNS TRIGGER AS $$
SELECT stock_quantity FROM booksWHERE book_id = p_book_id v_customer_id INT := 999; -- Non-existent customer BEGIN
); END; BEGIN IF TG_OP = 'INSERT' THEN INSERT INTO book_changes_log (action_type,
$$ LANGUAGE plpgsql; IF NOT EXISTS ( book_id) VALUES ('INSERT', NEW.book_id); ELSIF TG_OP = 'UPDATE'
3. Write a function get_customer_order_count(customer_id INT) that returns SELECT 1 FROM customers WHERE customer_id = v_customer_id THEN INSERT INTO book_changes_log
the total number of orders placed by a customer. ) THEN (action_type, book_id) VALUES ('UPDATE', NEW.book_id); ELSIF TG_OP =
CREATE OR REPLACE FUNCTION get_customer_order_count(p_customer_id RAISE EXCEPTION 'Customer not found'; 'DELETE' THEN INSERT INTO book_changes_log (action_type, book_id)
INT) END IF; VALUES ('DELETE', OLD.book_id);
RETURNS INT AS $$ INSERT INTO orders (customer_id, total_amount) END IF;
BEGIN VALUES (v_customer_id, 0); RETURN NULL;
RETURN ( END; END;
SELECT COUNT(*) $$; $$ LANGUAGE plpgsql; CREATE TRIGGER log_book_changes_trigger
FROM orders 3. Write a function that deletes a book from the books table. If the book is out AFTER INSERT OR UPDATE OR DELETE ON books FOR EACH ROW
WHERE customer_id = p_customer_id of stock (stock_quantity = 0), raise an exception saying "Cannot delete: Book EXECUTE FUNCTION log_book_changes();
); out of stock". Cursor Questions
END; CREATE OR REPLACE FUNCTION delete_book_safe(p_book_id INT) 1. Write a PL/pgSQL block that uses a cursor to iterate over all the books in the
$$ LANGUAGE plpgsql; RETURNS VOID AS $$ BEGIN books table and prints their title and price.
4. Write a function get_average_book_rating(book_id INT) that returns the IF EXISTS ( CREATE OR REPLACE FUNCTION update_order_total()
average rating for a book based on reviews from the reviews table. SELECT 1 FROM books WHERE book_id = p_book_id RETURNS TRIGGER AS $$
CREATE OR REPLACE FUNCTION get_average_book_rating(p_book_id INT) AND stock_quantity = 0 BEGIN
RETURNS DECIMAL(3,2) AS $$ ) THEN UPDATE orders
BEGIN RAISE EXCEPTION 'Cannot delete: Book out of stock'; SET total_amount = (
RETURN ( END IF; SELECT SUM(quantity * price)
SELECT COALESCE(AVG(rating), 0) FROM reviews WHERE book_id = DELETE FROM books WHERE book_id = p_book_id; FROM order_items WHERE order_id = NEW.order_id
p_book_id END; )
); END; $$ LANGUAGE plpgsql; $$ LANGUAGE plpgsql; WHERE order_id = NEW.order_id;
5. Write a function update_book_price(book_id INT, new_price DECIMAL) 4. Write a PL/pgSQL block that updates the stock quantity for a book. If the RETURN NEW;
that updates the price of a book only if the new price is greater than the stock quantity is set to a negative number, raise an exception saying "Stock END;
current price, otherwise, raise an exception saying "Price must be quantity cannot be negative". $$ LANGUAGE plpgsql;
increased". DO $$ DECLARE CREATE TRIGGER update_order_total_trigger AFTER INSERT OR UPDATE ON
CREATE OR REPLACE FUNCTION update_book_price(p_book_id INT, v_book_id INT := 1; v_new_quantity INT := -5; order_items FOR EACH ROW EXECUTE FUNCTION update_order_total();
p_new_price DECIMAL) BEGIN 2. Write a function that uses a cursor to return all orders for a given customer,
RETURNS VOID AS $$ IF v_new_quantity < 0 THEN including the order_id, order_date, and total_amount.
DECLARE v_current_price RAISE EXCEPTION 'Stock quantity cannot be negative'; CREATE OR REPLACE FUNCTION prevent_book_deletion()
DECIMAL; END IF; RETURNS TRIGGER AS $$
BEGIN UPDATE books SET stock_quantity = v_new_quantity WHERE book_id = BEGIN
SELECT price INTO v_current_price FROM books WHERE book_id = v_book_id; END; $$; IF EXISTS (
p_book_id; IF p_new_price <= v_current_price THEN 5. Write a PL/pgSQL block that attempts to insert a review for a book. If the SELECT 1 FROM order_items WHERE book_id = OLD.book_id
RAISE EXCEPTION 'Price must be increased'; rating is not between 1 and 5, raise an exception with the message "Invalid ) THEN
END IF; rating. Rating must be between 1 and 5". RAISE EXCEPTION 'Cannot delete book with existing orders';
UPDATE books DO $$ DECLARE END IF;
SET price = p_new_price v_book_id INT := 1; v_customer_id INT := 1; v_rating INT := 6; v_review_text RETURN OLD;
WHERE book_id = p_book_id; END; TEXT := 'Great book!'; END;
$$ LANGUAGE plpgsql; Procedure BEGIN $$ LANGUAGE plpgsql;
Questions: IF v_rating NOT BETWEEN 1 AND 5 THEN RAISE EXCEPTION 'Invalid rating. CREATE TRIGGER prevent_book_deletion_trigger BEFORE DELETE ON books
1. Write a procedure Rating must be between 1 and 5'; FOR EACH ROW EXECUTE FUNCTION prevent_book_deletion();
add_new_order(customer_id INT, books_with_quantities END IF; 3. Write a function that uses a cursor to calculate the total number of books
JSONB) that creates a new order for a customer and inserts the order items INSERT INTO reviews (book_id, customer_id, rating, review_text) sold for each book (sum of quantities in order_items), and returns a record
into the order_items table based on the books and quantities provided in the VALUES (v_book_id, v_customer_id, v_rating, v_review_text); set of book titles and total sales.
JSON array. CREATE OR REPLACE PROCEDURE add_new_order( CREATE OR REPLACE FUNCTION get_book_sales()
END;
p_customer_id INT, p_books_with_quantities JSONB RETURNS TABLE ( book_title VARCHAR(255),
$$; Trigger
) AS $$ DECLARE total_books_sold INT ) AS $$
questions:
v_order_id INT; 1. Write a trigger that automatically updates the total_amount field in the DECLARE book_cursor CURSOR FOR
v_book_record RECORD; BEGIN orders table whenever an order_items record is inserted or updated. SELECT b.book_id, b.title FROM books b;
-- Create the order CREATE OR REPLACE FUNCTION update_order_total() book_rec RECORD; total_sold INT; BEGIN
INSERT INTO orders (customer_id, total_amount) RETURNS TRIGGER AS $$ CREATE TEMPORARY TABLE book_sales_temp (
VALUES (p_customer_id, 0) BEGIN book_id INT,
RETURNING order_id INTO v_order_id; UPDATE orders SET total_amount = ( book_title VARCHAR(255),
-- Insert order items SELECT SUM(quantity * price) FROM order_items WHERE order_id = total_books_sold INT
FOR v_book_record IN NEW.order_id) );
SELECT * FROM jsonb_to_recordset(p_books_with_quantities) AS x(book_id WHERE order_id = NEW.order_id; RETURN NEW; OPEN book_cursor;
INT, quantity INT) END; LOOP
LOOP $$ LANGUAGE plpgsql; FETCH book_cursor INTO book_rec; EXIT WHEN NOT FOUND;
INSERT INTO order_items (order_id, book_id, quantity, price) CREATE TRIGGER update_order_total_trigger AFTER INSERT OR UPDATE ON SELECT COALESCE(SUM(quantity), 0) INTO total_sold FROM order_items
SELECT v_order_id, v_book_record.book_id, v_book_record.quantity, order_items WHERE book_id = book_rec.book_id;
books.price INSERT INTO book_sales_temp
FOR EACH ROW EXECUTE FUNCTION update_order_total();
FROM books WHERE book_id = v_book_record.book_id; 2. Write a trigger that prevents deleting a book from the books table if it has VALUES (book_rec.book_id, book_rec.title, total_sold);
END LOOP; any related entries in the order_items table. END LOOP; CLOSE book_cursor;
-- Update total amount CREATE OR REPLACE FUNCTION prevent_book_deletion() RETURN QUERY
UPDATE orders RETURNS TRIGGER AS $$ SELECT book_title, total_books_sold FROM book_sales_temp ORDER BY
SET total_amount = ( BEGIN total_books_sold DESC; END;
SELECT SUM(quantity * price) FROM order_itemsWHERE order_id = IF EXISTS ( $$ LANGUAGE plpgsql;
v_order_id) SELECT 1 FROM order_items WHERE book_id = OLD.book_id 4. Write a function that uses a cursor to fetch all customers who have placed an
WHERE order_id = v_order_id; END; $$ LANGUAGE plpgsql; ) THEN RAISE EXCEPTION 'Cannot delete book with existing orders'; order, along with the number of orders they have placed.
2. Write a procedure add_review(book_id INT, customer_id INT, rating INT, CREATE OR REPLACE FUNCTION get_customers_with_order_count()
END IF; RETURN OLD; END;
review_text TEXT) that allows a customer to add a review for a book. If the RETURNS TABLE ( customer_name VARCHAR(255), total_orders
$$ LANGUAGE plpgsql;
customer has already reviewed that book, raise an exception. INT ) AS $$ DECLARE
CREATE TRIGGER prevent_book_deletion_trigger BEFORE
CREATE OR REPLACE PROCEDURE add_review(p_book_id INT, p_customer_id customer_cursor CURSOR FOR SELECT customer_id, name FROM
DELETE ON books FOR EACH ROW EXECUTE FUNCTION
INT, p_rating INT, p_review_text TEXT) AS $$ customers; customer_rec RECORD; order_count INT; BEGIN CREATE
prevent_book_deletion();
BEGIN 3. Write a trigger that automatically updates the stock_quantity of a book in TEMPORARY TABLE customer_order_count_temp (
-- Check for existing review the books table when an order_item is inserted, updated, or deleted. customer_name VARCHAR(255), total_orders INT);
IF EXISTS (SELECT 1 FROM reviews WHERE book_id = p_book_id AND CREATE OR REPLACE FUNCTION update_book_stock_trigger() OPEN customer_cursor;
customer_id = p_customer_id) THEN RETURNS TRIGGER AS $$ LOOP
RAISE EXCEPTION 'Customer has already reviewed this book'; FETCH customer_cursor INTO customer_rec;
BEGIN
END IF; IF TG_OP = 'INSERT' THEN EXIT WHEN NOT FOUND;
-- Insert new review UPDATE books SET stock_quantity = stock_quantity - NEW.quantity WHERE SELECT COUNT(*) INTO order_count FROM orders
INSERT INTO reviews (book_id, customer_id, rating, review_text) book_id = NEW.book_id; WHERE customer_id = customer_rec.customer_id;
VALUES (p_book_id, p_customer_id, p_rating, p_review_text); ELSIF TG_OP = 'UPDATE' THEN UPDATE books SET stock_quantity = IF order_count > 0 THEN
END; $$ LANGUAGE plpgsql; stock_quantity + OLD.quantity - NEW.quantity WHERE book_id = INSERT INTO customer_order_count_temp VALUES
3. Write a procedure cancel_order(order_id INT) that deletes all order items NEW.book_id; (customer_rec.name, order_count);
and the order itself from the orders and order_items tables. ELSIF TG_OP = 'DELETE' THEN END IF;
CREATE OR REPLACE PROCEDURE cancel_order(p_order_id INT) AS $$ UPDATE books SET stock_quantity = stock_quantity + OLD.quantity END LOOP;
BEGIN DELETE FROM order_items WHERE order_id = p_order_id; DELETE CLOSE customer_cursor;
WHERE book_id = OLD.book_id;
FROM orders WHERE order_id = p_order_id; END; $$ LANGUAGE plpgsql; END IF; RETURN NULL; END; RETURN QUERY
4. Write a procedure update_book_stock(book_id INT, quantity INT) that SELECT customer_name, total_orders FROM customer_order_count_temp
$$ LANGUAGE plpgsql;
updates the stock quantity for a book. If the new quantity is less than zero, ORDER BY total_orders DESC;
CREATE TRIGGER update_stock_quantity_trigger AFTER INSERT OR UPDATE
raise an exception.
OR DELETE ON order_items FOR EACH ROW END; $$ LANGUAGE plpgsql;
CREATE OR REPLACE PROCEDURE update_book_stock(p_book_id INT,
EXECUTE FUNCTION update_book_stock_trigger(); 5. Write a function that uses a cursor to fetch all reviews for a book, ordered
p_quantity INT) AS $$ BEGIN by the review date, and returns the book’s title, the reviewer’s name,
4. Write a trigger that logs all changes to the books table (insert, update, delete)
IF p_quantity < 0 THEN RAISE EXCEPTION 'Stock quantity cannot be negative'; rating, and review text.
into a new table book_changes_log with columns for the action type, book ID,
END IF; and timestamp. CREATE TABLE book_changes_log ( log_id SERIAL PRIMARY CREATE OR REPLACE FUNCTION get_book_reviews(p_book_id INT)
UPDATE books KEY, action_type VARCHAR(10), RETURNS TABLE ( book_title VARCHAR(255), reviewer_name
SET stock_quantity = p_quantity book_id INT, VARCHAR(255), rating INT, review_text TEXT, review_date
WHERE book_id = p_book_id; change_timestamp TIMESTAMP DEFAULT CURRENT_TIMESTAMP TIMESTAMP
END; $$ LANGUAGE plpgsql; ); ) AS $$ DECLARE
5. Write a procedure bulk_update_book_prices(price_increase DECIMAL) that CREATE OR REPLACE FUNCTION log_book_changes() RETURNS TRIGGER AS $$ review_cursor CURSOR FOR
increases the price of all books by the given percentage. CREATE OR BEGIN SELECT r.review_id FROM reviews r WHERE r.book_id = p_book_id
REPLACE PROCEDURE bulk_update_book_prices( p_price_increase ORDER BY r.review_date; review_rec RECORD; book_title_var
IF TG_OP = 'INSERT' THEN INSERT INTO book_changes_log (action_type,
DECIMAL) AS $$ BEGIN UPDATE books SET price = price * (1 + book_id) VALUES ('INSERT', NEW.book_id); ELSIF TG_OP = 'UPDATE' VARCHAR(255);
p_price_increase / 100); END; $$ LANGUAGE plpgsql; Exception Handling BEGIN
THEN INSERT INTO book_changes_log
Questions: SELECT title INTO book_title_var FROM books WHERE book_id = p_book_id;
(action_type, book_id) VALUES ('UPDATE', NEW.book_id); ELSIF TG_OP =
1. Write a function that updates the price of a book. If the new price is OPEN review_cursor;
'DELETE' THEN INSERT INTO book_changes_log (action_type, book_id)
negative, raise an exception with the message: "Price cannot be negative". LOOP
VALUES ('DELETE', OLD.book_id);
CREATE OR REPLACE FUNCTION update_book_price_safe( p_book_id INT, FETCH review_cursor INTO review_rec;
END IF; RETURN NULL;
p_new_price DECIMAL ) RETURNS VOID AS $$
END; $$ LANGUAGE plpgsql; CREATE TRIGGER log_book_changes_trigger EXIT WHEN NOT FOUND;
235 Lab Exam

RETURN QUERY RAISE NOTICE 'Product: %, Price: %.2f', rec.NAME, rec.PRICE; WHERE s.salary BETWEEN min_salary AND max_salary;
SELECT book_title_var, END IF; IF NOT FOUND THEN
c.name, r.rating, r.review_text, END LOOP; RAISE NOTICE 'Salary must be between %.2f and %.2f.', get_salary('min'),
r.review_date IF row_num = 0 THEN get_salary('max'); END IF;
FROM reviews r RAISE NOTICE 'No products found in the Phone category.'; END $$ LANGUAGE plpgsql;
JOIN customers c ON r.customer_id = c.customer_id WHERE r.review_id = END IF; 4) •Write a function to return a table with customer id, product id, total
review_rec.review_id; END $$; quantity per product, order_level for a given customer (pass customer id as
END LOOP; CLOSE review_cursor; END; $$ LANGUAGE plpgsql; PLPGSQL 4) Write an anonymous block to update a salesman's bonus. Use SELECT INTO an argument) for products with highest and lowest purchased quantities. (You
Lab 2: to retrieve the income generated by the salesman. Update the bonus as must read minimal number of rows using CURSOR). Order level value is
1) Write an anonymous block in PostgreSQL that uses a date of birth as a follows: “Highest” or “Lowest” depending on the quantity. Keep in mind that there
variable and calculates the age in years, months, and days. Output the •If the salesman has not sold anything, set the bonus to 0. could be more than one product with the same quantity.
results in a human-readable format. •If the salesman's income is below 35,000, set the bonus to 300. CREATE OR REPLACE FUNCTION get_customer_product_details(customer_id
DO $$ DECLARE •If the salesman's income is above or equal to 70,000, set the bonus to 1,000. INT)
dob DATE := '1995-05-15'; -- Replace with the desired date of birth •Otherwise, set the bonus to 500. RETURNS TABLE(product_id INT, total_quantity INT, order_level TEXT) AS $$
years INT; months INT; days INT; DO $$ DECLARE rec RECORD; DECLARE
BEGIN total_income DECIMAL(10, max_quantity INT;
SELECT EXTRACT(YEAR FROM AGE(CURRENT_DATE, dob)), 2); new_bonus INT; BEGIN min_quantity INT;
EXTRACT(MONTH FROM AGE(CURRENT_DATE, dob)), FOR rec IN BEGIN
EXTRACT(DAY FROM AGE(CURRENT_DATE, dob)) SELECT s.SID, COALESCE(SUM(o.QUANTITY * (p.PRICE - p.COST)), 0) AS income SELECT MAX(quantity), MIN(quantity) INTO max_quantity, min_quantity
INTO years, months, days; FROM SALESMAN s FROM orders WHERE cid = customer_id;
RAISE NOTICE 'Age: % years, % months, and % days.', years, months, days; LEFT JOIN ORDERS o ON s.SID = o.SID
END $$; LEFT JOIN PRODUCTS p ON o.PID = p.PID RETURN QUERY SELECT pid, SUM(quantity), 'Highest'
2) Write an anonymous block to retrieve the product name, category, cost price, GROUP BY s.SID LOOP FROM orders
selling price, and profit margin for a product with ID 112. Print the results. total_income := WHERE cid = customer_id AND quantity = max_quantity
DO $$ rec.income; IF GROUP BY pid;
DECLARE total_income = 0 THEN
prod_name VARCHAR; category VARCHAR; cost_price new_bonus := 0; RETURN QUERY SELECT pid, SUM(quantity), 'Lowest'
DECIMAL; selling_price DECIMAL; profit_margin DECIMAL; ELSIF total_income < 35000 THEN FROM orders
BEGIN new_bonus := 300; ELSIF WHERE cid = customer_id AND quantity = min_quantity
SELECT name, category, cost, price, (price - cost) total_income >= 70000 THEN GROUP BY pid;
INTO prod_name, category, cost_price, selling_price, profit_margin new_bonus := 1000; ELSE END $$ LANGUAGE plpgsql;
FROM products WHERE pid = 112; new_bonus := 500; •Write a procedure that will print the total expenditure, name and order level
RAISE NOTICE 'Product: %, Category: %, Cost: %, Price: %, Profit Margin: %', END IF; for each company
prod_name, category, cost_price, selling_price, profit_margin; UPDATE SALESMAN SET BONUS = new_bonus WHERE SID = rec.SID; CREATE OR REPLACE PROCEDURE print_company_expenditures()
END $$; END LOOP; LANGUAGE plpgsql AS $$ DECLARE
3) Retrieve the full name, gender, and age category of a salesman based on their END $$; rec RECORD;
SID using an IF-ELSE statement. PLPGSQL Lab 4 BEGIN
DO $$ 1) Create a function that accepts a date range as input. The function should FOR rec IN SELECT c.company,
DECLARE return a list containing the names of all products, customers, or sales agents SUM(o.quantity * p.price) AS total_expenditure,
prod_name VARCHAR; category VARCHAR; cost_price involved in orders placed within that timeframe. Utilize type conversion (::) as CASE
DECIMAL; selling_price DECIMAL; profit_margin DECIMAL; necessary and illustrated above. Finally, print the resulting list of names. WHEN SUM(o.quantity) = (SELECT MAX(SUM(quantity)) FROM
BEGIN Hint: For the time being, you can implement an if-condition to check the orders GROUP BY cid) THEN 'Highest'
SELECT name, category, cost, price, (price - cost) provided table name and construct the query accordingly. Manually specify the
WHEN SUM(o.quantity) = (SELECT MIN(SUM(quantity)) FROM
INTO prod_name, category, cost_price, selling_price, profit_margin table name within the query. In the coming weeks, we'll delve into creating
orders GROUP BY cid) THEN 'Lowest'
FROM products WHERE pid = 112; dynamic queries.
END AS order_level
RAISE NOTICE 'Product: %, Category: %, Cost: %, Price: %, Profit Margin: %', CREATE OR REPLACE FUNCTION get_names_in_date_range(start_date DATE,
FROM orders o
prod_name, category, cost_price, selling_price, profit_margin; end_date DATE, table_name TEXT)
JOIN customer c ON o.cid = c.cid
END $$; RETURNS TABLE(name TEXT) AS $$
JOIN products p ON o.pid = p.pid
BEGIN
4) Convert the IF-ELSE logic from Task 3 into a CASE statement. GROUP BY c.company
DO $$ IF table_name = 'products' THEN
LOOP
DECLARE RETURN QUERY SELECT DISTINCT p.name
RAISE NOTICE 'Company: %, Total Expenditure: %.2f, Order Level: %',
sid INT := 111; -- Replace with the desired SID full_name CHAR(20); FROM orders o JOIN products p ON o.pid = p.pid
rec.company, rec.total_expenditure, rec.order_level;
gender CHAR(1); age INT; age_category VARCHAR; WHERE o.date_ordered BETWEEN start_date AND end_date;
END LOOP;
BEGIN ELSIF table_name = 'customers' THEN
END $$;
SELECT full_name, gender, age INTO full_name, gender, age FROM RETURN QUERY SELECT DISTINCT c.company
PLPGSQL Lab 5
salesman WHERE sid = sid; age_category := CASE FROM orders o JOIN customer c ON o.cid = c.cid
1a) Create a program that finds a product with a name similar to a name
WHEN age > 60 THEN 'Old Adults' WHERE o.date_ordered BETWEEN start_date AND end_date;
provided by a user. If deleted successfully it sends a message "product with
WHEN age BETWEEN 40 AND 60 THEN 'Middle Aged Adults' ELSIF table_name = 'salesman' THEN
pid=... was deleted from category=.... if several products with that name
WHEN age BETWEEN 18 AND 39 THEN 'Young Adults' RETURN QUERY SELECT DISTINCT s.full_name exists it should revert the changes and say “OPERATION FAILED!! You can
ELSE 'Underage' FROM orders o JOIN salesman s ON o.sid = s.sid delete one product at a time only". Make sure all possible errors are handled
END; WHERE o.date_ordered BETWEEN start_date AND end_date; and userfriendly messages are given.
RAISE NOTICE 'Name: %, Gender: %, Age Category: %', full_name, gender, END IF; 1b) Make necessary changes to the procedure above and make sure a product
age_category; END $$ LANGUAGE plpgsql; that has a profit margin above 1000 is never deleted by using EXCEPTION..
END $$; 2) Create a procedure that takes an amount (X%) and a gender as input. This DO $$ DECLARE
PLPGSQL Lab 3: procedure should update the salaries of employees matching the specified product_name TEXT := 'Samsung'; -- Replace with user input
1) Write a PL/pgSQL anonymous block that selects all columns from the orders gender by the given amount. However, if the total budget required for this product_count INT; profit_margin DECIMAL; product_id
table and completes the following requirements. salary increase exceeds 5000, the procedure should abort the changes and INT; category TEXT;
• Adds a new calculated column named demand_level to the result set. display a message indicating that the update was aborted due to insufficient BEGIN
• Calculates: The demand_level based on the quantity sold: budget. The message should specify that “The raise amount (X%) needed is
SELECT COUNT(*), MAX(pid), MAX(price - cost), MAX(category)
• If quantity ≥ 5, set demand_level to "High Demanded Product". beyond the budget by XXXX amount, lower the amount (X), else print salary
INTO product_count, product_id, profit_margin, category
updated with total raise amount of (Y). CREATE OR REPLACE PROCEDURE
• If 2 ≤ quantity ≤ 4, set demand_level to "Medium Demanded Product". FROM products
update_salaries(raise_percent FLOAT,
• Otherwise, set demand_level to "Low Demanded Product". WHERE name LIKE '%' || product_name || '%';
emp_gender CHAR(1))
Display the result in the following format.
LANGUAGE plpgsql AS $$
DO $$ IF profit_margin > 1000 THEN
DECLARE
BEGIN RAISE NOTICE 'Deletion not allowed. Profit margin exceeds 1000 for
total_increase DECIMAL;
FOR rec IN SELECT OID, QUANTITY, product: %', product_name;
BEGIN
CASE RETURN;
total_increase := (SELECT SUM(salary * raise_percent / 100)
WHEN QUANTITY >= 5 THEN 'High Demanded Product' END IF;
FROM salesman
WHEN QUANTITY BETWEEN 2 AND 4 THEN 'Medium
WHERE gender = emp_gender);
Demanded Product' IF product_count = 1 THEN
IF total_increase > 5000 THEN
ELSE 'Low Demanded Product' DELETE FROM products WHERE pid = product_id; RAISE
RAISE NOTICE 'The raise amount (%.2f%%) exceeds the budget by %.2f.',
END AS demand_level NOTICE 'Product with pid=% was deleted from category=%.',
raise_percent, total_increase - 5000;
FROM ORDERS product_id, category;
ELSE
LOOP ELSE
UPDATE salesman
RAISE NOTICE 'Order ID: %, Quantity: %, Demand Level: %', rec.OID, RAISE NOTICE 'OPERATION FAILED!! You can delete one product at a time
SET salary = salary + (salary * raise_percent / 100)
rec.QUANTITY, rec.demand_level; only.'; END IF;
WHERE gender = emp_gender; RAISE NOTICE
END LOOP; EXCEPTION WHEN OTHERS THEN
'Salaries updated. Total raise amount: %.2f.',
END $$; RAISE NOTICE 'An unexpected error occurred. Please check input and
total_increase;
2) Write a PL/pgSQL anonymous block that evaluates the following conditions: constraints.';
END IF;
• If quantity_sold ≤ 3 and profit < 1000, display the message: "We need to sell END $$;
END $$;
these products at a lower profit." 2a) Write a trigger that when a staff salary is updated it prints the staff id, new
3) Write a function that returns salesman id, initials and experience period (use
• If quantity_sold ≥ 4 and profit > 1000, display the message: "These products and old salary and how much the salary is raised.
AGE), for those being paid within the range of X and Y (condition MUST BE
are best in offer." CREATE OR REPLACE FUNCTION log_salary_changes()
checked in the query). If no employee found then it should print “Salary must
•Otherwise, display the message "No changes are required in our Sales Plan." RETURNS TRIGGER AS $$
be between max_salary and min_salary”. Where max_salary and min_salary
DO $$ BEGIN
are the actual max and min salaries calculated through function.
BEGIN RAISE NOTICE 'Staff ID: %, Old Salary: %, New Salary: %, Raise Amount: %',
a) Write a function to get the min OR max salary (one function only!)
FOR rec IN SELECT o.PID, o.QUANTITY, p.PRICE, p.COST, p.NAME OLD.sid, OLD.salary, NEW.salary, NEW.salary - OLD.salary;
CREATE OR REPLACE FUNCTION get_salary(extreme TEXT)
FROM ORDERS o JOIN PRODUCTS p ON o.PID = RETURN NEW;
RETURNS DECIMAL AS $$
p.PID LOOP profit := rec.QUANTITY * (rec.PRICE - END $$ LANGUAGE plpgsql;
BEGIN
rec.COST);
IF extreme = 'min' THEN CREATE TRIGGER salary_change_logger
IF rec.QUANTITY <= 3 AND profit < 1000 THEN
RETURN (SELECT MIN(salary) FROM salesman); AFTER UPDATE OF salary ON salesman
RAISE NOTICE 'We need to sell % at a lower profit: Quantity %, Profit FOR EACH ROW EXECUTE FUNCTION log_salary_changes();
ELSIF extreme = 'max' THEN
%.2f', rec.NAME, rec.QUANTITY, profit; 2b) Write a single trigger that checks if the price is 80% or less above the cost.
RETURN (SELECT MAX(salary) FROM salesman);
ELSIF rec.QUANTITY >= 4 AND profit > 1000 THEN RAISE • If insert then: send a message to user that the insert was
END IF;
NOTICE '% is best in offer: Quantity %, Profit %.2f', rec.NAME, ignored for id=x because the price must be at least 80 percent above the cost
END $$ LANGUAGE plpgsql;
rec.QUANTITY, profit; and do not proceed with the insert operation.
b) Write a function to get initials of a given name (use space as delimiter,
ELSE • If updated then: if neither price nor cost is updated,
assume name consists of first name and last name only) CREATE OR REPLACE
RAISE NOTICE 'No changes are required for %: Quantity %, Profit %.2f', proceed with the changes. Else send a message to user that price change was
FUNCTION get_initials(full_name TEXT)
rec.NAME, rec.QUANTITY, profit; set to original price because the price must be 80 percent or above the price,
RETURNS TEXT AS $$
END IF; and make sure the update of all columns are processed except for the price
BEGIN
END LOOP; (consider all updates cases that could happen on the table)
RETURN SUBSTRING(full_name FROM 1 FOR 1) || '. ' ||
END $$; CREATE OR REPLACE FUNCTION enforce_price_rules()
SUBSTRING(SPLIT_PART(full_name, ' ', 2) FROM 1 FOR 1) || '.';
3) Write a PL/pgSQL anonymous block that declares a cursor to fetch product RETURNS TRIGGER AS $$ BEGIN
END $$ LANGUAGE plpgsql;
categories, product names, and prices from a specified table. It iterates over IF TG_OP = 'INSERT' THEN
c) Write the main function to complete the Task 3 instructions given above
the cursor, checking if the current row's category matches the "Phone" IF NEW.price < NEW.cost * 1.8 THEN
which will display initials, and check the range of X and Y.
category. If a match is found, it prints the product name and price at odd row RAISE NOTICE 'Insert ignored for id=%. Price must be at least 80%%
CREATE OR REPLACE FUNCTION get_salesman_details(min_salary DECIMAL,
numbers. If no match is found after all rows are processed, it prints a message above cost.', NEW.pid;
indicating that the "Phone" category does not exist in the table. The sample max_salary DECIMAL)
RETURN NULL; END
output is shown below: DO $$ DECLARE RETURNS TABLE(sid INT, initials TEXT, experience INTERVAL) AS $$
IF;
cur CURSOR FOR SELECT CATEGORY, NAME, PRICE FROM PRODUCTS; BEGIN RETURN QUERY
ELSIF TG_OP = 'UPDATE' THEN
rec RECORD; row_num INT := 0; SELECT s.sid,
IF NEW.price < NEW.cost * 1.8 THEN
BEGIN get_initials(s.full_name),
NEW.price := OLD.price;
FOR rec IN cur LOOP row_num := row_num + 1; AGE(CURRENT_DATE, s.date_hired)
RAISE NOTICE 'Price change reverted for id=%. Price must be at least
IF rec.CATEGORY = 'Phone' AND row_num % 2 = 1 THEN FROM salesman s
80%% above cost.', NEW.pid;
235 Lab Exam

END IF;
END IF;
RETURN NEW;
END $$ LANGUAGE plpgsql;
CREATE TRIGGER price_rules_enforcer BEFORE INSERT OR UPDATE ON products
FOR EACH ROW EXECUTE FUNCTION enforce_price_rules(); 2c) The salary
change trigger is no more needed. Do what needs to be done!
ALTER TABLE products DISABLE TRIGGER price_rules_enforcer; 2d) The
company is in the process of changing their pricing strategy and would not
want the price and cost difference policy to be implemented until further
notice. Do what needs to be done!
DROP TRIGGER price_rules_enforcer ON products; The company implemented
a new policy to keep a history of all changes made on all tables in a SINGLE
table. For now, we will focus on order table only, but do not hardcode the table
name. Below is the data that needs to be saved in history table
• Id
• Name of the table changed (do not hard code it to orders)
• Name of the column changed
• Old value (if not applicable make it NULL)
• New value (if not applicable make it NULL)
• Which task was performed (delete, update, insert)
• When the change has happened
3a) Create the table (HINT: define all columns except id as string data type,
because the values get casted automatically) CREATE TABLE history ( id SERIAL
PRIMARY KEY, table_name TEXT, column_name TEXT, old_value TEXT,
new_value TEXT,
task_performed TEXT,
change_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
3b) Write the trigger (you can hard code the column name but not the table
name)
CREATE OR REPLACE FUNCTION log_order_changes()
RETURNS TRIGGER AS $$
BEGIN
IF TG_OP = 'DELETE' THEN
INSERT INTO history (table_name, column_name, old_value,
task_performed)
VALUES ('orders', 'ALL', ROW(OLD.*)::TEXT, 'DELETE');
ELSIF TG_OP = 'UPDATE' THEN
INSERT INTO history (table_name, column_name, old_value, new_value,
task_performed)
SELECT 'orders', column_name, old_val::TEXT, new_val::TEXT, 'UPDATE'
FROM (
SELECT UNNEST(ARRAY['cid', 'pid', 'sid', 'quantity', 'date_ordered']) AS
column_name,
UNNEST(ARRAY[OLD.cid, OLD.pid, OLD.sid, OLD.quantity,
OLD.date_ordered::TEXT]) AS old_val,
UNNEST(ARRAY[NEW.cid, NEW.pid, NEW.sid, NEW.quantity,
NEW.date_ordered::TEXT]) AS new_val
) AS changes WHERE old_val IS DISTINCT FROM new_val;
ELSIF TG_OP = 'INSERT' THEN INSERT INTO history
(table_name, column_name, new_value, task_performed)
VALUES ('orders', 'ALL', ROW(NEW.*)::TEXT, 'INSERT');
END IF;
RETURN NULL;
END $$ LANGUAGE plpgsql;
4) Use any large dataset for this task and record the performance of select
statement using b-tree and hash algorithm and discuss the best use-cases of
both algorithms. b-tee index:
CREATE INDEX btree_customer_id ON orders_dataset(customer_id); EXPLAIN
ANALYZE SELECT * FROM orders_dataset WHERE customer_id = 302; Hash:
CREATE INDEX hash_customer_id ON orders_dataset USING HASH
(customer_id);
EXPLAIN ANALYZE SELECT * FROM orders_dataset WHERE customer_id = 302;
B-Tree: Optimal for range queries and ordered retrievals.
Hash: Better for equality searches but unsuitable for range queries.

Problem_solving:
Create a function which should trigger to save the old price and amount
raised in price_hist table once the product price is changed. Submit your
code in the provided space below.
Note: Your will only receive marks based on the code submission.

You might also like

pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy