How should I structure my MySQL database? - mysql

First of all, sorry if this might be a stupid question. I'm very new to the world of MySQL, so...
Anyway, my question is this: I'm planning on having a database that deals with (for now) two types of users, let's say Admins and Users. My aim is to have ONE table containing all users, aptly named "users". Below is a rough outline of my MySQL command (which I haven't tested yet so errors are likely):
CREATE TABLE users {
user_id int UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
user_type int NOT NULL REFERENCES user_types(user_type_id),
ssn char(10) NOT NULL,
password varchar(40) NOT NULL,
first_name varchar(30) NOT NULL,
last_name varchar(30) NOT NULL,
address varchar(80) NOT NULL
} engine = InnoDB;
The "user_type" column above will refer to another table called "user_types", which lists the different user types for the website (I'm doing this for the sake of having the option to add more user types later):
CREATE TABLE user_types {
user_type_id int UNSIGNED NOT NULL PRIMARY KEY,
user_type_desc varchar(10) NOT NULL
} engine = InnoDB;
INSERT INTO user_types (user_type_id, user_type_desc) VALUES(1,'Admin'),(2,'User');
My aim is to link "Users" with "Admins"; one "User" (child) can have one "Admin" (parent), but one "Admin" (parent) can have several "Users" associated (children). The goal for me is to create a simple appointment calendar, and for that I need to connect users with their admins (one-to-one relationships in the sense that the appointment is between one user and one admin). Now the question is:
1) Is it possible to achieve this by having ONE table for all users? If so, how do I do it in a good way? Right now I was thinking of creating a table called "assignments":
CREATE TABLE assignments {
assign_id int UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
patient_id int NOT NULL REFERENCES users(user_id),
doctor_id int NOT NULL REFERENCES users(user_id)
} engine = InnoDB;
But the above code looks strange to me; can I do that kind of foreign key linking to the same table without any dangers? Below is also the SQL 'code' for the "appointments" table:
CREATE TABLE appointments {
appointment_id int UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
assign_id int FOREIGN KEY REFERENCES assignments(assign_id),
date_time datetime NOT NULL,
description varchar(200) NOT NULL
};
That is, every entry in the "appointments" table points to a certain assignment between an "Admin" and a "User".
2) How can I achieve the one-to-many relationship between "Admins" and "Users" in an easy way, or rather, a proper way?
Any help or suggestions would be greatly appreciated, and sorry if these questions are stupid!

Your proposed assignments table would work if you had a many-to-many relationship between Users and Admins. Since you've described the relationship as 1-to-many (one Admin may have many Users), I would simply add an admin_id column to your users table and make it a self-referencing foreign key back to the users table.
CREATE TABLE users {
user_id int UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
user_type int NOT NULL REFERENCES user_types(user_type_id),
ssn char(10) NOT NULL,
password varchar(40) NOT NULL,
first_name varchar(30) NOT NULL,
last_name varchar(30) NOT NULL,
address varchar(80) NOT NULL,
admin_id int REFERENCES users(user_id)
} engine = InnoDB;

In the users table add admin_userid that References users(user_id)
That way, each user points back to users table to the admin user they belong to.
Using this column a doctor can list all his patients and the assignements table can be used with appointments.
But will a certain user ALWAYS get a meeting with the same doctor/admin?
What about vacations?

Related

Log user updates

In the making of a small community networking site, but I was seeking some advice as to how best to design a table that will store data to be pulled as a stream that shows for example:
User X has added a friend!
User Y has commented on a post!
User X changed their profile picture!
User X has changed their motto!
Currently this is my setup but before I continue wanted to know if I was on the right track
update_id int PK
member_id int FK
friend_id int FK
update_action text
update_time datetime
update_hidden tinyint
At the moment I was planning to run an extra insert query to update this table with each activity when a user does it, not sure if that is the best or in my case a good way to get this done. Any tips or advice will be appreciated thanks for your time.
You can have a table for all the different activities that your system should offer.
CREATE TABLE activity
(
id MEDIUMINT UNSIGNED PRIMARY KEY UNIQUE NOT NULL AUTO_INCREMENT,
description NVARCHAR(126) UNIQUE NOT NULL
);
For example: has commented on a post, has a new friend, etc.
And then log all the activities that happen, depending on the id of the activity (so that you can manually define where in the code which activity should be used) and store it somehow like this:
CREATE TABLE activityLog
(
id MEDIUMINT UNSIGNED PRIMARY KEY UNIQUE NOT NULL AUTO_INCREMENT,
userId UNSIGNED NOT NULL,
friendId UNSIGNED DEFAULT NULL,
acitivityId UNSIGNED NOT NULL,
timestamp TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
hidden TINYINT(1) DEFAULT 0,
FOREIGN KEY(userId) REFERENCES users(id),
FOREIGN KEY(friendId) REFERENCES users(id),
FOREIGN KEY(acitivityId) REFERENCES activity(id)
);
Assuming that you have your users stored in a table called 'users'.
Just make sure that It's easy to create new activities, easy to link the events, etc.

SQL foreign keys and referencing other tables

I'm trying link two tables together using a foreign key. One table is users, the other is userInfo. When I delete a user I also want to delete their info as well. When I delete a user from the users table their entry in usersInfo is still there. I can't seem to figure out what I'm doing wrong.
CREATE TABLE users (
userid INT AUTO_INCREMENT PRIMARY KEY,
username VARCHAR(30) NOT NULL,
email VARCHAR(50) NOT NULL,
password VARCHAR(32) NOT NULL,
userlevel INT UNSIGNED NOT NULL,
rating int,
organization int(1),
timestamp varchar(20) NOT NULL,
);
Here is my table for userinfo. Yes, I know it could be in the same table. I'm just doing this for an easy example.
CREATE TABLE usersInfo(
userid int auto_increment NOT NULL,
userlocation varchar(50),
about varchar(300),
userkeywords varchar(150),
FOREIGN KEY(userid) REFERENCES users(userid) ON DELETE CASCADE ON UPDATE CASCADE
);
Edit - problem solved. Thanks to everyone who helped.
Added TYPE = InnoDB at the end of the CREATE TABLE statements.
Add ENGINE=INNODB to these CREATE TABLE statements to ensure they're InnoDB tables. MySQL versions below 5.5.5 will default to MyISAM, which does not support foreign-key relationships, but which will not throw an error if you define a foreign-key relationship either.

How do you create a constraint on parent tables that also constrains the child tables?

I am not sure how to phrase the question so I'll illustrate the tables and the explain what I want to achieve.
-- static table of the entity classes supported by the application
create table entity_type (
id integer not null auto_increment,
name varchar(30) not null,
primary key(id)
);
-- static table of statuses supported by the application
create table entity_status (
id integer not null auto_increment,
name varchar(30) not null,
primary key(id)
);
-- table of valid combinations
create table entity_type_entity_status_link (
entity_type_id integer not null,
entity_status_id integer not null,
unique key(entity_type_id, entity_status_id),
foreign key(entity_type_id) references entity_type(id),
foreign key(entity_status_id) references entity_status(id),
);
-- The tables where user types and statuses are defined
create table user_type (
id integer not null auto_increment,
name varchar(30) not null,
entity_type_id integer not null,
primary key(id),
foreign key(entity_type_id) references entity_type(id)
);
create table user_status (
id integer not null auto_increment,
name varchar(30) not null,
entity_status_id integer not null,
primary key(id),
foreign key(entity_status_id) references entity_status(id)
);
-- table of valid pairs
create table user_type_user_status_link (
user_type_id integer not null,
user_status_id integer not null,
unique key(user_type_id, user_status_id),
foreign key(user_type_id) references user_type(id),
foreign key(user_status_id) references user_status(id),
);
The basic premise behind these tables is that the system supports core types and statuses and the user is able to create their own user types and statues that derive from these.
The question I have is that I cannot see a way of creating any database constraints on the user_type_user_status_link table to ensure that the you cannot insert a file_type - file_status pair where the parent entity_type - entity_status is itself not valid. Or is this something that would have to be done with triggers.
The basic premise behind these tables is that the system supports core
types and statuses and the user is able to create their own user types
and statues that derive from these.
Although that sounds like a laudable goal on the surface, the effect is to delegate database design to your users. Database design, because the effect of your desire to set foreign key references to a subset of the rows in entity_type_entity_status_link means each of those subsets is a defacto, unnamed table.
This approach never ends well.
What you've developed is the "One True Lookup Table". Google that for a host of reasons why OTLT is an anti-pattern.
The best solution is to model real things in your tables. (Entity isn't a real thing. It's an abstraction of a real thing.) Something along the lines of either
create table file_status (
file_status varchar(30) primary key
);
or
create table file_status (
file_status_id integer primary key,
file_status varchar(30) not null unique
);
would work well for file statuses.
In the case of the second one, you can set a foreign key reference to either the id number (saves space, requires an additional join) or to the status text (takes more space, eliminates a join). Note that you need the unique constraint on the status text; your original design allows the user to enter the same text multiple times. (You could end up with 30 rows where entity_type.name is 'File'.
You should use triggers for that.
MySQL does not support constraints of the form that will prevent what you want.

help with database modeling

I have two tables: one for areas (like science, sport, education), and another for professions (like scientist, designer, golf player). There is a foreign relationship between the two tables, which works without any problems at the moment.
But now I need another table to put "number of workers", "average age", "years in the company" (this list is possibly different for each profession). What is the best way to do this? Create another table? What would be the parent? Basically, it is a third statement.
CREATE TABLE group (
id smallint(5) unsigned NOT NULL auto_increment,
area varchar(30),
PRIMARY KEY (id)
)
CREATE TABLE job (
ref int(10) unsigned NOT NULL auto_increment,
jobid smallint(5) unsigned NOT NULL,
job varchar(50),
PRIMARY KEY (ref)
)
ALTER TABLE job
ADD CONSTRAINT FK_job
FOREIGN KEY (jobid) REFERENCES group(id)
ON UPDATE CASCADE
ON DELETE CASCADE;
From what I understand I would set up a third table as follows
Table: Employee
First_Name varchar(30)
Last Name varchar(30)
Age (int(3))
Employment Date (DATE)
Active (Yes/No)
JobFK (Points to emprego.PK)
With this kind of setup you can use joins on your tables to calculate how many workers are in the same profession. The average age of those employees, and how long they have been with the company. Given more information about your current tables I could even describe the sql queries for that information.

Creating tables for user authentication - room for improvements?

At the moment I'm developing a web application. For that, I need to create a database for user authentication.
I have something like the following in mind:
create table users
(
id SMALLINT UNSIGNED NOT NULL AUTO_INCREMENT,
username VARCHAR(100) NOT NULL,
password VARCHAR(60) NOT NULL,
PRIMARY_KEY(id),
UNIQUE(username)
);
create table roles
(
id SMALLINT UNSIGNED NOT NULL AUTO_INCREMENT,
role VARCHAR(100) NOT NULL,
PRIMARY_KEY(id),
UNIQUE(role)
);
create table user_roles
(
user_id SMALLINT UNSIGNED NOT NULL,
role_id SMALLINT UNSIGNED NOT NULL,
PRIMARY_KEY(user_id)
UNIQUE (user_id, role_id),
);
Passwords are fixed size but I use varchar because I've read somewhere that in a table where you have both char and varchar columns, the char columns get converted to varchar.
Also, would it be beneficial to use FOREIGN KEY CONSTRAINTS?
Don't use SMALLINTs for IDs. Seriously. Why would anybody do that!? Use a larger integer type.
The primary key on user_roles isn't going to work. Make (user_id, role_id) the primary key and put separate (non-unique) indexes on the fields you will be querying on.
You could add foreign keys that link user_roles to the other tables. If you use ON DELETE CASCADE, it'll even delete the stale links if you ever delete a user or a role.