Better way to organize DB relations? - mysql

Lets say I have tables:
company (id, name)
user (id, username)
employee (id, user_id, company_id, position) -- position is an integer enum
department (id, title, company_id)
And I also need to store relation user is a manager of department
One department may have many managers, and user may be manager in many departments
So I have two ways to do that:
Make manager table look like this: manager (employee_id, department_id)
Or manager(user_id, department_id)
in first case I guarantee integrity (only employee might be a manager), but in order to reach users fields from department I'll have to do three joins department -> manager -> employee -> user.
In second case I have one less join but I have to ensure integrity manually (delete manager, when employee gets deleted etc..)
I can think of another one approach, it's composite keys on employee and manager tables, but since I work with Doctrine 2 it does not recommend to use it.
So how you think will be better? Or might be something is wrong in the first place? Also where should I lookup next time to be able to solve it by my own? How should I test it?

Related

Constrains on a child table in mySQL

I have this situation:
MANAGER (ManagerID, Salary, .... , email)
PROJECT (ProjectID, ..., Date)
Since there is relationship M:N between Manager an project, I'll have a third table:
Manager_has_Project( ManagerID, ProjectID )
where ( ManagerID, ProjectID ) is the compound PK for Manager_has_Project
Let's suppose we have to delete a Manager who has created some projects from our database: SQL won't make us do that. We could add the constraint on the fk ManagerID in the child table "ON DELETE CASCADE", but in this case we will lose information about, for example, how many managers worked for a project. The alternative is "ON DELETE SET NULL" but, since ManagerID is part of the compound pK of Manager_has_Project, we can't set a PK as null.
What would recommend to do?
If you want to to keep the information, use soft deletes rather than actually removing the rows.
That is, add a column, say is_deleted or deletion_datetime that indicates that a Manager has been deleted. Then you can keep all the information, even about "deleted" managers.
You can use views so "normal" queries would only return managers who are not deleted.

can a many to many relationship have another many to many relationship with another table?

Consider this case,
a user can have many groups,
groups can have many users
only people belonging to a particular group can have access to a car belonging to that group
So i am not sure how the tables will look like
below are the tables
-- Creating a new User
INSERT INTO Users (UserLogin, UserPassword, UserName)
VALUES ('SomeUser', 'SecretPassword', 'UserName');
-- Creating a new Groups
INSERT INTO Groups (GroupName, GroupDescription)
VALUES ('GroupName', 'GroupDescription');
-- Finally, updating the junction
INSERT INTO UserGroup (UserId, GroupId)
VALUES ('UserId', 'GroupId');
consider another table cars
INSERT INTO Cars (Name, Model)
VALUES ('SomeCar', 'Model');
only those people who belong to a group can have access to the carsS
So should cars have many to many relationships with UserGroups?
INSERT INTO CarUserGroup (UserGroupId, CarId)
VALUES ('UserGroupId', 'CarId');
OR Should group have a one-to-many relationship with cars?
INSERT INTO Cars (Name, Model, GroupId)
VALUES ('GroupId','SomeCar', 'Model');
can u tell which approach is the best?
thanks
If a given car can have only a single group relationship, then your second version in theory would work:
Cars (Name, Model, GroupId)
However, this approach becomes undesirable as soon as a car can be associated with more than one group. The reason is that then we would be duplicating the car's metadata:
SomeCar, SomeModel, Group1
SomeCar, SomeModel, Group2
In general, you would do better to go with the standard junction table approach, and have a table which keeps tracks only of the relationships between cars and groups:
SomeCar, Group1
SomeCar, Group2
Then, let the Cars table exist to store the metadata for each car, with one car occupying only a single record:
SomeCar, SomeModel, SomeOtherMetadata
Since,
only people belonging to a particular group can have access to a car belonging to that group
So you should create a One-to-Many relationship between groups and cars. Now, why this should be done? The intuition behind this is that a user can have a car only if it belongs to a particular group, so the dependency of cars is on group. But since the car is a composite attribute of the group table, you make its owm table with a one-to-many relationship.
Another point is, when you delete a group, all cars associated with this group should also be deleted ( this is optional, you can also have null foreign key, in which case it will be an orphan child if you dont want to delete these entries from cars table ).

Search and update on INSERT

A client needs to migrate a large volume of data and I feel this question could be generic enough for SO.
Legacy system
Student profiles contain fields like names, emails etc, as well as university name. The university name is represented as a string and as such is repeated which is wasteful and slow.
Our new form
A more efficient solution is to have a table called university that only stores the university name once with a foreign key (university_id) and the HTML dropdown just POSTs the university_id to the server. This makes things much faster for doing GROUP BY queries, for example. New form data going into the database works fine.
The problem
How can we write a query that will INSERT all the other columns (first_name, last_name, email, ...) but then rather than inserting the university string, find out its university_id from the university table and INSERT the corresponding int instead of the original string? (scenario: data is in a CSV file that we will manipulate into INSERT INTO syntax)
Many thanks.
Use INSERT INTO ... SELECT with a LEFT JOIN. Left is chosen so that student record won't get discarded if it has a null value for university_name.
INSERT INTO students_new(first_name, last_name, email, university_id)
SELECT s.first_name, s.last_name, s.email, u.university_id
FROM students_old s
LEFT JOIN university u ON s.university_name = u.university_name
Table and column names are to be replaced for real ones. Above assumes that your new table for students holding foreign key to university is students_new while the old one (from before normalisation) is students_old.

How do we make a table relationship when one index could be from two different tables?

I have table called addresses that stores N:N sub-information from those two tables people and companies.
So I created another table called address_links that have the fields address_id and contact_id so that contact id could come from a people or from a companies record.
So I need another field that by the creation of the record, points to the correct table. But what can I do to automatize that interpretation when I make a query that shows the name of the owners of that addresses in a query list? I tried IF in order to case that 3rd field by selecting the table but it did not worked.
Explain Note: Some address may be house or workplace for more than one person.
If you need to link to different tables, then normally you would want to have a separate link table for each. This allows the database to enforce referential integrity and eliminates the need for special if statements, it also makes the database easier to understand if another developer looks at it without having to understand special implementation details.
Example Tables (columns):
Addresses (AddressId, Address, City, State, Zip, ...)
Persons (PersonId, FirstName, ...)
Companies (CompanyId, Name, ...)
AddressPersonLinks (AddressId, PersonId)
AddressCompanyLinks (AddressId, CompanyId)

Normalization of database for timesheet tool and ensure data integrity

I'm creating a timesheet application. I have the following entities (amongst others):
Company
Employee = an employee associated with a company
Client = a client associated with a company
So far I have the following (abbreviated) database setup:
Company
- id
- name
Employee
- id
- companyId (FK to Company.id)
- name
Client
- id
- companyId (FK to Company.id)
- name
Now, I want an employee to be associated with a client, but only if that client is associated with the company the employee works for. How would you guarantee this data integrity on a database level? Or should I just depend on the application to guarantee this data integrity?
I thought about creating a many to many table like this:
EmployeeClient
- employeeId (FK to Employee.id)
- companyId \ (combined FK to Client.companyId, Client.id)
- clientId /
Thus, when I insert a client for an employee along with the employee's company id, the database should prevent this when the client is not associated with the employee's company id. Does this make sense? Because this still doesn't guarantee the employee is associated with the company. How do you deal with these things?
UPDATE
The scenario is as followed:
A company has multiple employees. Employees will only be linked to one company.
A company has multiple clients also. Clients will only be linked to one company.
(Company is a sandbox, so to speak).
An employee of a company can be linked to a client of it's company, but only if the client is part of the company's clientele.
In other words:
The application will allow a company to create/add employees and create/add clients (hence the companyId FK in the Employee and Client tables). Next, the company will be allowed to assign certain clients to certain of it's employees (EmployeeClient table).
Imagine an employee working on projects for a few clients for which s/he can write billable hours, but the employee must not be allowed to write billable hours for clients they are not assigned to by their employer (the company). So, employees will not automatically have access to all their company's clients, but only to those that the company has selected for them. Hopefully this has shed some more light on the matter.
If you want to do it from the database level then I would put the logic in a stored procedure. The stored proc code will then associate the two if applicable but this means that (given you put the foreign key to the employee in the client table) a client is only associated with one employee. Is this what you want?
Also take note though that an employee in your table is indirectly associated with all such clients via its company association. If all employees are automatically associated with all new clients of their company then perhaps you just want to write a query that checks for this.
(This is not an answer, but it didn't really fit in as a question comment.)
The data presented for your design question begs a number of questions:
Are employees to be associated with companies and clients? Or...
Are employees only associated with clients, and (thus) the company associated with that client?
If employess and clients are associated with companies, is an employee thus associated with all employees of that company, or must you pick and choose?
Update
As far as data modelling is concerned, it seems like all you need to do is expand the foreign key in EmployeeClient into Employee like so:
EmployeeClient
- companyId
- employeeId
- clientId
Compound primary key on all three columns.
Foreign key on (companyId, clientId) into Client
Foreign key on (companyId, employeeId) into Employee
Thus, all relations defined in EmployeeClient require both Client and Employee to share the same clientId.