Loading content...
Throughout this module, we've built a comprehensive understanding of weak entities: their definition, identifying relationships, partial keys, and total participation constraints. Now comes the critical practical step: translating these conceptual constructs into actual database tables.
Mapping weak entities to relational schemas is more nuanced than mapping strong entities. The composite key structure, cascading foreign keys, and existence dependencies all require careful implementation. A misstep in this translation can result in databases that fail to enforce the semantics we've carefully modeled.
This page provides a systematic algorithm for weak entity mapping, complete with SQL implementations, handling of complex scenarios, and best practices from production database design.
By the end of this page, you will understand the complete algorithm for mapping weak entities to tables, how to construct composite primary keys, proper foreign key constraint syntax, handling of multi-level weak entity hierarchies, real-world SQL implementation patterns, and common pitfalls to avoid.
Mapping a weak entity to a relational schema follows a well-defined algorithm. This systematic approach ensures all semantic constraints are properly captured in the physical implementation.
When creating tables in SQL, owner (strong) entity tables must be created before weak entity tables. The foreign key constraint requires the referenced table to exist. In migration scripts, order your CREATE TABLE statements accordingly.
Let's apply the mapping algorithm to a simple case: the classic Employee-Dependent example with a single-attribute partial key.
ER Model:
Owner Entity: EMPLOYEE
Weak Entity: DEPENDENT
Applying the Algorithm:
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384
-- ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ-- STEP 1: Create Owner (Strong) Entity Table-- ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββCREATE TABLE Employee ( employee_id VARCHAR(10) NOT NULL, name VARCHAR(100) NOT NULL, department VARCHAR(50), hire_date DATE, -- Primary key: simple, single column CONSTRAINT pk_employee PRIMARY KEY (employee_id), -- Optional: Check constraint for valid ID format CONSTRAINT chk_employee_id CHECK (employee_id LIKE 'E%')); -- ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ-- STEP 2: Create Weak Entity Table-- ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββCREATE TABLE Dependent ( -- Column from owner's primary key (step 5) -- NOT NULL is implicit since it's part of PK employee_id VARCHAR(10) NOT NULL, -- Partial key / discriminator (step 2) dependent_name VARCHAR(100) NOT NULL, -- Other simple attributes (step 2) birth_date DATE, relationship VARCHAR(20), -- Composite Primary Key (step 6) -- Combines owner's PK + partial key CONSTRAINT pk_dependent PRIMARY KEY (employee_id, dependent_name), -- Foreign Key to Owner (step 7) -- CASCADE ensures existence dependency CONSTRAINT fk_dependent_employee FOREIGN KEY (employee_id) REFERENCES Employee(employee_id) ON DELETE CASCADE ON UPDATE CASCADE, -- Optional: Check constraint for valid relationship values CONSTRAINT chk_relationship CHECK (relationship IN ('Spouse', 'Son', 'Daughter', 'Parent', 'Other'))); -- ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ-- Create indexes for common query patterns-- ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ-- Index on relationship for filtering queriesCREATE INDEX idx_dependent_relationship ON Dependent(relationship); -- Index on birth_date for age-based queriesCREATE INDEX idx_dependent_birthdate ON Dependent(birth_date); -- ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ-- Sample Data Insertion-- ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ-- Must insert owner firstINSERT INTO Employee VALUES ('E001', 'Alice Johnson', 'Engineering', '2020-01-15');INSERT INTO Employee VALUES ('E002', 'Bob Smith', 'Marketing', '2019-06-01'); -- Now can insert dependentsINSERT INTO Dependent VALUES ('E001', 'Sarah Johnson', '2015-03-20', 'Daughter');INSERT INTO Dependent VALUES ('E001', 'Michael Johnson', '2018-07-10', 'Son');INSERT INTO Dependent VALUES ('E002', 'Emily Smith', '1990-11-05', 'Spouse'); -- ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ-- Verify mapping with query-- ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββSELECT e.employee_id, e.name AS employee_name, d.dependent_name, d.relationship, d.birth_dateFROM Employee eLEFT JOIN Dependent d ON e.employee_id = d.employee_idORDER BY e.employee_id, d.dependent_name;When a weak entity has a composite partial key (multiple attributes forming the discriminator), the resulting primary key becomes larger. Let's examine the warehouse storage bin example.
ER Model:
Owner Entity: WAREHOUSE
Weak Entity: STORAGE_BIN
Primary Key Composition:
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869
-- Owner EntityCREATE TABLE Warehouse ( warehouse_id VARCHAR(10) PRIMARY KEY, name VARCHAR(100) NOT NULL, address VARCHAR(200), capacity INT NOT NULL); -- Weak Entity with Composite Partial KeyCREATE TABLE StorageBin ( -- Owner's primary key warehouse_id VARCHAR(10) NOT NULL, -- Composite partial key (all three form the discriminator) aisle CHAR(2) NOT NULL, shelf INT NOT NULL, position VARCHAR(10) NOT NULL, -- Other attributes max_weight DECIMAL(10,2) NOT NULL DEFAULT 100.00, current_product VARCHAR(50), quantity INT DEFAULT 0, -- Four-column composite primary key! CONSTRAINT pk_storage_bin PRIMARY KEY (warehouse_id, aisle, shelf, position), -- Foreign key to owner CONSTRAINT fk_bin_warehouse FOREIGN KEY (warehouse_id) REFERENCES Warehouse(warehouse_id) ON DELETE CASCADE, -- Business rules CONSTRAINT chk_quantity CHECK (quantity >= 0), CONSTRAINT chk_position CHECK (position IN ('LEFT', 'CENTER', 'RIGHT'))); -- Another entity that references the weak entity-- Note: Must include ALL four PK columns in the foreign key!CREATE TABLE InventoryMovement ( movement_id INT PRIMARY KEY AUTO_INCREMENT, -- Reference to StorageBin requires ALL FOUR columns from_warehouse_id VARCHAR(10), from_aisle CHAR(2), from_shelf INT, from_position VARCHAR(10), to_warehouse_id VARCHAR(10), to_aisle CHAR(2), to_shelf INT, to_position VARCHAR(10), product_id VARCHAR(50) NOT NULL, quantity INT NOT NULL, movement_date TIMESTAMP DEFAULT CURRENT_TIMESTAMP, -- Foreign keys reference ALL columns of the weak entity's PK CONSTRAINT fk_from_bin FOREIGN KEY (from_warehouse_id, from_aisle, from_shelf, from_position) REFERENCES StorageBin(warehouse_id, aisle, shelf, position), CONSTRAINT fk_to_bin FOREIGN KEY (to_warehouse_id, to_aisle, to_shelf, to_position) REFERENCES StorageBin(warehouse_id, aisle, shelf, position));Notice how InventoryMovement requires eight columns just to reference two storage bins! This illustrates why composite keys can become unwieldy. Consider surrogate keys when weak entities will be heavily referenced by other tables.
When weak entities form a hierarchyβa weak entity is owner to another weak entityβthe keys cascade. Each level inherits all key columns from its owner. Let's map the University β Department β Course β Section hierarchy.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110
-- ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ-- LEVEL 0: Strong Entity (Root)-- ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββCREATE TABLE University ( university_id CHAR(5) PRIMARY KEY, name VARCHAR(100) NOT NULL, location VARCHAR(100), founded_year INT); -- ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ-- LEVEL 1: First Weak Entity (1 owner key + 1 partial key = 2 PK cols)-- ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββCREATE TABLE Department ( -- From owner (University) university_id CHAR(5) NOT NULL, -- Partial key dept_code VARCHAR(10) NOT NULL, -- Attributes dept_name VARCHAR(100) NOT NULL, building VARCHAR(50), budget DECIMAL(12,2), -- 2-column PK PRIMARY KEY (university_id, dept_code), -- FK to owner FOREIGN KEY (university_id) REFERENCES University(university_id) ON DELETE CASCADE ON UPDATE CASCADE); -- ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ-- LEVEL 2: Second Weak Entity (2 owner keys + 1 partial key = 3 PK cols)-- ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββCREATE TABLE Course ( -- From grandparent + parent (Department's full PK) university_id CHAR(5) NOT NULL, dept_code VARCHAR(10) NOT NULL, -- Partial key course_number VARCHAR(10) NOT NULL, -- Attributes title VARCHAR(100) NOT NULL, credits INT NOT NULL, description TEXT, -- 3-column PK PRIMARY KEY (university_id, dept_code, course_number), -- FK to owner (Department) - references its full composite PK FOREIGN KEY (university_id, dept_code) REFERENCES Department(university_id, dept_code) ON DELETE CASCADE ON UPDATE CASCADE, CONSTRAINT chk_credits CHECK (credits BETWEEN 1 AND 6)); -- ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ-- LEVEL 3: Third Weak Entity (3 owner keys + 1 partial key = 4 PK cols)-- ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββCREATE TABLE Section ( -- From great-grandparent + grandparent + parent (Course's full PK) university_id CHAR(5) NOT NULL, dept_code VARCHAR(10) NOT NULL, course_number VARCHAR(10) NOT NULL, -- Partial key section_number CHAR(3) NOT NULL, -- Attributes semester VARCHAR(20) NOT NULL, year INT NOT NULL, instructor VARCHAR(100), room VARCHAR(20), max_enrollment INT DEFAULT 30, -- 4-column PK PRIMARY KEY (university_id, dept_code, course_number, section_number), -- FK to owner (Course) - references its full 3-column composite PK FOREIGN KEY (university_id, dept_code, course_number) REFERENCES Course(university_id, dept_code, course_number) ON DELETE CASCADE ON UPDATE CASCADE); -- ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ-- Querying the Hierarchy with Natural Keys-- ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ-- The beauty of natural composite keys: self-documenting queries SELECT s.university_id, s.dept_code, s.course_number, s.section_number, c.title AS course_title, d.dept_name, u.name AS university_name, s.instructor, CONCAT(s.semester, ' ', s.year) AS termFROM Section sJOIN Course c USING (university_id, dept_code, course_number)JOIN Department d USING (university_id, dept_code)JOIN University u USING (university_id)WHERE s.semester = 'Fall' AND s.year = 2024ORDER BY u.name, d.dept_name, c.title, s.section_number;When column names match across tables (as they do with cascaded keys), the USING clause simplifies joins significantly. Instead of writing ON a.col1 = b.col1 AND a.col2 = b.col2, you can write USING (col1, col2).
When a weak entity has a multivalued attribute (an attribute that can have multiple values), we create a separate table for that attribute. This separate table references the weak entity using its full composite primary key.
Example Scenario:
Consider a Course entity (weak, under Department) with a multivalued attribute Prerequisites (a course can have multiple prerequisites).
The multivalued attribute becomes its own table that references Course's full composite key.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172
-- Weak entity (from previous example)CREATE TABLE Course ( university_id CHAR(5) NOT NULL, dept_code VARCHAR(10) NOT NULL, course_number VARCHAR(10) NOT NULL, title VARCHAR(100) NOT NULL, credits INT NOT NULL, PRIMARY KEY (university_id, dept_code, course_number), FOREIGN KEY (university_id, dept_code) REFERENCES Department(university_id, dept_code) ON DELETE CASCADE); -- ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ-- Multivalued Attribute: Course Prerequisites-- This is essentially a "weak entity of a weak entity"-- ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββCREATE TABLE CoursePrerequisite ( -- Full PK of the owning weak entity (Course) university_id CHAR(5) NOT NULL, dept_code VARCHAR(10) NOT NULL, course_number VARCHAR(10) NOT NULL, -- The prerequisite reference (could be from any department) prereq_university CHAR(5) NOT NULL, prereq_dept VARCHAR(10) NOT NULL, prereq_course VARCHAR(10) NOT NULL, -- Additional attributes of the relationship min_grade CHAR(2) DEFAULT 'D', -- Minimum grade required is_concurrent BOOLEAN DEFAULT FALSE, -- Can be taken concurrently -- PK: The course + each prereq forms a unique pair PRIMARY KEY (university_id, dept_code, course_number, prereq_university, prereq_dept, prereq_course), -- FK to the weak entity (Course) that owns this attribute FOREIGN KEY (university_id, dept_code, course_number) REFERENCES Course(university_id, dept_code, course_number) ON DELETE CASCADE, -- FK to the prerequisite course (also a weak entity!) FOREIGN KEY (prereq_university, prereq_dept, prereq_course) REFERENCES Course(university_id, dept_code, course_number) ON DELETE RESTRICT, -- Don't allow deleting prereq if referenced -- Prevent course from being its own prerequisite CONSTRAINT chk_no_self_prereq CHECK ( NOT (university_id = prereq_university AND dept_code = prereq_dept AND course_number = prereq_course) )); -- Example: CS 201 requires CS 101 and MATH 101INSERT INTO CoursePrerequisite VALUES ('MIT01', 'CS', '201', 'MIT01', 'CS', '101', 'C', FALSE), ('MIT01', 'CS', '201', 'MIT01', 'MATH', '101', 'D', FALSE); -- Query: Find all prerequisites for a courseSELECT CONCAT(p.prereq_dept, ' ', p.prereq_course) AS prerequisite, prereq_c.title AS prereq_title, p.min_grade AS min_grade_requiredFROM CoursePrerequisite pJOIN Course prereq_c ON p.prereq_university = prereq_c.university_id AND p.prereq_dept = prereq_c.dept_code AND p.prereq_course = prereq_c.course_numberWHERE p.university_id = 'MIT01' AND p.dept_code = 'CS' AND p.course_number = '201';In some designs, a weak entity may have multiple owner entitiesβits identity depends on two or more strong entities simultaneously. This often occurs with intersection entities that represent M:N relationships where the relationship itself has attributes.
Example: Student Enrollment in Course Sections
Consider an Enrollment entity that represents a student's registration in a specific section:
Enrollment is weak because:
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980
-- Owner 1: Strong EntityCREATE TABLE Student ( student_id VARCHAR(10) PRIMARY KEY, name VARCHAR(100) NOT NULL, email VARCHAR(100) UNIQUE, enrollment_year INT); -- Owner 2: Weak Entity (from previous hierarchy)-- Section with PK: (university_id, dept_code, course_number, section_number) -- ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ-- Weak Entity with TWO Owners-- PK combines keys from BOTH owners-- ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββCREATE TABLE Enrollment ( -- From Owner 1: Student student_id VARCHAR(10) NOT NULL, -- From Owner 2: Section (all 4 columns of its composite PK!) university_id CHAR(5) NOT NULL, dept_code VARCHAR(10) NOT NULL, course_number VARCHAR(10) NOT NULL, section_number CHAR(3) NOT NULL, -- Attributes of the enrollment itself enrollment_date DATE NOT NULL DEFAULT (CURRENT_DATE), grade CHAR(2), status VARCHAR(20) DEFAULT 'ACTIVE', -- FIVE-column composite primary key! -- Combines Student's PK (1) + Section's PK (4) = 5 columns PRIMARY KEY (student_id, university_id, dept_code, course_number, section_number), -- FK to Owner 1: Student FOREIGN KEY (student_id) REFERENCES Student(student_id) ON DELETE CASCADE, -- FK to Owner 2: Section (references all 4 columns) FOREIGN KEY (university_id, dept_code, course_number, section_number) REFERENCES Section(university_id, dept_code, course_number, section_number) ON DELETE CASCADE, CONSTRAINT chk_grade CHECK ( grade IS NULL OR grade IN ('A+','A','A-','B+','B','B-','C+','C','C-','D+','D','D-','F','W','I') ), CONSTRAINT chk_status CHECK ( status IN ('ACTIVE', 'WITHDRAWN', 'COMPLETED') )); -- Useful indexes for common query patternsCREATE INDEX idx_enrollment_section ON Enrollment(university_id, dept_code, course_number, section_number);CREATE INDEX idx_enrollment_student ON Enrollment(student_id);CREATE INDEX idx_enrollment_grade ON Enrollment(grade) WHERE grade IS NOT NULL; -- Example dataINSERT INTO Enrollment (student_id, university_id, dept_code, course_number, section_number)VALUES ('S12345', 'MIT01', 'CS', '101', '001'); -- Query: Student's transcriptSELECT e.student_id, s.name AS student_name, CONCAT(e.dept_code, ' ', e.course_number) AS course, c.title AS course_title, c.credits, e.section_number, e.grade, CONCAT(sec.semester, ' ', sec.year) AS termFROM Enrollment eJOIN Student s USING (student_id)JOIN Course c USING (university_id, dept_code, course_number)JOIN Section sec USING (university_id, dept_code, course_number, section_number)WHERE e.student_id = 'S12345'ORDER BY sec.year DESC, sec.semester DESC;The Enrollment table has a 5-column primary key. Any entity referencing Enrollment would need all 5 columns in its foreign key. This is where natural keys become impractical. Consider adding a surrogate key (enrollment_id) while keeping the natural key as a unique constraint.
While the standard mapping algorithm preserves full semantic meaning, practical considerations sometimes lead to alternative approaches. Understanding these trade-offs helps you make informed design decisions.
Adding Surrogate Keys to Weak Entities:
Instead of using the full composite natural key as PK, add a synthetic identifier:
CREATE TABLE Dependent (
dependent_id INT PRIMARY KEY AUTO_INCREMENT,
employee_id VARCHAR(10) NOT NULL,
dependent_name VARCHAR(100) NOT NULL,
birth_date DATE,
relationship VARCHAR(20),
-- Natural key as UNIQUE constraint
UNIQUE (employee_id, dependent_name),
FOREIGN KEY (employee_id) REFERENCES Employee(employee_id)
ON DELETE CASCADE
);
Pros:
Cons:
When to Use:
We've completed our comprehensive exploration of weak entities with this final page on relational mapping. Let's consolidate all the essential concepts from this module:
Congratulations! You've mastered weak entitiesβone of the most important advanced concepts in ER modeling. You now understand weak entity definition, identifying relationships, partial keys, total participation, and relational mapping. These concepts are essential for accurate database design and will serve you throughout your career in database management.
Module Summary - Weak Entities:
| Concept | Key Point |
|---|---|
| Weak Entity | Cannot be uniquely identified by own attributes |
| Identifying Relationship | Transfers identity from owner to weak entity |
| Partial Key | Discriminates within owner's scope |
| Total Participation | Every weak entity must have an owner |
| Mapping | Composite PK + FK with CASCADE |
What's Next in Chapter 10:
The next module covers Identifying Relationships in greater depth, exploring the owner-dependent relationship dynamic, discriminator patterns, and how identifying relationships manifest across different modeling scenarios.