MySQL Database Structure for Followers - php

I would like to implement a follow/favorite system. I can think of 2 ways of implementing a database/table structure but am unsure of which one to implement. Which one of these would be considered best practices and most importantly why?
I put all of my followers in a single string. By putting all followers in a single string it reduces the amount of redundant rows.
Ex.
id (1) || user_id (1) || follower_ids (2, 3, 45)
'CREATE TABLE `users` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`username` varchar(20) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci AUTO_INCREMENT=1';
'CREATE TABLE `follow` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`user_id` int(10) unsigned NOT NULL,
`follower_ids` text NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci AUTO_INCREMENT=1';
OR
I put each follow_id individually but adds redundancy by having 3 rows for the same user_id.
Ex.
id (1) || user_id (1) || follower_id (2)
id (2) || user_id (1) || follower_id (3)
id (3) || user_id (1) || follower_id (45)
'CREATE TABLE `users` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`username` varchar(20) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci AUTO_INCREMENT=1';
'CREATE TABLE `follow` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`user_id` int(10) unsigned NOT NULL,
`follower_id` int(10) unsigned NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci AUTO_INCREMENT=1';

Your second option with a slight modification on the field names, as both the follower and the followed are BOTH user_id's. As mentioned by John, add foreign keys to both of the *_user_id fields in the follow table.
Additionally, never have plural table names. 'user' and 'follow' are sufficient. I personally prefer tables like 'follow' to have a prefix like 'xref_' so that i know it's simply a cross reference table allowing a many-to-many relationship (A user can follow many users, and a user may have many following users).
'CREATE TABLE `user` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`username` varchar(20) NOT NULL,
) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci AUTO_INCREMENT=1';
'CREATE TABLE `follow` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`followed_user_id` int(10) unsigned NOT NULL,
`follower_user_id` int(10) unsigned NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci AUTO_INCREMENT=1';

The best way is neither. You should have a middle table between follow table and followers table. The middle table have only two columns. follow_id and followers_id.
With this kind of approach you omit the disadvantages of two solutions you have mentioned. You do not have to proccess a string and you have not duplicate entries and the performances with only indexes included are pretty fast.
'CREATE TABLE `follow` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci AUTO_INCREMENT=1';
'CREATE TABLE `user` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`username` varchar(255) unsigned NOT NULL,
....
PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci AUTO_INCREMENT=1';
'CREATE TABLE `follow_user` (
`user_id` int(10) unsigned NOT NULL,
`follower_id` int(10) unsigned NOT NULL,
)
Cause you change your post a lot, i think that your second approach is better if followers are the same as users. Because you only store the indexex of follow and users and a good select query to see what a single user follows something is way more better than parsing and searching a string The duplicate entries are no problem cause they are only indexes and there's no problem to that.

Related

Implicit MySQL Join on Update Statement - 0 rows affected

I'm trying to get this MySQL code to work, but it's saying 0 rows affected.
UPDATE assessments, assessment_types
SET assessments.assessment_type_id = assessment_types.id
WHERE (assessment_types.description = "Skills Assessment" AND assessments.id = 2);
Basically I have assessment_types with id and description column, and I just have the id in the assessments.assessment_type_id
I need to update the id.
I searched and couldn't find quite what I need for this.
Thanks!
Table Data:
assessment_types
id description
1 Knowledge Assessment
2 Skill Assessment
3 Personal Information
4 Natural Skills
Table Structure:
--
-- Table structure for table `assessments`
--
CREATE TABLE IF NOT EXISTS `assessments` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(255) COLLATE utf8_bin NOT NULL,
`acronym` varchar(255) COLLATE utf8_bin NOT NULL,
`assessment_type_id` int(11) NOT NULL,
`language_id` int(11) NOT NULL,
`date_created` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
`date_updated` date NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `id` (`id`),
KEY `assessment_type_id` (`assessment_type_id`),
KEY `language_id` (`language_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin AUTO_INCREMENT=2385 ;
--
-- Table structure for table `assessment_types`
--
CREATE TABLE IF NOT EXISTS `assessment_types` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`description` varchar(255) CHARACTER SET latin1 NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `id` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin AUTO_INCREMENT=7 ;
You can try doing an explicit join of the two tables in your UPDATE statement:
UPDATE assessments a
INNER JOIN assessment_types at
ON a.assessment_type_id = at.id
SET a.assessment_type_id = at.id
WHERE (at.description = "Skills Assessment" AND a.id = 2);

Red bean php R::getAll doesn't return any records

Here is database structure, generated with red bean php:
CREATE TABLE `attrib` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`type` tinyint(3) unsigned DEFAULT NULL,
`value` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci AUTO_INCREMENT=10 ;
CREATE TABLE `attrib_photo` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`attrib_id` int(11) unsigned DEFAULT NULL,
`photo_id` int(11) unsigned DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `UQ_e472845bf988796e2d3eceb91e2745b95f3aa534` (`attrib_id`,`photo_id`),
KEY `index_for_attrib_photo_attrib_id` (`attrib_id`),
KEY `index_for_attrib_photo_photo_id` (`photo_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci AUTO_INCREMENT=19 ;
CREATE TABLE `photo` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`filepath` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
`date` set('1') COLLATE utf8_unicode_ci DEFAULT NULL,
`iscolored` tinyint(3) unsigned DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci AUTO_INCREMENT=4 ;
ALTER TABLE `attrib_photo`
ADD CONSTRAINT `attrib_photo_ibfk_1` FOREIGN KEY (`attrib_id`) REFERENCES `attrib` (`id`) ON DELETE CASCADE,
ADD CONSTRAINT `attrib_photo_ibfk_2` FOREIGN KEY (`photo_id`) REFERENCES `photo` (`id`) ON DELETE CASCADE;
I'm making manually constructed query:
output of R::debug(); R::getAll($sql);
select filepath
from photo
inner join attrib as attrib4 on attrib4.type = 4 and attrib4.value in ("Dmitry")
inner join attrib_photo as attrib_photo4 on attrib_photo4.attrib_id = attrib4.id and attrib_photo4.photo_id = photo.id WHERE 1=1 LIMIT 0,25
Array ( )
resultset: 0 rows
[]
The result set is empty, but the base contains records, which fall under requirements, and running sql manually in phpMyAdmin shows them.
What's the problem with query, are there any additional debug info I can deal with to solve the problem? Thanks.
Update:
A little detail was missing, in original query I used unicode string for photo attribute search. After some investigations I found that php code works correctly and wrong encoding conversion happens on mysql side. Decision was to change collation of whole database to utf8_unicode_ci. Thanks for attention, and sorry for missing info in question.

MySQL JOIN query more tables and return more result in one select

I would question, sorry my bad english :(
I have multiple tables
CREATE TABLE `user` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`username` varchar(50) COLLATE utf8_slovak_ci NOT NULL,
`password` varchar(200) COLLATE utf8_slovak_ci NOT NULL,
`status` tinyint(1) NOT NULL,
PRIMARY KEY (`id`,`username`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8 COLLATE=utf8_slovak_ci;
CREATE TABLE `user_acl` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`id_user` int(11) unsigned NOT NULL,
`group` smallint(5) unsigned NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8 COLLATE=utf8_slovak_ci;
CREATE TABLE `user_profil` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`email` varchar(100) COLLATE utf8_slovak_ci NOT NULL,
`fullname` varchar(100) COLLATE utf8_slovak_ci NOT NULL,
`profil` text COLLATE utf8_slovak_ci NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8 COLLATE=utf8_slovak_ci;
Query:
SELECT user.*, prf.*, acl.*
FROM (SELECT * FROM user LIMIT 1) AS user
LEFT JOIN user_acl AS acl ON (acl.id_user = user.id)
INNER JOIN user_profil AS prf ON (user.id = prf.id)
I have table user, user_acl, user_profil
table user and user_profil are indexed under id what is the common key
Table user_acl have id_usercommon key with table user (id) in the table but user_acl There are more rows for the table user and I need all rows from a table user_acl in one query.
You can get MySQL to combine values from multiple rows into one row with GROUP_CONCAT:
SELECT user.*, prf.*, GROUP_CONCAT(acl.id), GROUP_CONCAT(acl.group)
FROM user
LEFT JOIN user_acl AS acl ON (acl.id_user = user.id)
INNER JOIN user_profil AS prf ON (user.id = prf.id)
GROUP BY user.id;
You can't. You have to separate query, if you need more than one acl row to one user row.
[EDIT]: But if you need to to it with one query then you should use somekindof bitwise operations. http://codingrecipes.com/how-to-write-a-permission-system-using-bits-and-bitwise-operations-in-php

Sphinx PHP One-to-Many Search

I have the following mysql tables:
CREATE TABLE `video` (
`video_id` int(11) unsigned NOT NULL auto_increment,
`title` varchar(255) NOT NULL default '',
`description` text NOT NULL,
PRIMARY KEY (`video_id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
CREATE TABLE `video_categories` (
`cat_id` int(11) unsigned NOT NULL auto_increment,
`name` varchar(255) NOT NULL default '',
PRIMARY KEY (`cat_id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
CREATE TABLE `video_category` (
`video_id` int(11) unsigned NOT NULL default '0',
`cat_id` int(11) unsigned NOT NULL default '0',
KEY `video_id` (`video_id`),
KEY `cat_id` (`cat_id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
CREATE TABLE `video_tags` (
`tag_id` int(11) unsigned NOT NULL auto_increment,
`video_id` int(11) unsigned NOT NULL default '0',
`name` varchar(255) NOT NULL default '',
KEY `video_id` (`video_id`),
KEY `name` (`name`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
I created a sphinx configuration file and i can search from PHP. The problem is when i want to search for related videos, a related video must be in the same category as the video i'm searching for. I can do this with MVA and and SetFilter('categories', array(3)) for example, however the total number of matches results is the global one (i need total to display pagination via ajax) not the one in the category.
Any ideas how i can search through videos (documents in sphinx) that are only in a specified category?
Thanks,
Adrian.
You can define for the category ID an integer attribute in Sphinx:
sql_attr_uint = cat_id
And then just add #cat_id=12345 to your query.

How do i serlialize the product using php?

i am building a real estate application where in it will store the properties and search it. the property will have different categories like (residential, commercial, industrial or agricultural). based upon the category i want to serailize each and every property listing . for example the property with id 1 belongs to resedential will have the serial code rs_SOMERANDOMUNIQUENUMBER. and for commercial it can be cm_SOMERANDOMUNIQUENUMBER and so on. for this my database table looks like this.
CREATE TABLE IF NOT EXISTS `propSerials` (
`id` bigint(20) NOT NULL auto_increment,
`serial` varchar(50) NOT NULL,
`property_id` int(10) UNIQUE NOT NULL,
PRIMARY KEY  (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
what would be the best possible format to store the serial with the prefix according to category?
thank you
Why dont you add another column that holds category_id and in category table add column with prefixes for that category.
CREATE TABLE IF NOT EXISTS `propSerials` (
`id` bigint(20) NOT NULL auto_increment,
`serial` varchar(50) NOT NULL,
`property_id` int(10) UNIQUE NOT NULL,
`category_id` int(10) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE IF NOT EXISTS `propCategories` (
`id` bigint(20) NOT NULL auto_increment,
`category` varchar(50) NOT NULL,
`property_prefix` char(3) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
In query you can:
SELECT CONCAT('prefix_', 'serial');

Categories