Database Concurrency, Normalization, and PL/pgSQL Programming
Transaction Concurrency: Preventing the Lost Update Problem
The Lost Update Problem occurs when two transactions simultaneously update the same data, resulting in one update overwriting the changes made by the other. This concurrency issue leads to data inconsistency.
It can be prevented using locking mechanisms, such as row-level exclusive locks, which ensure that only one transaction can modify the specific data item at any given time.
Core Database Concepts: True or False Statements
Using procedural language SQL (PL/SQL, PL/pgSQL, etc.), we can write blocks of code containing standard SQL statements and procedural extensions that are stored and executed at the DBMS server. → True
From the data modeler’s perspective, the objective of normalization is to ensure that all tables are at least in Third Normal Form (3NF). → True
An SQL View is a physical table used to extract data from base tables. → False (A View is a virtual table.)
A DML trigger is a special stored procedure fired implicitly when a Data Manipulation Language (DML) event (INSERT, UPDATE, DELETE) occurs. → True
According to the normalization process (specifically Boyce-Codd Normal Form or BCNF), all nonprime attributes in a table are dependent on the candidate key(s), the entire candidate key(s) (in case of composite keys), and nothing but the candidate key(s). → True
Strong consistency provides immediate consistency but can result in higher latency and lower availability. In contrast, eventual consistency prioritizes availability but can lead to temporary data inconsistencies. → True
Relational vs. NoSQL Databases
Relational databases (e.g., MySQL, PostgreSQL) utilize structured schemas, rely on SQL, and support strong consistency enforced by ACID transactions.
NoSQL databases (e.g., MongoDB, Cassandra) are typically schema-less, support eventual consistency, and are better suited for unstructured or rapidly changing data. NoSQL systems generally offer higher scalability and flexibility, making them ideal for Big Data applications.
Database Programming Examples (PL/pgSQL)
SQL View Definition
Creating a view to list members from Fes who rented items after April 25, 2024:
CREATE VIEW FesMembersAfterApril2024 AS
SELECT M.MEM_NUM, M.MEM_FNAME, M.MEM_LNAME
FROM MEMBERSHIP M
JOIN RENTAL R ON M.MEM_NUM = R.MEM_NUM
WHERE M.MEM_CITY = 'Fes' AND R.RENT_DATE > '2024-04-25';PL/pgSQL Function: Calculate Total Rental Amount
A function to calculate the total fee for a specific rental number (rnum):
CREATE OR REPLACE FUNCTION total_rental_amount(rnum INT)
RETURNS NUMERIC
LANGUAGE plpgsql
AS $$
DECLARE
total NUMERIC := 0;
BEGIN
SELECT SUM(DETAIL_FEE)
INTO total
FROM DETAILRENTAL
WHERE RENT_NUM = rnum;
RETURN total;
END;
$$;PL/pgSQL Trigger: Update Membership Balance
This trigger function updates the member’s balance immediately after a new rental record is inserted:
CREATE OR REPLACE FUNCTION update_balance() RETURNS TRIGGER
LANGUAGE plpgsql
AS $$
BEGIN
UPDATE MEMBERSHIP
SET MEM_BALANCE = MEM_BALANCE + (
SELECT SUM(DETAIL_FEE)
FROM DETAILRENTAL
WHERE RENT_NUM = NEW.RENT_NUM
)
WHERE MEM_NUM = NEW.MEM_NUM;
RETURN NEW;
END;
$$;Applying the trigger to the RENTAL table:
CREATE TRIGGER trg_update_balance
AFTER INSERT ON RENTAL
FOR EACH ROW
EXECUTE PROCEDURE update_balance();PL/pgSQL Procedure: Calculate Movie Total Cost
A procedure to calculate and display the total rental revenue generated by a specific movie (movnum):
CREATE OR REPLACE PROCEDURE movie_total_cost(movnum INT)
LANGUAGE plpgsql
AS $$
DECLARE
total NUMERIC := 0;
BEGIN
SELECT SUM(DETAIL_FEE)
INTO total
FROM DETAILRENTAL D
JOIN VIDEO V ON D.VID_NUM = V.VID_NUM
WHERE V.MOVIE_NUM = movnum;
RAISE NOTICE 'Total amount from renting this movie: %', total;
END;
$$;PL/pgSQL Function: Calculate Late Fee
A function to determine the late fee based on the difference between the return date and the due date for a specific rental detail:
CREATE OR REPLACE FUNCTION calc_late_fee(rentid INT, vidid INT)
RETURNS NUMERIC
LANGUAGE plpgsql
AS $$
DECLARE
days_late INT := 0;
daily_fee NUMERIC := 0;
BEGIN
SELECT DETAIL_DAILYLATEFEE,
(DETAIL_RETURNDATE - DETAIL_DUEDATE)
INTO daily_fee, days_late
FROM DETAILRENTAL
WHERE RENT_NUM = rentid AND VID_NUM = vidid;
IF days_late <= 0 THEN
RETURN 0;
ELSE
RETURN days_late * daily_fee;
END IF;
END;
$$;