Changes between Version 2 and Version 3 of Normalization


Ignore:
Timestamp:
03/02/26 16:38:58 (3 weeks ago)
Author:
231035
Comment:

--

Legend:

Unmodified
Added
Removed
Modified
  • Normalization

    v2 v3  
    11= Normalization
    2 == Initial De-Normalized Relation and Functional Dependencies
    3 === Unified De-Normalized Relation
     2== Initial de-normalized relation and functional dependencies
     3=== Global set of attributes (single de-normalized relation)
    44This represents the initial de-normalized relation before any normalization is applied.
    5 {{{
    6 R(
    7 user_id, username, user_email, password_hash, fullname, name, surname, user_created_at,
    8 blocked_reason, blocked_at, is_blocked,
    9 
    10 animal_id, animal_name, species, breed, sex, date_of_birth, photo_url, owner_id,
    11 
    12 listing_id, listing_status, listing_price, listing_description, listing_created_at,
    13 
    14 appointment_id, appointment_date_time, appointment_status, appointment_notes,
    15 
    16 clinic_id, clinic_name, clinic_address, clinic_location, clinic_city,
    17 clinic_email, clinic_phone,
    18 
    19 application_id, application_name, application_email, application_phone,
    20 application_city, application_address, submitted_at, reviewed_at,
    21 reviewed_by, application_status, denial_reason,
    22 
    23 healthrecord_id, health_type, health_description, health_date,
    24 
    25 review_id, rating, review_comment, review_created_at,
    26 review_updated_at, is_deleted, reviewer_id,
    27 target_user_id, target_clinic_id,
    28 
    29 notification_id, notification_type, notification_message,
    30 notification_created_at, is_read,
    31 
    32 favorite_user_id, favorite_listing_id
    33 )
    34 }}}
    35 === Initial Set of Functional Dependencies
    36 User dependencies
    37 {{{
    38 user_id → username
    39 user_id → user_email
    40 user_id → password_hash
    41 user_id → fullname
    42 user_id → name
    43 user_id → surname
    44 user_id → user_created_at
    45 user_id → blocked_reason
    46 user_id → blocked_at
    47 user_id → is_blocked
    48 }}}
    49 Animal dependencies
    50 {{{
    51 animal_id → animal_name
    52 animal_id → species
    53 animal_id → breed
    54 animal_id → sex
    55 animal_id → date_of_birth
    56 animal_id → photo_url
    57 animal_id → owner_id
    58 }}}
    59 Listing dependencies
    60 {{{
    61 listing_id → listing_status
    62 listing_id → listing_price
    63 listing_id → listing_description
    64 listing_id → listing_created_at
    65 listing_id → animal_id
    66 }}}
    67 Appointment dependencies
    68 {{{
    69 appointment_id → appointment_date_time
    70 appointment_id → appointment_status
    71 appointment_id → appointment_notes
    72 appointment_id → animal_id
    73 appointment_id → clinic_id
    74 }}}
    75 Vet Clinic dependencies
    76 {{{
    77 clinic_id → clinic_name
    78 clinic_id → clinic_address
    79 clinic_id → clinic_location
    80 clinic_id → clinic_city
    81 clinic_id → clinic_email
    82 clinic_id → clinic_phone
    83 }}}
    84 Vet Clinic Application dependencies
    85 {{{
    86 application_id → application_name
    87 application_id → application_email
    88 application_id → application_phone
    89 application_id → application_city
    90 application_id → application_address
    91 application_id → submitted_at
    92 application_id → reviewed_at
    93 application_id → reviewed_by
    94 application_id → application_status
    95 application_id → denial_reason
    96 }}}
    97 Health Record dependencies
    98 {{{
    99 healthrecord_id → health_type
    100 healthrecord_id → health_description
    101 healthrecord_id → health_date
    102 healthrecord_id → animal_id
    103 }}}
    104 Review dependencies
    105 {{{
    106 review_id → rating
    107 review_id → review_comment
    108 review_id → review_created_at
    109 review_id → review_updated_at
    110 review_id → is_deleted
    111 review_id → reviewer_id
    112 review_id → target_user_id
    113 review_id → target_clinic_id
    114 }}}
    115 Notification dependencies
    116 {{{
    117 notification_id → notification_type
    118 notification_id → notification_message
    119 notification_id → notification_created_at
    120 notification_id → is_read
    121 notification_id → user_id
    122 }}}
    123 Fav listing dependencies
    124 {{{
    125 (favorite_user_id, favorite_listing_id) → ∅
    126 }}}
    127 == Candidate Keys and Primary Key Selection
     5To avoid duplicate attribute names, role-based user references are renamed.
     6{{{
     7R = {
     8-- User / roles
     9user_id, username, email, name, surname, full_name, password_hash, user_created_at,
     10client_id, is_blocked, blocked_at, blocked_reason,
     11admin_id,
     12owner_id,
     13
     14-- Notification
     15notification_id, notification_type, notification_message, notification_created_at, is_read,
     16
     17-- Animal
     18animal_id, animal_name, located_name, species, animal_type, breed, sex, date_of_birth, photo_url,
     19
     20-- Listing
     21listing_id, listing_status, listing_created_at, price, listing_description,
     22
     23-- Favorite listing
     24(saved relationship has no separate id in the ER, so it is represented by keys) saved_user_id, saved_listing_id,
     25
     26-- Vet clinic
     27clinic_id, clinic_name, clinic_email, clinic_phone, clinic_address, clinic_location, clinic_city,
     28
     29-- Vet clinic application
     30application_id, app_name, app_email, app_phone, app_city, app_address, submitted_at, app_status, reviewed_at, reviewed_by, denial_reason,
     31
     32-- Appointment
     33appointment_id, date_time, appointment_status, notes,
     34
     35-- Health record
     36healthrecord_id, hr_type, hr_description, hr_date,
     37
     38-- Review (and subtypes)
     39review_id, reviewer_id, rating, comment, review_created_at, updated_at, is_deleted,
     40target_user_id, target_clinic_id
     41}
     42}}}
     43=== Functional dependencies (initial set)
     44User / specialization
     45{{{
     46FD1: user_id → username, email, name, surname, full_name, password_hash, user_created_at
     47FD2: client_id → user_id, is_blocked, blocked_at, blocked_reason
     48FD3: admin_id → user_id
     49FD4: owner_id → client_id
     50}}}
     51Notification
     52{{{
     53FD5: notification_id → notification_type, notification_message, notification_created_at, is_read, user_id
     54}}}
     55Vet Clinic / application
     56{{{
     57FD6: clinic_id → clinic_name, clinic_email, clinic_phone, clinic_address, clinic_location, clinic_city
     58FD7: application_id → app_name, app_email, app_phone, app_city, app_address, submitted_at, app_status, reviewed_at, reviewed_by, denial_reason, clinic_id
     59}}}
     60Animal
     61{{{
     62FD8: animal_id → animal_name, located_name, species, animal_type, breed, sex, date_of_birth, photo_url, owner_id
     63}}}
     64Listing
     65{{{
     66FD9: listing_id → listing_status, listing_created_at, price, listing_description, animal_id, admin_id
     67}}}
     68Appointment
     69{{{
     70FD10: appointment_id → date_time, appointment_status, notes, animal_id, clinic_id
     71}}}
     72Health Record
     73{{{
     74FD11: healthrecord_id → hr_type, hr_description, hr_date, animal_id, appointment_id
     75}}}
     76Review and subtypes
     77{{{
     78FD12: review_id → reviewer_id, rating, comment, review_created_at, updated_at, is_deleted
     79FD13: review_id → target_user_id
     80FD14: review_id → target_clinic_id
     81}}}
     82Favorite Listing
     83{{{
     84FD15: (saved_user_id, saved_listing_id) → /
     85}}}
     86== Candidate keys and primary key selection
     87=== Attributes appearing only on the left side
     88{{{
     89notification_id, application_id, clinic_id, animal_id, listing_id,
     90appointment_id, healthrecord_id, review_id,
     91timestamp-like attributes are not keys here; saved is composite.
     92}}}
     93[[BR]]
     94Because these identifiers can’t be derived from other attributes, they must be included in any superkey of the fully de-normalized R.
     95[[BR]]
     96A valid superkey for R is:
     97{{{
     98K = {notification_id, application_id, clinic_id, animal_id, listing_id,
     99 appointment_id, healthrecord_id, review_id, saved_user_id, saved_listing_id}
     100}}}
     101Its closure includes all descriptive attributes via FD1–FD15, so it determines all attributes of R.
     102=== Chosen primary key
     103We select the minimal superkey built from "left-only identifiers":
     104[[BR]]
     105Primary key (of the initial de-normalized R):
     106[[BR]]
     107{{{
     108{notification_id, application_id, clinic_id, animal_id, listing_id,
     109appointment_id, healthrecord_id, review_id, saved_user_id, saved_listing_id}
     110}}}
     111== Normal form checks
     112=== Check for 1NF
     113R satisfies 1NF (all attributes are atomic and each row is uniquely identified by the chosen primary key).
     114=== Check for 2NF
     115R does not satisfy 2NF, because many non-key attributes depend only on a part of the composite primary key:
     116* listing_id → price, listing_description, listing_status, …
     117* animal_id → species, breed, …
     118* clinic_id → clinic_name, …
     119* user_id → username, email, …
     120* review_id → rating, comment, …
     121* notification_id → message, type, …
     122So we decompose by grouping attributes by the key they depend on.
     123== Decomposition to 2NF
     124=== Grouping by determinants (partial dependencies)
     125* user_id → username, email, name, surname, full_name, password_hash, user_created_at
     126* client_id → user_id, is_blocked, blocked_at, blocked_reason
     127* admin_id → user_id
     128* owner_id → client_id
     129* notification_id → notification_type, notification_message, notification_created_at, is_read, user_id
     130* clinic_id → clinic_name, clinic_email, clinic_phone, clinic_address, clinic_location, clinic_city
     131* application_id → app_*, submitted_at, app_status, reviewed_at, reviewed_by, denial_reason, clinic_id
     132* animal_id → animal_*, owner_id
     133* listing_id → listing_*, animal_id, admin_id
     134* appointment_id → date_time, appointment_status, notes, animal_id, clinic_id
     135* healthrecord_id → hr_type, hr_description, hr_date, animal_id, appointment_id
     136* review_id → reviewer_id, rating, comment, review_created_at, updated_at, is_deleted
     137* review_id → target_user_id / target_clinic_id (subtype dependent)
     138* (saved_user_id, saved_listing_id) → /
     139=== 2NF relations(first decomposition)
     140* Users(user_id, username, email, name, surname, full_name, password_hash, user_created_at)
     141* Clients(client_id, user_id, is_blocked, blocked_at, blocked_reason)
     142* Admins(admin_id, user_id)
     143* Owners(owner_id, client_id)
     144* Notifications(notification_id, user_id, notification_type, notification_message, notification_created_at, is_read)
     145* VetClinics(clinic_id, clinic_name, clinic_email, clinic_phone, clinic_address, clinic_location, clinic_city)
     146* 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)
     147* Animals(animal_id, owner_id, animal_name, located_name, species, animal_type, breed, sex, date_of_birth, photo_url)
     148* Listings(listing_id, admin_id, animal_id, listing_status, listing_created_at, price, listing_description)
     149* Appointments(appointment_id, clinic_id, animal_id, date_time, appointment_status, notes)
     150* HealthRecords(healthrecord_id, appointment_id, animal_id, hr_type, hr_description, hr_date)
     151* Reviews(review_id, reviewer_id, rating, comment, review_created_at, updated_at, is_deleted)
     152* FavoriteListings(saved_user_id, saved_listing_id)
     153{{{Lossless join:}}} each relation shares its determinant key(s) with the original R.
     154{{{Dependency preservation:}}} FD1–FD15 are preserved because each FD now lives entirely inside one of the new relations.
     155== Check for 3NF(and decomposition)
     156Now we look for transitive dependencies inside the produced relations.
     157=== Typical transitive issues from the ER
     158* In Clients(client_id, user_id, …) we have client_id → user_id and user_id → (email, username, …) but those user attributes are no longer in Clients, so this is already clean.
     159* In Listings(listing_id, admin_id, animal_id, …) we have admin_id → user_id (FD3) but user data is in Users, not in Listings, so no transitive dependency remains in Listings.
     160* In VetClinicApplications(application_id, clinic_id, …) we have clinic_id → clinic attributes, but clinic attributes are not stored there.
     161=== Review subtypes(User Review and Clinic Review)
     162The ER has specialization:
     163[[BR]]
     164 Review is generalized into UserReview and ClinicReview. If we keep target_user_id and target_clinic_id inside Reviews, we introduce subtype semantics and potential null-heavy design.
     165[[BR]]
     166So we decompose:
     167[[BR]]
     168{{{UserReviews}}}(review_id, target_user_id)
     169{{{ClinicReviews}}}(review_id, target_clinic_id)
     170Now:
     171* review_id determines the base review fields in Reviews (FD12)
     172* review_id determines subtype target in the subtype table (FD13/FD14)
     173=== Results after 3NF step
     174* UserReviews(review_id, target_user_id)
     175* ClinicReviews(review_id, target_clinic_id)
     176All relations are now in 3NF.
     177== Check for BCNF
     178For each relation, every non-trivial FD has a determinant that is a superkey:
     179* Users: user_id is key → BCNF
     180* Clients: client_id is key → BCNF
     181* Admins: admin_id is key → BCNF
     182* Owners: owner_id is key → BCNF
     183* Notifications: notification_id is key → BCNF
     184* VetClinics: clinic_id is key → BCNF
     185* VetClinicApplications: application_id is key → BCNF
     186* Animals: animal_id is key → BCNF
     187* Listings: listing_id is key → BCNF
     188* Appointments: appointment_id is key → BCNF
     189* HealthRecords: healthrecord_id is key → BCNF
     190* Reviews: review_id is key → BCNF
     191* UserReviews / ClinicReviews: review_id is key → BCNF
     192* FavoriteListings: (saved_user_id, saved_listing_id) is key → BCNF
     193{{{So the schema satisfies BCNF.}}}
     194== Check for 4NF(multivalued dependencies)
     195Potential 4NF risks usually come from independent multi-valued facts forced into one table. Here, we already separated M:N facts into their own relations:
     196* FavoriteListings(saved_user_id, saved_listing_id) is its own table
     197* Review subtypes are separate
     198* Appointments and HealthRecords are separate entities (no independent repeating groups inside them)
     199{{{So the schema satisfies 4NF.}}}
     200== Final relations
     201* Users(user_id, username, email, name, surname, full_name, password_hash, user_created_at)
     202* Clients(client_id, user_id, is_blocked, blocked_at, blocked_reason)
     203* Admins(admin_id, user_id)
     204* Owners(owner_id, client_id)
     205
     206* Notifications(notification_id, user_id, notification_type, notification_message, notification_created_at, is_read)
     207
     208* VetClinics(clinic_id, clinic_name, clinic_email, clinic_phone, clinic_address, clinic_location, clinic_city)
     209
     210* 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)
     211
     212* Animals(animal_id, owner_id, animal_name, located_name, species, animal_type, breed, sex, date_of_birth, photo_url)
     213
     214* Listings(listing_id, admin_id, animal_id, listing_status, listing_created_at, price, listing_description)
     215
     216* FavoriteListings(saved_user_id, saved_listing_id)
     217
     218* Appointments(appointment_id, clinic_id, animal_id, date_time, appointment_status, notes)
     219
     220* HealthRecords(healthrecord_id, appointment_id, animal_id, hr_type, hr_description, hr_date)
     221
     222* Reviews(review_id, reviewer_id, rating, comment, review_created_at, updated_at, is_deleted)
     223* UserReviews(review_id, target_user_id)
     224* ClinicReviews(review_id, target_clinic_id)
     225
     226Business constraint:
     227A ClinicReview is allowed only if the reviewer has at least one Appointment at that clinic with status = 'DONE'.
     228== Conclusion
     229By decomposing the initial unified Petify relation using the functional dependencies derived from the ER model, we obtained a set of relations that are dependency-preserving, lossless-join, and satisfy BCNF (and effectively 4NF). This aligns with the fact that the ER model is already structured around proper entities and relationship tables (especially for saved/favorites and review subtypes).