MySQL count and join - php

I'm building a tasks system wich has 4 tables:
tasks
id | user_id | end_date
-------------------------
2 | 1 | 2011-02-10
users
id | username
--------------
1 | johndoe
--------------
2 | janedoe
roles
id | role_name
--------------
1 | coordinator
and tasks_roles_users
id | task_id | user_id | role_id
---------------------------------
1 | 2 | 2 | 1
Each task has a creator (ie: johndoe is the owner of task #2), and each task has several users with different roles on that task, in my example "janedoe" is the task #2 coordinator.
I'm stuck trying to show to "janedoe" and "johndoe" how many due tasks they have, and I'm having this problem since "johndoe" hasn't a role in the task, he's just the task owner.
So how can I tell to both they have 1 task due?

You can accomplish this by doing a LEFT JOIN
SELECT u.id, u.username,
IFNULL(t.Cnt,0) OwnCount,
IFNULL(tr.Cnt,0) RoleCount
IFNULL(t.Cnt,0) + IFNULL(tr.Cnt,0) TotalCount
FROM users u LEFT JOIN (
SELECT user_id, COUNT(*) cnt
FROM tasks
GROUP BY user_id
) t ON u.id = t.user_id
LEFT JOIN (
SELECT user_id, COUNT(*) cnt
FROM tasks_roles_users
GROUP BY user_id
) tr ON u.id = tr.user_id
WHERE t.user_id IS NOT NULL OR tr.user_id IS NOT NULL

A simple way to do this is to add an owner role and treat it like any other role. Another way would be to use a UNION.

SELECT COUNT(*) FROM tasks_roles_users WHERE user_id = "2"
That will get all tasks that "janedoe" has a roll in.
If there aren't very many rolls (say, less that 8), you might want to save the rolls as constants in your code, instead of making SQL queries for such a small thing.
define('ROLL_COORDINATOR', 1); // just an integer unique from other roll constants

SELECT COUNT(*) FROM tasks_roles_users WHERE user_id IN (SELECT id FROM users WHERE username = 'johndoe')
SELECT COUNT(*) FROM tasks_roles_users WHERE user_id IN (SELECT id FROM users WHERE username = 'janedoe')

Related

How to get 3 Users on one ROW using SQL ? (MySQL)

I have two arrays like this,
First Table (infos):
------------------------------
| ID | User1 | User2 | User3 |
------------------------------
| 1 | 20 | 30 | 12 |
------------------------------
Second Table (Users):
---------------------
| ID | Name | Email |
---------------------
| 12 | Test | Test# |
---------------------
| 20 | Bla | Test# |
---------------------
| 30 | Bate | Test# |
---------------------
I want to get the information of users on one row from the IDs on the first table.
I try by getting The row from the first table and fetching on users, but I want to optimize the function with just one Query.
SELECT * FROM infos;
SELECT * FROM Infos i,Users u WHERE u.ID = u.User1 (or 2 ...)
Is there any solution ?
You could use joining the table users 3 times, one for each userid you want show the related name (or other values):
select a.id
, a.user1
, b.Name as user1name
, a.user2
, c.name as user2name
, a.user3
, d.name as user3name
from infos a
inner join Users b on a.user1 = b.id
inner join Users c on a.user1 = c.id
inner join Users d on a.user1 = d.id
And just as suggested, you should not use old implicit join syntax based on comma-separated table names and where clause, you should use (since 1992) explicit joins. This syntax performs the same query, but is more clear.
This is a design error. Use a N:N relation (an additional table) to allow any number of users for the first table. With the relation, other queries will be easier.
A relation table looks like this:
create table relation
(
table1_id int unsigned not NULL,
table2_id int unsigned not NULL,
primary key(table1_id,table2_id)
);
A typical query (and I dislike a.* generally):
select a.*, b.*
from table1 a, table2 b, relation r
where r.table1_id = a.id
&& r.table2_id = b.id

Check if row has specific childs on join

I want to make a SQL to get the user which name is Mark and are the author of the posts with ids 1 and 3.
NOTE: It is unknown how many posts I need to check for. So it might need to generate that part of the SQL query using PHP.
How can that be done?
Users Table:
+----+----------+
| id | name |
+----+----------+
| 1 | Mark |
| 2 | John Doe |
+----+----------+
Posts Table
+----+-------------+-------------+
| id | text | author_id |
+----+-------------+-------------+
| 1 | First Post | 1 |
| 2 | Second Post | 2 |
| 3 | Last Post | 1 |
+----+-------------+-------------+
This is just a sample case of use, not real data.
NOTE: I know how to check if user is author on one post, but not multiple in the same row. So basicly that is what I need help with, I guess it must be a left join.
For making the check for the user named Mark and check if he is author for post id 1 I do the following:
SELECT users.*
FROM users
INNER JOIN posts
ON users.id = posts.author_id
WHERE
users.name = 'Mark'
&&
posts.author_id` = 1
I just selected the id from users. If you need more columns then just add it to the select and the group by clause.
SELECT users.id
FROM users
INNER JOIN posts ON users.id = posts.author_id
WHERE users.name = 'Mark'
AND posts.author_id in (1,3)
GROUP BY users.id
HAVING count(distinct posts.author_id) = 2
Use a sub-query to find only users with both 1 and 3:
SELECT users.*
FROM users
WHERE users.name = 'Mark'
and 2 = (select count(distinct posts.id)
where users.id = posts.author_id
and posts.id IN (1,3))
SELECT users.name, posts.posts, posts.authorid
FROM users INNER JOIN posts ON users.id = posts.authorid where posts.authorid = 1
You need to use HAVING clause to achieve desired outcome:
SELECT users.name
FROM users
INNER JOIN posts
on users.id = posts.author_id
WHERE users.name = 'Mark'
GROUP BY users.name
HAVING COUNT(posts.author_id) > 1

Show "0" in COUNT column when WHERE does not match?

What I would like to do is retrieve all data from a table, and order them by the number of games the user played in a specific category. Is there any way I can use some sort of "COUNT WHERE" sql statement?
here's what i have so far. it will only return the user if they have played a game in the "fps" category, but I want it to show all users in descending order even if they have not played an fps game. please excuse my crappy tables
SELECT user_data.user, COUNT(played_games.game_cat) as 'count'
FROM user_data, played_games
WHERE user_data.user_id = played_games.user_id and played_games.game_cat = 'fps'
GROUP BY user_data.user_id
ORDER BY 'count' DESC;
user_data table
user_id | user
1 | jeff
2 | herb
3 | dug
played_games table
id | user_id | game | game_cat
1 | 2 | kill | fps
2 | 1 | shoot| fps
3 | 2 | COD | fps
4 | 3 | dogs | cas
You need a LEFT OUTER JOIN to get the records even if a corresponding record does not exist in the other table.
SELECT user, coalesce(count(game_cat), 0) as count
FROM user_data LEFT OUTER JOIN played_games
ON user_data.user_id = played_games.user_id AND played_games.game_cat='fps'
GROUP BY user_data.user_id
ORDER BY count desc;
Gives the following result on my screen
+------+-------+
| user | count |
+------+-------+
| herb | 2 |
| jeff | 1 |
| dug | 0 |
+------+-------+
This is how I'd do it. No subquery, no COALESCE, no COUNTIF junk.
SELECT `users`.`user`, COUNT(`played_games`.id) AS `c`
FROM `users`
LEFT OUTER JOIN `played_games` ON
`users`.`user_id` = `played_games`.`user_id`
AND `played_games`.`game_cat` = "fps"
GROUP BY `users`.`user_id`
ORDER BY `c` DESC, `user` ASC
SQLFiddle (not sure if you can link them like this...)
Try this:
SELECT ud.user, coalesce(sum(pg.game_cat = 'fps'), 0) Total
FROM user_data ud
LEFT JOIN played_games pg ON ud.user_id = pg.user_id
GROUP BY ud.user_id
ORDER BY Total DESC
This will show all users and the amount of times they've played a game with category 'fps'.
The coalesce one is promising, but doesn't work for me, sigh~ I just found NULLIF is a good way to solve this problem. Remember to use LEFT JOIN
COUNT( NULLIF(TABLE.ATTR, 1) ) AS total_count
The TABLE.ATTR is some field that can be NULL, here is an example:
SELECT Posts.*, COUNT( NULLIF(Comments.user_email, 1) ) as comment_num
FROM (`Posts`)
LEFT OUTER JOIN `Comments` ON `Comments`.`post_id` = `Posts`.`id`
GROUP BY `Posts`.`id`
LIMIT 5
Got the idea from http://www.bennadel.com/blog/579-SQL-COUNT-NULLIF-Is-Totally-Awesome.htm
Below query the all game category with user id and order by count
select * from (SELECT user_data.user, COUNT(played_games.game_cat) as 'count'
FROM user_data, played_games
WHERE user_data.user_id = played_games.user_id(+) GROUP BY user_data.user_id)
order by count desc

How to determine if two users share some information without mutliple queries

I'm trying to work out how to allow a user, [V], visiting another user's profile, [A], to see all the groups that user [A] is in and also which of the groups they're both part of.
The tables are:
USERS TABLE
user_id | name | email....
1 | Drent | drents... [V]
2 | Dude2 | dude2#... [A]
3 | Dude3 | dude3#...
GROUPS TABLE
group_id | group_name | joining_policy
1 | The Crazies | invite_only
2 | Team OSM | open
3 | My Group | approval_needed
GOUP_USERS TABLE
group_id | user_id
1 | 1
1 | 2
3 | 2
2 | 1
2 | 3
I can do a general query for all the groups user [A] is part of:
SELECT groups.group_name FROM groups JOIN group_users
ON groups.group_id=group_users.group_id WHERE group_users.user_id=2 LIMIT 0,10
Which would of course return this:
The Crazies - <a href="$row['group_id']?join=$my_user_id>Join This Group</a>
My Groups - <a href="$row['group_id']?join=$my_user_id>Join This Group</a>
But what I want is a way for [V] to see which groups they share and which they can join
For example:
The Crazies - You're already a member
My Groups - <a href="$row['group_id']?join=$my_user_id>Join This Group</a>
At the moment I can only think of doing this using a subquery for each row returned but I'm sure there's an easier, more efficient way to do it using another join or a WHERE IN but so far everything I've tried hasn't worked.
Something like:
SELECT groups.group_name FROM groups JOIN group_users
ON groups.group_id=group_users.group_id JOIN users AS visitor
ON visitor.user_id=group_users.user_id WHERE group_users.user_id=2 LIMIT 0,10
but I know this doesn't work.
Any help would be greatly appreciated.
SELECT g.*, guv.group_id IS NOT NULL AS is_member
FROM group_users gua
JOIN group g
ON g.id = gua.group_id
LEFT JOIN
group_users guv
ON guv.group_id = gua.group_id
AND guv.user_id = $v
WHERE gua.user_id = $a
SELECT A.group_name, (u.user_id is not null) as AlreadyAMember
FROM
(
SELECT groups.group_id, groups.group_name
FROM groups
JOIN group_users ON groups.group_id=group_users.group_id
WHERE group_users.user_id= $A
LIMIT 0,10
) A
LEFT JOIN group_users u ON u.group_id=A.group_id and u.user_id= $V
The column AlreadyAMember is a boolean type (true/false)

Mysql two table query

I'm using two tables. First (friendlist), which contains users who are on the list of friends and the other table (members) that contains the basic data of the users.
Friendlist looks like:
id | myid | date | user
-----------------------------------------
001 | 50624 | 2010-01-01 | 32009
002 | 41009 | 2010-05-05 | 50624
003 | 50624 | 2010-03-02 | 23007
The column "myid" contains members who added other users (those from column "user") to their frindlist. I want to get the list of all users that I have added to list and those who add me to their friendlist.
In this example, if my id is 50624, the list would look like:
| allfriends |
---------------
32009
41009
23007
Then I need to check all users from "allfriend" list with data from the table "members". I want to get only the users with status 1.
The members table looks like:
id | status | photo
--------------------------------
32009 | 0 | 1.jpg
41009 | 1 | 2.jpg
23007 | 1 | 3.jpg
How this mysql query should look like?
Thanks for any help.
SELECT id, status, photo FROM members WHERE id IN(
SELECT user FROM friendlist WHERE myid = 50624
UNION ALL
SELECT myid FROM friendlist WHERE user = 50624
) AND status = 1
SELECT user AS allfriends
FROM friendlist
INNER JOIN members
ON user = id
WHERE myid = 50624 AND status = 1
UNION
SELECT myid AS allfriends
FROM friendlist
INNER JOIN members
ON user = id
WHERE user = 50624 AND status = 1`
my friendlist:
select
members.*
from friendlist
inner join members
on members.id=friendlist.user
where friendlist.myid=50624 and members.status=1;
people who are friend with me
select
members.*
from friendlist
inner join members
on members.id=friendlist.myid
where friendlist.user=50624 and members.status=1;
To combine both results, use union
select
members.*
from friendlist
inner join members
on members.id=friendlist.user
where friendlist.myid=50624 and members.status=1
union
select
members.*
from friendlist
inner join members
on members.id=friendlist.myid
where friendlist.user=50624 and members.status=1;
Something like this should work:
SELECT ALLFRIENDS.id, MEMBERS.status FROM members MEMBERS
JOIN (SELECT id FROM (
SELECT myid AS id FROM friendlist WHERE user=50624
UNION
SELECT user AS id FROM friendlist WHERE myid=50624
) AS tmp
) AS ALLFRIENDS ON ALLFRIENDS.id = MEMBERS.id
WHERE MEMBERS.status = 1;

Categories