views:

74

answers:

3

I understand how to design a database schema that has simple one-to-many relationships between its tables. I would like to know what the convention or best practice is for designating one particular relationship in that set as the primary one. For instance, one Person has many CreditCards. I know how to model that. How would I designate one of those cards as the primary for that person? Solutions I have come up with seem inelegant at best.


I'll try to clarify my actual situation. (Unfortunately, the actual domain would just confuse things.) I have 2 tables each with a lot of columns, let's say Person and Task. I also have Project which has only a couple of properties. One Person has many Projects, but has a primary Project. One Project has many Tasks, but sometimes has one primary Task with alternates, and other times has no primary task and instead a sequence of Tasks. There are no Tasks that are not part of a Project, but it isn't strictly forbidden.

PERSON (PERSON_ID, NAME, ...)
TASK (TASK_ID, NAME, DESC, EST, ...)
PROJECT (NAME, DESC)

I can't seem to figure a way to model the primary Project, primary Task, and the Task sequence all at the same time without introducing either overcomplexity or pure evil.

This is the best I've come up with so far:

PERSON (PERSON_ID, NAME, ...)
TASK (TASK_ID, NAME, DESC, EST, ...)
PROJECT (PROJECT_ID, PERSON_FK, TASK_FK, INDEX, NAME, DESC)
PERSON_PRIMARY_PROJECT (PERSON_FK, PROJECT_FK)
PROJECT_PRIMARY_TASK (PROJECT_FK, TASK_FK)

It just seems like too many tables for a simple concept.


Here's a question I've found that deals with a very similar situation: Database Design: Circular dependency.

Unfortunately, there didn't seem to be a consensus about how to handle the situation, and the "correct" answer was to disable the database consistency checking mechanism. Not cool.

+3  A: 

Well, it seems to me that a Person has two relationships with a CreditCard. One is that the person owns it, and the other is that they consider it their primary CreditCard. That tells me you have a one-to-one and a one-to-many relationship. The return relationship for the one-to-one is already in the CreditCard because of the one-to-many relationship its in.

This means I'd add primary_cc_id as a field in Person and leave CreditCard alone.

Omnifarious
Isn't this considered a circular reference?
speedmetal
@speedmetal: It might be a circular reference, but is that a bad thing? All the solutions have data consistency issues. Adding a bit field to CreditCard runs the risk of a person with more than one primary card. My solution runs the risk of a primary card that the person doesn't own. My solution is likely somewhat faster, and I think the possible data consistency issue is easier to check.
Omnifarious
My (limited) understanding is that a circular reference is a bad thing. Check out this question: http://stackoverflow.com/questions/1387252/database-design-circular-dependency
speedmetal
I just found another place in our application that does exactly what you've suggested. I was starting to lean your direction anyway, but now there's precedent :) Thanks!
speedmetal
@speedmetal, I read the stuff about circular references, and it makes sense. I think of it more as a "Is this REALLY the only way to do it?!" check. They should be avoided, but in some situations its just the right thing to do even though it's not optimal.
Omnifarious
+2  A: 

Two strategies:

  1. Use a bit column to indicate the preffered card.
  2. Use a PrefferedCardTable associating each Person with the ID of its preffered card.
Johannes Rudolph
1. I felt like the bit column would be a messy solution. Just didn't pass the smell test.2. I might go this direction. It just seems like another table shouldn't be necessary, but maybe it is. I'm going to see what other ideas I get first. Thanks.
speedmetal
It's a tradeoff between query performance, space and extensibility. The bit solution requires little space and is very fast and requires the least extension of the database (just a new column,no extra table no indices etc.). The second solution is cleaner and more extensible, however it requires more work and an additional join.
Johannes Rudolph
I don't like the bit column solution because it doesn't well express the constraint of one and only one preferred card. The extra table would have a primary key of person_id and so would basically be adding a new column to the person table.
Omnifarious
Omnifarious - that's an excellent point, it would be essentially the same as adding another column to the Person table.
speedmetal
Basically yes, keep in mind that you will need to do two joins on the credit card table now. The bit filed solution can always be enforced via a table constraint. I think you found a nice solution.
Johannes Rudolph
A: 

One person can have many credit cards; Then you'd need an identifier on each credit card to actually link that specific credit card to one individual - which I assume you've already made in your model (some kind of ID that links the person to that credit card).

Primary credit card (I assume you mean e.g. as a default credit card?) That would have to be some sort of manual operation (e.g. that you have a third table, that links them together and a column that specifies which one would be the default).

Person (SSN, Name)
CreditCard (CCID, AccountNumber)
P_CC (SSN, CCID, NoID)

So that would mean that if you connect a person to a credit card, you'd have to specify the NoID, as say '1', then design your query to per default find the credit card that belongs to this individual with NoID '1'.

This is of course just one way of doing it, maybe you'd want to limit by 0, 1 - and then sort them by the date the credit card was added to that person.

Maybe if you'd elaborate and give more information about your columns and ideas it'd make it easier.

Olsson