Reputation: 3184
I have four tables: ExternalName
, InternalName
, ExternalInternalNameMap
and Transaction
.
ExternalName and InternalName have a many to many relationship. ExternalInternalNameMap is the junction table between ExternalName and InternalName.
Constraints are:
Thus an ExternalInternalNameMap can have many transactions and a transaction can only correspond to one ExternalInternalNameMap.
The Transaction table must join to the other tables so that the ExternalName and InternalName can be associated with the Transaction.
Having the ExternalInternalNameMap.Id as foreign key in the Transaction table seems a natural choice, but when a row is initially added to the Transaction table, only the ExternalName is known. Later, a user maps an ExternalName to a InternalName. Only then do we have a row in ExternalInternalNameMap. The user needs to see the transaction data to know how to map an ExternalName to InternalName.
Option A:
Transaction could have a fk to ExternalInternalNameMap but InternalNameId (in that table) will initially be NULL. In that case, we cannot enforce duplicate InternalNameId and ExternalNameId combinations in that table.
Option B:
Transaction could contain a fk to ExternalName and fk to InternalName (which would initially be NULL). In that case though, the database does not enforce Transaction to contain valid ExternalName and InternalName mappings.
Is there another approach that resolves these issues?
Edit: Corrected diagram
Upvotes: 3
Views: 162
Reputation: 11018
One possible way is to introduce the notion of an 'unknown' internal name.
For each external name, you also introduce a record in ExternalInternalNameMap
that maps that external name to a special internal name 'Unknown'. So for every external name, (at most) one such 'unknown' mapping exists.
Basically, just follow approach A. When you create a transaction, refer to the 'unknown' mapping of the appropriate external name. Once the internal name becomes known, update the transaction; make it refer to the record in ExternalInternalNameMap
that contains the 'full' mapping.
Notice we are not introducing duplicate mappings here; you can put a unique constraint on the combination (ExternalNameId, InternalNameId)
.
Possible variations:
InternalName
, you may also decide to use a NULL reference in table ExternalInternalNameMap
. This does require column InternalNameId
to be nullable.ExternalName
, there is exactly one record in ExternalInternalNameMap
mapping that external name to 'unknown'), or you can create the 'unknown' mapping when you need it (unless it already exists), i.e. when creating the transaction.Upvotes: 1
Reputation: 7284
I will suggest having a combination of A and B options.
To enforce integrity constraint in database design level, you will need to use a composite primary key on the junction table.
PK of ExternalInternalNameMap
will be {EN_ID, IN_ID}
Having an optional foreign key of ExternalInternalNameMap
inside Transaction
table pulse a check constraint will result consistency.
Transaction table = {FK_EN_ID, FK_EINM_EN_ID, FK_EINM_IN_ID, ...}
check constraint on Transaction table =
{FK_EINM_EN_ID is null OR (FK_EINM_EN_ID = FK_EN_ID)}
Upvotes: 1
Reputation: 142366
FOREIGN KEYS cannot solve everything; forget about them for the moment...
When a new Transaction comes along:
Note that by doing these three steps in that order you always are guaranteed that a row exists before you establish a FK relation to it. (Oops -- why bother declaring FKs?)
So, I guess I am voting for something like Option A.
Keep in mind that whenever you need to get to Internal from Transaction, you will need to go through Map -- that is two JOINs. And, similarly, 2 JOINs to go the other direction. So, be sure there are INDEXes for each direction. (A FK generates an index in one direction.)
Upvotes: 0