I have to implement the tasks below:
Task:
At present, the database knows two types of messages:
Messages that a user posts and that are public for anyone and everyone to read
Messages that a user posts and that are non-public. These messages can only be read by users that the posting user has marked as friends.
In this step, you should add a third type of message. This third type of message should be readable by specified recipients only.
This means the database needs to provide the following:
A way of distinguishing between the three types of messages. This involves a change to the Message table.
A way of specifying who the recipients of a particular message are. This will probably require an additional table.
All this must again be achieved with minimal amount of storage, i.e., you must choose the appropriate data types from the MySQL manual. You may assume that the total number of messages that are added over time may reach 1,000,000,000.
Your job is to implement the necessary changes and additional table for this purpose and any keys and foreign key relationships required.
Here are my two tables first : User
CREATE TABLE IF NOT EXISTS `User` (
`user_id` int(10) unsigned NOT NULL auto_increment,
`given_name` varchar(60) default NULL,
`surname` varchar(60) default NULL,
`address` varchar(255) default NULL,
`city_id` int(10) unsigned NOT NULL,
`date_of_birth` datetime default NULL,
`email` varchar(80) default NULL,
PRIMARY KEY (`user_id`),
KEY `ix_user_surname` (`surname`),
KEY `ix_user_given_name` (`given_name`),
KEY `ix_user_name` (`given_name`,`surname`),
KEY `ix_user_date_of_birth` (`date_of_birth`),
KEY `ix_user_email` (`email`),
KEY `ix_user_city_id` (`city_id`)
) ENGINE=InnoDB
2nd table :Message
CREATE TABLE IF NOT EXISTS `Message` (
`message_id` int(10) unsigned NOT NULL auto_increment,
`owner_id` int(10) unsigned default NULL,
`subject` varchar(255) default NULL,
`body` text,
`posted` datetime default NULL,
`is_public` tinyint(4) default '0',
PRIMARY KEY (`message_id`),
KEY `ix_message_owner_id` (`owner_id`)
) ENGINE=InnoDB
MY SOLUTION: I was thinking of creating a new table called 'Message_level' and have columns 'message_level_id'(will refer to 1,2,3 as 1=public, 2=private, 3=specific) & 'message_level'(where it would state public,private and specific next to level). Then I can use the 'Message_level' as a foreign key into the 'Message' table and replace the 'is_public' column with 'message_level_id'.
Is my approach to this question right? is there another way I can do this to make it more efficient?
and how would I approach the second task of specifying who the recipients of a particular message are?
I would go like this:
User: user_id, given_name, ...
Message: message_id, owner_id (fk User), subject, body, posted, message_type_id (fk Message_type)...
Message_recipients: user_id (fk User), message_id (fk Message)
Message_type: message_type_id, description (1:public, 2:friends, 3:specific_recipients)
Related
I am currently working with a database that was auto generated by a tool (and is used in production)
(I will only speak about what is interesting for the question)
I have three tables : user, movie and userMovie.
the command show create table user return something like :
CREATE TABLE `users` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`username` varchar(255) DEFAULT NULL,
`email` varchar(255) DEFAULT NULL,
`password` varchar(255) DEFAULT NULL,
`other_field_1` varchar(255) DEFAULT NULL, -- not actual field name
PRIMARY KEY (`id`),
FULLTEXT KEY `SEARCH_USERS` (`username`,`other_field_1`)
)
the command show create table movie return something like :
CREATE TABLE `movie` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(255) DEFAULT NULL,
`link` varchar(255) DEFAULT NULL,
`another_field_1` varchar(255) DEFAULT NULL, -- not actual field name
`another_field_2` varchar(255) DEFAULT NULL, -- not actual field name
PRIMARY KEY (`id`),
FULLTEXT KEY `SEARCH_MOVIES` (`name`,`link`,`another_field_1`,`another_field_2`)
)
the command show create table userMovie return something like :
CREATE TABLE `userMovie` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`Name` varchar(255) DEFAULT NULL,
`user` int(11) DEFAULT NULL,
`field1` varchar(255) DEFAULT NULL, -- not actual field name
`field2` varchar(255) DEFAULT NULL, -- not actual field name
`field3` varchar(255) DEFAULT NULL, -- not actual field name
PRIMARY KEY (`id`),
FULLTEXT KEY `SEARCH_USER_MOVIE` (`Name`,`field1`,`field2`,`field3`)
)
Obviously, there is several issue with this code, the main ones being :
There is no foreign key,
The field userMovie.Name contain the name of the movie, not the id
I'm well aware of the inconsistency risk, but I'm more ignorant about the potential performance issue. Especially, there is a lot of records in the userMovie table, and we have to join it quite often with the movie table (and the user table)
However, as userMovie.Name is in the "FULLTEXT KEY", does that mean it is indexed ?
By the way, I think that only the tool previously mentioned had an use of this, and can probably be removed if needed.
I would want to know if there is a performance issue and ways to improve it. (It would also be awesome if the modification I'll be doing are "safe", as I don't want to break anything)
The column(s) in a FULLTEXT index are usable only for MATCH...AGAINST.
If you also want a BTree index on the column(s), provide a separate INDEX.
You can do
WHERE MATCH(`Name`,`field1`,`field2`,`field3`) AGAINST("...")
AND field4 > 123
Or even
WHERE MATCH(`Name`,`field1`,`field2`,`field3`) AGAINST("...")
AND name = 'abc'
However, this second format makes little sense. Usually a column is searched by either FULLTEXT or a regular index, not both.
What is the intent of the table userMovie? The name sounds like a many-to-many mapping table (eg, which movies each user has watched), but the columns do not reflect that.
To address a "performance issue", we need to see the SELECTs -- they have performance issues, not the schema. They guide what indexes are useful.
Martin Kleppmann in his book "Designing Data-Intensive Applications" is showcasing the following problem:
Claiming a username
On a website where each user has a unique username, two users may try to create
accounts with the same username at the same time. You may use a transaction to
check whether a name is taken and, if not, create an account with that name.
However, like in the previous examples, that is not safe under snapshot isolation.
Fortunately, a unique constraint is a simple solution here (the second transaction
that tries to register the username will be aborted due to violating the constraint).
I have a very similar use case, where 2 transactions are trying to claim the name of the entity.
At the beginning of each transaction, I run a select to see if such name was already taken. If it wasn't - create or update, depending on the operation requested by the user. This logic crumbles under concurrent attempts to claim/modify the name.
I am trying to see if there is a mechanism that allows implementing correct behavior under the Repeatable Read isolation level. Unique constraint violation thrown by the DB is not acceptable in my case, neither is a downgrade to Serializable execution.
Can I employ Select For ... Update here? Obviously, I won't be locking the concrete rows, but rather an entire table (correct me if I am wrong in my assumption) as I will not have pk index columns in the WHERE subclause?
Table structure:
CREATE TABLE `application_domains` (
`id` varchar(255) NOT NULL,
`name` varchar(255) NOT NULL,
`description` varchar(10000) DEFAULT NULL,
`org_id` varchar(255) NOT NULL,
`created_time` bigint(20) NOT NULL,
`updated_time` bigint(20) NOT NULL,
`created_by` varchar(16) NOT NULL,
`changed_by` varchar(16) NOT NULL,
`revision_id` varchar(16) DEFAULT NULL,
`topic_domain` varchar(255) NOT NULL,
`enforce_unique_topic_names` tinyint(1) NOT NULL DEFAULT '1',
`sample_id` varchar(255) DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `UK_orgId_name` (`org_id`,`name`),
UNIQUE KEY `UK_orgId_sampleId` (`org_id`,`sample_id`),
KEY `FK_references_application_domains_organization` (`org_id`),
KEY `FK_app_domain_samples_id_references_application_domains_tbl` (`sample_id`),
CONSTRAINT `FK_app_domain_samples_id_references_application_domains_tbl` FOREIGN KEY (`sample_id`) REFERENCES `application_domain_samples` (`id`) ON DELETE SET NULL ON UPDATE SET NULL,
CONSTRAINT `FK_references_application_domains_organization` FOREIGN KEY (`org_id`) REFERENCES `organizations` (`org_id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
We are building a system with concept of Admin and Employee. so basically Admin is an employee with all powers and can view all the data created by other Employee.
CREATE TABLE `Vendor` (
`vendor_Id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`name` varchar(40) NOT NULL,
`email_Id` varchar(40) DEFAULT NULL,
`landline_Number` varchar(15) DEFAULT NULL,
`mobile_Number` varchar(15) DEFAULT NULL,
`address_Line1` varchar(65) NOT NULL,
`address_Line2` varchar(65) DEFAULT NULL,
`city` varchar(255) NOT NULL,
`pincode` int(6) NOT NULL,
`country` varchar(255) NOT NULL,
PRIMARY KEY (`vendor_Id`),
) ENGINE=InnoDB AUTO_INCREMENT=8 DEFAULT CHARSET=latin1
CREATE TABLE `Employee` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`vendor_Id` int(10) unsigned DEFAULT NULL,
`name` varchar(40) NOT NULL,
`username` varchar(40) DEFAULT NULL,
`password` varchar(255) DEFAULT NULL,
`role` varchar(255) DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `employee_username_unique` (`username`),
KEY `employee_vendor_id_foreign` (`vendor_Id`),
CONSTRAINT `employee_vendor_id_foreign` FOREIGN KEY (`vendor_Id`) REFERENCES `Vendor` (`vendor_Id`)
) ENGINE=InnoDB AUTO_INCREMENT=12 DEFAULT CHARSET=latin1
CREATE TABLE `Action` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`emp_Id` int(10) unsigned DEFAULT NULL,
`name` varchar(60) NOT NULL,
`assigned_To` varchar(40) DEFAULT NULL,
`deadline` datetime(3) NOT NULL,
`notes` varchar(400) DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `action_emp_id_foreign` (`emp_Id`),
CONSTRAINT `action_emp_id_foreign` FOREIGN KEY (`emp_Id`) REFERENCES `Employee` (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=12 DEFAULT CHARSET=latin1
There are other tables Roles and EmployeeRoles which I thought were not required here.
Approach 1 : Now, when the Admin logs in to see all the Actions created by everyone
we first need to query the Employee table to find all the employees of that Vendor (we will have the Vendor_Id stored in the session when the admin/employee logs in)
Then query the Action table with where in employee_Id array from Step 1
Is this a good approach ?
Approach 2 : or in Action table, I shall store Vendor_Id for each record (mainly all this effort only so that when Admin logs in I can easily retrieve all the records for that Vendor. When Admin logs in from the session I can easily find the Vendor_Id and query the Action table.
I don't know at this moment which would be a better approach. Any suggestions ?
Like Action, there are other 3 tables where similar concept needs to be applied.
Edit 1: There can be a case where we can have multiple vendors registered under a Single brand (future extension) and the Super-Admin would like to analyze the data across multiple branches.
First approach is the basic normalisation approach. As you are putting vendor_id into session, you can also put employee array (having emp_ids belonging to that vendor) into session or cache. Here you would not have to query again and again as it will be refreshed when session or cache expires.
Second solution is the denormalised one. Here you would run into problems based on consistency. On updation of each vendor_id-emp_id mapping you need to update your action table too.
So you have to compare the volume of write queries to read queries. If read queries are too high then go with second. But I suppose there will be only 1-2 admins in an organisation of small size. I would go with Ist until I run into some serious performance issues.
You can stick to solution 1. With an index on the Vendor id in the employee table you should be good with an inner join with actions table.(Unless you are planning to have millions of rows in the tables and looking towards having of performance at single digit milliseconds level)
I'm try to figure out the optimal database structure for a scenario where users belong to one organization and within the organization table there is a field with all of the emails of the users associated with that organization. Within the users table I have a foreign key of organization_id which passes the value of the organization that user is associated with, but on the organization table I'm currently was planning on creating a members column that would add the email address (comma separated) for each user that is associated with that organization. I plan on using this column for WHERE conditions in retrieving records. Is this the smart way to do it or should I create another table called members that associated both the users and organization table?
Here is my organization table:
CREATE TABLE `organization` (
`organization_id` int(11) NOT NULL AUTO_INCREMENT,
`organization_name` varchar(255) DEFAULT NULL,
`admin` varchar(255) DEFAULT NULL,
`members` varchar(255) DEFAULT NULL,
`createdAt` datetime NOT NULL,
`updatedAt` datetime NOT NULL,
PRIMARY KEY (`organization_id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;
Here is my user table:
CREATE TABLE `user` (
`user_id` int(11) NOT NULL AUTO_INCREMENT,
`first_name` varchar(255) DEFAULT NULL,
`last_name` varchar(255) DEFAULT NULL,
`email` varchar(255) DEFAULT NULL,
`password` varchar(255) DEFAULT NULL,
`createdAt` datetime NOT NULL,
`updatedAt` datetime NOT NULL,
`organization_id` int(11) DEFAULT NULL,
PRIMARY KEY (`user_id`),
UNIQUE KEY `email` (`email`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;
Since it's a one-to-many (an organization has many users, but a user can only belong to one organization), I'd do a structure like this:
user
user_id first_name . . . email org_id
---------------------------------------------
(PK) (FK)
organization
org_id org_name . . .
------------------------------
(PK)
. . . which is what you have.
To get all members (and their emails or other info) of an organization:
SELECT u.user_id, u.email
FROM user u
WHERE org_id = (whatever value you want)
Inserting user_id or emails (or 'members') into the org table would be unnecessary duplication, since users are already linked to org's by the org_id. You can get an org mailing list from the above query.
I'd only do a members table if you had a many-to-many relationship.
If this is missing something, let me know, and I'll go back to the drawing board.
I'm in the process of building a common notification system for a webapp. The main technologies I'm using are Java, Spring MVC and Hibernate. I've been looking at several posts here and there, trying to come up with the solution that suits me best, taking into account recommended practices.
I've already coded my database tables and would like to receive some feedback in order to
improve my design to avoid big changes while I'm implementing my Java classes. My goal is to make it as complete, scalable and optimal as possible mantaining the complexity to the minimum possible.
Here's my code:
NOTIFICATION SAMPLE:
The #user added a new comment.
The user [USER_ID] [USER_ACTION] a new [OBJECT].
>>> Updates >>>
[05/02/14]
Fields id_recipient and seen removed from notification table. (#Kombajn zbożowy)
New table notification_user created. (#Kombajn zbożowy)
Lowercase identifiers. (#wildplasser)
notification
CREATE TABLE notification (
id_notification BIGINT(20) NOT NULL AUTO_INCREMENT,
id_notification_type BIGINT(20) NOT NULL,
id_action_type BIGINT(20) NOT NULL,
id_sender BIGINT(20) NOT NULL,
created_date TIMESTAMP NOT NULL,
url VARCHAR(300) NULL,
PRIMARY KEY (id_notification),
FOREIGN KEY (id_notification_type) REFERENCES notification _type (id_notification_type),
FOREIGN KEY (id_action_type) REFERENCES action_type (id_action_type),
FOREIGN KEY (id_sender) REFERENCES user (id_user)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
notification_user: so that one notification can be sent to many recipients (users).
CREATE TABLE notification_user (
id_notification BIGINT(20) NOT NULL,
id_recipient BIGINT(20) NOT NULL,
seen TINYINT(1) DEFAULT 0,
PRIMARY KEY (id_notification , id_recipient),
FOREIGN KEY (id_notification) REFERENCES notification (id_notification),
FOREIGN KEY (id_recipient) REFERENCES user (id_user)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
notification_type: refers to the type of object that was modified by the actions of a certain user. Example: comment, post, etc.
CREATE TABLE notification_type (
id_notification_type BIGINT(20) NOT NULL AUTO_INCREMENT,
notification_name VARCHAR(100) NOT NULL,
description VARCHAR(300) NULL,
PRIMARY KEY (id_notification_type)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
action_type: actions executed by the users which trigger the notifications. Typically: update, add, remove, etc.
CREATE TABLE action_type (
id_action_type BIGINT(20) NOT NULL AUTO_INCREMENT,
action_name VARCHAR(100) NOT NULL,
PRIMARY KEY (id_action_type)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;