Database Forum / General DB Topics / DB Theory / September 2008
primary key as subtype discriminator
|
|
Thread rating:  |
philiptaylor51@yahoo.com - 31 Aug 2008 17:51 GMT Hello,
I have something like:
TABLE A a PK
TABLE B b PK
TABLE AB (many-to-many table) a, b PK
TABLE AB-1 (AB subtype) a, b PK
TABLE AB-2 (AB subtype) a, b PK
Can I use the attribute "a" (part of PK) as subtype discriminator?
Thanks, philip
jefftyzzer - 03 Sep 2008 01:37 GMT On Aug 31, 9:51 am, philiptaylo...@yahoo.com wrote:
> Hello, > [quoted text clipped - 19 lines] > Thanks, > philip Hi, Philip.
I think that's a bad idea. I know this may sound a bit shop-worn, but all subtypes should be mutually exclusive and exhaustive. Further, they should each describe a semantically unique "kind of" the supertype, with each subtype described by attributes unique to it.
Taking these two guidelines together, this suggests (to me, anyway) that if all subtypes are known, then all instances of the domain of any category discriminator of these subtypes are also to be known.
Inspecting the value of the PK to decide which subtype to navigate to also smacks of "smart" keys, which gets us to the whole natural key vs. smart key vs. surrogate key holy war. Besides, if the PK is, say, a 4-byte unsigned integer, are you saying you'd have (in order to satisfy the "exhaustive" property) 4,294,967,296 subtypes?
Now, if you wanted to add a category discriminator to your AB supertype and make it part of a three part composite key, that's possible, assuming you're enforcing a business rule that mandates that you may only have one instance of each subtype.
Perhaps you can elaborate a bit more on what you're after, and we can humbly suggest better approaches?
Regards,
--Jeff
philiptaylor51@yahoo.com - 04 Sep 2008 14:12 GMT > I think that's a bad idea. I know this may sound a bit shop-worn, but Hi Jeff, thanks for your answer. I think that's a bad idea too, but for different reasons. In my opinion the compound PK in the subtypes becomes "redundant". You always know the value of part of the PK given a subtype. I see this like a normalization error.
> all subtypes should be mutually exclusive and exhaustive. Further, You can use a role entity to relax the mutual exclusivity requirement.
> they should each describe a semantically unique "kind of" the > supertype, with each subtype described by attributes unique to it.
> vs. smart key vs. surrogate key holy war. Besides, if the PK is, say, > a 4-byte unsigned integer, are you saying you'd have (in order to > satisfy the "exhaustive" property) 4,294,967,296 subtypes? What if, for instance, PK BETWEEN 1 AND 5? You are considering the whole 4-byte unsigned integer domain without constraints. Moreover I could use a natural key as category discriminator.
jefftyzzer - 05 Sep 2008 18:08 GMT On Sep 4, 6:12 am, philiptaylo...@yahoo.com wrote:
> > I think that's a bad idea. I know this may sound a bit shop-worn, but > [quoted text clipped - 16 lines] > whole 4-byte unsigned integer domain without constraints. Moreover I > could use a natural key as category discriminator. You're right, using a 1:M role is a great way getting around the mutual-exclusivity requirement (assuming that's what the business rules dictate). Your point in re: the integer is also well-taken: I should have said "up to 4,294,967,296 subtypes," but regardless, it was still a bit of an exaggeration.
Regards,
--Jeff
-CELKO- - 03 Sep 2008 12:46 GMT The classic scenario calls for a root class with all the common attributes and then specialized sub-classes under it. As an example, let's take the class of Vehicles and find an industry standard identifier (VIN), and add two mutually exclusive sub-classes, Sport utility vehicles and sedans ('SUV', 'SED').
CREATE TABLE Vehicles (vin CHAR(17) NOT NULL PRIMARY KEY, vehicle_type CHAR(3) NOT NULL CHECK(vehicle_type IN ('SUV', 'SED')), UNIQUE (vin, vehicle_type), ..);
Notice the overlapping candidate keys. I then use a compound candidate key (vin, vehicle_type) and a constraint in each sub-class table to assure that the vehicle_type is locked and agrees with the Vehicles table. Add some DRI actions and you are done:
CREATE TABLE SUV (vin CHAR(17) NOT NULL PRIMARY KEY, vehicle_type CHAR(3) DEFAULT 'SUV' NOT NULL CHECK(vehicle_type = 'SUV'), UNIQUE (vin, vehicle_type), FOREIGN KEY (vin, vehicle_type) REFERENCES Vehicles(vin, vehicle_type) ON UPDATE CASCADE ON DELETE CASCADE, ..);
CREATE TABLE Sedans (vin CHAR(17) NOT NULL PRIMARY KEY, vehicle_type CHAR(3) DEFAULT 'SED' NOT NULL CHECK(vehicle_type = 'SED'), UNIQUE (vin, vehicle_type), FOREIGN KEY (vin, vehicle_type) REFERENCES Vehicles(vin, vehicle_type) ON UPDATE CASCADE ON DELETE CASCADE, ..);
I can continue to build a hierarchy like this. For example, if I had a Sedans table that broke down into two-door and four-door sedans, I could a schema like this:
CREATE TABLE Sedans (vin CHAR(17) NOT NULL PRIMARY KEY, vehicle_type CHAR(3) DEFAULT 'SED' NOT NULL CHECK(vehicle_type IN ('2DR', '4DR', 'SED')), UNIQUE (vin, vehicle_type), FOREIGN KEY (vin, vehicle_type) REFERENCES Vehicles(vin, vehicle_type) ON UPDATE CASCADE ON DELETE CASCADE, ..);
CREATE TABLE TwoDoor (vin CHAR(17) NOT NULL PRIMARY KEY, vehicle_type CHAR(3) DEFAULT '2DR' NOT NULL CHECK(vehicle_type = '2DR'), UNIQUE (vin, vehicle_type), FOREIGN KEY (vin, vehicle_type) REFERENCES Sedans(vin, vehicle_type) ON UPDATE CASCADE ON DELETE CASCADE, ..);
CREATE TABLE FourDoor (vin CHAR(17) NOT NULL PRIMARY KEY, vehicle_type CHAR(3) DEFAULT '4DR' NOT NULL CHECK(vehicle_type = '4DR'), UNIQUE (vin, vehicle_type), FOREIGN KEY (vin, vehicle_type) REFERENCES Sedans (vin, vehicle_type) ON UPDATE CASCADE ON DELETE CASCADE, ..);
The idea is to build a chain of identifiers and types in a UNIQUE() constraint that go up the tree when you use a REFERENCES constraint. Obviously, you can do variants of this trick to get different class structures.
If an entity doesn't have to be exclusively one subtype, you play with the root of the class hierarchy:
CREATE TABLE Vehicles (vin CHAR(17) NOT NULL, vehicle_type CHAR(3) NOT NULL CHECK(vehicle_type IN ('SUV', 'SED')), PRIMARY KEY (vin, vehicle_type), ..);
Now start hiding all this stuff in VIEWs immediately and add an INSTEAD OF trigger to those VIEWs.
philiptaylor51@yahoo.com - 04 Sep 2008 01:58 GMT [cut]
> CREATE TABLE Vehicles > (vin CHAR(17) NOT NULL PRIMARY KEY, [quoted text clipped - 7 lines] > assure that the vehicle_type is locked and agrees with the Vehicles > table. Add some DRI actions and you are done: [cut]
Thanks for your answer. I'm not sure about this implementation of a generalization structure. The problem is the UNIQUE constraint and not only because it's redundant.
> CREATE TABLE Sedans > (vin CHAR(17) NOT NULL PRIMARY KEY, > vehicle_type CHAR(3) DEFAULT 'SED' NOT NULL > CHECK(vehicle_type IN ('2DR', '4DR', 'SED')), Here a Vehicle can be just a Sedan even if 2 subtypes are present (two- door and four-door). This violates the "exhaustive" rule.
> UNIQUE (vin, vehicle_type), > FOREIGN KEY (vin, vehicle_type) > REFERENCES Vehicles(vin, vehicle_type) If 'vehicle_type' is 2DR or 4DR, then you can't reference the Vehicles table - CHECK(vehicle_type IN ('SUV', 'SED')). If you change it into CHECK(vehicle_type IN ('SUV', 'SED', '2DR', '4DR')) then 2DR and 4DR are both subtypes of Vehicles and not of Sedans. Otherwise you are using a category discriminator to provide the domain for more than a generalization structure.
> ON UPDATE CASCADE > ON DELETE CASCADE, > ..); [cut]
-CELKO- - 04 Sep 2008 21:56 GMT >> Here a Vehicle can be just a Sedan even if 2 subtypes are present (two- door and four-door). This violates the "exhaustive" rule. << You can add a constraint to assure that only "leaf nodes" have more than one row in their table. The constraint can be a CREATE ASSERTION if your product has it, a trigger (ugh!) or a constraint on a locking column of the form " lock CHAR(1) DEFAULT 'X' NOT NULL UNIQUE CHECK (lock = 'X'), ". The bad news is that the last one is true for an empty table as well as a single row table.
|
|
|