views:

207

answers:

5

I'm thinking of designing a database schema similar to the following:

Person (
  PersonID int primary key,
  PrimaryAddressID int not null,
  ...
)

Address (
  AddressID int primary key,
  PersonID int not null,
  ...
)

Person.PrimaryAddressID and Address.PersonID would be foreign keys to the corresponding tables.

The obvious problem is that it's impossible to insert anything into either table. Is there any way to design a working schema that enforces every Person having a primary address?

A: 

The second FK (PersonId from Address to Person) is too restrictive, IMHO. Are you suggesting that one address can only have a single person?

Otávio Décio
Yeah, each address is specific to one person. I don't think it would make much difference though if I turned it into a many-to-many relationship.
jthg
Will you *ever* share address records? Could Mr. Person and Mrs. Person share the same Address, or would they have separate (but identical) Address records?
Ray
No, Address records will never be shared.
jthg
A: 

From your design, it seems that an address can apply to only one person, so just use the PersonID as the key to the address table, and drop the AddressID key field.

Ray
Each person can have multiple addresses.
jthg
But they can only have 1 primary address, correct?
GenericTypeTea
Yes. That was my reasoning for putting a PrimaryAddressID column in the Person table rather than having a IsPrimary column in the Address table.
jthg
Then you will have to allow a null value in your PrimaryAddressID field and enforce the business rule in your application rather than in the db - OR you could allow the PersonID field in the address table to be null, then fill it in later - OR you could key the address table with PersonID and AddressNumber
Ray
Can you explain "you could key the address table with PersonID and AddressNumber"?
jthg
Add a new AddressNumber field to the table and the key - thkey becomes PersonID and AddressNumber, replacing the original AddressID key. The first address for a person then gets AddressNumber 1, the second gets AddressNumber 2, etc. The numbers do not have to be sequential, and there can be gaps when addresses are deleted. I don't usually like two-field primary keys, so I would probably not use this solution myself - I would either allow null in the PrimaryAddressID field so I could fill it in later, or use a many-to-many table as someone else suggested.
Ray
+2  A: 

We mark the primary address in our address table and then have triggers that enforces only record per person can have it (but one record must have it). If you change the primary address, it will update the old primary address as well as the new one. If you delete a primary address and other addresses exist, it will promote one of them (basesd ona series of rules) to the primary address. If the address is inserted and is the first address inserted, it will mark that one automatically as the primary address.

HLGEM
This is definitely the design I've seen before - except without the triggers and enforcement being in the application. Aside from the extra maintenance, I'm not sure how reliable triggers would be. Are you sure that they cover all possible scenarios?
jthg
Yep, I'm sure and our triggers were very carefully tested and have worked without problem for years. Agree that using triggers to do this is a complex process (but so is doing it in the application and that is a far greater risk to data integrity) and they must be carefully tested (especially with multiple recordset inserts/updates/deletes). Triggers are critical to our system as we receive data from many applications and many other sources (we do several hundred data imports a week). This process must be in a trigger if you want to ensure data integrity.
HLGEM
+1  A: 

This is a perfect example of many-to-many relationship. To resolve that you should have intermediate PERSON_ADDRESS table. In other words;

PERSON table
person_id (PK)

ADDRESS table
address_id (PK)

PERSON_ADDRESS
person_id (FK) <= PERSON
address_id (FK) <= ADDRESS
is_primary (BOOLEAN - Y/N)

This way you can assign multiple addresses to a PERSON and also reuse ADDRESS records in multiple PERSONs (for family members, employees of the same company etc.). Using is_primary field in PERSON_ADDRESS table, you can identify if that person_addrees combination is a primary address for a person.

Mevdiven
I don't think this actually solves the problem - it just avoids it by relaxing the relationship. This similar to dropping the foreign key constraint on Address.PersonID
jthg
Then you should enforce this business rule (every person must have primary address) by a trigger on the PERSON table. The model I described should support that business rule.
Mevdiven
+1  A: 

"I believe this is impossible. You cannot create an Address Record until you know the ID of the person and you cannot insert the person record until you know an AddressId for the PrimaryAddressId field."

On the face of it, that claim seems SO appealing. However, it is quite propostrous.

This is a very common kind of problem that the SQL DBMS vendors have been trying to attack for perhaps decades already.

The key is that all constraint checking must be "deferred" until both inserts are done. That can be achieved under different forms. Database transactions may offer the possibility to do something like "SET deferred constraint checking ON", and you're done (were it not for the fact that in this particular example, you'd likely have to mess very hard with your design in order to be able to just DEFINE the two FK constraints, because one of them simply ISN'T a 'true' FK in the SQL sense !).

Trigger-based solutions as described here achieve essentially the same effect, but those are exposed to all the maintenance problems that exist with application-enforced integrity.

In their work, Chris Date & Hugh Darwen describe what is imo the true solution to the problem : multiple assignment. That is, essentially, the possibility to compose several distinct update statements and have the DBMS act upon it as if that were one single statement. Implementations of that concept do exist, but you won't find any that talks SQL.

Erwin Smout