5

say that i have a one to many relations where there are two tables, a Person table and a Belonging table. Now, each Person has ONLY ONE favorite belonging and a specific belonging cannot belong to another person as well.

My question is, where would that information be better kept ? In the Person table as a favorite_belonging_id or in the Belonging table as an is_favorite entry ? To my eyes, the first choice seems to be the better version, but I would like to hear what sql knowledgeable people have to say about it.

EDIT : A Person has many belongings but only ONE favorite belonging and each belonging can only belong to one person. It's a one to many association.

Spyros
  • 46,820
  • 25
  • 86
  • 129
  • @eggyal it's that one Person has many belongings :) Each person has many belongings but only one favorite. – Spyros Jul 07 '12 at 18:50

7 Answers7

3

I'd be tempted to go with your first suggestion (a favourite_belonging_id column in the Person table), as one can then create a foreign key reference from (person_id, favourite_belonging_id) to (owner_id, belonging_id) in the Belonging table.

If one were to go the other route of creating a is_favourite flag in the Belonging table, there is no obvious way of ensuring the 1:1 nature of person-favourite belonging relationships (a composite UNIQUE index over (owner_id, is_favourite) would fail when a person has multiple belongings that are not their favourite).

That said, it doesn't feel like this information really belongs in the Person table, as it isn't really a property of the person but rather it's a property of the Belonging. If you feel strongly about it, you could create a Favourites table that has a UNIQUE (or PRIMARY) index over person_id.

eggyal
  • 122,705
  • 18
  • 212
  • 237
  • It seems that both you and Randy think that a third table is most probably the best solution. I think i will follow that, thanx ! – Spyros Jul 07 '12 at 19:05
  • @SpyrosP: It's hard to say whether I think that it's best or not. It will carry a performance penalty whenever you need to find a favourite belonging: sometimes it's worth sacrificing data normalisation for performance. As I said, in the case of "people" and "belongings", it doesn't strike me as totally correct to have a belonging in the `Person` table (but that's not to say it isn't entirely appropriate in the context of your application!); in other situations, I might not have even thought of suggesting a third table. Personally, I'd probably stick it in `Person` and not worry any further. – eggyal Jul 07 '12 at 19:08
  • 2
    @eggyal: I would say it's neither a property of the Belonging, it's actually a property of the *relation*. This is the reason why it's not clear where to put it in the first place, because like you said, one would need a relation table for the relation to have properties, a flexibility which is mostly sacrificed for performance reasons. – oxc Jul 07 '12 at 21:48
2

to me it does NOT belong in the person table since it has nothing to do with the base person.

if you have only the belonging table - which i also assume has a person_id in it, then this is where you are expressing the relationship between the belonging and the person, and it is where the qualifier should also go.

another option is to have a third table in the middle linking the two - in this case, the favorite flag goes there.

edit: my preference in design would be the third table option - here you can put a begin date and end date as well as the favorite flag - this would allow you to theoretically trade a belonging to another person at some point in time and still know what happened.

Randy
  • 16,480
  • 1
  • 37
  • 55
  • I see, i was actually thinking of a third table myself, but i think it's a bit redundant in this case. As an entity, it also feels a bit unrelated with the Person table and more into the belongings, but in terms of relativity, it wouldn't fit any of them, since a favorite belonging refers to two entities, a person and a belonging. A third table seems more like 'oop' based than sql based in my eyes though. Moreover, putting it in the Person table, makes it faster to locate, since there are so many belongings. I would prefer to keep third tables for many-many relationships :/ – Spyros Jul 07 '12 at 18:59
  • the third table is actually not OOP but normalized - since the belonging is an independent thing and the person is a different independent thing and the relation between them is real, and it contains other attributes.. – Randy Jul 07 '12 at 19:02
  • to SQL the fact that is_favorite='Y' is a simple where clause addition - so the idea of 'faster' is not really the right way to think of it. (a nice index on person_id and is_favorite will be super fast) – Randy Jul 07 '12 at 19:03
  • oh ok, i see, don't know much about normalization but it sounds fair. So you think that a table like favourite_user_belongings is the best solution, right ? +1 btw, thanx for the idea. – Spyros Jul 07 '12 at 19:03
  • i think just plain old person_belonging to list all the things a person owns.. then the other attributes like is_favorite, and maybe some dates.. – Randy Jul 07 '12 at 19:05
  • I take it the only thing stopping a person from having more than 1 favourite belonging in [person_belonging] would be the code unless you are planning to add a constraint on the table? 1 thing to think about as well is the future plans for your database... would there every be a reason to keep a history of a person's favourites over time (if your allowing them to change them at all). – Pricey Jul 07 '12 at 21:32
1

I see that pretty much all the different options have already been laid out in different answers, but instead of commenting on all to give you my impression on what I think you should do, I'll just create an answer myself.

Just to be clear on how I understand how the system works: All users can have multiple belongings, but any belonging can only be help by one person.

In this case, it makes the most sense to have a user_id in the belongings table that can tie a belonging to a person. Once a user_id is set, nobody else can claim it anymore.

Now, as to the 'favorite' part, there are several things you can do. What truly is the best way to do it strongly depends on the queries you plan on running on it. Some consider adding a JOIN table, but honestly this is a lot of additional data that is rather pointless; there is likely going to be the exact amount of rows in it as the user table and by putting it in a separate table, there is a lot you can't do (for example, see how many people DON'T have a favorite). Likewise, a JOIN table would make no sense for the user_belonging relationship, as there is a 1:1 relationship between the belonging and the amount of people who can have it.
So I believe there are two viable options: either add a field (/switch) in the belongings table to indicate of a user's belonging is his/ her favorite, or add a field to the user table to indicate which belonging is the user's favorite. I would personally think that the latter holds the most merit, but depending on the queries you run, it might make more sense to to the former. Overall, the biggest difference is whether you want to process things pre-insert or post-select; e.g. in the latter situation, you will have to run an independent query to figure out if the user already has a favorite (in the former case this won't be necessary as you would put a unique index on the field in the user table), whereas in a post-select situation you will have to do cross reference which of the selected belongings from the belonging table is the user's favorite.

Please let me know if I explained myself clearly, or if you have any further questions.

Battle_707
  • 708
  • 5
  • 15
1

The following may not be the best options because it offers a somewhat unconventional method of flagging the favourite belonging. The advantage, though, is that this way you'll have just two tables with no circular references and every person will be guaranteed to have no more than one favourite belonging.

So, it's two tables, people (or persons) and belongings. The people table has this structure:

person_id INT AUTO_INCREMENT,
other columns as necessary,

PRIMARY KEY (person_id)

The belongings table is created like this:

belonging_id INT AUTO_INCREMENT,
person_id    INT NOT NULL,
is_favourite enum ('1'),
other columns as necessary,

PRIMARY KEY (belonging_id),
FOREIGN KEY (person_id) REFERENCING people (person_id),
UNIQUE (person_id, is_favourite)

The key element is declaring is_favourite as a nullable enum with a single possible value. This way, when you declare a unique constraint on the pair of (person_id, is_favourite), you are allowed to have as many rows with the same person_id and empty (null) is_favourite as possible, because unique constraints ignore rows where at least one member is null. And you won't be able to create more than one person_id with is_favourite = '1', because that would violate the unique constraint.

Andriy M
  • 76,112
  • 17
  • 94
  • 154
1

Neither. My suggestion is to add another table person_favourite_belonging, like this:

CREATE TABLE person
( person_id INTEGER NOT NULL
--- various other columns about Persons
, PRIMARY KEY (person_id)
) ;

CREATE TABLE belonging
( belonging_id INTEGER NOT NULL 
, person_id INTEGER NOT NULL 
--- various other columns about Belongings
, PRIMARY KEY (belonging_id)
, UNIQUE KEY (person_id, belonging_id)       --- this Unique constraint is needed
, FOREIGN KEY (person_id)
    REFERENCES person (person_id)
) ;

CREATE TABLE person_favourite_belonging
( person_id INTEGER NOT NULL 
, belonging_id INTEGER NOT NULL
, PRIMARY KEY (person_id)
, FOREIGN KEY (person_id, belonging_id)      --- for this Foreign Key constraint
    REFERENCES belonging (person_id, belonging_id)
) ;

This is just my preferred way of doing this. There are alternatives and all have their pros and cons. The pros with this approach are:

  • No circular path in the Foreign Key constraints (and therefore):
  • No chicken and egg problems when inserting, deleting or updating Persons, Belongings or Favourite Belongings.
  • All foreign key columns can be defined as NOT NULL.
  • The integrity can be enforced at the database level.
  • If your requirements change and you want to have 2 (or more) favourites per person, you only change appropriately the constraints at the Favourite table.

Check also my answer in this question (with an almost identical problem): In SQL, is it OK for two tables to refer to each other?

Community
  • 1
  • 1
ypercubeᵀᴹ
  • 113,259
  • 19
  • 174
  • 235
0

favourite_thing is a FK to the belonging table (if that table exists, otherwise it could be a domain) , but in an additional constraint, you can force belonging_id in the persons table to be unique.

UPDATE:

DROP table belonging;
CREATE table belonging
        ( id INTEGER PRIMARY KEY
        , description varchar
        );

DROP table person;
CREATE table person
        ( id INTEGER PRIMARY KEY
        , description varchar
        , favourite_thing INTEGER REFERENCES belonging (id)
        );

-- Now add the unique constraint
-- NOTE: favourite_thing can still be NULL
ALTER TABLE person
        ADD CONSTRAINT must_be_unique UNIQUE (favourite_thing)
        ;

UPDATE 2: if every belonging belongs to exactly one person, you could add an owner field to belongings:

CREATE table belonging
        ( id INTEGER PRIMARY KEY
        , owner_id INTEGER NOT NULL REFERENCES person(id)
        , description varchar
        );

DROP table person CASCADE;
CREATE table person
        ( id INTEGER PRIMARY KEY
        , description varchar
        , favourite_thing INTEGER REFERENCES belonging (id)
        );

ALTER TABLE person
        ADD CONSTRAINT must_be_unique UNIQUE (favourite_thing)
        ;
wildplasser
  • 43,142
  • 8
  • 66
  • 109
  • it's not a uniqueness problem. Normally a belonging has a user_id in it. However, i need to track the user's favorite belonging. – Spyros Jul 07 '12 at 18:55
  • But a belonging can only be the favourite for one person, or can share more than one person the same favourite belonging? – wildplasser Jul 07 '12 at 18:57
  • Yes, a specific belonging only belongs to a certain person and cannot belong to another one. The problem though is that a person with 30 belongings can only have one favorite belonging. But, i am not asking of how to constrain that, but how to actually design the database table attributes. – Spyros Jul 07 '12 at 19:01
  • You can add the N=30 belongings for a person in a person*belonging-table, independently of the single favourite. The favourite is still unique(in the persons table). – wildplasser Jul 07 '12 at 19:07
-1

Actually you present a one-to-one relation. So you can: 1. Hold it in Person table. 2. Hold it in Belonging table. 3. Hold it in both. 4. Hold it in separate table.

urir
  • 1,960
  • 3
  • 23
  • 40
  • option one and two make sense, three and four don't. Unless in specific situations, there is no need to hold redundant information. – Battle_707 Jul 07 '12 at 19:01
  • @Battle_707 - read the other answers for explaination why the normalized relation table is not 'redundant information' but is actually a nice model. – Randy Jul 07 '12 at 23:16
  • @Randy: I see. I did not consider your application. But in case you want to track more transaction data, a third table would indeed be the most logical way. On the flip side, the query becomes double jointed--in the sense that A relates to B and B relates to C, A and C don't have a direct link. Depending on what kind of queries are used and how the sql server is setup, this could be a disadvantage. – Battle_707 Jul 08 '12 at 03:15