How should I design my activity table? - php

I made a "follow system". The DB design looks like this
CREATE TABLE IF NOT EXISTS `users_followers` (
`id` int(11) NOT NULL PRIMARY KEY AUTO_INCREMENT COMMENT 'auto incrementing USER_FOLLOWER_ID for each row unique index',
`user_id` int(11) NOT NULL COMMENT 'foriegn key to UserId column in users table',
`follower_id` int(11) NOT NULL COMMENT 'foriegn key to UserId column in users table',
`follower_since_timestamp` bigint(20) DEFAULT NULL COMMENT 'timestamp of the follow'
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci AUTO_INCREMENT=1;
Before the follow system, all inputs/post/entries were visible to all. Going on a big feed/wall. Now with a couple 100 users it is hard to navigate.
Should I do it the way it was before: 1)check if I follow a user and fetch all information or should I future proof it and make an activity table?: 2)Check if i follow a user, look at the activity table and fetch the appropriate data from post table.
With the activity table it seems I can have a much bigger overview of what's going on. I could for example have a column deleted, but the data is still there in post.
Is an activity table necessary?

Just sharing my two cents, I built a system that enable researchers (well users of the system) to follow each other based on the study of interest, I did it exactly the way you proposed, it really made it easy to pull out data from the data, like for example if User A has a study interest of JavaScript, I can easily use the user_id of User A in table Activity, to pull areas of interest of User A, I hope you get what I am trying to say tho?

Related

Creating a Like function php mysql ajax

Alright, I have been searching and finding very much about this subject,
but nothing satisfying.
I want to keep track of who has liked what, not just add a +1 to a table.
I have three tables: posts, comments and likes.
The table design looks like this for the moment
CREATE TABLE IF NOT EXISTS `likes` (
`like_id` int(11) NOT NULL PRIMARY KEY AUTO_INCREMENT,
`post_id_fk` INT(11),
`comment_id_fk` INT(11),
`uid_fk` int(11) NOT NULL,
`date` datetime NOT NULL DEFAULT '0000-00-00 00:00:00',
`ip` varchar(39) COLLATE utf8_unicode_ci NOT NULL DEFAULT '0.0.0.0',
FOREIGN KEY (uid_fk) REFERENCES users(uid),
FOREIGN KEY (post_id_fk) REFERENCES post(post_id),
FOREIGN KEY (comment_id_fk) REFERENCES comments(comments_id),
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci AUTO_INCREMENT=1;
And as you can see i have three different foreign keys, uid_fk for the user uid so i can know who has liked what. And now comes there problem, one foreign key to post_id and one for comments_id.
Mysql won't accept a foreign key if it doesn't exist. if i want to "like" a comment, it won't let be because of the foreign key of post_id_fk.
How to solve this DB mess?
And the AJAX like/delike problem:
I found this jQuery : Changing class of button with AJAX call when i was searching, and it looks simpel and very nice. And i have also follow this http://pluscss.com/tutorials/ajax-like-script-using-php-mysql-jquery tutorial. But I'm having problems combine them.
This is what I'm trying to do:
count the current amount of likes
check if the user have liked it before
give the user the option to like (or delike if previously liked)
with ajax and like.php
Could someone help me with this i would be very thankful!
It would be better to separate the tables.
Create a post_likes table and comments_likes table.
That way not only you're getting rid of your existing problem but the structure is more decoupled and more reusable.

Alternatives for a many-to-many link table

I am working on a project where I want to allow the end user to basically add an unlimited amount of resources when creating a hardware device listing.
In this scenario, they can store both the quantity and types of hard-drives. The hard-drive types are already stored in a MySQL Database table with all of the potential options, so they have the options to set quantity, choose the drive type (from dropdown box), and add more entries as needed.
As I don't want to create a DB with "drive1amount", "drive1typeid", "drive2amount", "drive2typeid", and so on, what would be the best way to do this?
I've seen similar questions answered with a many-to-many link table, but can't think of how I could pull this off with that.
Something like this?
CREATE TABLE `hardware` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(256) NOT NULL,
`quantity` int(11) NOT NULL,
`hardware_type_id` int(11) NOT NULL,
PRIMARY KEY (`id`),
KEY `type_id` (`hardware_type_id`),
CONSTRAINT `hardware_ibfk_1` FOREIGN KEY (`hardware_type_id`) REFERENCES `hardware_type` (`id`)
) ENGINE=InnoDB
hardware_type_id is a foreign key to your existing table
This way the table doesnt care what kind of hardware it is
Your answer relies a bit on your long term goals with this project. If you want to posses a data repository which has profiles all different types of hardware devices with their specifications i suggest you maintain a hardware table of each different types of hardware. For example you will have a harddisk table which consist of all different models and types of hardisks out there. Then you can assign a record from this specific table to the host configuration table. You can build the dataset as you go from the input from user.
If this is not clear to you let me know i will create a diagram and upload for you.

Single mysql table for private messaging

I'm trying to create a single table for private messaging on a website. I created the following table which I think is efficient but I would really appreciate some feedback.
CREATE TABLE IF NOT EXISTS `pm` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`user_id` int(11) NOT NULL,
`to` int(11) NOT NULL,
`date` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
`subject` varchar(255) DEFAULT NULL,
`message` text NOT NULL,
`read` tinyint(1) NOT NULL DEFAULT '0',
`deleted` tinyint(1) NOT NULL DEFAULT '0',
PRIMARY KEY (`id`)
FOREIGN KEY (user_id) REFERENCES User(user_id)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=1 ;
I have 2 columns that determine the status of the message: read and deleted
If read = 1, the message has been read by the receiver. If deleted = 1, either the sender or the receiver deleted the message from the sent or received inbox. If deleted = 2 both users deleted the message, therefor delete the row from the database table.
I see that you don't have have any indexes explicitly stated. Having the appropriate indexes on your table could improve your performance significantly. I also believe that for your message column you may want to consider making i a varchar with a max size explicitly stated. Other than those two items which you may already taken care of your table looks pretty good to me.
MySQL Table Performance Guidelines:
Add appropriate indexes to tables. Indexes aren't just for primary/unique keys add them to frequently referenced columns.
Explicitly state maximum lengths. Fixed length tables are faster than their counterpart
Always have an id column.
Add NOT NULL where ever you can. The nulls still take up space
Know your data types. Knowledge is power and can save on performance and space
Interesting Articles:
VarChar/TEXT Benchmarks
Similar Question
Some Best Practices
Data Type Storage Requirements
The articles and some of the items I have listed may not be 100% correct or reliable so make sure you do a bit of your own research if you are interested in further tuning your performance.
A few comments:
Charset=latin1 is going to piss some people of I'd suggest charset=utf8.
I'd suggest putting a foreign key check in not only on user_id, but on to as well.
Also I'd put an index on date, as you will be doing a lot of sorting on that field.
You need to split deleted in two fields, otherwise you will not know which user has deleted the message. (deleted_by_user, deleted_by_recipient)
Note that date is a reserved word and you'll need to change it into message_date or `backtick` it in your queries.
some comments:
not bad.
i would name the table something that other people might guess out of context. so maybe private_message instead of pm.
i would be explicit on the user column names, so maybe from_user_id, and to_user_id instead of 'user_id' and 'to'
i would consider pulling out the status into a new table with status, user_id, and date - this should give you a lot more flexibility in who is doing what to the message over time.
For displaying both the receiver's inbox and the senders outbox (and being able to delete messages respectively), you will probably need more information that what you currently have encoded. I would suggest a "deleted" field for each party. (As long as this is limited to only 1 user on each end and no broadcast messages, this works. This does not scale to broadcast messages, however, which would require more than 1 table to do efficiently)
You may also want to enforce key relationships with ON DELETE and ON UPDATE:
FOREIGN KEY (user_id) REFERENCES User(user_id) ON DELETE CASCADE ON UPDATE CASCADE,
FOREIGN KEY (to) REFERENCES User(user_id) ON DELETE CASCADE ON UPDATE CASCADE
The removal or modification of a user will propagate changes or deletions to the messages table.
I think you may need to add an column called Parent_Message_ID which will have the parent mail ID. So that replies can also included.
If you think in future to add replies to your private messages.

What is the most efficient way to create a forum lightbulb (unread) system?

Alright, another interesting problem over at Route 50.
We wanted to implement a true forum lightbulb system where posts that are unread by a user (after the user's account is created) show as unread until that status is cleared or until the user reads them.
We figured the best and easiest way to do this would be to implement a table of unread messages.
The Columns are: user_id, board_id, thread_id, post_id, timestamp, and hidden
This is working very well and very quickly for seeing which boards/threads/posts are unread (and linking to them) per user, however it is INCREDIBLY slow for a user to post to the forum even though only a single SQL query is being run:
INSERT IGNORE INTO `forums_lightbulb` SELECT `id`,'x','x','x',UNIX_TIMESTAMP(),0 FROM `users`
I'm sure this is the result of having 3065 user accounts. How can I speed up this process? I'd prefer to keep the system as Real-Time as possible.
Important Note: Please limit your answers to a shared hosting environment with no additional budget. We are limited to PHP and MySQL 5.1.53-log
What PHPBB does is a very quick way to do it. It keeps a table that marks for each thread and each forum when the last time was a user opened it. And uses that to determine if there are unread messages. It allows a Users*Topics + Users*Forums storage usage scheme while allowing a check with pretty simple and fast queries.
You can see how it works from the database structure.
# Table: 'phpbb_forums_track'
CREATE TABLE phpbb_forums_track (
user_id mediumint(8) UNSIGNED DEFAULT '0' NOT NULL,
forum_id mediumint(8) UNSIGNED DEFAULT '0' NOT NULL,
mark_time int(11) UNSIGNED DEFAULT '0' NOT NULL,
PRIMARY KEY (user_id, forum_id)
) CHARACTER SET `utf8` COLLATE `utf8_bin`;
# Table: 'phpbb_topics_track'
CREATE TABLE phpbb_topics_track (
user_id mediumint(8) UNSIGNED DEFAULT '0' NOT NULL,
topic_id mediumint(8) UNSIGNED DEFAULT '0' NOT NULL,
forum_id mediumint(8) UNSIGNED DEFAULT '0' NOT NULL,
mark_time int(11) UNSIGNED DEFAULT '0' NOT NULL,
PRIMARY KEY (user_id, topic_id),
KEY topic_id (topic_id),
KEY forum_id (forum_id)
) CHARACTER SET `utf8` COLLATE `utf8_bin`;
Sorry to say, but the solution proposed in the question is an unscalable design.
I was looking into this problem over here, which had a decent discussion on it (before I showed up). Take a look there.
In your case, storing U*M records to track "unread" posts, where U is the number of users and M is the number of messages, would get out of control very, very quickly. This is because its best-case efficiency requires all users to read every message (and most users don't care about everything, as most everything on a forum is noise). Average case, maybe 20% of users have read 100% of posts, but 80% have read near 0% of posts and never will read the rest. This means that you're being forced to store 0.8*U*M, with U and M only ever increasing, geometrically. No amount of indexing will fix this.
The previous #will-hartung answer has the more efficient approach.
I see that this is pretty old, and I hope you found a better solution in the meantime.
Here is the most efficient way:
have a table called read_threads which stores the thread_id, and user_id
have a column in the users table called mark_read_date which stores the date the user clicked on the mark all threads read link in your forum
in order to determine if a thread is read, your query will check if it is in the read_threads table, or if its last_post_date (the date the last post was made to it) is older than the users mark_read_date
It is important that you also remove all rows from the read_threads table when a user clicks the mark all threads read link in your forum.
On read:
insert into read_articles(user_id, article_id);
On display:
SELECT a.*, r.user_id FROM articles a
LEFT OUTER JOIN read_articles r ON (a.article_id = r.article_id and r.user_id = $user_id)
WHERE (article_filter, like forum or thread id, or whatever)
On your result set, if user_id is not null, then they've read the article. Otherwise, they haven't.
Index as appropriate. Server warm with biscuits and jam.

Help with php/mysql mailer

I'm working on a real estate site and need to make notification mailer: when new property is inserted on a site, people who subscribed for notification in that particular country and/or area and/or city and/or particular property operation (rental, selling) will receive a notification on email. One person could subscribe for different areas, cities, etc, not only one. One person will receive only one notification a week let say if there are new properties for him, though. And I'm thinking on how better to create a mysql table for subscribers in order to easy retrieve them. Table like:
create table subscribers(
user_email varchar(255),
area_id int(4));
is a bad idea, because if there will be let say 100,000 (looking to the future) subscribers and each will subscribe for 10 areas there will be 1,000,000 rows in a table. So, I'm looking for efficient solution to do such task.
If you have additional recommendations, I will like to hear them.
Thanks in advance!
You should use a cross-reference (many-to-many) table. This will make data more normalized:
CREATE TABLE `areas` (
`id` int(10) unsigned NOT NULL auto_increment,
`name` varchar(255) NOT NULL
PRIMARY KEY (`id`)
)
CREATE TABLE `subscribers` (
`id` int(10) unsigned NOT NULL auto_increment,
`email` varchar(255) NOT NULL
PRIMARY KEY (`id`)
)
-- cross ref table
CREATE TABLE `areas_subscribers` (
`area_id` int(10) unsigned NOT NULL,
`subscriber_id` int(10) unsigned NOT NULL,
UNIQUE KEY (`area_id`,`subscriber_id`)
)
And a million rows is not a problem. Especially with a cross ref table.
there will be 1,000,000 rows in a table
So what? mySQL can handle it.
As far as I can see, the way you are doing it is perfectly fine. It's nicely normalized, I can't think of a better method.
Your table looks correct, assuming that user_email is the primary key identifying your users. If so, add to your subscribers table a PRIMARY KEY (user_email, area_id) to indicate that both fields together make up your primary key.
Your concern about duplicating e-mails has little to do with the schema design and more to do with the query you intend to run. That, of course, will depend largely on how your other data are stored, but might look something like:
SELECT DISTINCT user_email WHERE area_id IN (...)
(For a list of area_id values that have seen listings in the past week.)
That's a simple query that could be optimized and improved given the rest of your schema, but it illustrates how easy it is to avoid generating multiple e-mails despite the same person being listed multiple times.
You can make an extra table of the email addresses.
So you only store an ID in the subscriber table and not the same email address over and over again (whereas there might be some optimizations in the database anyway).

Categories