views:

263

answers:

4

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

+2  A: 

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, and senderOutsideLocationFK
  • 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

marc_s
To improve data integrity, you could also define a check constraint (if your DBMS supports it) that requires only one of the three ID columns be non-null.
Christian Hayter
It strikes me that this solution would cause an enormous amount of complexity whenever you wanted to get information out of the table. You would have to code the application to distinguish among the three cases. And a design that REQUIRES two NULL values in every row doesn't seem to honor the spirit of data normalization, either.
Larry Lustig
@Larry: I totally agree - it's not pretty. These kind of "reference one of those x tables" scenarios never are :-(
marc_s
@Christian: yes, absolutely - good point!
marc_s
@marc_s @Christian: Thank you! This sounds plausible. I will give it a try.
Norbert
A: 

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.

ashik_gurung
+1  A: 

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
)
Quassnoi
+1 I was going to suggest something similar but you beat me to it, and with table definitions too!
APC
This is similar to a suggestion I made, below. I would change this schema in the following ways: 1) Move any common columns in the three specific tables (notably name or description) to the tSenderReceiver table (makes basic retrieval much easier) and 2) add a separate autonumbered primary key to tSenderReceiver to save a column in tTransaction and ease the JOIN.
Larry Lustig
`@Larry Lusting`: `tSenderReceiver` as it is now will hardly participate in any joins, it will be only used to ensure integrity. However, if there are some common columns, then your suggestion makes sense indeed. I now remember to have given a similar answer before: http://stackoverflow.com/questions/1493229/multiple-yet-mutually-exclusive-foreign-keys-is-this-the-way-to-go
Quassnoi
+1  A: 

SQL does not support a foreign key of the form "either this column in table X or that column in table Y". You can:

  1. 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.

  2. Move your referential integrity from a foreign key into a trigger, if supported by your database.

Larry Lustig