Reputation: 142
I am modeling a normalized database for a heat network (it should be in 3NF). In this table the structure is like this (I removed unimportant data):
Heat_units
(id (PK), name)Clients
(id (PK), client_name, heat_unit_id (FK))Heat_unit_consumption_history
(id (PK), date, heat_unit_id (FK), used_energy) - where (date, heat_unit_id) - represent a composite keyInvoices
(id (PK), start_date, end_date, client_id (FK))Invoice_details
(invoice_id (PK, FK), heat_unit_consumption_history_id (PK, FK), price_id (FK)) - invoice_id and heat_unit_consumption_history_id are a composite primary keyPrices_history
(id(PK), start_date, price) - start_date is a unique keyHere I can see only one way wrong data can be added... If in invoice_details we link to the consumption history of another heat unit. I haven't found a way, other than by programming this constraint, to make sure that in invoice_details I will have only consumption_history_id's for the heat_unit of the current client.
Is there a way I can add this constraint in the database? Or is it ok if I add it in the specifications and implement it programatically?
Edit: This is the database before adding surrogate keys
Heat_units (id (PK), name);
Clients (client_code (PK), client_name, heat_unit_id (FK));
Heat_unit_consumption_history ( date (PK), heat_unit_id (PK, FK), used_energy) - where (date, heat_unit_id) - represent a composite primary key;
Invoices (invoice_number (PK), start_date, end_date, client_code (FK));
Invoice_details (invoice_number (PK, FK), heat_unit_consumption_date (PK, FK), heat_unit_id (FK), price_history_start_date (FK)) - invoice_number and heat_unit_consumption_date are a composite primary key;
Prices_history (start_date (PK), price)
Upvotes: 1
Views: 83
Reputation: 56755
I have found in cases like this that the use of surrogate keys (the ID columns) is confusing the ability to see the desired inter-table constraints when there are multiple or transitive dependencies.
Firstly when you use a surrogate key, you should not treat that as an excuse to not have a natural key. As much as possible (+90% of the time) the table's natural key should be preserved in the design as an Alternate Key (AK). An alternate key is just an additional Unique Key(UK) constraint on the table. Functionally this UK is also an alias for the PK, and thus in data-design we call them Alternate Keys (AK), though in physical implementation they are Unique Keys (UK).
Thus a table like this:
Clients(id (PK), client_name, heat_unit_id (FK))
Is unacceptable because clients obviously can be naturally identified by their name also. Thus this is much more likely to be the correct design for the Clients table:
Clients(id (PK), client_name (AK), heat_unit_id (FK))
Now, why this is important, is because the best way to data-design is to treat the surrogate keys as physical implementation artifacts (for almost all cases) that you only add at the end of the logical design. (The only exceptions to this are things that there is no reasonable way to define a natural key, like say the lines of a cash register receipt or the physical lines of a text file.)
If you look at this answer: Enforce composite unique constraint that depends on parent column value I walk through how to do this for complex cases with transitive relational constraints like yours. Unfortunately, I am do not have enough time today to walk through the whole process for your example, but hopefully you can figure it out from this (or perhaps some other answerer).
In any event, the overall technique is
Upvotes: 1