0

The database I am designing is part of a project I am working on. I have come up with 3 simple "business rules":

  • Customers must either be Individuals or Businesses (a business belongs to a certain type).
  • A Customer must place at least one Order.
  • Orders must contain their historical shipping address (i.e. if a customer updates their address, any Orders associated with the Customer must reference the old address).

I have come up with the following design based on the above requirements:

enter image description here

However, there is a problem with normalization.
In order for the 3rd requirement to be met, I am saving the shipping address into the Order at the time of creation. Additionally, I am referencing an address in the Customer table (which is potentially the same).

In the bottom right of the image, I extracted the Address-related attributes into its own table as the first step in my attempt to normalise the design.

Over the past week, I have searched for hints without much luck. I know about this post and this one, yet the implementation is rather ambiguous for my scenario/case.

The thought of making a single table with all addresses, then referencing each as required has crossed my mind but doesn't seem like the right (let alone best) way of achieving this goal.

My question is simple:

How do I meet the 3rd requirement of associating an address with a Customer and an Order historically, with normalisation in mind?

Any help is very much appreciated. Cheers.

2
  • Glad this requirement was considered. Denormalizing an putting a copy of the address in each order is quite sane. The alternative is a historical address table that has an id. Don't try to create an id per customer, it will be a mess to query.
    – danblack
    Dec 11, 2018 at 3:58
  • Out of curiosity, what is the second constraint for? Can a customer not create an account separately to making their first order? Dec 22, 2021 at 0:51

2 Answers 2

1

You have addresses in 3 tables. Shrink that down to 1. Then have a customer_address_id in the Customers table and ship_address_id in the Orders table. Then, ship_address_id can be nullable. Do not make all the fields in Addresses Nullable. (Perhaps region and/or post_code can be left blank -- but make them DEFAULT '' instead of NULLable.

If a Customer is 'shipping' to himself, simply copy the customer_address_id into Orders.ship_address_id.

One more thing... In order to not lose "old addresses", be careful about when (if ever) you change any row in Addresses. You may end up with unused entries, but that is a small price to pay for the data integrity you require.

0

I would suggest that if you are introducing a dedicated Address entity (table and class), then this entity should be responsible for all address handling using foreign keys and remove the in-table address columns. This will reduce code duplication and potential for bugs when copying address data between tables (by not needing to in the first place).

To ensure historical data integrity, you might make addresses immutable as a business rule. When a customer updates an address in their account, create a copy of the original with the updated details and reassign the account's address ID(s) where appropriate to the updated copy.

This would ensure that historical orders link to immutable addresses while removing the need to duplicate addresses for each order.

You will typically be updating customer addresses much less frequently than orders are created.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.