SQL many-to-many relation 3 ways - mysql

Hej all,
Let's say I have 4 tables named "user", "office", "product", "event".
And another table named "document". A same document can be assigned to
one or many users, offices, products and events. So here we need a
many-to-many relationship. But I have 3 ways to do that :
-a table named "user_document", another named "office_document", "product_document" and "event_document" which all have a field named
"document_id" which is foreign key for document id and another field
"user_id" (for user_document) which is foreign key to user id (and so
on with office, product and event of course...)
OR
-a table named "document_ownership" which has these fields : "document_id", "user_id", "office_id", "product_id" and "event_id".
Here document_id should be not Null and one (or more) of other fields
that can be Null. For example if I set a same document for a user and
a product, I will have a row with document_id, user_id and product_id
not Null.
OR
-a table named "document_ownership" that will have these fields : "document_id", "relation_type" and "relation_id". Here relation_type
field is for example a string (which represent the relation table
name) or a foreign key pointing to another additionnal table named for
example "relationtype" in which we have strings like "user" (id=1),
"office" (id=2), "product" (id=3) and "event" (id=4) (which also
represent the relation table name), and relation_id which is the id of
the specified relation table (relation_type)
My question is, what is the pro/cons of all these 3 ways of doing what I want and what should be the best practice please ?
Thanks in advance for your advices,
Michal

This question is not really answerable as asked. A purist would say that approach 1 is correct but it is not always that simple. Think of it like this - your database design should express the relationships between the data and what the data means. So each of your approaches imply several things about the nature of the data.
Approach 1 says that user, office, product and event are important, and oh yeah they can have documents. Maybe.
Approach 2 says that documents are important, and we need to track what each document relates to. So the document is the key thing and everything else is annotated around that.
Approach 3 is more complicated and technical and does not really give an idea of how you want the data to be used.
In all cases the data is same. It is just designing the data to tell the story of how it should be used.
Sorry to wax lyrical. Just my $0.02.

In a data conception (Merise) view you have :
Document-0,n---------0,n-User
Document-0,n---------0,n-Event
...
This is the logical view.
When you transform this to physical data view you will end up with 1 more table for each relation.
So the 1st solution is the way to go, if you want to apply best practice in data modelisation.
Concerning the two other solutions, which breaks some normal form :
the second solution is a total no go. You will have a lot of null value everywhere and will strugle to do some basic statistic because of that.
The third solution, that looks like a spaghetthi plate, will globally work and is, in my point of view, a good alternative. IF you can handle the loss of constraint integrity

Related

SQL for one to one between a single table

I'd like to know what the best way of reflecting relations between precisely two rows from a single (my)sql table is?
Exemplified, we have:
table Person { id, name }
If I want to reflect that persons can be married monogamously (in liberal countries at least), is it better to use foreign keys within the Person:
table Person { id, name, spouse_id(FK(Person.id)) }
and then create stored procedures to marry and divorce Persons (ensuring mutual registration of the marriage or annulment of it + triggers to handle on_delete events..
or use a mapping table:
table Marriage {
spouse_a(FK(Person.id)),
spouse_b(FK,Person.id) + constraint(NOT IN spouse_a))
}
This way divorces (delete) would simply be delete queries without triggers to cascade, and marriage wouldn't require stored procedure.
The constraint is to prevent polygamy / multi-marriage
I guess the second option is preferred? What is the best way to do this?
I need to be able to update this relation on and off, so it has to be manageable..
EDIT:
Thanks for the replies - in practice the application is physical point-to-point interfaces in networking, where it really is a 1:1 relationship (monogamous marriage), and change in government, trends etc will not change this :)
I'm going to use a separate table with A & B, having A < B checked..
To ensure monogamy, you simply want to ensure that the spouses are unique. So, this almost does what you want:
create table marriage (
spouse_a int not null unique,
spouse_b int not null unique
);
The only problem is that a given spouse can be in either table. One normally handles this with a check constraint:
check (spouse_a < spouse_b)
Voila! Uniqueness for the relationship.
Unfortunately, MySQL does not support check constraints. So you can implement this using a trigger or at the application layer.
Option #1 - Add relationships structurally
You can add one additional table for every conceivable relationship between two people. But then, when someone asks for a new ralationship you forgot to add structurally, you'll need to add a new table.
And then, there will be relationship for three people at a time. And then four. And then, variable size relationships. You name it.
Option #2 - Model relationships as tables
To make it fool proof (well... never possible) you could model the relationships into a new table. This table can have several properties such as size, and also you can model restrictions to it. For example, you can decide to have a single person be the "leader of the cult" if you wish to.
This option requires more effor to design, but will resist much more options, and ideas from your client that you never thought before.

best practice for foreign key relationships

Which is the best practice to create the foreign key from the mentioned two-
possibility 1:
table1 : user(id,name,password)
table2 : exams(id,name)
table 3 : user_exam(is,user_id,exam_id)
possibility 2:
table1 : exams(id,name)
table2 : user(id,name,password, exam_id)
Based on what you are modeling, I would guess that you have a many-to-many relationship between Exams and users. In other words, you coudl have exams without users and users without exams. In this case model 2 does not work at all.
In model 2 each user would only get one id or you woudl have to mhave multipel users records each time an exam for tehm is added, This increase the likelihood of data integrity problems espcially since password is there. Do not even consider using model 2 unless you can guarantee there will never be a need for more than one exam.
Depending on what type of exams you are talking about the user_exams table should probably include additional information such as a date. What else you might need depends on the meaning of teh data you are modeling.
Your question is more like an opinion based question but as far as I know foreign keys should be in the junction table. So Possibility 1 is an ideal approach to use the foreign key.

DB design for one-to-one single column table

I'm unsure the best route to take for this example:
A table that holds information for a job; salary, dates of employment etc. The field I am wondering how best to store is 'job_title'.
Job title is going to be used as part of an auto-complete field so
I'll be using a query to fetch results.
The same job title will be used by multiple jobs in the DB.
Job title is going to be a large part of many queries in the
application.
A single job only ever has one title.
1 . Should I have a 2 tables, job and job_title, job table referencing the job_title table for its name.
2 . Should I have a 2 tables, job and job_title but store title as a direct value in job, job_title just storing a list of all preexisting values (somewhat redundant)?
3 . Or should I not use a reference table at all / other suggestion.
What is your choice of design in this situation, and how would it change in a one to many design?
This is an example, the actual design is much larger however I think this well conveys the issue.
Update, To clarify:
A User (outside scope of question) has many Jobs, a job (start/end date, {job title}) has a title, title ( name (ie. 'Web Developer' )
Your option 1 is the best design choice. Create the two tables along these lines:
jobs (job_id PK, title_id FK not null, start_date, end_date, ...)
job_titles (title_id PK, title)
The PKs should have clustered indexes; jobs.title_id and job_titles should have nonclustered or secondary indexes; job_titles.title should have a unique constraint.
This relationship can be modeled as 1-to-1 or 1-to-many (one title, many jobs). To enforce 1-to-1 modeling, apply a unique constraint to jobs.title_id. However, you should not model this as a 1-to-1 relationship, because it's not. You even say so yourself: "The same job title will be used by multiple jobs in the DB" and "A single job only ever has one title." An entry in the jobs table represents a certain position held by a certain user during a certain period of time. Because this is a 1-to-many relationship, a separate table is the correct way to model the data.
Here's a simple example of why this is so. Your company only has one CEO, but what happens if the current one steps down and the board appoints a new one? You'll have two entries in jobs which both reference the same title, even though there's only one CEO "position" and the two users' job date ranges don't overlap. If you enforce a 1-to-1 relationship, modeling this data is impossible.
Why these particular indexes and constraints?
The ID columns are PKs and clustered indexes for hopefully obvious reasons; you use these for joins
jobs.title_id is an FK for hopefully obvious data integrity reasons
jobs.title_id is not null because every job should have a title
jobs.title_id needs an index in order to speed up joins
job_titles.title has an index because you've indicated you'll be querying based on this column (though I wouldn't query in such a fashion, especially since you've said there will be many titles; see below)
job_titles.title has a unique constraint because there's no reason to have duplicates of the same title. You can (and will) have multiple jobs with the same title, but you don't need two entries for "CEO" in job_titles. Enforcing this uniqueness will preserve data integrity useful for reporting purposes (e.g. plot the productivity of IT's web division based on how many "web developer" jobs are filled)
Remarks:
Job title is going to be used as part of an auto-complete field so I'll be using a query to fetch results.
As I mentioned before, use key-value pairs here. Fetch a list of them into memory in your app, and query that list for your autocomplete values. Then send the ID off to the DB for your actual SQL query. The queries will perform better that way; even with indexes, searching integers is generally quicker than searching strings.
You've said that titles will be user created. Put some input sanitation and validation process in place, because you don't want redundant entries like "WEB DEVELOPER", "web developer", "web developer", etc. Validation should occur at both the application and DB levels; the unique constraint is part (but all) of this. Prodigitalson's remark about separate machine and display columns is related to this issue.
Edited: after getting the clarify
A table like this is enough - just add the job_title_id column as foreign key in the main member table
---- "job_title" table ---- (store the job_title)
1. pk - job_title_id
2. unique - job_title_name <- index this
__ original answer __
You need to clarify what's the job_title going represent
a person that hold this position?
the division/department that has this position?
A certain set of attributes? like Sales always has a commission
or just a string of what was it called?
From what I read so far, you just need the "job_title" as some sort of dimension - make the id for it, make the string searchable - and that's it
example
---- "employee" table ---- (store employee info)
1. pk - employee_id
2. fk - job_title_id
3. other attribute (contract_start_date, salary, sex, ... so on ...)
---- "job_title" table ---- (store the job_title)
1. pk - job_title_id
2. unique - job_title_name <- index this
---- "employee_job_title_history" table ---- (We can check the employee job history here)
1. pk - employee_id
2. pk - job_title_id
3. pk - is_effective
4. effective_date [edited: this need to be PK too - thanks to KM.]
I still think you need to provide us a use-case - that will greatly improve both of our understanding I believe
If there are only a few fixed job titles you might want to use an enum in our database.
See http://dev.mysql.com/doc/refman/5.0/en/enum.html
If that's not supported by your version of mysql simply encode it with a numerical index and resolve it to a human readable form in your queries.

unnecessary normalization

My friend and I are building a website and having a major disagreement. The core of the site is a database of comments about 'people.' Basically people can enter comment and they can enter the person the comment is about. Then viewers can search the database for words that are in the comment or parts of the person name. It is completely user generated. For example, if someone wants to post a comment on a mispelled version of a person's name, they can, and that's OK. So there may be multiple spellings of different people listed as several different entries (some with middle name, some with nickname, some mispelled, etc.), but this is all OK. We don't care if people make comments about random people or imaginary people.
Anyway, the issue is about how we are structuring the database. Right now it is just one table with the comment ID as the primary key, and then there is a field for the 'person' the comment is about:
comment ID - comment - person
1 - "he is weird" - John Smith
2 - "smelly girl" - Jenny
3 - "gay" - John Smith
4 - "owes me $20" - Jennyyyyyyyyy
Everything is working fine. Using the database, I am able to create pages that list all the 'comments' for a particular 'person.' However, he is obsessed that the database isn't normalized. I read up on normalization and learned that he was wrong. The table IS currently normalized, because the comment ID is unique and dictates the 'comment' and the 'person.' Now he is insistant that 'person' should have it's OWN table because it is a 'thing.' I don't think it is necessary, because even though 'person' really is the bigger container (one 'person' can have many 'comments' about them), the database seems to operate just fine with 'person' being an attribute of the comment ID. I use various PHP calls for different SQL selections to make it magically appear more sophisticated on the output and the different way the user can search and see results, but in reality, the set-up is quite simple. I am now letting users rank comments with thumbs up and thumbs down, and I keep a 'score' as another field on the same table.
I feel that there is currently no need to have a separate table for just unique 'person' entries because the 'persons' don't have their own 'score' or any of their own attributes. Only the comments do. My friend is so insistant that it is necessary for efficiency. Finally I said, "OK, if you want me to create a separate table and let 'person' be it's own field, then what would be the second field? Because if a table has just a single column, it seems pointless. I agree that we may later create a need to give 'person' it's own table, but we can deal with that then." He then said that strings can't be primary keys, and that we would convert the 'persons' in the current table to numbers, and the numbers would be the primary key in the new 'person' table. To me this seems unnecessary and it would make the current table harder to read. He also thinks it will be impossible to create the second table later, and that we need to anticipate now that we might need it for something later.
Who is right?
In my opinion your friend is right.
Person should live in a different table and you should try to normalize. Don't overdo-it, though.
In the long run you may want to do more things with your site, say you want to attach multiple files to a person (ie. pictures) you'll be very thankfull then for the normalization.
Creating a new table for person and using the key of that table in place of the person attribute has nothing to do with normalization. It may be a good idea for other reasons but doing so does not make the database "more normalized" than not doing it. So you are right: as far as normalization is concerned, creating another table is unnecessary.
I would vote for your friend. I like to normalize and plan for the future and even if you never need it, this normalization is so easy to do it literally takes no time. You can create a view that you query in order to make your SQL cleaner and eliminate the need for you to join the tables yourself.
If you have already reached all of your capabilities and have no plans for expansion of capabilities I think you leave it as it is.
If you plan to add more, namely allowing people to have accounts, or anything really, I think it might be smart to separate your data into Person, Comments tables. Its not hard and makes expanding your functionality easier.
You're right.
Person may be a thing in general, but not in your model. If you were going to hassle people into properly identifying the person they're talking about, a Person table would be necessary. For example, if the comments were only about persons already registered in the database.
But here it looks like you have an unstructured data, without identity; and that nothing/nobody is interested in making sure whether "jenny" and "jennyyy" are in fact the same person, not to mentionned "jenny doe", and "my cousin"...
Well, there are two schools of thought. One says, create your data model in the most normalized way possible, then de-normalize if you need more efficiency. The other is basically "do the minimum work necessary for the job, then change it as your requirements change". Also known as YAGNI (You aren't going to need it).
It all depends on where you see this going. If this is all it will be, then your approach is probably fine. If you intend to improve it with new features over time, then your friend is right.
If you never intend to associate the person column with a user or anything else and data apparently needs no consistency or data integrity checks, just why is this in a relational database at all? Wouldn't this be a use case for a nosql database? Or am I missing something?
Normalization is all about functional dependencies (FD's). You need to identify all of the
FD's that exist among the attributes of your data model before it can be fully normalized.
Lets review what you have:
Any given instance of a CommentId functionally determines the Person (FD: CommentId -> Person)
Any given instance of a CommentId functionally determines the Comment (FD: CommentId -> Comment)
Any given instance of a CommentId functionally determines the UserId (FD: CommentId -> UserId)
Any given instance of a CommentId functionally determines the Score (FD: CommentId -> Score)
Everything here is a dependant attribute on CommentId and
CommentId alone. This might lead you to the belief that a relation (table) containing all of, or a subset of, the
above attributes must be normalized.
First thing to ask yourself is why did you create the CommentId attribute anyway? Strictly speaking,
this is a manufactured attribute - it does not relate to anything 'real'. CommentId is
commonly referred to as a surrogate key. A surrogate key is just a made up value that stands in
for a unique value set corresponding to some other group of attributes. So what group of attributes is CommentId
a surrogate for? We can figure that
out by asking the following questions and adding new FD's to the model:
1) Does a Comment have to be unique? If so the FD: Comment -> CommentId must be true.
2) Can the same Comment be made multiple times as long as it is about a different Person? If so, then
FD: Person + Comment -> CommentId must be true and the FD in 1 above is false.
3) Can the same Comment be made multiple times about the same Person provided it was made by
different UserId's? If so, the FDs in 1 and 2 cannot be true but
FD: Person + Comment + UserId -> CommentId may be true.
4) Can the same Comment be made multiple times about the same Person by the same UserId but
have different Scores? This implies FD: Person + Comment + UserId' + Score -> CommentId is true and the others are false.
Exactly one of the above 4 FD's above must be true. Whichever it is affects how your data model is normalized.
Suppose FD: Person + Comment + UserId -> CommentId turns out to be true. The logical
consequences are that:
Person + Comment + UserId and CommentId serve as equivalent keys with respect to Score
Score should be put in a relation with one but not both of its keys (to avoid transitive dependencies).
The obvious choice would be CommentId since it was specifically created as a surrogate.
A relation comprised of: CommentId, Person, Comment, UserId is needed to tie the
Key to its surrogate.
From a theoretical point of view, the surrogate key CommentId is not
required to make your data model or database work. However, its presence may affect how relations are constructed.
Creation of surrogate keys is a practical issue of some importance.
Consider what might happen if you choose to not use a surrogate key but the full
attribute set Person + Comment + UserId in its place, especially if it was required
on multiple tables as a foreign or primary key:
Comment might add a lot of space overhead
to your database because it is repeated in multiple tables. It is probably more than a couple of characters long.
What happens if someone chooses to edit a Comment? That change needs to be propagated
to all tables where Comment is part of a key. Not a pretty sight!
Indexing long complex keys can take a lot of space and/or make for slow update performance
The value assigned to a surrogate key never changes, no matter what you do to the values
associated to the attributes that it determines. Updating the dependant attributes is now
limited to the one table defining the surrogate key. This is of huge practical significance.
Now back to whether you should be creating a surrogate for Person. Does Person live
on the left hand side of many, or any, FDs? If it does, its value will propogate through your
database and there is a case for creating a surrogate for it. Whether Person is a text or numeric attribute is irrelevant to the choice of creating a surrogate key.
Based on what you have said, there is at best a weak argument to create a
surrogate for Person. This argument is based on the suspicion that its value may at some point become a key or part of a key at some point in the future.
Here's the deal. Whenever you create something, you want to make sure that it has room to grow. You want to try to anticipate future projects and future advancements for your program. In this scenario, you're right in saying that there is no need currently to add a persons table that just holds 1 field (not counting the ID, assuming you have an int ID field and a person name). However, in the future, you may want to have other attributes for such people, like first name, last name, email address, date added, etc.
While over-normalizing is certainly harmful, I personally would create another, larger table to hold the person with additional fields so that I can easily add new features in the future.
Whenever you're dealing with users, there should be a dedicated table. Then you can just join the tables and refer to that user's ID.
user -> id | username | password | email
comment -> id | user_id | content
SQL to join the comments to the users:
SELECT user.username, comment.content FROM user JOIN comment WHERE user.id = comment.user_id;
It'll make it so much easier in the future when you want to find information about that specific user. The amount of extra effort is negligible.
Concerning the "score" for each comment, that should also be a separate table as well. That way you can connect a user to a "like" or "dislike."
With this database, you might feel that it is okay but there may be some problem in the future when you want the users to know more from the database.Suppose you want to know about the number of comments made on a person with the name='abc'.In this case ,you will have to go through the entire table of comments and keep counting.In place of this, you can have an attribute called 'count' for every person and increment it whenever a comment is made on that person.
As far as normalization is concerned,it is always better to have a normalized database because it reduces redundancy and makes the database intuitive to understand. If you are expecting that your database will go large in future then normalization must be present.

Best way to link a table to 2 different keys?

I'm designing a mySQL DB and I'm having the following issue:
Say I have a wall_posts table. Walls can belong to either an event or a user.
Hence the wall_posts table must references either event_id or user_id (foreign key constraint).
What is the best way to build such a relationship, considering I must always be able to know who the walls belong to ... ?
I've been considering using 2 tables, such as event_wall_posts and user_wall_posts so one got an event_id field and the other a user_id one, but I believe there must be something much better than such a redundant workaround ...
Is there a way to use an intermediate table to link the wall_posts to either an event_id or a user_id ?
Thanks in advance,
Edit : seems there is not a clear design to do this and both approach seem okay, so,
which one will be the fastest if there is a lots of data ?
Is it preferable to have 2 separates table (so queries might be faster, since there will be twice less data in tables ...), or is it still preferable to have a better OO approach with a single wall_posts table referencing a wall table (and then both users and events will have a uniquewall_id`)
Why is it redundant? You won't write code twice to handle them, you will use the same code, just change the name of the table in the SQL.
Another reason to take this approach is that some time in the future you will discover you need new different fields for each entity.
What you're talking about is called an exclusive arc and it's not a good practice. It's hard to enforce referential integrity. You're probably better off using, in an object sense, a common supertype.
That can be modelled in a couple of ways:
A supertype table called, say, wall. Two subtype tables (user_wall and event_wall) that link to a user and event respectively as the owner. The wall_posts table links to the supertype table; or
Putting both entity types into one table and having a type column. That way you're not linking to two separate tables.
Go for the simplest solution: add both an event_id and a user_id column to the wall_posts table. Use constraints to enforce that one of them is null, and the other is not.
Anything more complex smells like overnormalization to me :)
A classical approach to this problem is:
Create a table called wall_container and keep properties common to both users and events in it
Reference both users and events to wall_container
Reference wall_posts to wall_container
However, this is not very efficient and it's not guaranteed that this wall_container doesn't containt records that are not either a user or an event.
SQL is not particularly good in handling multiple inheritance.
Your wall and event has their own unique IDs .. right?? then their is no need for another table . let the wall_post table have a attribute as origin which will direct to the record of whatever the record is event's or users. '
If the wall and event may have same ID then make a table with three attributes origin(primary), ID number and type. There ID number will be what you set, type defining what kind of entity does the ID represent and origin will be a new ID which you will generate maybe adding different prefix. In case of same ID the origin table will help you immensely for other things to other than wall posts.