SQL database (Firebird): problem with a foreign key
-
06-07-2019 - |
Question
My SQL database (Firebird) has a table named tTransaction. It contains two columns, senderFK and receiverFK. There are three other tables, tBufferStock, tFacility and tOutsideLocation.
A sender or a receiver can be either a buffer stock, our own facility or a location outside.
My problem is that I don't know how to let senderFK or receiverFK reference the right table.
I've thought of a new table between sender and the three possible senders with an ID, a number between 1 and 3 for the table and the referenced ID within this table, but actually that doesn't solve the problem. Any ideas?
Norbert
Solution
What you're trying to do cannot be done in SQL. You cannot reference up to three different tables with a single FK.
What you need to do would be:
- create additional columns
senderBufferstockFK
,senderFacilityFK
, andsenderOutsideLocationFK
- connect those to the appropriate tables
- have a check constraint (if supported) or a trigger or some other mechanism on your main table to make sure only one of those three has a value at any given time
This would mean, at any given time, only one of the three "fk" column could have a value on it, but each FK column would be a specific FK to a specific table.
You could put this directly into the table you're talking about, or you could externalize this into a separate table and from your main table just reference that "intermediary" table, and from there have these three FK
YourTable.SenderFK --> Intermediary.PK
Intermediary.SenderBufferstockFK --> tBufferstock.ID
Intermediary.SenderFacilityFK --> tFacility.ID
Intermediary.SenderOutsideLocationFK --> tOutsideLocation.ID
Or you can just drop the FK-relationship, but that's definitely NOT a good idea!
Marc
OTHER TIPS
Try the following schema:
tSenderReceiver (type INT, id INT, PRIMARY KEY (type, id))
tTransaction (id INT PRIMARY KEY, senderType INT, senderId INT, receiverType INT, receiverID INT,
FOREIGN KEY (senderType, senderID) REFERENCES tSenderReceiver,
FOREIGN KEY (receiverType, receiverID) REFERENCES tSenderReceiver
)
tBufferStock (type INT, id INT,
CHECK (type = 1),
PRIMARY KEY (type, id),
FOREIGN KEY (type, id) REFERENCES tSenderReceiver
)
tFacility (type INT, id INT,
CHECK (type = 2),
PRIMARY KEY (type, id),
FOREIGN KEY (type, id) REFERENCES tSenderReceiver
)
tOutsideLocation (type INT, id INT,
CHECK (type = 3),
PRIMARY KEY (type, id),
FOREIGN KEY (type, id) REFERENCES tSenderReceiver
)
SQL does not support a foreign key of the form "either this column in table X or that column in table Y". You can:
Refactor your database so that all three possible foreign key tables are combined into one, possibly called tCounterParty. This is definitely appropriate if the structure of those tables is identical or very similar. If they are not similar you can still take this approach and use three other tables, linked to tCounterParty, to hold the varying information.
Move your referential integrity from a foreign key into a trigger, if supported by your database.
can't you use 3 columns for sender and 3 for receiver? so you'll have bufferSenderFK, facilitySenderFK and facilitySenderFK. for a single transaction, 1 column can be used and other two will be null.