Changes between Initial Version and Version 1 of Normalization


Ignore:
Timestamp:
04/21/26 00:24:18 (11 days ago)
Author:
221296
Comment:

--

Legend:

Unmodified
Added
Removed
Modified
  • Normalization

    v1 v1  
     1= Normalization
     2
     3== Initial de-normalized relation and functional dependencies
     4
     5=== Global set of attributes
     6This represents the initial de-normalized relation before any normalization is applied.
     7Attribute names are made unique to avoid duplication.
     8{{{
     9R = {
     10user_id, username, email, name, surname, full_name, password_hash, user_created_at,
     11client_id, is_blocked, blocked_at, blocked_reason,
     12admin_id,
     13owner_id,
     14
     15notification_id, notification_type, notification_message, notification_created_at, is_read,
     16
     17animal_id, animal_name, located_name, species, animal_type, breed, sex, date_of_birth, photo_url,
     18
     19listing_id, listing_status, listing_created_at, price, listing_description,
     20
     21saved_user_id, saved_listing_id,
     22
     23clinic_id, clinic_name, clinic_email, clinic_phone, clinic_address, clinic_location, clinic_city,
     24
     25application_id, app_name, app_email, app_phone, app_city, app_address, submitted_at, app_status, reviewed_at, reviewed_by, denial_reason,
     26
     27appointment_id, date_time, appointment_status, notes,
     28
     29healthrecord_id, hr_type, hr_description, hr_date,
     30
     31review_id, reviewer_id, rating, comment, review_created_at, updated_at, is_deleted,
     32target_user_id, target_clinic_id
     33}
     34}}}
     35
     36=== Functional dependencies (initial set)
     37
     38User / specialization
     39{{{
     40FD1: user_id → username, email, name, surname, full_name, password_hash, user_created_at
     41FD2: client_id → user_id, is_blocked, blocked_at, blocked_reason
     42FD3: admin_id → user_id
     43FD4: owner_id → client_id
     44}}}
     45
     46Notification
     47{{{
     48FD5: notification_id → notification_type, notification_message, notification_created_at, is_read, user_id
     49}}}
     50
     51Vet Clinic / application
     52{{{
     53FD6: clinic_id → clinic_name, clinic_email, clinic_phone, clinic_address, clinic_location, clinic_city
     54FD7: application_id → app_name, app_email, app_phone, app_city, app_address, submitted_at, app_status, reviewed_at, reviewed_by, denial_reason, clinic_id
     55}}}
     56
     57Animal
     58{{{
     59FD8: animal_id → animal_name, located_name, species, animal_type, breed, sex, date_of_birth, photo_url, owner_id
     60}}}
     61
     62Listing
     63{{{
     64FD9: listing_id → listing_status, listing_created_at, price, listing_description, animal_id, admin_id
     65}}}
     66
     67Appointment
     68{{{
     69FD10: appointment_id → date_time, appointment_status, notes, animal_id, clinic_id
     70}}}
     71
     72Health Record
     73{{{
     74FD11: healthrecord_id → hr_type, hr_description, hr_date, animal_id, appointment_id
     75}}}
     76
     77Review and subtypes
     78{{{
     79FD12: review_id → reviewer_id, rating, comment, review_created_at, updated_at, is_deleted
     80FD13: review_id → target_user_id
     81FD14: review_id → target_clinic_id
     82}}}
     83
     84Favorite Listing
     85{{{
     86The relation FavoriteListings has composite key (saved_user_id, saved_listing_id)
     87and no additional non-trivial functional dependencies.
     88}}}
     89
     90== Candidate keys and primary key selection
     91=== Determining a candidate key
     92To determine a candidate key of the universal de-normalized relation, we must identify a minimal set of identifiers whose closure determines all attributes in R.
     93{{{#!comment It is not correct to simply collect all identifiers, because some identifiers are functionally determined by others. For example:
     94application_id → clinic_id
     95listing_id → animal_id, admin_id
     96animal_id → owner_id
     97owner_id → client_id
     98healthrecord_id → appointment_id, animal_id
     99appointment_id → clinic_id, animal_id
     100
     101
     102Therefore, attributes such as clinic_id, animal_id, appointment_id, admin_id, owner_id, and client_id do not need to appear explicitly in the candidate key if they can already be derived from other identifiers.
     103}}}
     104
     105A candidate key for the initial de-normalized relation is:
     106
     107{{{
     108K = {notification_id, application_id, listing_id, healthrecord_id, review_id, saved_user_id, saved_listing_id}
     109}}}
     110
     111Its closure includes all attributes of R, so it determines the whole relation.
     112
     113=== Closure proof for K
     114{{{
     115Let K = {notification_id, application_id, listing_id, healthrecord_id, review_id, saved_user_id, saved_listing_id}
     116
     117Compute K+:
     118Start: K+ = K
     119
     120From FD5:
     121notification_id → notification_type, notification_message, notification_created_at, is_read, user_id
     122Add: notification_type, notification_message, notification_created_at, is_read, user_id
     123
     124From FD1:
     125user_id → username, email, name, surname, full_name, password_hash, user_created_at
     126Add: username, email, name, surname, full_name, password_hash, user_created_at
     127
     128From FD7:
     129application_id → app_name, app_email, app_phone, app_city, app_address, submitted_at, app_status, reviewed_at, reviewed_by, denial_reason, clinic_id
     130Add: app_name, app_email, app_phone, app_city, app_address, submitted_at, app_status, reviewed_at, reviewed_by, denial_reason, clinic_id
     131
     132From FD6:
     133clinic_id → clinic_name, clinic_email, clinic_phone, clinic_address, clinic_location, clinic_city
     134Add: clinic_name, clinic_email, clinic_phone, clinic_address, clinic_location, clinic_city
     135
     136From FD9:
     137listing_id → listing_status, listing_created_at, price, listing_description, animal_id, admin_id
     138Add: listing_status, listing_created_at, price, listing_description, animal_id, admin_id
     139
     140From FD3:
     141admin_id → user_id
     142user_id already belongs to K+
     143
     144From FD8:
     145animal_id → animal_name, located_name, species, animal_type, breed, sex, date_of_birth, photo_url, owner_id
     146Add: animal_name, located_name, species, animal_type, breed, sex, date_of_birth, photo_url, owner_id
     147
     148From FD4:
     149owner_id → client_id
     150Add: client_id
     151
     152From FD2:
     153client_id → user_id, is_blocked, blocked_at, blocked_reason
     154Add: is_blocked, blocked_at, blocked_reason
     155user_id already belongs to K+
     156
     157From FD11:
     158healthrecord_id → hr_type, hr_description, hr_date, animal_id, appointment_id
     159Add: hr_type, hr_description, hr_date, appointment_id
     160animal_id already belongs to K+
     161
     162From FD10:
     163appointment_id → date_time, appointment_status, notes, animal_id, clinic_id
     164Add: date_time, appointment_status, notes
     165animal_id and clinic_id already belong to K+
     166
     167From FD12:
     168review_id → reviewer_id, rating, comment, review_created_at, updated_at, is_deleted
     169Add: reviewer_id, rating, comment, review_created_at, updated_at, is_deleted
     170
     171From FD13:
     172review_id → target_user_id
     173Add: target_user_id
     174
     175From FD14:
     176review_id → target_clinic_id
     177Add: target_clinic_id
     178}}}
     179
     180Therefore, K+ = R, so K is a superkey.
     181
     182K is also minimal, because removing any attribute from K would cause the loss of one independent component of the universal relation
     183{{{#!comment
     184* without notification_id, notification attributes cannot be derived
     185* without application_id, application attributes cannot be derived
     186* without listing_id, listing attributes cannot be derived
     187* without healthrecord_id, health record attributes cannot be derived
     188* without review_id, review attributes cannot be derived
     189* without saved_user_id or saved_listing_id, the FavoriteListings relationship cannot be identified
     190}}}
     191
     192So K is a candidate key.
     193
     194=== Chosen primary key
     195We select the candidate key above as the primary key of the initial de-normalized relation:
     196
     197{{{
     198{notification_id, application_id, listing_id, healthrecord_id, review_id, saved_user_id, saved_listing_id}
     199}}}
     200
     201== Normal form checks
     202
     203=== Check for 1NF
     204R satisfies 1NF because all attributes are atomic and there are no repeating groups.
     205Each row is uniquely identified by the chosen composite candidate key.
     206
     207=== Check for 2NF
     208R does not satisfy 2NF because it has a composite primary key and many non-key attributes depend only on part of that key rather than on the whole key.
     209
     210Examples:
     211* listing_id → listing_status, listing_created_at, price, listing_description
     212* animal_id → animal_name, species, breed, sex, date_of_birth, photo_url, owner_id
     213* clinic_id → clinic_name, clinic_email, clinic_phone, clinic_address, clinic_location, clinic_city
     214* review_id → reviewer_id, rating, comment, review_created_at, updated_at, is_deleted
     215* notification_id → notification_type, notification_message, notification_created_at, is_read, user_id
     216
     217These are partial dependencies on components of the composite key, so R violates 2NF.
     218
     219== Decomposition to 2NF
     220
     221=== Grouping by determinants (partial dependencies)
     222* user_id → username, email, name, surname, full_name, password_hash, user_created_at
     223* client_id → user_id, is_blocked, blocked_at, blocked_reason
     224* admin_id → user_id
     225* owner_id → client_id
     226* notification_id → notification_type, notification_message, notification_created_at, is_read, user_id
     227* clinic_id → clinic_name, clinic_email, clinic_phone, clinic_address, clinic_location, clinic_city
     228* application_id → app_name, app_email, app_phone, app_city, app_address, submitted_at, app_status, reviewed_at, reviewed_by, denial_reason, clinic_id
     229* animal_id → animal_name, located_name, species, animal_type, breed, sex, date_of_birth, photo_url, owner_id
     230* listing_id → listing_status, listing_created_at, price, listing_description, animal_id, admin_id
     231* appointment_id → date_time, appointment_status, notes, animal_id, clinic_id
     232* healthrecord_id → hr_type, hr_description, hr_date, animal_id, appointment_id
     233* review_id → reviewer_id, rating, comment, review_created_at, updated_at, is_deleted
     234* review_id → target_user_id / target_clinic_id
     235* FavoriteListings has key (saved_user_id, saved_listing_id) only
     236
     237=== 2NF relations (first decomposition)
     238* Users(user_id, username, email, name, surname, full_name, password_hash, user_created_at)
     239* Clients(client_id, user_id, is_blocked, blocked_at, blocked_reason)
     240* Admins(admin_id, user_id)
     241* Owners(owner_id, client_id)
     242* Notifications(notification_id, user_id, notification_type, notification_message, notification_created_at, is_read)
     243* VetClinics(clinic_id, clinic_name, clinic_email, clinic_phone, clinic_address, clinic_location, clinic_city)
     244* VetClinicApplications(application_id, clinic_id, app_name, app_email, app_phone, app_city, app_address, submitted_at, app_status, reviewed_at, reviewed_by, denial_reason)
     245* Animals(animal_id, owner_id, animal_name, located_name, species, animal_type, breed, sex, date_of_birth, photo_url)
     246* Listings(listing_id, admin_id, animal_id, listing_status, listing_created_at, price, listing_description)
     247* Appointments(appointment_id, clinic_id, animal_id, date_time, appointment_status, notes)
     248* HealthRecords(healthrecord_id, appointment_id, animal_id, hr_type, hr_description, hr_date)
     249* Reviews(review_id, reviewer_id, rating, comment, review_created_at, updated_at, is_deleted)
     250* FavoriteListings(saved_user_id, saved_listing_id)
     251
     252{{{Lossless join:}}}
     253[[BR]]
     254The decomposition is lossless because each new relation is formed from a determinant that becomes the key of that relation. The common attributes used in the decomposition are keys in at least one of the resulting relations, so no information is lost.
     255
     256{{{Dependency preservation:}}}
     257[[BR]]
     258The decomposition is dependency-preserving because each functional dependency FD1–FD14 is represented fully inside one of the resulting relations. FavoriteListings has only its composite key and no additional non-trivial dependencies.
     259
     260== Check for 3NF (and decomposition)
     261Now we look for transitive dependencies inside the produced relations.
     262
     263=== Typical transitive issues from the ER
     264* In Clients(client_id, user_id, …), we have client_id → user_id, but user descriptive attributes are stored in Users, so no transitive dependency remains inside Clients.
     265* In Listings(listing_id, admin_id, animal_id, …), we have listing_id → admin_id and globally admin_id → user_id, but user attributes are stored in Users, not in Listings, so no transitive dependency remains inside Listings.
     266* In VetClinicApplications(application_id, clinic_id, …), we have application_id → clinic_id and clinic_id determines clinic attributes, but those attributes are stored in VetClinics, not in VetClinicApplications, so no transitive dependency remains there either.
     267
     268=== Review subtypes (User Review and Clinic Review)
     269The ER model contains a specialization of Review into UserReview and ClinicReview.
     270If both target_user_id and target_clinic_id are stored in Reviews, the relation mixes subtype-specific semantics and may introduce nulls or invalid combinations.
     271So we decompose:
     272{{{
     273UserReviews(review_id, target_user_id)
     274ClinicReviews(review_id, target_clinic_id)
     275}}}
     276
     277Now:
     278* Reviews stores the common review attributes
     279* UserReviews stores the target user of a user review
     280* ClinicReviews stores the target clinic of a clinic review
     281
     282=== Results after 3NF step
     283* UserReviews(review_id, target_user_id)
     284* ClinicReviews(review_id, target_clinic_id)
     285
     286After separating subtype-specific attributes into UserReviews and ClinicReviews, all resulting relations satisfy 3NF.
     287
     288== Check for BCNF
     289Each resulting relation is also in BCNF because, in every non-trivial functional dependency within a relation, the determinant is a superkey of that relation.
     290
     291* Users: user_id is key → BCNF
     292* Clients: client_id is key → BCNF
     293* Admins: admin_id is key → BCNF
     294* Owners: owner_id is key → BCNF
     295* Notifications: notification_id is key → BCNF
     296* VetClinics: clinic_id is key → BCNF
     297* VetClinicApplications: application_id is key → BCNF
     298* Animals: animal_id is key → BCNF
     299* Listings: listing_id is key → BCNF
     300* Appointments: appointment_id is key → BCNF
     301* HealthRecords: healthrecord_id is key → BCNF
     302* Reviews: review_id is key → BCNF
     303* UserReviews: review_id is key → BCNF
     304* ClinicReviews: review_id is key → BCNF
     305* FavoriteListings: (saved_user_id, saved_listing_id) is key → BCNF
     306{{{So the schema satisfies BCNF.}}}
     307
     308== Check for 4NF (multivalued dependencies)
     309No additional non-trivial multivalued dependencies are identified in the resulting schema.
     310The many-to-many relationship FavoriteListing is already decomposed into the separate relation FavoriteListings(saved_user_id, saved_listing_id), so no further decomposition is required for 4NF.
     311{{{The schema satisfies 4NF.}}}
     312
     313== Final relations
     314* Users(user_id, username, email, name, surname, full_name, password_hash, user_created_at)
     315* Clients(client_id, user_id, is_blocked, blocked_at, blocked_reason)
     316* Admins(admin_id, user_id)
     317* Owners(owner_id, client_id)
     318* Notifications(notification_id, user_id, notification_type, notification_message, notification_created_at, is_read)
     319* VetClinics(clinic_id, clinic_name, clinic_email, clinic_phone, clinic_address, clinic_location, clinic_city)
     320* VetClinicApplications(application_id, clinic_id, app_name, app_email, app_phone, app_city, app_address, submitted_at, app_status, reviewed_at, reviewed_by, denial_reason)
     321* Animals(animal_id, owner_id, animal_name, located_name, species, animal_type, breed, sex, date_of_birth, photo_url)
     322* Listings(listing_id, admin_id, animal_id, listing_status, listing_created_at, price, listing_description)
     323* FavoriteListings(saved_user_id, saved_listing_id)
     324* Appointments(appointment_id, clinic_id, animal_id, date_time, appointment_status, notes)
     325* HealthRecords(healthrecord_id, appointment_id, animal_id, hr_type, hr_description, hr_date)
     326* Reviews(review_id, reviewer_id, rating, comment, review_created_at, updated_at, is_deleted)
     327* UserReviews(review_id, target_user_id)
     328* ClinicReviews(review_id, target_clinic_id)
     329
     330== Conclusion
     331The normalization process confirms the same structural design obtained from the ER model in phase 2.