Is having relationships to a pivot table a good idea? - mysql

Suppose we have the following tables:
companies
id
name
...
categories
id
name
category_company
category_id
company_id
Now, for each category the company belongs to we need to store the products they offer for that category. I'm wondering if it's a good idea to add an auto-incrementing primary key to the pivot table and then use that id on the products table as shown below.
category_company
id
category_id
company_id
products
id
category_company_id
name
Is there a better way to handle this? In the real proyect that I'm working on I have 5 more tables that depend on the category company relationship.

There are other approaches to your problem, but yours is correct too. You may or may not have the id in your category_company table, depending on if you are using some specific framework or ORM, but as #AndrewShmig stated you don't need it.
IMPORTANT: Remember you are building many-to-many relationships, so you need to add a primary key or unique compound index to 'category_company' table on columns 'category_id' and 'company_id', even if you are keeping the 'id' column. You don't want the same Company listed twice in a Category, or vice versa.
In MVC model ORM's your approach (with an 'id' in category_company / pivot) is prefered, because you can assign additional information to the relationship itself, treatening your pivot table like another object.
Is not the same, but think of these two cases:
If you are a more Object Oriented programmer, something like the second option may fit your needs (which is kind of what you have), plus you can add extra information for the relationship like 'categorized_by', 'date_of_categorization' or something like that.
*If you have 5+ tables that depend on that relationship (like 'products'), of course handling the relationship in an intermediate table is way better than to have 'category_id' and 'company_id' fields in each of them.
Sources:
http://guides.rubyonrails.org/association_basics.html#the-has-and-belongs-to-many-association
Many-to-many relationships examples

Related

How do I add mulltiple records and avoid using multi-field values?

I'm creating a database for personnel records and trying to ease record creation for the user and avoid a kludgy solution. The tables are:
people:
people_id,
person_name,
person_category_id
person_category:
person_category_id,
person type
document_requirement:
document_requirement_id,
document_requirement_name,
person_category_id,
document_section_id
document_section:
document_section_id,
document_section
I've created an append query (inner join) that populates a table caLLed document_repository which contains all of the required documents for all of the people. (I use a primary key composed of people_ID & document_id to avoid duplicates when the append query runs.) Here is the document_repository table.
document_respository:
document_repository_id,
people_id,
person category_id,
document_id,
document_section_id,
document_attachment
I'd like to be able to allow the user to create a document requirement that is applicable to multiple person categories. I understand I should avoid multi field values, which doesn't work anyway with inner joins. For example, if people categories include doctors and nurses, I'd like to be able to create a new document requirement that applies to both people categories (e.g., doctors and nurses), without having to create two separate document requirements.
More information needed?
Suggestions on design changes and/or queries?
Thanks!
snapshot of tables and relationships
What you describe is a many to many relationship. Each document requirement can be applicable to multiple person categories and different document requirements can be applicable to the same person category.
To have a many to many relationship between two entities (tables) in your database, you need another table to relate them. This additional table contains the primary key of both tables and each record in this table represents a link between the two entities.
Your naming is different between your text and your diagram, but I'll assume you want to have document_requirement records that can link to zero or more person_category records.
You need a table which for example could be called document_requirement_person_category and contains the following fields:
document_requirement_id - foreign key referencing PK of document_requirement
person_category_id - foreign key referencing PK of person_category
You then add a record to this link table for each person category that relates to each document requirement.
Edit: BTW, (if I'm reading your schema correctly), you already have a many to many relationship in your schema: document_repository allows a relationship between multiple people and a document requirement as well as multiple document requirements and a person. That's a many to many relationship.

SQL many-to-many relation 3 ways

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

Mysql, list of values in a column

I have a table that stores information about categories ('cats' let's say) and another table (say 'cars' meaning it stores infos about cars - a lame example, but that doesn't matter) that has a column called 'parent_cat_id' which should reference 'id' col in 'cats'.
Now comes the problem: any car from 'cars' could belong to more then one category.
There's an obvious, but very lame, solution: store in parent_cat_id a delimited list of numbers, but, if i apply that, I couldn't have any foreign key constraint and should jump into parsing these values. Although parsing isn't such a big deal, but I can't believe MySQL doesn't have anything pre-built.
Also it looks like selecting cars by category will involve some substring and other things.
Having no foreign key is a far bigger problem.
The only thing I was able to find is a 'SET' type, but it should be pre-defined what values it can store during database development, also it can have only 64 pre-defined values while there could be 100s of categories.
The normal way of modeling this kind of many-to-many relationship is to introduce a third table (a junction table) that holds information about relationships between items in the two other tables.
In your example you would have three tables:
Category
Cars
CarCategory which has foreign keys to both the primary keys in Category and Cars
This is pretty fundamental relational modeling and you might want to look at a nice tutorial or book to get further knowledge about the concepts.
Just as you recognize storing delimited lists of keys is a bad idea as it breaks the model.

Model a table that can have a relationship with several tables

I have a table called 'notes', on this table I need to track who made that note, but the problem is that the creator of the note can be a user stored in one of three possible tables:
users
leads
managers
I have though of simply create three fields on 'notes' to represent the three possible relations: note.user, note.lead, note.manager
With this approach I would be forced to create three table joins when requesting the notes to gather the creators information, and I don't think that is the way to go, so I would like to hear your ideas or comments and what would be the best approach on this.
For me personally this smells like a design problem on a totally different part of the schema: Are manageers not users? Do leads carry person information?
With any approach that creates a relation between one column and one of three others, you will need three joins for the select. If you can't rectify the underlying problem, I recommend you use
note_type ENUM('users','leads','managers')
as an additional field and
SELECT
...
IFNULL(users.name(IFNULL(managers.name,leads.name))) AS name
..
FROM notes
LEFT JOIN users ON notes.note_type='users' AND users.id=notes.note_source
LEFT JOIN managers ON notes.note_type='managers' AND managers.id=notes.note_source
LEFT JOIN leads ON notes.note_type='leads' AND leads.id=notes.note_source
...
for the query
I think you need to abstract out the concept of a user id, so that it does not depend on their role. The author of a note could then be specified by the user id.
Users could be assigned roles, and maybe more than one.
The correct way to structure this would be to pull all common data out of users, leads, and managers. Unify this data into a "contact" table. Then if you want to get all notes for a given manager:
managers->contacts->notes
for a lead:
leads->contacts->notes
Notice your original post: "the problem is that the creator of the note can be a user stored in one of three possible tables"
From the structure of your sentence you even admit that all these entities have something in common; they are all users. Why not make the DB reflect this?
you have to model a parent table for the three tables you already have. Define a table that depicts generally user, leads and manager tables. Something like "Person". So you have all of the ids of the three tables and any common attributes on the Person table. And when you must define the relationship you put the foreign id "Person_ID" on the note table. And when you model user, leads and manager tables you also put the primary key as a foreign key to the Person table.
So you would have something like this:
Table users:
Users(
person_id primary key
...(attributes of Users)
foreign key person_id references Person.person_id
)
This model i depict is common to any relational model you have to model using parents and childs

Shared Primary Key

I would guess this is a semi-common question but I can't find it in the list of past questions. I have a set of tables for products which need to share a primary key index. Assume something like the following:
product1_table:
id,
name,
category,
...other fields
product2_table:
id,
name,
category,
...other fields
product_to_category_table:
product_id,
category_id
Clearly it would be useful to have a shared index between the two product tables. Note, the idea of keeping them separate is because they have largely different sets of fields beyond the basics, however they share a common categorization.
UPDATE:
A lot of people have suggested table inheritance (or gen-spec). This is an option I'm aware of but given in other database systems I could share a sequence between tables I was hoping MySQL had a similar solution. I shall assume it doesn't based on the responses. I guess I'll have to go with table inheritance... Thank you all.
It's not really common, no. There is no native way to share a primary key. What I might do in your situation is this:
product_table
id
name
category
general_fields...
product_type1_table:
id
product_id
product_type1_fields...
product_type2_table:
id
product_id
product_type2_fields...
product_to_category_table:
product_id
category_id
That is, there is one master product table that has entries for all products and has the fields that generalize between the types, and type-specified tables with foreign keys into the master product table, which have the type-specific data.
A better design is to put the common columns in one products table, and the special columns in two separate tables. Use the product_id as the primary key in all three tables, but in the two special tables it is, in addition, a foreign key back to the main products table.
This simplifies the basic product search for ids and names by category.
Note, also that your design allows each product to be in one category at most.
It seems you are looking for table inheritance.
You could use a common table product with attributes common to both product1 and product2, plus a type attribute which could be either "product2" or "product1"
Then tables product1 and product2 would have all their specific attributes and a reference to the parent table product.
product:
id,
name,
category,
type
product1_table:
id,
#product_id,
product1_specific_fields
product2_table:
id,
#product_id,
product2_specific_fields
First let me state that I agree with everything that Chaos, Larry and Phil have said.
But if you insist on another way...
There are two reasons for your shared PK. One uniqueness across the two tables and two to complete referential integrity.
I'm not sure exactly what "sequence" features the Auto_increment columns support. It seem like there is a system setting to define the increment by value, but nothing per column.
What I would do in Oracle is just share the same sequence between the two tables. Another technique would be to set a STEP value of 2 in the auto_increment and start one at 1 and the other at 2. Either way, you're generating unique values between them.
You could create a third table that has nothing but the PK Column. This column could also provide the Autonumbering if there's no way of creating a skipping autonumber within one server. Then on each of your data tables you'd add CRUD triggers. An insert into either data table would first initiate an insert into the pseudo index table (and return the ID for use in the local table). Likewise a delete from the local table would initiate a delete from the pseudo index table. Any children tables which need to point to a parent point to this pseudo index table.
Note this will need to be a per row trigger and will slow down crud on these tables. But tables like "product" tend NOT to have a very high rate of DML in the first place. Anyone who complains about the "performance impact" is not considering scale.
Please note, this is provided as a functioning alternative and not my recommendation as the best way
You can't "share" a primary key.
Without knowing all the details, my best advice is to combine the tables into a single product table. Having optional fields that are populated for some products and not others is not necessarily a bad design.
Another option is to have a sort of inheritence model, where you have a single product table, and then two product "subtype" tables, which reference the main product table and have their own specialized set of fields. Querying this model is more painful than a single table IMHO, which is why I see it as the less-desirable option.
Your explanation is a little vague but, from my basic understanding I would be tempted to do this
The product table contains common fields
product
-------
product_id
name
...
the product_extra1 table and the product_extra2 table contain different fields
these tables habe a one to one relationship enforced between product.product_id and
product_extra1.product_id etc. Enforce the one to one relationship by setting the product_id in the Foreign key tables (product_extra1, etc) to be unique using a unique constraint.
you will need to decided on the business rules as to how this data is populated
product_extra1
---------------
product_id
extra_field1
extra_field2
....
product_extra2
---------------
product_id
different_extra_field1
different_extra_field2
....
Based on what you have above the product_category table is an intersecting table (1 to many - many to 1) which would imply that each product can be related to many categories
This can now stay the same.
This is yet another case of gen-spec.
See previous discussion