There is another way to solve this problem that will result in a more flexible data model and, hopefully, less code. It will be easier to add more types of things.
I think the solution suggested earlier does not meet all the requirments of the problem.
I'm assuming that we can call all these things objects, and that you are only storing the
current location of all objects relative to other objects. Retain the current tables that you described earlier: StarSystem, DeathStar, Destroyer and TieFighter.
Code:
StarSystem(ID, Name, ...) [Drop the location column since it will always be null.]
DeathStar (ID, Name, Location, ...)
Destroyer (Id, name, location, ...)
TieFighter (Id, name, location, ...)
One way to ensure that the Ids are unique across all four tables is to use the same sequence generator for all four tables. (Other solutions are possible for this problem.)
Have a table called, say, object_type, that defines these object_types.
Code:
object_type( type_code, name)
This could have sample data such as:
Row 1: 'SS', 'StarSystem'
Row 2: 'DS', 'DeathStar'
Row 3: 'D', 'Destroyer'
Row 4: 'TIE', 'TIEFighter')
The point of this is to help enforce the location rules outlined in your problem statement.
Define another table called location_rule (say) for this purpose.
Code:
location_rule (parent_object_type, child_object_type)
This table will be used to enforce the location containment rules.
Sample data:
Row 1: 'SS', 'DS' (says that a StarSystem can contain a DeathStar
(or, equivalently, that a DeathStar can be located in a StarSystem.)
Row 2: 'SS', 'D'
Row 3: 'SS', 'TIE'
Row 4: 'DS', 'D' (a Destroyer can be located inside a DeathStar)
etc.
How do you guarantee that a user/developer does not define a DeathStar to be located in a TIEFighter?
Write a small procedure/function to determine whether a given object_type can be located within another object_type. For example, you could write a small function called is_valid_location (child_object_type, parent_object_type) that returns a boolean value (true or false)
The call to is_valid_location('DS', 'SS') asks if a 'DeathStar' can be located in a 'StarSystem'. This should return true. While calls such as is_valid_location ('SS', 'DS') or is_valid_location('DS', 'TIE') should return false.
This can be done most simply by querying the location_rule table with the values passed in as parameters of the funciton.
Hope that clears up the need for the location_rule table and the object_type table.
For each of the tables 'DeathStar', 'Destroyer' and 'TieFighter' enforce this rule before updating or inserting each row of data.
That leaves us with the problem of determining the object_type corresponding to an ID. Create a view, say, v_object_id_type, that queries the four tables to get the types.
Code:
Pseudo_Code:
select 'SS' as object_type, id from starSystem
Union
select 'DS', id from DeathStar
...
that gets information from the required tables.
(Alternatively, you can create a table with this structure and ensure that a row of data is added or removed to this table whenever a new row of data is added or removed from any of the four tables. I prefer the view unless you plan to have millions of objects.)
Hope that was not too long or confusing.
One of the benefits of this design is that when a new type of object is added, it is relatively easy to make the changes. The changes will be limited only to those things that actually changed, nothing else. For example, data will need to be added to the object_type table (a new object_type is being defined), the location_rule table (what objects can this new type contain, and where can ths new object be located?); a new table for the new type of object that was created, and data for that table; and the view should be modified with the new Union for the new table.
The function that validates the location will not need to be changed, neither will the triggers on any old tables.
Change is limited to only those things that actually change!
Ravi