How do I query mysql data with array - php

I have 2 tables colorcode & users
colorcode
ID colorid colorname
------------------------
1 1 yellow
2 2 black
3 3 red
4 4 white
users
ID userid colorid
------------------------
1 1 1,2
2 2 3,4
3 3 1,3,4
4 4 1
How do I retrieve & query individual colorid
$aa = $db->query("SELECT * FROM colorcode");
$colors = array();
while ($colordata = mysql_fetch_assoc($aa)) {
$colors[] = $colordata["colorid"];
}
Let's say I want query which users have yellow color & what it's the statement should I use for users
SELECT .. FROM users
WHERE colorid ....

It's a bad design... since you're trying to access the individual color_ids in the user table, but have stored them as a comma-separated list, you canot have the database do a normal join for you - you've killed off the main point of using a relational database by making it impossible to for the database to do the relating for you.
However, since you're on mysql, you're in luck - mysql has a function for cases like this:
SELECT users.ID, userid, GROUP_CONCAT(colorcode.colorname)
FROM users
LEFT JOIN colorcode ON FIND_IN_SET(colorcode.ID, users.colorid)
GROUP BY users.id

SELECT * FROM users
WHERE colorid LIKE "%1%"
But what I would really do is make a link table from users to colors:
usersToColors:
ID userid colorid
------------------------
1 1 1
2 1 2
3 2 3
4 2 4
...
Then you could do:
SELECT * FROM users u, usersToColors utc
WHERE u.userid = utc.userid
AND utc.colorid = 1;
Or even:
SELECT * FROM users u, usersToColors utc, colors c
WHERE u.userid = utc.userid
AND utc.colorid = c.colorid
AND c.colorname = "yellow";

Related

Optimal way to store follower and following MySQL

I have 2 tables one is user and another is company. Both have the following columns common while other columns are different.
id email name
1 first#email.com First user
2 second#email.com Second User
3 third#email.com Third User
Now a user can follow many companies but a company does not follow back user. Company should have data with users who follow them and a user should also store the companies they are following.
What is the simplest and optimal to make this happen on MySQL? Either should I create separate table for it or just add a column on existing tables with array of followers. Please answer assuming I have working level of knowledge on PHP and MySQL.
You have the tables User and Company. Let's suppose they contain these values:
User
UserId
UserName
UserMail
1
Alice
alice#mail.com
2
Bob
bob#mail.com
Company
CompanyId
CompanyName
CompanyAddress
1
Microsoft
Redmond
2
Apple
Cupertino
3
Google
Mountain View
Then you should create a many-to-many table - e.g. let's call it UserCompany - which contains at least the columns UserId and CompanyId (coming from the first two tables).
Let's suppose that Alice follows Microsoft and Apple, and Bob follows Google. Your new UserCompany table will contain the following data:
UserId
CompanyId
1
1
1
2
2
3
Company should have data of users who follow them
Here you are the query to get the data of the users that follow Microsoft:
SELECT u.UserName, u.UserMail
FROM User AS u
JOIN UserCompany AS uc ON u.UserId = uc.UserId
WHERE uc.CompanyId = 1
User should also have data of companies they are following.
And here you are the query to get the data of the companies followed by Alice:
SELECT c.CompanyName, c.CompanyCity
FROM UserCompany AS uc
JOIN Company AS c ON c.CompanyId = uc.CompanyId
WHERE uc.UserId = 1
The simplest solution is to have a MANY-TO-MANY join table to link these 2 tables. You can do this with either 2 MANY-TO-MANY tables or a single one.
For example 2 table:
user_follows_company
ID CompanyID UserID
---- ------ ---------
1 1 5
1 1 6
1 1 8
1 2 5
You can get the companies a user follows by using the following SQL:
SELECT * FROM company c JOIN user_follows_company ufc on ufc.companyID = c.ID WHERE user = $USER_ID
company_follows_user
ID CompanyID UserID
---- --------- ---------
1 3 5
1 3 6
1 5 3
1 5 4
You can get the users a company follows by using the following SQL:
SELECT * FROM user u JOIN company_follow_user cfu on cfu.userID = u.ID WHERE user = $USER_ID
or you can do this with a single table and have a column that designates which direction is being followed:
follows_table
ID CompanyID UserID Initiator
---- --------- --------- ---------
1 3 5 Company
1 3 6 Company
1 5 3 Company
1 5 4 Company
1 5 1 User
1 6 1 User
1 8 1 User
1 5 2 User
Note that storing the "initiator" as a string is not a good idea - it probably should be an int or an ENUM
To query on this table, do the following queries:
Companies a user follows:
SELECT * FROM company c JOIN follows_table ft on ft.userID = c.ID WHERE user = $USER_ID AND ft.initiator = 'user'
Users a company follows:
SELECT * FROM user u JOIN follows_table ft on ft.userID = u.ID WHERE companyID = $$COMPANY_ID AND ft.initiator = 'company'

" People Who Liked this Also Liked " Query in Mysql PHP

Music table
id | title
1 Rap God
2 Blank Space
3 Bad Blood
4 Speedom
5 Hit 'em up
Like table
u_id | m_id
1 1
1 2
1 4
1 5
2 3
2 4
2 5
3 1
3 5
4 1
4 2
4 5
Now if someone visits music with m_id = 1
Then the output might be like
m_id
5
2
4
To explain this a bit...
As m_id = 1 is liked by users -> {1,3,4} which in turn likes ->{2,4,5} musics. Since m_id=5 is liked by max number of users its first followed by m_id = 2 and m_id = 4.
My Try
I queried the users who liked m_id = 1
SELECT u_id FROM likes WHERE m_id =1
Then i stored in in an array and selected each of their likes and
arranged them in desc order of count.
But it is a very slow and long process is there any way i can do this ?
p.s I have heard of Association Rules and Bayesian theorem can be user to achieve this. But can anyone help me out with an example ?
You can JOIN back on the Like table and do something like this.
SELECT also_like.m_id, COUNT(also_like.m_id)
FROM [like] AS did_like
JOIN [like] AS also_like ON
also_like.u_id = did_like.u_id
AND also_like.m_id != did_like.m_id
WHERE did_like.m_id = 1
GROUP BY also_like.m_id
ORDER BY COUNT(also_like.m_id)
Essentially you are getting a list of users who liked an item then getting a complete list of those user's likes excluding the item they just liked.
You can then add a HAVING clause or LIMIT to filter things down a bit more.
using a subquery ...
SELECT m_id, count(u_id) as Rank FROM `like`
WHERE u_id in
(
SELECT u_id
FROM `like`
WHERE m_id = 1
)
AND m_id <> 1
GROUP BY m_id
ORDER BY Rank DESC
and optionally
LIMIT 0, 10
or how many "alsolikes" you want to display

Mysql query with IF

I have two tables
Users
Id user grade access_level
1 Alice 1 1
2 Charles 3 3
3 Ben 2 2
Class
Id name grade
1 biology 1
2 math 2
3 geography 2
I can read $username = $_SESSION['MM_Username'] and I want to select for each logged user a list of classes depending on their access_level. For access_level => 3 the user can see all the classes, for access_level < 3 user can see only classes with the same grade with his/hers. So:
For Charles, with access_level=3, I want this result
Id name grade
1 biology 1
2 math 2
3 geography 2
For Alice, with access_level=1 and grade = 1, I want this result
Id name grade
1 biology 1
And for Ben, with access_level=2 and grade = 2, I want this result
Id name grade
1 math 2
2 geography 2
Added from the comment
They have the access_levels based on their position in school. Let's say that Charles is a professor and Alice and Ben are students. Students can see only their classes, but the teacher can see everything.
You could use a INNER JOIN with OR:
SELECT Class.*
FROM
Class INNER JOIN Users
ON Class.grade = Users.grade
OR Users.access_level>=3
WHERE
User='username';
Please see fiddle here.
Try this.
SELECT *
FROM Users as U, Class as C
WHERE U.user = $username
AND U.access_level>=C.grade
I'm not quite sure what are you going to achieve, According to my understanding you can do something like this below.
SELECT u.user,c.name
FROM user AS u INNER JOIN class AS c ON u.grade = c.grade
WHERE u.access_level = 1 AND u.grade = 1
SELECT u.user,c.name
FROM user AS u INNER JOIN class AS c ON u.grade = c.grade
WHERE u.access_level = 2 AND u.grade = 2
SELECT u.user,c.name
FROM users AS u INNER JOIN class AS c ON u.grade = c.grade
WHERE u.grade != 3

PHP MySQL Query

Currently I have 3 MySQL tables, one filed with the users information, one with their course history and the last one filled with their reward history.
I am attempting to create a "rewards" program if you will, using these two databases and a PHP script. As far as the reward requirements, I have 2 arrays setup for the two types of rewards:
//Pen Reward with course A & B required
$pen=array("a","b")
//Cup Reward with course B & C required
$cup=array("b","c")
I can't figure out how to first query for all user IDs, then go through their history. If they match the requirements set above, then to update the reward table to 1 (to show that they received the award)
UsersDB
userid name
----------------
1 bill
2 john
3 steve
HistoryDB
userid courseid
------------------
1 a
1 b
2 b
3 a
3 c
RewardHistoryDB
userid pen cup
---------------------
1 0 0
2 0 0
3 0 0
Ideally it after the PHP script runs, it would update the RewardHistoryDB to reflect that user 1 received a pen, and user 3 received a cup.(the DB would look like this:)
RewardHistoryDB (after)
userid pen cup
---------------------
1 1 0
2 0 0
3 0 1
Here is the PHP code I am using right now (not complete):
$userid=array();
$i=0;
//Find All users
$result = mysqli_query($con,"SELECT userid FROM usersdb")
while ($row = mysqli_fetch_array($result)){
$universityid[$i]=$row['universityid'];
$i++;
}
//Find History
$courseid=array();
foreach ($userid as $userid1){
$result = mysqli_query($con,"SELECT courseid FROM historydb WHERE userid='".$userid1."'");
while ($row = mysqli_fetch_array($result)){
$courseid[]=$row;
}
}
//Update Reward DB
//Don't even know where to start...
Any help would be greatly appreciated.
If you have any questions, please let me know.
Thank you in advanced!
http://sqlfiddle.com/#!2/1a96b/1/0
You can do this just as queries and without doing any logic in PHP
update RewardHistoryDB
set Pen = 1
where UserID in (
select UserID
from HistoryDB
where CourseID in ('A','B')
group by UserID
having count(UserID)=2);
update RewardHistoryDB
set Cup = 1
where UserID in (
select UserID
from HistoryDB
where CourseID in ('B','C')
group by UserID
having count(UserID)=2);
Or you can even do it in one, large statement...
update RewardHistoryDB
left outer join (
select UserID as Pens_UserID
from HistoryDB
where CourseID in ('A','B')
group by UserID
having count(UserID)=2 ) as Pens
on RewardHistoryDB.UserID = Pens.Pens_UserID
left outer join (
select UserID as Cups_UserID
from HistoryDB
where CourseID in ('B','C')
group by UserID
having count(UserID)=2 ) as Cups
on RewardHistoryDB.UserID = Cups.Cups_UserID
set Pen = case when Pens.Pens_UserID is not null then 1 else 0 end,
Cup = case when Cups.Cups_UserID is not null then 1 else 0 end

Help with limiting a joined mysql database query

I have written a query which returns all records with some many-to-many joins correctly for the entire set or an individual article using WHERE a.id = ?
SELECT a.id, date_added, title, content, category_id, person_id, organization_id, c.name AS category_name, firstname, lastname, o.name AS organization_name
FROM articles AS a
LEFT OUTER JOIN articles_categories AS ac ON a.id=ac.article_id
LEFT OUTER JOIN categories AS c ON c.id=ac.category_id
LEFT OUTER JOIN articles_people AS ap ON a.id=ap.article_id
LEFT OUTER JOIN people AS p ON p.id=ap.person_id
LEFT OUTER JOIN articles_organizations AS ao ON a.id=ao.article_id
LEFT OUTER JOIN organizations AS o ON o.id=ao.organization_id
ORDER BY date_added
BUT!
I've hit a brick wall trying to work out how to limit the articles to a specific number of IDs, for working with pagination.
I'm ideally trying to use as simple and clear SQL statements as possible because I'm using the codeigniter framework with their active record class.
http://codeigniter.com/user_guide/database/active_record.html
Would really appreciate some help as I don't want to revert to using multiple queries for this as I've tried to reduce it down to a single query for database efficiency.
Have search around and tried some alternatives but nothing seems to work. Many thanks!
For example the results I return are like this
---------------------------------------------------------------------
id title category_id person_id organization_id
---------------------------------------------------------------------
1 test 1 1 1
1 test 2 1 1
1 test 1 2 1
1 test 1 1 2
1 test 5 1 1
1 test 8 1 1
1 test 1 4 1
1 test 1 4 2
1 test 1 1 1
2 test 2 2 1 1
2 test 2 1 2 1
2 test 2 1 1 2
2 test 2 5 1 1
2 test 2 8 1 1
2 test 2 1 4 1
2 test 2 1 4 2
I need the results like this so that I can create sub-arrays in the php like this:
$articles = $query->result_array();
$output = array();
foreach ($articles as $article) {
// set up article details
$article_id = $article['id'];
// add article details
$output[$article_id]['article_id'] = $article_id;
$output[$article_id]['date_added'] = $article['date_added'];
$output[$article_id]['title'] = $article['title'];
$output[$article_id]['content'] = $article['content'];
// set up people details and add people array with details if exists
if (isset($article['person_id'])) {
$person_id = $article['person_id'];
$output[$article_id]['people'][$person_id]['person_id'] = $person_id;
$output[$article_id]['people'][$person_id]['lastname'] = $article['lastname'];
$output[$article_id]['people'][$person_id]['firstname'] = $article['firstname'];
}
// set up organizations details and add organizations array with details if exists
if (isset($article['organization_id'])) {
$organization_id = $article['organization_id'];
$output[$article_id]['organizations'][$organization_id]['organization_id'] = $organization_id;
$output[$article_id]['organizations'][$organization_id]['organization_name'] = $article['organization_name'];
}
// set up categories details and add categories array with details if exists
if (isset($article['category_id'])) {
$category_id = $article['category_id'];
$output[$article_id]['categories'][$category_id]['category_id'] = $category_id;
$output[$article_id]['categories'][$category_id]['category_name'] = $article['category_name'];
}
}
But if I just use LIMIT (with offset etc) 1
the results I get are
---------------------------------------------------------------------
id title category_id person_id organization_id
---------------------------------------------------------------------
1 test 1 1 1
instead of
---------------------------------------------------------------------
id title category_id person_id organization_id
---------------------------------------------------------------------
1 test 1 1 1
1 test 2 1 1
1 test 1 2 1
1 test 1 1 2
1 test 5 1 1
1 test 8 1 1
1 test 1 4 1
1 test 1 4 2
1 test 1 1 1
which is my desired result.
OK, so finally I worked out how it is possible.
Thought i'd include it here in case anyone else has the same problem.
Changing this line
FROM articles AS a
to this
FROM (SELECT * FROM articles LIMIT 5,3) AS a
does what I wanted.
So, why don't you use OFFSET 0,10 and LIMIT *number_of_results* in the SQL Query? (if I understood the question)
Specific number of IDs... WHERE ID IN (2,4,6,8)... ?
Are you using codeigniter's pagination?
http://codeigniter.com/user_guide/libraries/pagination.html
You can easily limit the number of records that are being returned using the MySQL LIMIT clause. This can be achieved like the following with your sample query.
SELECT a.id, date_added, title, content, category_id, person_id, organization_id, c.name AS category_name, firstname, lastname, o.name AS organization_name
FROM articles AS a
LEFT OUTER JOIN articles_categories AS ac ON a.id=ac.article_id LEFT OUTER JOIN categories AS c ON c.id=ac.category_id
LEFT OUTER JOIN articles_people AS ap ON a.id=ap.article_id LEFT OUTER JOIN people AS p ON p.id=ap.person_id
LEFT OUTER JOIN articles_organizations AS ao ON a.id=ao.article_id LEFT OUTER JOIN organizations AS o ON o.id=ao.organization_id
ORDER BY date_added
LIMIT 10
Where 10 is the number of records you wish to display. The MySQL LIMIT clause allows you to specify a limit of the number of records and an initial offset. Like so:
LIMIT <offset>,<limit>
In your case <offset> would be the current page * the number of records on a page. <limit> would be the number of records you would like to display per page.

Categories