Log user updates - mysql

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.

Related

Using databases and tables (MySQL)

On my website, customers have the option of creating an event with various items (that have attributes like seller, color, etc.).
Should I have ONE database and a new table for each event? I don't know of another way to program this and splitting every customer/event into a new database seems like a bad solution, but I'm new to databases and don't know if that's stupid.
I assume that I'd have a TABLE with user IDs, a TABLE for each event, and a TABLE that links the user to the event(s) he/she created. Is this the optimal way to do this? All in one database?
Thanks!
You should have a one-to-many relationship between a user table, and an event table. The event table should have the user ID as a foreign key.
CREATE TABLE user (
id int UNSIGNED AUTO_INCREMENT NOT NULL,
name varchar(50) NOT NULL,
last_modified timestamp NOT NULL,
PRIMARY KEY (id),
UNIQUE KEY (name)
);
CREATE TABLE event (
id int UNSIGNED AUTO_INCREMENT NOT NULL,
user_id int UNSIGNED NOT NULL,
name varchar(50) NOT NULL,
description varchar(500) NOT NULL,
last_modified timestamp NOT NULL,
PRIMARY KEY (id),
FOREIGN KEY (user_id) REFERENCES user.id ON UPDATE CASCADE ON DELETE CASCADE
);
So then, you have your users, and when you add an event, you just assign the user_id to whatever user the event is for. Hopefully that gives you something to build from.
If you have a small number of event types, and the event types are very different in nature (different properties) then you may create a different table for each event type.
However usually you will create one database with one table for all the events, with a column for event type or code (if needed). You also don't need to create a table that contain all the event types, your code can contain them. You table should contain only the actual events that were fired.

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.

Junction tables with non unique entry

I'm beginning to build a stamp collecting web app. Python/flask backend (i think :)) mySQL as db. I don't know much about db design so please keep that in mind if I do some really stupid mistake in the way I thought it out. I was thinking of splitting the data into 3 tables.
users table (all the users should be added upon registration to this table)
stamps table (all stamps should reside here and only modified by me)
owned table (junction table with user_id and stamp_id as foreign keys)
Question : if I put user_id and stamp_id as primary key , there will only be one unique entry of this type for example user_1 has card_1. But user_1 might have a duplicate of card_1 so i should have 2 rows
user_1 card_1
user_1 card_1
Another problem that arises is that I want to include state of owned stamp. For example user_1 might have a card_1 in mint condition and a card_1 in bad condition. As far as I understand I can only enter one unique pair of user_1 card_1 . What can I do to get the desired result? Also if there's a better way of doing this please let me know.
Aditional question. I was using mysql workbench to try to plot the db so I have a question about the sql it generates. the CONSTRAINT "fk_gibberish", is that normal or ... why is that ?
CREATE TABLE IF NOT EXISTS `stampcollect`.`users` (
`user_id` INT UNSIGNED NOT NULL AUTO_INCREMENT ,
`user_username` VARCHAR(45) NULL ,
`user_password` VARCHAR(45) NULL ,
`user_email` VARCHAR(45) NULL ,
PRIMARY KEY (`user_id`) )
CREATE TABLE IF NOT EXISTS `stampcollect`.`stamps` (
`stamp_id` INT UNSIGNED NOT NULL AUTO_INCREMENT ,
`stamp_name` VARCHAR(45) NULL ,
PRIMARY KEY (`stamp_id`) )
CREATE TABLE IF NOT EXISTS `stampcollect`.`owned` (
`user_id` INT NOT NULL ,
`stamp_id` INT NOT NULL ,
`stamp_status` BIT NULL ,
PRIMARY KEY (`user_id`, `stamp_id`) ,
INDEX `fk_{F5DBEF0D-24E0-4AFF-A5CB-2A6A0D448C96}` (`stamp_id` ASC) ,
CONSTRAINT `fk_{22B4468E-A5FB-4702-A8A9-576AA48A0543}`
FOREIGN KEY (`user_id` )
REFERENCES `stampcollect`.`users` (`user_id` ),
CONSTRAINT `fk_{F5DBEF0D-24E0-4AFF-A5CB-2A6A0D448C96}`
FOREIGN KEY (`stamp_id` )
REFERENCES `stampcollect`.`stamps` (`stamp_id` ));
If users can own the same stamp in multiple states then the state should go in the "owned" table and be part of the key. If he can own multiple copies of the same stamp then it would make sense to have a "quantity" column in that table (not part of the key).
Add an id field with auto-increment on your owned table, and make that the primary key.
Regarding the other question: it's just Workbench generating a unique id for your foreign key. You can rename them, just keep them unique.

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.

How should I structure my MySQL database?

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?