| 467 | | * {{{Doctors(doctor_id, d_first_name, d_last_name, d_email, level_id, specialization_id, department_id)}}} |
| 468 | | * {{{DoctorLevels(level_id, level_name)}}} |
| 469 | | * {{{Specializations(specialization_id, specialization_name)}}} |
| 470 | | * {{{Departments(department_id, department_name)}}} |
| 471 | | * {{{Patients(patient_id, p_first_name, p_last_name, p_email, date_of_birth, blood_type, gender, phone_number, embg)}}} |
| 472 | | * {{{Admin(admin_id, admin_username, admin_name, admin_lastname, admin_email)}}} |
| 473 | | * {{{LabTechnician(technician_id, tech_username, tech_name, tech_lastname, tech_email)}}} |
| 474 | | * {{{Users(user_id, users_username, users_password, users_role, users_first_name, users_last_name, is_active)}}} |
| 475 | | * {{{Appointments(appointment_id, appt_patient_id, appt_doctor_id, appointment_date, appointment_time, appointment_status)}}} |
| 476 | | * {{{Diagnoses(diagnosis_id, diagnosis_name, diagnosis_description, diag_patient_id, diag_doctor_id)}}} |
| 477 | | * {{{Procedures(procedure_id, procedure_type, procedure_sched_date, procedure_description, procedure_cost, proc_doctor_id, proc_diagnosis_id)}}} |
| 478 | | * {{{ProcedureResults(proc_result_id, proc_result_description, proc_result_date, procedure_id)}}} |
| 479 | | * {{{PerformedProcedures(performed_id, perf_proc_id, perf_doctor_id, perf_patient_id, perf_diagnosis_id, perf_date, perf_notes)}}} |
| 480 | | * {{{Prescriptions(prescription_id, medication_name)}}} |
| 481 | | * {{{PrescriptionRestrictions(restriction_id, restriction_description, prescription_id)}}} |
| 482 | | * {{{LabTests(test_id, test_name, test_description, test_cost)}}} |
| 483 | | * {{{LabResults(lab_result_id, lab_result_value, lab_result_date, test_id)}}} |
| 484 | | * {{{PerformedLabTests(performed_test_id, pt_test_id, pt_patient_id, pt_doctor_id, pt_technician_id, pt_test_date, pt_notes)}}} |
| 485 | | * {{{MedicalRecords(record_id, patient_id)}}} |
| 486 | | * {{{Allergies(allergy_id, allergy_name, allergy_severity)}}} |
| 487 | | * {{{Symptoms(symptom_id, symptom_name, symptom_description)}}} |
| 488 | | * {{{Referrals(referral_id, referral_reason, referral_date, record_id, ref_from_doctor_id, ref_to_doctor_id)}}} |
| 489 | | * {{{MedicalReports(report_id, report_description, report_date, record_id, report_doctor_id)}}} |
| 490 | | * {{{Billing(bill_id, total_cost, payment_status, payment_date, record_id, billing_admin_id)}}} |
| 491 | | * {{{MedicalRecordSymptoms(record_id, symptom_id, record_symptom_severity)}}} |
| 492 | | * {{{MedicalRecordAllergies(record_id, allergy_id, record_allergy_reaction, record_allergy_severity)}}} |
| 493 | | * {{{PrescriptionRecords(prescription_id, record_id, dosage, frequency, duration, prescription_notes)}}} |
| | 464 | {{{ |
| | 465 | Doctors(doctor_id, d_first_name, d_last_name, d_email, level_id, specialization_id, department_id) |
| | 466 | DoctorLevels(level_id, level_name) |
| | 467 | Specializations(specialization_id, specialization_name) |
| | 468 | Departments(department_id, department_name) |
| | 469 | Patients(patient_id, p_first_name, p_last_name, p_email, date_of_birth, blood_type, gender, phone_number, embg) |
| | 470 | Admin(admin_id, admin_username, admin_name, admin_lastname, admin_email) |
| | 471 | LabTechnician(technician_id, tech_username, tech_name, tech_lastname, tech_email) |
| | 472 | Users(user_id, users_username, users_password, users_role, users_first_name, users_last_name, is_active) |
| | 473 | Appointments(appointment_id, appt_patient_id, appt_doctor_id, appointment_date, appointment_time, appointment_status) |
| | 474 | Diagnoses(diagnosis_id, diagnosis_name, diagnosis_description, diag_patient_id, diag_doctor_id) |
| | 475 | Procedures(procedure_id, procedure_type, procedure_sched_date, procedure_description, procedure_cost, proc_doctor_id, proc_diagnosis_id) |
| | 476 | ProcedureResults(proc_result_id, proc_result_description, proc_result_date, procedure_id) |
| | 477 | PerformedProcedures(performed_id, perf_proc_id, perf_doctor_id, perf_patient_id, perf_diagnosis_id, perf_date, perf_notes) |
| | 478 | Prescriptions(prescription_id, medication_name) |
| | 479 | PrescriptionRestrictions(restriction_id, restriction_description, prescription_id) |
| | 480 | LabTests(test_id, test_name, test_description, test_cost) |
| | 481 | LabResults(lab_result_id, lab_result_value, lab_result_date, test_id) |
| | 482 | PerformedLabTests(performed_test_id, pt_test_id, pt_patient_id, pt_doctor_id, pt_technician_id, pt_test_date, pt_notes) |
| | 483 | MedicalRecords(record_id, patient_id) |
| | 484 | Allergies(allergy_id, allergy_name, allergy_severity) |
| | 485 | Symptoms(symptom_id, symptom_name, symptom_description) |
| | 486 | Referrals(referral_id, referral_reason, referral_date, record_id, ref_from_doctor_id, ref_to_doctor_id) |
| | 487 | MedicalReports(report_id, report_description, report_date, record_id, report_doctor_id) |
| | 488 | Billing(bill_id, total_cost, payment_status, payment_date, record_id, billing_admin_id) |
| | 489 | MedicalRecordSymptoms(record_id, symptom_id, record_symptom_severity) |
| | 490 | MedicalRecordAllergies(record_id, allergy_id, record_allergy_reaction, record_allergy_severity) |
| | 491 | PrescriptionRecords(prescription_id, record_id, dosage, frequency, duration, prescription_notes) |
| | 492 | }}} |
| 504 | | Each relation below comes from one FD: its determinant becomes the new table's key, and |
| 505 | | that same determinant stays behind as a foreign key connecting it to the rest. Because |
| 506 | | the FD already guarantees the determinant decides everything in the new table, every |
| 507 | | split passes the lossless-join test automatically. |
| 508 | | |
| 509 | | ||= # =||= Relation produced =||= Shared column =||= FD used =|| |
| 510 | | || 1 || `Doctors` || `doctor_id` || F4 || |
| 511 | | || 2 || `DoctorLevels` || `level_id` || F1 || |
| 512 | | || 3 || `Specializations` || `specialization_id` || F2 || |
| 513 | | || 4 || `Departments` || `department_id` || F3 || |
| 514 | | || 5 || `Patients` || `patient_id` || F5 || |
| 515 | | || 6 || `Admin` || `admin_id` || F6 || |
| 516 | | || 7 || `LabTechnician` || `technician_id` || F7 || |
| 517 | | || 8 || `Users` || `user_id` || F8 || |
| 518 | | || 9 || `Appointments` || `appointment_id` || F9 || |
| 519 | | || 10 || `Diagnoses` || `diagnosis_id` || F10 || |
| 520 | | || 11 || `Procedures` || `procedure_id` || F11 || |
| 521 | | || 12 || `ProcedureResults` || `proc_result_id` || F12 || |
| 522 | | || 13 || `PerformedProcedures` || `performed_id` || F13 || |
| 523 | | || 14 || `Prescriptions` || `prescription_id` || F14 || |
| 524 | | || 15 || `PrescriptionRestrictions` || `restriction_id` || F15 || |
| 525 | | || 16 || `LabTests` || `test_id` || F16 || |
| 526 | | || 17 || `LabResults` || `lab_result_id` || F17 || |
| 527 | | || 18 || `PerformedLabTests` || `performed_test_id` || F18 || |
| 528 | | || 19 || `MedicalRecords` || `record_id` || F19 || |
| 529 | | || 20 || `Allergies` || `allergy_id` || F20 || |
| 530 | | || 21 || `Symptoms` || `symptom_id` || F21 || |
| 531 | | || 22 || `Referrals` || `referral_id` || F22 || |
| 532 | | || 23 || `MedicalReports` || `report_id` || F23 || |
| 533 | | || 24 || `Billing` || `bill_id` || F24 || |
| 534 | | || 25 || `MedicalRecordSymptoms` || `{record_id, symptom_id}` || F25 || |
| 535 | | || 26 || `MedicalRecordAllergies` || `{record_id, allergy_id}` || F26 || |
| 536 | | || 27 || `PrescriptionRecords` || `{prescription_id, record_id}` || F27 || |
| 537 | | |
| 538 | | Rows 25–27 work the same way, just with two columns acting as the determinant instead of |
| 539 | | one. F25, F26, and F27 each show that the pair alone determines the whole relation and |
| 540 | | that pair is the new table's primary key, so the same test still applies. |
| 541 | | |
| 542 | | Since each of the 27 splits is lossless on its own, the whole chain is too, therefore joining the |
| 543 | | pieces back together, one at a time in reverse order, which rebuilds R exactly. |
| 544 | | |
| 545 | | === Dependency preservation === |
| 546 | | |
| 547 | | Every FD from F1 through F27 landed fully inside one relation, its determinant and all |
| 548 | | of its dependents together, never split across two tables and never left out entirely. |
| 549 | | So the FDs that hold across `Doctors`, `DoctorLevels`, and every relation through |
| 550 | | `PrescriptionRecords` are the same as the FDs we started with, which is what makes the |
| 551 | | decomposition dependency preserving. |
| | 502 | Each relation below comes from one FD: its determinant becomes the new table's key, and that same determinant stays behind as a foreign key connecting it to the rest. Because the FD already guarantees the determinant decides everything in the new table, every split passes the lossless-join test automatically. |
| | 503 | |
| | 504 | === Decomposition into relations === |
| | 505 | |
| | 506 | '''`Doctors`(doctor_id, d_first_name, d_last_name, d_email, level_id, specialization_id, department_id)''' |
| | 507 | |
| | 508 | R1 = R - {d_first_name, d_last_name, d_email} (level_id, specialization_id, department_id remain in R since they are needed as determinants for steps 2-4) |
| | 509 | |
| | 510 | Lossless join: The shared column with R is `doctor_id`. Since `doctor_id -> Doctors` holds (F4), the relation can be reconstructed via join on `doctor_id`. |
| | 511 | |
| | 512 | Dependency preservation: F4 is preserved entirely within the new relation `Doctors`. |
| | 513 | |
| | 514 | '''`DoctorLevels`(level_id, level_name)''' |
| | 515 | |
| | 516 | R2 = R1 - {level_name, level_id} |
| | 517 | |
| | 518 | Lossless join: Shared column is `level_id`. `level_id -> DoctorLevels` holds (F1), so the relation is reconstructed via join on `level_id`. |
| | 519 | |
| | 520 | Dependency preservation: F1 preserved entirely in `DoctorLevels`. |
| | 521 | |
| | 522 | '''`Specializations`(specialization_id, specialization_name)''' |
| | 523 | |
| | 524 | R3 = R2 - {specialization_name, specialization_id} |
| | 525 | |
| | 526 | Lossless join: Shared column `specialization_id`. `specialization_id -> Specializations` holds (F2). |
| | 527 | |
| | 528 | Dependency preservation: F2 preserved entirely in `Specializations`. |
| | 529 | |
| | 530 | '''`Departments`(department_id, department_name)''' |
| | 531 | |
| | 532 | R4 = R3 - {department_name, department_id} |
| | 533 | |
| | 534 | Lossless join: Shared column `department_id`. `department_id -> Departments` holds (F3). |
| | 535 | |
| | 536 | Dependency preservation: F3 preserved entirely in `Departments`. |
| | 537 | |
| | 538 | '''`Patients`(patient_id, p_first_name, p_last_name, p_email, date_of_birth, blood_type, gender, phone_number, embg)''' |
| | 539 | |
| | 540 | R5 = R4 - {p_first_name, p_last_name, p_email, date_of_birth, blood_type, gender, phone_number, embg} (patient_id remains in R - needed later for MedicalRecords) |
| | 541 | |
| | 542 | Lossless join: Shared column `patient_id`. `patient_id -> Patients` holds (F5). |
| | 543 | |
| | 544 | Dependency preservation: F5 preserved entirely in `Patients`. |
| | 545 | |
| | 546 | '''`Admin`(admin_id, admin_username, admin_name, admin_lastname, admin_email)''' |
| | 547 | |
| | 548 | R6 = R5 - {admin_username, admin_name, admin_lastname, admin_email} |
| | 549 | |
| | 550 | Lossless join: Shared column `admin_id`. `admin_id -> Admin` holds (F6). |
| | 551 | |
| | 552 | Dependency preservation: F6 preserved entirely in `Admin`. |
| | 553 | |
| | 554 | '''`LabTechnician`(technician_id, tech_username, tech_name, tech_lastname, tech_email)''' |
| | 555 | |
| | 556 | R7 = R6 - {tech_username, tech_name, tech_lastname, tech_email} |
| | 557 | |
| | 558 | Lossless join: Shared column `technician_id`. `technician_id -> LabTechnician` holds (F7). |
| | 559 | |
| | 560 | Dependency preservation: F7 preserved entirely in `LabTechnician`. |
| | 561 | |
| | 562 | '''`Users`(user_id, users_username, users_password, users_role, users_first_name, users_last_name, is_active)''' |
| | 563 | |
| | 564 | R8 = R7 - {users_username, users_password, users_role, users_first_name, users_last_name, is_active} |
| | 565 | |
| | 566 | Lossless join: Shared column `user_id`. `user_id -> Users` holds (F8). |
| | 567 | |
| | 568 | Dependency preservation: F8 preserved entirely in `Users`. |
| | 569 | |
| | 570 | '''`Appointments`(appointment_id, appt_patient_id, appt_doctor_id, appointment_date, appointment_time, appointment_status)''' |
| | 571 | |
| | 572 | R9 = R8 - {appt_patient_id, appt_doctor_id, appointment_date, appointment_time, appointment_status} |
| | 573 | |
| | 574 | Lossless join: Shared column `appointment_id`. `appointment_id -> Appointments` holds (F9). |
| | 575 | |
| | 576 | Dependency preservation: F9 preserved entirely in `Appointments`. |
| | 577 | |
| | 578 | '''`Diagnoses`(diagnosis_id, diagnosis_name, diagnosis_description, diag_patient_id, diag_doctor_id)''' |
| | 579 | |
| | 580 | R10 = R9 - {diagnosis_name, diagnosis_description, diag_patient_id, diag_doctor_id} |
| | 581 | |
| | 582 | Lossless join: Shared column `diagnosis_id`. `diagnosis_id -> Diagnoses` holds (F10). |
| | 583 | |
| | 584 | Dependency preservation: F10 preserved entirely in `Diagnoses`. |
| | 585 | |
| | 586 | '''`Procedures`(procedure_id, procedure_type, procedure_sched_date, procedure_description, procedure_cost, proc_doctor_id, proc_diagnosis_id)''' |
| | 587 | |
| | 588 | R11 = R10 - {procedure_type, procedure_sched_date, procedure_description, procedure_cost, proc_doctor_id, proc_diagnosis_id} (procedure_id remains - needed for ProcedureResults) |
| | 589 | |
| | 590 | Lossless join: Shared column `procedure_id`. `procedure_id -> Procedures` holds (F11). |
| | 591 | |
| | 592 | Dependency preservation: F11 preserved entirely in `Procedures`. |
| | 593 | |
| | 594 | '''`ProcedureResults`(proc_result_id, proc_result_description, proc_result_date, procedure_id)''' |
| | 595 | |
| | 596 | R12 = R11 - {proc_result_description, proc_result_date, procedure_id} |
| | 597 | |
| | 598 | Lossless join: Shared column `proc_result_id`. `proc_result_id -> ProcedureResults` holds (F12). |
| | 599 | |
| | 600 | Dependency preservation: F12 preserved entirely in `ProcedureResults`. |
| | 601 | |
| | 602 | '''`PerformedProcedures`(performed_id, perf_proc_id, perf_doctor_id, perf_patient_id, perf_diagnosis_id, perf_date, perf_notes)''' |
| | 603 | |
| | 604 | R13 = R12 - {perf_proc_id, perf_doctor_id, perf_patient_id, perf_diagnosis_id, perf_date, perf_notes} |
| | 605 | |
| | 606 | Lossless join: Shared column `performed_id`. `performed_id -> PerformedProcedures` holds (F13). |
| | 607 | |
| | 608 | Dependency preservation: F13 preserved entirely in `PerformedProcedures`. |
| | 609 | |
| | 610 | '''`Prescriptions`(prescription_id, medication_name)''' |
| | 611 | |
| | 612 | R14 = R13 - {medication_name} (prescription_id remains - needed for PrescriptionRestrictions and PrescriptionRecords) |
| | 613 | |
| | 614 | Lossless join: Shared column `prescription_id`. `prescription_id -> Prescriptions` holds (F14). |
| | 615 | |
| | 616 | Dependency preservation: F14 preserved entirely in `Prescriptions`. |
| | 617 | |
| | 618 | '''`PrescriptionRestrictions`(restriction_id, restriction_description, prescription_id)''' |
| | 619 | |
| | 620 | R15 = R14 - {restriction_description} |
| | 621 | |
| | 622 | Lossless join: Shared column `restriction_id`. `restriction_id -> PrescriptionRestrictions` holds (F15). |
| | 623 | |
| | 624 | Dependency preservation: F15 preserved entirely in `PrescriptionRestrictions`. |
| | 625 | |
| | 626 | '''`LabTests`(test_id, test_name, test_description, test_cost)''' |
| | 627 | |
| | 628 | R16 = R15 - {test_name, test_description, test_cost} (test_id remains - needed for LabResults) |
| | 629 | |
| | 630 | Lossless join: Shared column `test_id`. `test_id -> LabTests` holds (F16). |
| | 631 | |
| | 632 | Dependency preservation: F16 preserved entirely in `LabTests`. |
| | 633 | |
| | 634 | '''`LabResults`(lab_result_id, lab_result_value, lab_result_date, test_id)''' |
| | 635 | |
| | 636 | R17 = R16 - {lab_result_value, lab_result_date, test_id} |
| | 637 | |
| | 638 | Lossless join: Shared column `lab_result_id`. `lab_result_id -> LabResults` holds (F17). |
| | 639 | |
| | 640 | Dependency preservation: F17 preserved entirely in `LabResults`. |
| | 641 | |
| | 642 | '''`PerformedLabTests`(performed_test_id, pt_test_id, pt_patient_id, pt_doctor_id, pt_technician_id, pt_test_date, pt_notes)''' |
| | 643 | |
| | 644 | R18 = R17 - {pt_test_id, pt_patient_id, pt_doctor_id, pt_technician_id, pt_test_date, pt_notes} |
| | 645 | |
| | 646 | Lossless join: Shared column `performed_test_id`. `performed_test_id -> PerformedLabTests` holds (F18). |
| | 647 | |
| | 648 | Dependency preservation: F18 preserved entirely in `PerformedLabTests`. |
| | 649 | |
| | 650 | '''`MedicalRecords`(record_id, patient_id)''' |
| | 651 | |
| | 652 | R19 = R18 - {patient_id} (record_id remains - needed for Referrals, MedicalReports, Billing, and the three composite-key relations) |
| | 653 | |
| | 654 | Lossless join: Shared column `record_id`. `record_id -> MedicalRecords` holds (F19). |
| | 655 | |
| | 656 | Dependency preservation: F19 preserved entirely in `MedicalRecords`. |
| | 657 | |
| | 658 | '''`Allergies`(allergy_id, allergy_name, allergy_severity)''' |
| | 659 | |
| | 660 | R20 = R19 - {allergy_name, allergy_severity} |
| | 661 | |
| | 662 | Lossless join: Shared column `allergy_id`. `allergy_id -> Allergies` holds (F20). |
| | 663 | |
| | 664 | Dependency preservation: F20 preserved entirely in `Allergies`. |
| | 665 | |
| | 666 | '''`Symptoms`(symptom_id, symptom_name, symptom_description)''' |
| | 667 | |
| | 668 | R21 = R20 - {symptom_name, symptom_description} |
| | 669 | |
| | 670 | Lossless join: Shared column `symptom_id`. `symptom_id -> Symptoms` holds (F21). |
| | 671 | |
| | 672 | Dependency preservation: F21 preserved entirely in `Symptoms`. |
| | 673 | |
| | 674 | '''`Referrals`(referral_id, referral_reason, referral_date, record_id, ref_from_doctor_id, ref_to_doctor_id)''' |
| | 675 | |
| | 676 | R22 = R21 - {referral_reason, referral_date, ref_from_doctor_id, ref_to_doctor_id} |
| | 677 | |
| | 678 | Lossless join: Shared column `referral_id`. `referral_id -> Referrals` holds (F22). |
| | 679 | |
| | 680 | Dependency preservation: F22 preserved entirely in `Referrals`. |
| | 681 | |
| | 682 | '''`MedicalReports`(report_id, report_description, report_date, record_id, report_doctor_id)''' |
| | 683 | |
| | 684 | R23 = R22 - {report_description, report_date, report_doctor_id} |
| | 685 | |
| | 686 | Lossless join: Shared column `report_id`. `report_id -> MedicalReports` holds (F23). |
| | 687 | |
| | 688 | Dependency preservation: F23 preserved entirely in `MedicalReports`. |
| | 689 | |
| | 690 | '''`Billing`(bill_id, total_cost, payment_status, payment_date, record_id, billing_admin_id)''' |
| | 691 | |
| | 692 | R24 = R23 - {total_cost, payment_status, payment_date, billing_admin_id} |
| | 693 | |
| | 694 | Lossless join: Shared column `bill_id`. `bill_id -> Billing` holds (F24). |
| | 695 | |
| | 696 | Dependency preservation: F24 preserved entirely in `Billing`. |
| | 697 | |
| | 698 | '''`MedicalRecordSymptoms`(record_id, symptom_id, record_symptom_severity)''' |
| | 699 | |
| | 700 | R25 = R24 - {record_symptom_severity} |
| | 701 | |
| | 702 | Lossless join: Shared column {`record_id`, `symptom_id`}. `{record_id,symptom_id} -> MedicalRecordSymptoms` holds (F25) - the same principle, just with a composite determinant. |
| | 703 | |
| | 704 | Dependency preservation: F25 preserved entirely; the relation already depends on the whole composite key, so it is not split further. |
| | 705 | |
| | 706 | '''`MedicalRecordAllergies`(record_id, allergy_id, record_allergy_reaction, record_allergy_severity)''' |
| | 707 | |
| | 708 | R26 = R25 - {record_allergy_reaction, record_allergy_severity} |
| | 709 | |
| | 710 | Lossless join: Shared column {`record_id`, `allergy_id`}. `{record_id,allergy_id} -> MedicalRecordAllergies` holds (F26). |
| | 711 | |
| | 712 | Dependency preservation: F26 preserved entirely; already depends on the whole composite key. |
| | 713 | |
| | 714 | '''`PrescriptionRecords`(prescription_id, record_id, dosage, frequency, duration, prescription_notes)''' |
| | 715 | |
| | 716 | R27 = R26 - {dosage, frequency, duration, prescription_notes, prescription_id, record_id} |
| | 717 | |
| | 718 | Lossless join: Shared column {`prescription_id`, `record_id`}. `{prescription_id,record_id} -> PrescriptionRecords` holds (F27). |
| | 719 | |
| | 720 | Dependency preservation: F27 preserved entirely; already depends on the whole composite key. |
| | 721 | |
| | 722 | ---- |
| | 723 | |
| | 724 | '''Final result:''' after step 27, R27 = { doctor_id, admin_id, technician_id, user_id, appointment_id, diagnosis_id, proc_result_id, performed_id, restriction_id, lab_result_id, performed_test_id, allergy_id, symptom_id, referral_id, report_id, bill_id } - exactly the 16 PK attributes, nothing more. This confirms the decomposition is complete: everything partially dependent has been removed, leaving only the composite key that "links" the rest through foreign keys in all 27 new relations. |
| | 725 | |
| | 726 | Since each of the 27 splits is lossless on its own, the whole chain of decompositions is lossless too (joining all the pieces back together, one at a time, reconstructs R exactly). Likewise, every FD from F1 through F27 is fully contained within a single relation, none split across tables, which means the decomposition is also dependency preserving. |
| 556 | | 2NF removed **partial** dependencies. We now check whether any **transitive** |
| 557 | | dependency remains so a non-key attribute reachable only through another non-key |
| 558 | | attribute. Because the 2NF grouping step split on every determinant found in F1–F27, |
| 559 | | no relation ended up keeping a borrowed descriptive attribute alongside the foreign |
| 560 | | key that points to where it actually lives — so no transitive dependency survived |
| 561 | | into the 2NF output. A few examples confirm this: |
| 562 | | |
| 563 | | `Doctors` keeps level_id, specialization_id, department_id as foreign keys, but |
| 564 | | level_name, specialization_name, department_name stay in `DoctorLevels`, |
| 565 | | `Specializations`, `Departments` are never duplicated back into `Doctors`. |
| 566 | | |
| 567 | | `ProcedureResults` keeps only the foreign key procedure_id, the procedure's own |
| 568 | | procedure_type, procedure_cost, etc. stay in `Procedures`. `Procedures` |
| 569 | | keeps only proc_diagnosis_id, the diagnosis's diagnosis_name and diagnosis_description |
| 570 | | stay in `Diagnoses`. |
| 571 | | |
| 572 | | `LabResults` keeps only the foreign key test_id. test_name, test_description and |
| 573 | | test_cost stay in `LabTests`. |
| 574 | | |
| 575 | | `Referrals`, `MedicalReports`, `Billing`, `Appointments`, `PerformedProcedures`, |
| 576 | | `PerformedLabTests`, `PrescriptionRestrictions` each carry only foreign keys back to |
| 577 | | record_id / doctor_id / patient_id / admin_id / technician_id / procedure_id |
| 578 | | / test_id / prescription_id and never the descriptive attributes belonging to the |
| 579 | | entity on the other end of that key. |
| 580 | | `MedicalRecordSymptoms`, `MedicalRecordAllergies`, |
| 581 | | `PrescriptionRecords` have composite keys with no non-key attribute that could point |
| 582 | | through another non-key attribute, so they aren't affected either way. |
| 583 | | |
| 584 | | In conclusion, every non-key attribute in every relation depends only and |
| 585 | | fully on that relation's own key. The schema is in '''3NF''', and since this is a |
| 586 | | by-product of how the 2NF split was done rather than a separate fix meaning that no further |
| 587 | | decomposition is needed at this stage. |
| | 731 | 2NF removed partial dependencies. We now check whether any transitive dependency remains — i.e. a non-key attribute reachable only through another non-key attribute. Because the 2NF grouping step split on every determinant found in F1–F27, no relation ended up keeping a borrowed descriptive attribute alongside the foreign key that points to where it actually lives — so no transitive dependency survived into the 2NF output. A few examples confirm this: |
| | 732 | |
| | 733 | * `Doctors` keeps level_id, specialization_id, department_id as foreign keys, but level_name, specialization_name, and department_name stay in `DoctorLevels`, `Specializations`, and `Departments` — they are never duplicated back into `Doctors`. |
| | 734 | * `ProcedureResults` keeps only the foreign key procedure_id, the procedure's own procedure_type, procedure_cost, etc. stay in `Procedures`. `Procedures` keeps only proc_diagnosis_id, the diagnosis's diagnosis_name and diagnosis_description stay in `Diagnoses`. |
| | 735 | * `LabResults` keeps only the foreign key test_id. test_name, test_description and test_cost stay in `LabTests`. |
| | 736 | * `Referrals`, `MedicalReports`, `Billing`, `Appointments`, `PerformedProcedures`, `PerformedLabTests`, `PrescriptionRestrictions` each carry only foreign keys back to record_id / doctor_id / patient_id / admin_id / technician_id / procedure_id / test_id / prescription_id and never the descriptive attributes belonging to the entity on the other end of that key. `MedicalRecordSymptoms`, `MedicalRecordAllergies`, `PrescriptionRecords` have composite keys with no non-key attribute that could point through another non-key attribute, so they aren't affected either way. |
| | 737 | |
| | 738 | In conclusion, every non-key attribute in every relation depends only and fully on that relation's own key. The schema is in 3NF. This is a by-product of how the 2NF split was done, rather than something that required a separate fix — so no further decomposition is needed at this stage. |
| 695 | | Nothing is actually being split here. These fourteen pairs were already separate, |
| 696 | | attribute only tables back in the FD section, since none of them ever had an extra |
| 697 | | column that would tie them to something else during 2NF. This section just confirms |
| 698 | | that keeping them separate was correct. If any two had been merged that would create redundant, repeated rows. Keeping each pair on its own avoids that. |
| 699 | | |
| 700 | | * {{{DiagnosisMedicalRecords(diagnosis_id, record_id)}}} |
| 701 | | * {{{DoctorMedicalRecords(doctor_id, record_id)}}} |
| 702 | | * {{{MedicalRecordProcedures(record_id, procedure_id)}}} |
| 703 | | * {{{MedicalRecordLabResults(record_id, lab_result_id)}}} |
| 704 | | * {{{PatientAllergies(patient_id, allergy_id)}}} |
| 705 | | * {{{PatientSymptoms(patient_id, symptom_id)}}} |
| 706 | | * {{{DiagnosisSymptoms(diagnosis_id, symptom_id)}}} |
| 707 | | * {{{DiagnosisProcedures(diagnosis_id, procedure_id)}}} |
| 708 | | * {{{SpecializationProcedures(specialization_id, procedure_id)}}} |
| 709 | | * {{{DepartmentProcedures(department_id, procedure_id)}}} |
| 710 | | * {{{BillingProcedures(bill_id, procedure_id)}}} |
| 711 | | * {{{BillingLabTests(bill_id, test_id)}}} |
| 712 | | * {{{MedicalReportLabResults(report_id, lab_result_id)}}} |
| 713 | | * {{{AllergyPrescriptionRestrictions(allergy_id, restriction_id)}}} |
| 714 | | |
| 715 | | '''Lossless join''' |
| 716 | | The general MVD theorem guarantees that for any relation |
| 717 | | containing `X`, `Y`, and other attributes `Z` where `X ->> Y` holds, splitting into |
| 718 | | `(X, Y)` and `(X, Z)` always reconstructs the original without loss. Since none of |
| 719 | | these fourteen pairs was ever combined with a competing multivalued group in any |
| 720 | | table we produced, that theorem is satisfied trivially rather than needing to be |
| 721 | | invoked as an active repair. |
| 722 | | |
| 723 | | '''Dependency preservation''' |
| 724 | | None of these fourteen relations carries a non-trivial |
| 725 | | FD of its own. Both attributes are needed together just to identify a membership row, |
| 726 | | so there's nothing beyond the membership fact itself that needs preserving. |
| | 820 | Nothing is actually being split here. These fourteen pairs were already separate, attribute-only tables back in the FD section, since none of them ever had an extra column that would tie them to something else during 2NF. This section just confirms that keeping them separate was correct. If any two had been merged, that would create redundant, repeated rows. Keeping each pair on its own avoids that. |
| | 821 | |
| | 822 | === Lossless join & dependency preservation per relation === |
| | 823 | |
| | 824 | '''`DiagnosisMedicalRecords`(diagnosis_id, record_id)''' |
| | 825 | |
| | 826 | MVD: `record_id ->> diagnosis_id` |
| | 827 | |
| | 828 | Lossless join: The general MVD theorem states that for a relation containing `X`, `Y`, and other attributes `Z` where `X ->> Y` holds, splitting into `(X, Y)` and `(X, Z)` reconstructs the original without loss. Here `X = record_id`, `Y = diagnosis_id`, so `(record_id, diagnosis_id)` and the rest of `MedicalRecords`'s attributes split losslessly. |
| | 829 | |
| | 830 | Dependency preservation: This relation carries no non-trivial FD of its own. Both attributes are needed together simply to identify a membership row. |
| | 831 | |
| | 832 | '''`DoctorMedicalRecords`(doctor_id, record_id)''' |
| | 833 | |
| | 834 | MVD: `record_id ->> doctor_id` |
| | 835 | |
| | 836 | Lossless join: `X = record_id`, `Y = doctor_id`. `record_id ->> doctor_id` holds, so `(record_id, doctor_id)` splits losslessly from the rest. |
| | 837 | |
| | 838 | Dependency preservation: This relation carries no non-trivial FD of its own. Both attributes are needed together simply to identify a membership row. |
| | 839 | |
| | 840 | '''`MedicalRecordProcedures`(record_id, procedure_id)''' |
| | 841 | |
| | 842 | MVD: `record_id ->> procedure_id` |
| | 843 | |
| | 844 | Lossless join: `X = record_id`, `Y = procedure_id`. `record_id ->> procedure_id` holds, so `(record_id, procedure_id)` splits losslessly from the rest. |
| | 845 | |
| | 846 | Dependency preservation: This relation carries no non-trivial FD of its own. Both attributes are needed together simply to identify a membership row. |
| | 847 | |
| | 848 | '''`MedicalRecordLabResults`(record_id, lab_result_id)''' |
| | 849 | |
| | 850 | MVD: `record_id ->> lab_result_id` |
| | 851 | |
| | 852 | Lossless join: `X = record_id`, `Y = lab_result_id`. `record_id ->> lab_result_id` holds, so `(record_id, lab_result_id)` splits losslessly from the rest. |
| | 853 | |
| | 854 | Dependency preservation: This relation carries no non-trivial FD of its own. Both attributes are needed together simply to identify a membership row. |
| | 855 | |
| | 856 | '''`DiagnosisSymptoms`(diagnosis_id, symptom_id)''' |
| | 857 | |
| | 858 | MVD: `diagnosis_id ->> symptom_id` |
| | 859 | |
| | 860 | Lossless join: `X = diagnosis_id`, `Y = symptom_id`. `diagnosis_id ->> symptom_id` holds, so `(diagnosis_id, symptom_id)` splits losslessly from the rest. |
| | 861 | |
| | 862 | Dependency preservation: This relation carries no non-trivial FD of its own. Both attributes are needed together simply to identify a membership row. |
| | 863 | |
| | 864 | '''`DiagnosisProcedures`(diagnosis_id, procedure_id)''' |
| | 865 | |
| | 866 | MVD: `diagnosis_id ->> procedure_id` |
| | 867 | |
| | 868 | Lossless join: `X = diagnosis_id`, `Y = procedure_id`. `diagnosis_id ->> procedure_id` holds, so `(diagnosis_id, procedure_id)` splits losslessly from the rest. |
| | 869 | |
| | 870 | Dependency preservation: This relation carries no non-trivial FD of its own. Both attributes are needed together simply to identify a membership row. |
| | 871 | |
| | 872 | '''`SpecializationProcedures`(specialization_id, procedure_id)''' |
| | 873 | |
| | 874 | MVD: `procedure_id ->> specialization_id` |
| | 875 | |
| | 876 | Lossless join: `X = procedure_id`, `Y = specialization_id`. `procedure_id ->> specialization_id` holds, so `(procedure_id, specialization_id)` splits losslessly from the rest. |
| | 877 | |
| | 878 | Dependency preservation: This relation carries no non-trivial FD of its own. Both attributes are needed together simply to identify a membership row. |
| | 879 | |
| | 880 | '''`DepartmentProcedures`(department_id, procedure_id)''' |
| | 881 | |
| | 882 | MVD: `procedure_id ->> department_id` |
| | 883 | |
| | 884 | Lossless join: `X = procedure_id`, `Y = department_id`. `procedure_id ->> department_id` holds, so `(procedure_id, department_id)` splits losslessly from the rest. |
| | 885 | |
| | 886 | Dependency preservation: This relation carries no non-trivial FD of its own. Both attributes are needed together simply to identify a membership row. |
| | 887 | |
| | 888 | '''`BillingProcedures`(bill_id, procedure_id)''' |
| | 889 | |
| | 890 | MVD: `procedure_id ->> bill_id` |
| | 891 | |
| | 892 | Lossless join: `X = procedure_id`, `Y = bill_id`. `procedure_id ->> bill_id` holds, so `(procedure_id, bill_id)` splits losslessly from the rest. |
| | 893 | |
| | 894 | Dependency preservation: This relation carries no non-trivial FD of its own. Both attributes are needed together simply to identify a membership row. |
| | 895 | |
| | 896 | '''`PatientAllergies`(patient_id, allergy_id)''' |
| | 897 | |
| | 898 | MVD: `patient_id ->> allergy_id` |
| | 899 | |
| | 900 | Lossless join: `X = patient_id`, `Y = allergy_id`. `patient_id ->> allergy_id` holds, so `(patient_id, allergy_id)` splits losslessly from the rest. |
| | 901 | |
| | 902 | Dependency preservation: This relation carries no non-trivial FD of its own. Both attributes are needed together simply to identify a membership row. |
| | 903 | |
| | 904 | '''`PatientSymptoms`(patient_id, symptom_id)''' |
| | 905 | |
| | 906 | MVD: `patient_id ->> symptom_id` |
| | 907 | |
| | 908 | Lossless join: `X = patient_id`, `Y = symptom_id`. `patient_id ->> symptom_id` holds, so `(patient_id, symptom_id)` splits losslessly from the rest. |
| | 909 | |
| | 910 | Dependency preservation: This relation carries no non-trivial FD of its own. Both attributes are needed together simply to identify a membership row. |
| | 911 | |
| | 912 | ---- |
| | 913 | |
| | 914 | The remaining three pairs are standalone many-to-many links rather than part of a competing MVD group, so they were never at risk of collision, but they still need their own table since a plain many-to-many relationship can't be expressed as a functional dependency: |
| | 915 | |
| | 916 | '''`BillingLabTests`(bill_id, test_id)''' |
| | 917 | |
| | 918 | Lossless join: `{bill_id, test_id}` is the only many-to-many link at either anchor, so this pair was never combined with a competing multivalued group in any table produced. The pairing is kept in its own relation, which trivially reconstructs via join on `{bill_id, test_id}`. |
| | 919 | |
| | 920 | Dependency preservation: This relation carries no non-trivial FD of its own. Both attributes are needed together simply to identify a membership row. |
| | 921 | |
| | 922 | '''`MedicalReportLabResults`(report_id, lab_result_id)''' |
| | 923 | |
| | 924 | Lossless join: `{report_id, lab_result_id}` is the only many-to-many link at either anchor, kept in its own relation, trivially reconstructed via join on `{report_id, lab_result_id}`. |
| | 925 | |
| | 926 | Dependency preservation: This relation carries no non-trivial FD of its own. Both attributes are needed together simply to identify a membership row. |
| | 927 | |
| | 928 | '''`AllergyPrescriptionRestrictions`(allergy_id, restriction_id)''' |
| | 929 | |
| | 930 | Lossless join: `{allergy_id, restriction_id}` is the only many-to-many link at either anchor, kept in its own relation, trivially reconstructed via join on `{allergy_id, restriction_id}`. |
| | 931 | |
| | 932 | Dependency preservation: This relation carries no non-trivial FD of its own. Both attributes are needed together simply to identify a membership row. |
| | 933 | ---- |