View Single Post
  #13  
Old August 7th, 2006, 11:31 AM posted to microsoft.public.access.tablesdbdesign
Jamie Collins
external usenet poster
 
Posts: 1,705
Default How to enforce subtypes/supertypes in Access 2000?


Graham Mandeno wrote:
In a Jet (Access) database, there is no way to enforce this sort of entity
subclassing at the engine level. The only way to do that would be to have
two FK fields in tblContacts - one for IndivID and one for OrgID, and have a
table-level constraint (validation rule) to specify that they cannot both be
Null.


That's got to be a misstatement!

The standard approach to implementing such subclass is to have a
superclass for both individuals and organizations; this can certainly
be implemented in Jet using referential integrity. If there are only
two types, each of which needs a base table in the schema, a
ContactTypes table is overkill:

[I'm avoiding PRIMARY KEY in favour of NOT NULL UNIQUE because
clustering on disk is OT; syntax requires ANSI query mode in the Access
UI or an OLE DB (e.g. ADO) connection.]

CREATE TABLE LegalPersons (
legal_person_ID INTEGER NOT NULL UNIQUE,
legal_person_type VARCHAR(5) NOT NULL,
CHECK (legal_person_type IN ('Indiv', 'org')),
UNIQUE (legal_person_type, legal_person_ID)
);

CREATE TABLE Individuals (
legal_person_ID INTEGER NOT NULL UNIQUE,
legal_person_type VARCHAR(5) NOT NULL,
CHECK (legal_person_type = 'Indiv'),
UNIQUE (legal_person_type, legal_person_ID),
FOREIGN KEY (legal_person_type, legal_person_ID)
REFERENCES LegalPersons (legal_person_type, legal_person_ID),
last_name VARCHAR(35) NOT NULL...
);

CREATE TABLE Organizations (
legal_person_ID INTEGER NOT NULL UNIQUE,
legal_person_type VARCHAR(5) NOT NULL,
CHECK (legal_person_type = 'Org'),
UNIQUE (legal_person_type, legal_person_ID),
FOREIGN KEY (legal_person_type, legal_person_ID)
REFERENCES LegalPersons (legal_person_type, legal_person_ID),
trading_name NVARCHAR(255) NOT NULL...
);

CREATE TABLE Contacts (
legal_person_ID INTEGER NOT NULL,
legal_person_type VARCHAR(5) NOT NULL,
FOREIGN KEY (legal_person_type, legal_person_ID)
REFERENCES LegalPersons (legal_person_type, legal_person_ID),
address_line_1 VARCHAR(30) NOT NULL...
needs a key
);


OK, I know what you are thinking: how to prevent a contact being
created for a row in LegalPersons that does not exist in either
Individuals or Organizations?

Something like this:

CHECK (1 = (SELECT IIF(Contacts.legal_person_type = 'Org', 1, COUNT(*))
FROM Individuals
WHERE Individuals.legal_person_ID = Contacts.legal_person_ID)
),
CHECK (1 = (SELECT IIF(Contacts.legal_person_type = 'Indiv', 1,
COUNT(*))
FROM Organizations
WHERE Organizations.legal_person_ID = Contacts.legal_person_ID)
),
....

Even you implied structure (i.e. omitting the LegalPerson table), you
*can* implement the foreign keys in Jet: using the same CHECKs as
above:

CREATE TABLE Individuals (
individual_ID INTEGER NOT NULL UNIQUE,
last_name VARCHAR(35) NOT NULL...
);

CREATE TABLE Organizations (
organization_ID INTEGER NOT NULL UNIQUE,
trading_name NVARCHAR(255) NOT NULL...
);

CREATE TABLE Contacts (
legal_person_ID INTEGER NOT NULL,
legal_person_type VARCHAR(5) NOT NULL,
CHECK (legal_person_type IN ('Indiv', 'org')),
CHECK (1 = (SELECT IIF(Contacts.legal_person_type = 'Org', 1, COUNT(*))
FROM Individuals
WHERE Individuals.individual_ID = Contacts.legal_person_ID)
),
CHECK (1 = (SELECT IIF(Contacts.legal_person_type = 'Indiv', 1,
COUNT(*))
FROM Organizations
WHERE Organizations.organization_ID = Contacts.legal_person_ID)
),
address_line_1 VARCHAR(30) NOT NULL...
needs a key
);

I would not recommend 'mixing' identifiers in this way. However, I
trust the above is sufficient for you to realize that it is possible to
enforce such constraints at the engine level in Jet.

Jamie.

--