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.
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.
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.
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.
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.
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?
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)
;
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.
Related
I have a table called tracks with the fields id and composer. The field composer might be just one name, or two names separated by commas, or a hundred names separated by commas. This seems like bad practice, and makes it very complicated to write queries where each individual value of composer has to be treated as one.
I could make more fields, like composer2, composer3 etc. up to a hundred, but the next day I might have a value with contains 101 names. What would be a better way to store this data in a mysql database?
The better way: another table.
What you have is called a multi-valued attribute. Your example where you can have up to 100 composers suggests that you really mean there is no fixed limit.
You know what else has no fixed limit in SQL? Rows.
It is common practice to store each value of a multi-valued attribute on its own row, with a reference back to the primary table.
CREATE TABLE Tracks (
id INT PRIMARY KEY
... other single-valued attributes of a track ...
);
CREATE TABLE Composers (
id INT PRIMARY KEY
... composer name and other attributes of a composer ...
);
CREATE TABLE TrackComposers (
trackId INT,
composerId INT,
PRIMARY KEY (trackId, composerId),
FOREIGN KEY (trackId) REFERENCES Tracks (id),
FOREIGN KEY (composerId) REFERENCES Composers (id)
);
The TrackComposers table in this example implements a many-to-many relationship between Tracks and Composers. For each track, there may be many rows in TrackComposers. Each row references one track and one composer.
This way you don't have any limit to the number of composers for a given track, and you also have other good features, like it's easy to find all the tracks composed by a certain person.
Lets says that I have an order table and item table :
CREATE TABLE if not exists ORDERS (
ORDERID INTEGER AUTO_INCREMENT,
ORDERTYPE VARCHAR (20) NOT NULL,
ShippedTime VARCHAR(40),
ORDERDATE DATE,
PRIMARY KEY (ORDERID),
);
CREATE TABLE if not exists ITEM(
ITEMID INTEGER AUTO_INCREMENT,
NAME VARCHAR (20) NOT NULL,
PRICE INTEGER NOT NULL CHECK (PRICE > 0),
PRIMARY KEY (ITEMID)
);
and the relation between the both tables will be existof :
CREATE TABLE if not exists EXISTOF (
ORDERID INTEGER NOT NULL,
ITEMID INTEGER NOT NULL,
FOREIGN KEY (ORDERID) REFERENCES ORDERS(ORDERID) ON DELETE CASCADE,
FOREIGN KEY (ITEMID) REFERENCES ITEM(ITEMID) ON DELETE CASCADE,
PRIMARY KEY (ORDERID,ITEMID)
);
The explanation should be for each order has multiple item and each item belongs to many orders.
If I do like this it will not be work because the ids are primary keys and I can't insert for specific order multiple item and also it can not items belongs to multiple order.
Does anyone have any recommendation how to do that?
Your Existof Table is not flexible enough. The way most order processing systems deal with this situation is to add a column, which we can call Quantity, to the Existof table. The default value is 1, but other quantities can be put in as well.
So if a given order wants to order say 5 reams of paper,and ream of paper in a product, the entry for this item in Existof will have a quantity of 5.
This assumes that all 5 reams are interchangeable, and therefore described by the same data. If some of the paper reams are of different colors, than they ought to be different products.
Create an intermediate table OrderItems with foreign keys item_id and order_id. There are other options but this is the easiest way I find to break down many-many relationships!
"... have to be ..." -- no. FOREIGN KEYs are never "required".
A FK provides three things:
A dynamic check that there is a matching element. This is useful as an integrity check on the data, but is not mandatory.
An INDEX to make the above check significantly faster. Manually specifying an INDEX is just as good. Anyway, a PRIMARY KEY is an index.
"Casscading delete, etc". This is an option that few schemas use, or even need.
There are 3 main types of "relations" between tables:
1:1 -- But why bother having two tables? The columns could simply be in a single table. (There are exceptions.)
1:many -- (This sounds like "many items in one order"??) That is implemented by simply having order_id in the Items table. (And index that column.) Optionally, it can be a FK. Others call the table OrderItems. And it links to a Products table.
many:many -- This is when you need an extra table with (usually) exactly two columns, namely ids into the other two tables. (Eg, Student vs class) Each column could be an FK, but the optimal indexes are PRIMARY KEY(a_id, b_id) and INDEX(b_id, a_id). The FKs would see that you already have indexes starting with a_id and b_id, so it would not create an extra index. Do not have "a unique junction table ID"; it is less efficient than the PK I suggest here. (More discussion: http://mysql.rjweb.org/doc.php/index_cookbook_mysql#many_to_many_mapping_table)
Back to your proposed design. I suggest that "item" implies the product and quantity of that product and the price charged at that time. Hence it needs to be 1:many. And that "product" is what you are thinking of. Please change the table name so I am not confused.
Now, another issue... Price. Is the price fixed forever? Or is the price going to be different for today's Orders than for yesterday's? Again, the Item and Price are tied to one Order. There may be a Price on the Product table, and that may be "current_price", which gets used when creating new Orders.
ShippedTime VARCHAR(40) -- Perhaps should be DATETIME?
I have an application that has coaches, clients and workouts.
One coach can have many clients and one client can have many coaches = many to many
One coach can create many workouts and one workout can only be created by one coach = one to many
Also, one client can create many workouts and one workout can only be created by one client = one to many
My problem here is that a workout could only be created by either a coach or a client. Not both. What’s the best way to structure the tables and fields then?
Right now I have a workout that has id: Pk, coachId: Fk, clientId: Fk. But that means that either coachId or clientId are gonna be null on every row. I guess that’s not best practice?
I’ve also thought of just having one user table with a role table connected to it. But I don’t think that’s gonna be optimal/possible since a coach and a client are gonna have many different fields and relations (eg a coach could have certificates and diplomas and a client needs to be able to be assigned to a workout). Also a client need to be able to be assigned to one or many coaches and vice versa.
Current design:
Coach Client Workout CoachClient (composite)
id id id coachId Fk
coachId Fk clientId Fk
clientId Fk
You are on the right path by having two separate FKs (foreign keys) on the table workout.
Now, to make sure only one of the FKs is always null and the other FK is always NOT null you can add a constraint:
create table workout (
id int primary key not null,
coach_id int,
client_id int,
constraint fk1 foreign key (coach_id) references coach (id),
constraint fk2 foreign key (client_id) references client (id),
constraint chk1 check (coach_id is null and client_id is not null
or coach_id is not null and client_id is null)
);
The magic is in the last constraint chk1. It enforces that one and only one of them is not null.
Clients and Coaches are both Persons. So having one table solves your FK problem.
However, if there are a lot of differences in the columns of "clients" and "coaches", then this probably cause more problems than it cures.
Please note that
Foreign Keys have only a small number of properties; you are asking for more than they can give.
A Foreign key implicitly creates an index, to assist with performance; you can build the INDEX without having an FK.
A Foreign key is a 'constraint' that is checked at runtime. Since your complicated check can't be handled by an FK, maybe you should abandon the FK?
I am trying to figure out relationships between my tables and the notion of how it all inter-relates is not clear.
Lets say I have a Person table (first name, last name, etc) where the Primary key is social security number.
I also have a Fireman table that has just 2 columns -- date when joined the fire company, and a unique fireman number. The 3rd column would be a link back to the Person table.
Initially, I made SSN in the Fireman table to be a foreign key linking to SSN in the Person table. But doesnt it imply that every time I create a new Fireman, I can re-use the same SSN over and over? The foreign key constraint would not be violated -- so its "all good" -- but its not what I want. Does it make sense? I dont want to allow for different unique Fireman numbers all sharing the same SSN number. So how do I set this up -- whats Primary, whats foreign, whats one to one and whats one to many.
A key can be primary and foreign all at the same time in a one-to-one relationship.
You can scrap your primary key from the Fireman table, and just use SSN as the unique key. Since it's one-to-one, there's no need for a separate identifier.
Alternatively, you could create a unique index on the column SSN in your fireman table. That would prevent duplicate entry.
Note that data validation, relationships and indexes are all separate things. You can use indexes and relationships to validate data, but you can also use different concepts, like constraints.
I am designing a data model for tourism-site. I have a table for places (countries, resorts, hotels) and a table for tours. The tables are pretty different in fields so the cant be merged into one. Both of them have usual auto_increment as id. Places and tours have photos so there is a third table for photos. This table has 'parent' field in which I plan to store the parent (place or tour) id.
Which is the best way to design these tables? One table for all photos and two tables as 'parents' for the photos. For now I have added 'parent_type' column to photos table, so when my script displays a tour it calls photos by its (parent) id and type (parent_type) 'tour' from the photos table...
Upd:
Is there a more graceful solution? With just 3 tables and no 'parent_type' column?
(cant post a diagram... here's the link http://share.xmind.net/yentsun/tourism-site-data-model/)
Country, hotel and resort are sub-type of a place. The place table contains all fields common to places, while country, hotel and resort tables contain fields specific to each one. One tour contains many places, one place can be a part of many tours.
Here is example code for Place and Country -- it is T-SQL, but you'll get the idea.
CREATE TABLE Place
(
PlaceID int NOT NULL ,
Type varchar(2)
);
ALTER TABLE Place
ADD CONSTRAINT PK_Place PRIMARY KEY CLUSTERED (PlaceID ASC)
;
ALTER TABLE Place
ADD CONSTRAINT FK1_Place FOREIGN KEY (ParentID) REFERENCES Place(PlaceID)
;
CREATE TABLE Country
(
PlaceID int NOT NULL
);
ALTER TABLE Country
ADD CONSTRAINT PK_Country PRIMARY KEY CLUSTERED (PlaceID ASC)
;
ALTER TABLE Country
ADD CONSTRAINT FK1_Country FOREIGN KEY (PlaceID) REFERENCES Place(PlaceID)
ON DELETE CASCADE
ON UPDATE CASCADE
;
UPDATE after comment
Sorry, four tables is my best for this one.
There are no Parents involved - you just have photos with two atttributes - Place and Tour.
So use a Photos table with two foreign keys, one for Tour, the other for Place. And then of course
a Tours table and a Places table.
If you need to know which Tours went to which Places, deal with it directly with a Tour_Places table
which justifies itself independently.
As for "Parentness", this solution still lets you identify, for a Tour (or Place), which Photos are associated.
I had the same situation a while ago. I used a 'set' type for the parent_type. Never store names for your type, use integers because they can be read much faster. And also place indexes on your foreign keys.