DSA, Database System & Operating System β Data Models, Normalization, and SQL, NEC licence examination syllabus (Nepal Engineering Council).
An entity that cannot be identified on its own β it borrows part of its identity from an owner.
on_delete=CASCADE and Rails' dependent: :destroy. Search "aggregate root domain driven design" β DDD's "aggregate" is the same concept arrived at from a software-design direction.-- ===== the strong owner =====
CREATE TABLE employee (
emp_id INTEGER PRIMARY KEY,
name VARCHAR(60) NOT NULL,
salary NUMERIC(10,2)
);
-- ===== the weak entity =====
-- PK = owner's key + partial key
-- ON DELETE CASCADE is REQUIRED by the semantics
CREATE TABLE dependant (
emp_id INTEGER NOT NULL,
name VARCHAR(60) NOT NULL, -- partial key
relationship VARCHAR(20) NOT NULL,
dob DATE,
PRIMARY KEY (emp_id, name),
FOREIGN KEY (emp_id) REFERENCES employee(emp_id)
ON DELETE CASCADE
);
-- ===== a second, very common example =====
CREATE TABLE orders (
order_no INTEGER PRIMARY KEY,
order_date DATE NOT NULL,
customer VARCHAR(60) NOT NULL
);
CREATE TABLE order_line (
order_no INTEGER NOT NULL,
line_no SMALLINT NOT NULL, -- partial key
item VARCHAR(60) NOT NULL,
qty INTEGER NOT NULL CHECK (qty > 0),
unit_price NUMERIC(10,2) NOT NULL CHECK (unit_price >= 0),
PRIMARY KEY (order_no, line_no),
FOREIGN KEY (order_no) REFERENCES orders(order_no)
ON DELETE CASCADE
);
-- ===== data showing why the partial key is only partial =====
INSERT INTO employee VALUES
(1, 'Ram Bahadur', 68000),
(2, 'Sita Devi', 72000);
INSERT INTO dependant VALUES
(1, 'Gita', 'daughter', '2015-03-12'),
(1, 'Hari', 'son', '2018-07-05'),
(2, 'Gita', 'daughter', '2016-01-20');
-- 'Gita' appears TWICE. Legal, because the PK is
-- (emp_id, name): (1,'Gita') and (2,'Gita') are distinct.
-- The name ALONE cannot identify a dependant.
INSERT INTO dependant VALUES
(1, 'Gita', 'niece', '2019-01-01');
-- ERROR: duplicate key value violates unique constraint
-- "dependant_pkey"
-- Employee 1 cannot have two dependants both called Gita.
INSERT INTO dependant VALUES (99, 'Ghost', 'son', '2020-01-01');
-- ERROR: violates foreign key constraint β no employee 99.
-- A weak entity cannot exist without its owner.
-- ===== CASCADE in action =====
DELETE FROM employee WHERE emp_id = 1;
-- Employee 1 and BOTH of their dependants are removed.
-- Without CASCADE this DELETE would fail, and without the
-- FK it would leave orphaned rows identifying nothing.
SELECT * FROM dependant;
-- emp_id | name | relationship | dob
-- -------+------+--------------+-----------
-- 2 | Gita | daughter | 2016-01-20
id β including weak entities β because ORMs and REST APIs are simpler with single-column keys. That works, but it does not remove the need for a UNIQUE (order_no, line_no) constraint; without it you can insert two "line 2"s in the same order. The natural key still has to be enforced, whether or not it is the primary key.
-- A weak entity can itself own another weak entity, giving a
-- chain of identifying relationships. The key grows at each
-- level.
-- LEVEL 1: strong
CREATE TABLE building (
bldg_code CHAR(4) PRIMARY KEY,
name VARCHAR(60) NOT NULL
);
-- LEVEL 2: weak, owned by building
-- room 101 exists in many buildings, so room_no is partial
CREATE TABLE room (
bldg_code CHAR(4) NOT NULL,
room_no SMALLINT NOT NULL, -- partial key
capacity SMALLINT NOT NULL CHECK (capacity > 0),
PRIMARY KEY (bldg_code, room_no),
FOREIGN KEY (bldg_code) REFERENCES building(bldg_code)
ON DELETE CASCADE
);
-- LEVEL 3: weak, owned by room (which is itself weak)
-- The key is now THREE columns.
CREATE TABLE seat (
bldg_code CHAR(4) NOT NULL,
room_no SMALLINT NOT NULL,
seat_no SMALLINT NOT NULL, -- partial key
is_broken BOOLEAN NOT NULL DEFAULT FALSE,
PRIMARY KEY (bldg_code, room_no, seat_no),
FOREIGN KEY (bldg_code, room_no)
REFERENCES room(bldg_code, room_no)
ON DELETE CASCADE
);
INSERT INTO building VALUES ('PULC', 'Pulchowk Campus Block A'),
('CTVR', 'Thapathali Block');
INSERT INTO room VALUES ('PULC', 101, 60), ('PULC', 102, 40),
('CTVR', 101, 80);
-- room 101 exists in BOTH buildings β legal
INSERT INTO seat VALUES
('PULC', 101, 1, FALSE), ('PULC', 101, 2, TRUE),
('CTVR', 101, 1, FALSE);
-- seat 1 exists in PULC-101 and CTVR-101 β legal
-- one CASCADE removes rooms AND their seats, transitively
DELETE FROM building WHERE bldg_code = 'PULC';
SELECT b.bldg_code, r.room_no, COUNT(s.seat_no) AS seats
FROM building b
JOIN room r ON r.bldg_code = b.bldg_code
LEFT JOIN seat s ON s.bldg_code = r.bldg_code
AND s.room_no = r.room_no
GROUP BY b.bldg_code, r.room_no
ORDER BY b.bldg_code, r.room_no;
-- bldg_code | room_no | seats
-- ----------+---------+-------
-- CTVR | 101 | 1
-- Only Thapathali survives; PULC's 2 rooms and 2 seats
-- were cascaded away by ONE delete.
Create a free account to tick topics off, take notes as you read, watch the video lessons and get a day-by-day study plan built around your exam date.
Loadingβ¦