If you take (NATURAL) ANIMAL AND PREFERRED FOOD ADDITION, then you get a table that lists for each animal its type and its preferred food.
You want this combination to be “valid” for each individual animal, where “valid” means “appear on the list of valid animal / food type combinations listed in the PRODUCTS section.
So, you have a restriction that is somewhat similar to FK, but this time the “foreign key” appears not in the base table, but in the union of two tables. For this type of constraint, the SQL language has CHECK and ASSERTIONS constraints.
The ASSERTION version is the simplest. This is a limitation (I was somewhat liberal with attribute names in order to avoid simply renaming attributes that obfuscate the point)
CREATE ASSERTION <name for your constraint here> CHECK NOT EXISTS (SELECT ANIMAL_TYPE, FOOD_TYPE FROM ANIMALS NATURAL JOIN PREF_FOOD WHERE (ANIMAL_TYPE, FOOD_TYPE) NOT IN SELECT ANIMAL_TYPE, FOOD_TYPE FROM FOOD_TYPE);
But your average SQL engine will not support ASSERTION. Therefore, you should use CHECK restrictions. For example, for the PREF_FOOD table, the CHECK constraint you need might look something like this:
CHECK EXISTS (SELECT 1 FROM FOOD NATURAL JOIN ANIMAL WHERE ANIMAL_TYPE = <animal type of inserted row> AND FOOD_TYPE = <food type of inserted row>);
In theory, this should be sufficient to enforce your constraint, but then your average SQL engine will not support this CHECK constraint again due to references to tables other than the constraint defined.
Thus, you have options to resort to rather complex (*) settings, such as catcall, or to force a restriction using triggers (and you will have to write quite a lot (at least three or six) thought about this in detail), and your the next best option is to provide this in the application code, and once again there will be three or six (more or less) different places where the same number of different checks should be implemented.
In all of these three scenarios, you will most likely want to document the existence of the constraint, and what exactly is being discussed, in some other place. None of the three will become very obvious to a third party reading this design what this means.
(*) "complex" may not be the right word, but note that such solutions rely on intentional redundancy , so it intentionally goes below 3NF with the design . This means that your design undergoes anomaly updates, which means that it will be more difficult for the user to update the database and maintain its consistency (precisely because of deliberate redundancy).