database schema for two similar users - mysql

I have a doubt about this DB schema I'm making.
I have two similar users, but one has extra information than the other:
Type 1 : Administrator
- Name
- Lastname
- Email
- Password
Type 2: Student
- Name
- Lastname
- Email
- Password
- Status
- Sex
- Among other type of personal information fields
So, I'm hesitating about either make these two separate tables, and when they're going to log in, query them both (Because I have only one logging screen), or unify them as only one table User, and make another called like "extra" with a foreign key from User pointed to the latter.
What would be the most efficent way to accomplish this? Thanks for your time

I would make two tables and do the join after log in. Cache the extra facts about the user after they're logged in.
You should have a User table with these columns:
Id, Name, Lastname, Email, Password, IsAdmin
With a Student table:
UserId, Status, Sex, ...
A Student must also be a User - this will reduce duplication of data.
If you need more permissions than IsAdmin then remove that column and make UserPermissions and Permission tables.
If you're really that concerned about a join, then just make everything nullable and in one User table. I doubt it will matter in your use case (this is a much bigger topic).

An administrator is a role played by a person.
A student is a role played by a person.
A person could play one role at a time, or maybe multiple down the road. This is a business rule and should not factor into your database schema.
Use single table inheritance to allow for different types of roles in the same table.
create table people (
person_id int primary key,
given_name varchar(...),
surname varchar(...),
password varchar(...)--consider a `users` table instead
);
create table roles (
role_id int primary key,
person_id int not null references people(person_id), --use the long-hand foreign key syntax for mysql
type varchar(...), --admin or student
status varchar(...), --consider PostgreSQL over mysql, as it supports check constraints
sex varchar(...)
);

Related

Mysql Storing multi values in a column

I have a tables called userAccounts userProfiles and usersearches.
Each userAccount may have multiply Profiles. Each user may have many searches.
I have the db set up working with this. However in each search there may be several user profiles.
Ie, each user account may have a profile for each member of their family.
They then want to search and include all or some of their family members in their search. The way i would kinda like it to work is have a column in user searches called profiles and basically have a list of profileID that are included in that search. (But as far as i know, you can't do this in sql)
The only way i can think i can do this is have 10 columns called profile1, profile2 ... profile10 and place each profileid into the column and 0 or null in the unused space. (but this is clearly messy )
Creating columns of the form name1...nameN is a clear violation of the Zero, One or Infinity Rule of database normalization. Arbitrarily having ten of them is not the right approach, that's an assumption that will prove to be either wildly generous or too constrained most of the time. Since you're using a relational database, try and store your data relationally.
Consider the schema:
CREATE TABLE users (
id INT PRIMARY KEY AUTO_INCREMENT NOT NULL,
name VARCHAR(255),
UNIQUE KEY index_on_name (name)
);
CREATE TABLE profiles (
id INT PRIMARY KEY AUTO_INCREMENT NOT NULL,
user_id INT NOT NULL,
name VARCHAR(255),
email VARCHAR(255),
KEY index_on_user_id (user_id)
);
With that you can create zero or more profile records as required. You can also add or remove fields from the profile records without impacting the main user records.
If you ever want to search for all profiles associated with a user:
SELECT ... FROM profiles
LEFT JOIN users ON
users.id=profiles.user_id
WHERE users.name=?
Using a simple JOIN or subquery you can easily exercise this relationship.

SQL Best practices: Using a column to store two different kinds of data?

Suppose I have a login table containing id, username, password, type, and linked_id
The type column specifies whether that login belongs to an employee or a client.
If they are an employee, linked_id is their ID in the employee's table.
If they are a client, linked_id is their ID in the client's table.
Intuitively, this seems like a bad idea, but other than dealing with foreign key constraints, I can't think or a reason not to.
This also brings up another question: Is it ALWAYS important to have FK constraints in situations like this?
Thoughts?
I think you are thinking about your table design incorrectly. I would have the login information in one table and then point to the login record from the appropriate table. In other words, point to your login record from the employee and clients tables rather than pointing to a client or employee record from the login record. That allows you to have your foreign key constraints and removes the need for an extra field of "type"
As an example:
Login: Id(PK), Username, Password
Employees: Id(PK), Name, EmployeeNumber, Login_Id(FK_LoginId)
Clients: Id(PK), Name, Address, Login_Id(FK_LoginId)
Then, you inherently know what "type" each login is by what table the data is stored in.
The easiest solution would be to simply add an additional column in your database and have EmployeeId and ClientId to make the table structure easier to understand.
You could also argue that you're missing an Entity in your database design. As an example, what if an Employee was also a client? How would the current system deal in that circumstance?
You could have an intermediate table to manage this and determine the status of that Person.
I.e. have the table Login.
Fields: ID, username, password, type, PersonId
And then another table Person with the EmployeeId and ClientId, although I do think this would cause a lot of NULL values it would support the above circumstance I have described.
As an additional note, I would not want to look at the existing Login table, identify the linked_id field and then have to decide which table this column relates to, Employee or Client?
You could also split this out like #randyh22 has suggested and remove the linked_id field altogether and have the Login identifiers in the child tables (Employees and Clients).
Restructure:
Login table: LoginId, Username, Password (Ensure it is hashed)
Employee table: EmployeeId, Forename, Surname, LoginId
Client table: ClientId, Forename, Surname, LoginId

Database design - how to implement user group table?

I want to make user group system that imitates group policy in instant messengers.
Each user can create as many as groups as they want, but they cannot have groups with duplicate names, and they can put as many friends as they want into any groups.
For example, John's friend Jen can be in 'school' group of John and 'coworker' group of John at the same time. And, it is totally independent from how Jen puts John into her group.
I'm thinking two possible ways to implement this in database user_group table.
1.
user_group (
id INT PRIMARY KEY AUTO_INCREMENT,
user_id INT,
group_name VARCHAR(30),
UNIQUE KEY (user_id, group_name)
)
In this case, all groups owned by all users will have a unique id. So, id alone can identify which user and the name of the group.
2.
user_group (
user_id INT,
group_id INT AUTO_INCREMENT,
group_name VARCHAR(30),
PRIMARY KEY (user_id, group_id),
UNIQUE KEY (user_id, group_name)
)
In this case, group_id always starts from 0 for each user, so, there could exist many groups with same group_id s. But, pk pair (user_id, group_id) is unique in the table.
which way is better implementation and why?
what are advantages and drawbacks for each case?
EDIT:
added AUTO_INCREMENT to group_id in second scenario to insure it is auto-assigned from 0 for each user_id.
EDIT:
'better' means...
- better performance in SELECT/INSERT/UPDATE friends to the group since that will be the mostly used operations regarding the user group.
- robustness of database like which one will be more safe in terms of user size.
- popularity or general preference of either one over another.
- flexibility
- extensibility
- usability - easier to use.
Personally, I would go with the 1st approach, but it really depends on how your application is going to work. If it would ever be possible for ownership of a group to be changed, or to merge user profiles, this will be much easier to do in your 1st approach than in the 2nd. In the 2nd approach, if either of those situations ever happen, you would not only have to update your user_group table, but any dependent tables as well that have a foreign key relation to user_group. This will also be a many to many relation (there will be multiple users in a group, and a user will be a member of multiple groups), so it will require a separate joining table. In the 1st approach, this is fairly straightforward:
group_member (
group_id int,
user_id int
)
For your 2nd approach, it would require a 3rd column, which will not only be more confusing since you're now including user_id twice, but also require 33% additional storage space (this may or may not be an issue depending on how large you expect your database to be):
group_member (
owner_id int,
group_id int,
user_id int
)
Also, if you ever plan to move from MySQL to another database platform, this behavior of auto_increment may not be supported. I know in MS SQL Server, an auto_increment field (identity in MSSQL) will always be incremented, not made unique according to indexes on the table, so to get the same functionality you would have to implement it yourself.
Please define "better".
From my gut, I would pick the 2nd one.
The searchable pieces are broken down more, but that wouldn't be what I'd pick if insert/update performance is a concern.
I see no possible benefit to number 2 at all, it is more complex, more fragile (it would not work at all in SQL Server) and gains nothing. Remeber the groupId is without meaning except to identify a record uniquely, likely the user willonly see the group name not the id. So it doesn't matter if they all start from 0 or if there are gaps because a group was rolled back or deleted.

Database structure: Would this structure work with this m:m?

Here is my issue: (Using MySQL)
I have 2 entities called 'shops' and 'clients'. I also have a M:M table between 'clients' and 'shops' called 'clients_shops' (CakePHP naming convention). The reason I am doing it this way is that this is a SaaS application where 'clients' may have many 'shops' and 'shops' will definitely have many 'clients'.
However, I don't want to give a shop the ability to UPDATE/DELETE a 'client' record since what really needs to happen is that the 'shop' will EDIT/DELETE that 'client' from their own records, rather than from a master 'clients' table which is managed by the 'clients'.
Anyway, using this structure a 'shop' can run a query on the 'clients_shops' table to get a list of their clients and a 'client' can run a query a get a list of their 'shops'. Good so far...
So far, the database looks like this:
table.clients
client_id (PK, AI, NN)
table.shops
shop_id (PK, AI, NN)
table.clients_shops
clients_shops_id (PK,AI,NN)
client_id (FK)
shop_id (FK)
The ORM looks like this:
shops hasMany clients_shops
clients hasMany clients_shops
So far so good (I think...) but here is my question. Let's say that there is a third table named 'trips'. The 'trips' table stores information on individual bookings whereby a 'client' will make reservations for a 'trip' that is provided by a 'shop'. This is where my brain is getting mushy. How should I set this relationship up?
Is it this way:
table.trips
trips_id (PK,AI,NN)
clients_shops_id (FK) [which would contain keys for both the shop and the client]
Or is there a better way to do this, like another table that uses clients.client_id AND clients_shops.clients_shops_id.
Thanks in advance to anyone that actually read this whole thing!
Unless it's required by your ORM, you don't need a surrogate foreign key for clients/shops and everything that refers to it.
Make a composite PRIMARY KEY instead and refer to it from elsewhere:
CREATE TABLE clients_shops
(
client_id INT NOT NULL,
shop_id INT NOT NULL,
PRIMARY KEY (client_id, shop_id)
);
CREATE TABLE trips
(
trip_id INT NOT NULL PRIMARY KEY,
client_id INT NOT NULL,
shop_id INT NOT NULL,
trip_data …,
CONSTRAINT fk_trips_clients_shops
FOREIGN KEY (client_id, shop_id)
REFERENCES clients_shops
);
This model assumes that you maintain clients/shops relationships separately from the clients' transactions and not let clients buy from the shops unless they are "related".
Probably you want the relationship to appear automatically whenever a trip is ordered by a client from a shop. In this case, you only need the second table, and the first table is a mere
SELECT DISTINCT client_id, shop_id
FROM trips
Here is the Logical Diagram to handle what you are looking for. Depending on your requirements you can change the non-identying relationships (Client::Trip & Shop::Trip) to identifying relationships. If you do though I would limit it to only changing the Shop::Trip to identifying though. Also make changes to the Cardinality as you see fit.
I would probably make the trips table like this:
table.trips
trip_id (PK)
shop_id (FK to shops)
client_id (FK to clients)
other_trip_column_etc
I would not reference the m-m table clients_shops from the trips table - just reference the shop and client tables with individual foreign keys.
The clients_shops table represents the current relationship between a client and a shop. The trip should not depend on these relationships, because they could potentially change in the future, and you probably wouldn't want the trip's data to change over time - it should be a transactional record that specifies exactly what shop, client, and trip was scheduled at that given time, regardless of the current relationship between that client and shop.

Database design: different fields for multiple user types

I am building an application using MySQL 5.0.77 that contains
a) different user types (e.g. carpenter, mechanic, ..., plumber)
b) different input fields for each user type, e.g. user selects carpenter and is presented with fields pertaining to that profession, where the fields for each profession are different
My thinking is along the lines of:
Table: users
user_id
user_name
Table: carpentry
user_id
woodwork_rating
metalwork_rating
Table: plumbing
user_id
central_heating_rating
bathroom_rating
And so on...
This does not seem very good though since I could potentially end up with lots of tables and users existing in multiple tables with different fields.
I quite like the idea of a metatags table (like we see in Wordpress) so that each users field entry is stored, e.g.
Table: user_info
user_id
field
value
So we would have for example
1 | woodwork_rating | intermediate
1 | metalwork_rating | advanced
2 | woodowork_rating | advanced
My question is, how would you structure a database that has multiple fields for multiple users for which each user only fills in one category of the available fields?
Thanks
Table Users:
UserID: Autoinc PRIMARY KEY
(More user data columns here)
UserType: CHAR(5)
Table UserTypes
UserType: CHAR(5) PRIMARY KEY
Description: VARCHAR(50)
Table UserRatingList
UserRatingCode: CHAR(5) PRIMARY KEY
UserType: CHAR(5) REFERENCES UserTypes
Description: VARCHAR(50)
Table UserRatings
UserID: INTEGER PRIMARY KEY / REFERENCES Users
UserRatingCode: CHAR(5) PRIMARY KEY / REFERENCES UserRatingList
Rating: INTEGER (or whatever you prefer)
The table UserRatingList establishes the pattern of ratings that can be applied to each user type. UserRatings contains the actual ratings. I use CHAR(5) to provider readable codes without having to join in the Description fields, but you can change them to INTEGER if you want.
This structure can also be adapted to allow each user to have multiple types; simply create an addition UserTypeLinks table with UserID and UserType.
i would like to rely on 'mike' s last answer.
i am facing this exact problem. i am creating a network with the following types
Enterprise {name, mail, adress, etc ...}
Employee {name, mail, branch, jobdescription, etc ...}
Individual {name, mail, surname, age, town, etc...}
how about:
Table Users
userID (autoincr)
userType
mailadress
password
...
Table Usertypes
typeID
typeName
typeDescr
Table Enterprises
entID (autoincr)
userID
field 1
field 2
...
Table Employee
empID (autoincr)
userID
...
Table Individuals
indID (autoincr)
userID
...
does this make sense to you?
Are all the fields going to be "ratings" with the same datatype? If so I like Larry Lustig's solution.
Or could there be unrelated fields with different data types? dates, strings, decimals, integers? If so, the first solution of having 1 to 1 related tables that join with Users is OK. As long as you don't need to dynamically add new fields at run time.