126 | | There isn’t one attribute that is a candidate for primary key so BCNF is violated. |
127 | | |
128 | | Step 1: Decompose R by Order_id |
129 | | |
130 | | {order_id}* = {employee_id, o_timestamp, o_status, table_number, seat_capacity delivery_address, customer_id} |
131 | | |
132 | | R1 = {order_id, employee_id, o_timestamp, o_status, table_number, seat_capacity delivery_address, customer_id} |
133 | | |
134 | | R2 = {order_id, tip_amount, p_id, p_timestamp, payment_type, amount, oi_id, oi_timestamp, oi_is_processed, oi_price, oi_quantity, pr_id, pr_description, tax_class, pr_price, pr_name, manage_inventory, ca_id, ca_name, ca_is_available, i_quantity, i_restock_level} |
135 | | |
136 | | Step 2: Decompose R1 by table_number |
137 | | |
138 | | {table_number}*= {table_number, seat_capacity} |
139 | | |
140 | | R3={table_number,seat_capacity} |
141 | | |
142 | | R4= {order_id, employee_id, o_timestamp, o_status, table_number, delivery_address, customer_id} |
143 | | |
144 | | Due to the issue that when there is a TabOrder the delivery_address and customer_id will always be NULL values and when there is an OnlineOrder table_number will always have NULL value we split R4 into |
145 | | |
146 | | R5= {order_id, employee_id, o_timestamp, o_status} |
147 | | |
148 | | R6= {order_id, table_number} |
149 | | |
150 | | R7= {order_id, delivery_address, customer_id} |
151 | | |
152 | | Step3: Decompose R2 by p_id |
153 | | |
154 | | {p_id}I* = { order_id, tip_amount, p_id, p_timestamp, payment_type, amount} |
155 | | |
156 | | R8 = {order_id, tip_amount, p_id, p_timestamp, payment_type, amount} |
157 | | |
158 | | Because we have order_id in R8 we don’t need to keep p_id in R9. |
159 | | |
160 | | R9 = {order_id, oi_id, oi_timestamp, oi_is_processed, oi_price, oi_quantity, pr_id, pr_description, tax_class, pr_price, pr_name, manage_inventory, ca_id, ca_name, ca_is_available, i_quantity, i_restock_level |
161 | | |
162 | | Step 4: Decompose R9 by pr_id |
163 | | {pr_id}* = {pr_id, pr_description, tax_class, pr_price, pr_name, manage_inventory, ca_id, ca_name, ca_is_available, i_quantity, i_restock_level} |
164 | | |
165 | | R10 = {pr_id, pr_description, tax_class, pr_price, pr_name, manage_inventory, ca_id, ca_name, ca_is_available, i_quantity, i_restock_level} |
166 | | R11 = { order_id, oi_id, oi_timestamp, oi_is_processed, oi_price, oi_quantity, pr_id} |
167 | | Step 5: Decompose R10 by ca_id |
168 | | {ca_id}* = {ca_id, ca_name, ca_is_available} |
169 | | |
170 | | R12= {ca_id, ca_name, ca_is_available} |
171 | | |
172 | | R13 = {pr_id, pr_description, tax_class, pr_price, pr_name, manage_inventory, ca_id,i_quantity,i_restock_level} |
173 | | |
174 | | Step 6: Decompose Inventory from R13 |
175 | | |
176 | | {i_quantity, i_restock_level} are functionally dependent on pr_id only when manage_inventory is true. If manage_inventory is false it leads to NULL values in our table so we decompose R13 to |
177 | | |
178 | | R14 = {pr_id, pr_description, tax_class, pr_price, pr_name, manage_inventory, ca_id} |
179 | | |
180 | | And |
181 | | |
182 | | R15= {pr_id, i_quantity,i_restock_level} |
183 | | |
184 | | Final BCNF Realtions: |
185 | | R3(Tables), R5(Orders), R6(TabOrders), R7(OnlineOrders), R8(Payments), R11(OrderItems), R14(Products), R15(Inventory) |
186 | | |
187 | | == Conclusion |
188 | | Ultimately, the process of normalization validated our initial design. The final tables derived through formal decomposition were a perfect match for the original tables represented in the ER diagram, confirming the correctness of the schema. |
| 126 | Left only: oi_id, p_id, |
| 127 | |
| 128 | Left and right: table_number, order_id, pr_id, ca_id, |
| 129 | |
| 130 | Right only: employee_id, o_timestamp, o_status, delivery_address, customer_id, seat_capacity, p_timestamp, payment_type, amount, tip_amount, oi_timestamp, oi_is_processed, oi_price, oi_quantity, i_quantity, i_restock_level, ca_name, ca_is_available, manage_inventory, pr_description, tax_class, pr_price, pr_name |
| 131 | |
| 132 | From our analysis we will try {oi_id, p_id} as a candidate key: |
| 133 | |
| 134 | {oi_id, p_id}+= { oi_timestamp, oi_is_processed, oi_price, pr_id, order_id, oi_quantity, pr_description, tax_class, pr_price, pr_name, manage_inventory, i_quantity, i_restock_level, ca_id, ca_name, ca_is_available, -> p_timestamp, payment_type, amount, tip_amount, employee_id, o_timestamp, o_status, table_number, delivery_address, customer_id, seat_capacity} |
| 135 | |
| 136 | {oi_id, p_id} is the only candidate key and primary key. |
| 137 | |
| 138 | There are no multi-valued dependencies in our relation, and therefore it satisfies the first normal form. |
| 139 | |
| 140 | But our previous defined relation isn’t in 2NF because there is a partial dependency: |
| 141 | |
| 142 | p_id -> p_timestamp, payment_type, amount, tip_amount, order_id |
| 143 | |
| 144 | {p_id}+= {p_id, p_timestamp, payment_type, amount, tip_amount, order_id, employee_id, o_timestamp, o_status, table_number, delivery_address, customer_id, seat_capacity} |
| 145 | |
| 146 | Step 1: Decompose R by p_id |
| 147 | R1 = {p_id, p_timestamp, payment_type, amount, tip_amount, order_id, employee_id, o_timestamp, o_status, table_number, delivery_address, customer_id, seat_capacity} |
| 148 | |
| 149 | R2 = {oi_id, oi_timestamp, oi_is_processed, oi_price, order_id, pr_id, oi_quantity, pr_description, tax_class, pr_price, pr_name, manage_inventory, i_quantity, i_restock_level, ca_id, ca_name, ca_is_available} |
| 150 | |
| 151 | Step 2. Decompose R1 by order_id |
| 152 | Functional dependencies in R1: |
| 153 | |
| 154 | p_id -> p_timestamp, payment_type, amount, tip_amount, order_id |
| 155 | |
| 156 | order_id -> employee_id, o_timestamp, o_status, table_number, delivery_address, customer_id |
| 157 | table_number -> seat_capacity |
| 158 | |
| 159 | R1 is not in BCNF because of transitive dependencies |
| 160 | {order_id}+ = {order_id, employee_id, o_timestamp, o_status, table_number, delivery_address, customer_id, seat_capacity} |
| 161 | |
| 162 | R3 = {order_id, employee_id, o_timestamp, o_status, table_number, delivery_address, customer_id, seat_capacity} |
| 163 | |
| 164 | R4 = {p_id, p_timestamp, payment_type, amount, tip_amount, order_id} |
| 165 | Step 3. Decompose R3 by table_number |
| 166 | R3 is still not in BCNF because of transitive dependency |
| 167 | |
| 168 | order_id -> employee_id, o_timestamp, o_status, table_number, delivery_address, customer_id |
| 169 | table_number -> seat_capacity |
| 170 | |
| 171 | {table_number}+ = {table_number, seat_capacity} |
| 172 | |
| 173 | R5 = {table_number, seat_capacity} |
| 174 | |
| 175 | R6 = {order_id, employee_id, o_timestamp, o_status, table_number, delivery_address, customer_id} |
| 176 | |
| 177 | Step 4. Decompose R2 by pr_id |
| 178 | |
| 179 | R2 is not in BCNF because of transitive dependencies |
| 180 | |
| 181 | oi_id -> oi_timestamp, oi_is_processed, oi_price, pr_id, order_id, oi_quantity |
| 182 | |
| 183 | pr_id -> pr_description, tax_class, pr_price, pr_name, manage_inventory, i_quantity, i_restock_level, ca_id |
| 184 | |
| 185 | ca_id -> ca_name, ca_is_available |
| 186 | |
| 187 | {pr_id}+ = {pr_id, pr_description, tax_class, pr_price, pr_name, manage_inventory, i_quantity, i_restock_level, ca_id} |
| 188 | |
| 189 | R7 = {pr_id, pr_description, tax_class, pr_price, pr_name, manage_inventory, i_quantity, i_restock_level, ca_id, ca_name, ca_is_available} |
| 190 | |
| 191 | R8 = {oi_id, oi_timestamp, oi_is_processed, oi_price, pr_id, order_id, oi_quantity} |
| 192 | |
| 193 | In R8, I exclude attributes that are functionally dependent on order_id, as these dependencies have already been addressed in the decompositions from Steps 2 and 3 to avoid redundancy. |
| 194 | |
| 195 | Step 5. Decomposing R7 by ca_id |
| 196 | R7 is not in BCNF due to transitive dependencies |
| 197 | |
| 198 | pr_id -> pr_description, tax_class, pr_price, pr_name, manage_inventory, i_quantity, i_restock_level, ca_id |
| 199 | |
| 200 | ca_id -> ca_name, ca_is_available |
| 201 | |
| 202 | {ca_id}+ = {ca_id, ca_name, ca_is_available} |
| 203 | |
| 204 | R9 = {ca_id, ca_name, ca_is_available} |
| 205 | |
| 206 | R10 = {pr_id, pr_description, tax_class, pr_price, pr_name, manage_inventory, i_quantity, i_restock_level, ca_id} |
| 207 | |
| 208 | From the normalization steps, we obtain the relations R4 (Payments), R5 (Tables), R6 (Orders), R8 (OrderItems), R9 (Categories), and R10 (Products). These do not exactly match our initial database design. The difference comes from our goal to minimize NULL values and improve data organization. We chose to decompose the Orders table into TabOrders and OnlineOrders to separate dine-in and delivery-specific attributes. We also store inventory data in a separate Inventory table, where the primary key is product_id, because we give the user the option to store inventory information only for products they wish to track. This way, products without inventory data do not require NULL values in the main Products table. |