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
Related
why does this SQL Code not run?
user_chats
id | user_id | to_user_id | ad_id | timestamp
----------------+---------+------------+---------+-----------
1 | 1 | 6 | 13 | 1513516133
user_messages
id | chat_id | text | user_id | timestamp
----------------+---------+------------+---------+-----------
1 | 1 | Hello | 1 | 1513516133
2 | 1 | Hi! | 6 | 1513516754
I want to get the Chats and order them by user_messages.timestamp.
My SQL Code is:
SELECT user_chats.id,
user_chats.timestamp,
ad_id,
title,
user_chats.user_id
FROM user_chats
INNER JOIN ads
ON ads.id = ad_id
WHERE user_chats.user_id = "1"
OR user_chats.to_user_id = "1"
ORDER BY (SELECT id
FROM user_messages
WHERE chat_id = user_chats.id
ORDER BY user_messages.id DESC)
The issue is that you've used a subquery in your Order By clause: as this returns multiple results for each record in the main query it cannot be used to order the results of the main query.
I think you're trying to order the results by the latest message in each chat, but simply joining the user_messages table will mean you'll get duplicates (each chat being returned once per message). You can get around this by joining to an inline view:
SELECT DISTINCT user_chats.id,
user_chats.timestamp,
ad_id,
title,
user_chats.user_id
FROM user_chats
INNER JOIN ads
ON ads.id = ad_id
LEFT JOIN
--in line view aliased 'UM' returns one row per chat_id in user_messages, with the last timestamp for that ID
(SELECT max(timestamp) LastMessage,
chat_id
FROM user_messages
GROUP BY chat_id) um
ON um.chat_id = user_chats.id
WHERE user_chats.user_id = 1
OR user_chats.to_user_id = 1
ORDER BY um.LastMessage desc
I have a MySQL table of comments, like this:
+------------+------------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+------------+------------------+------+-----+---------+----------------+
| userid | int(11) | NO | | 0 | |
| comment | char(255) | NO | | NULL | |
| content | int(11) | NO | MUL | 0 | |
| ratings | int(11) | NO | | 0 | |
| datetime | datetime | NO | | NULL | |
| ip | int(10) unsigned | NO | | NULL | |
| is_updated | tinyint(2) | NO | | 0 | |
| record_num | int(11) | NO | PRI | NULL | auto_increment |
+------------+------------------+------+-----+---------+----------------+
Now I can fetch comments from this and usernames from another table using an INNER JOIN query like this.
I can get the top the three comments ORDER BY comments.ratings DESC:
SELECT comments.userid, users.username, comments.comment, comments.ratings, comments.datetime, comments.record_num , content.uploader , content.anonymous
FROM comments
LEFT JOIN users ON comments.userid = users.record_num
LEFT JOIN content ON comments.content = content.record_num
WHERE comments.content = ? ORDER BY comments.ratings DESC limit 3
and
Getting regular comments ORDER BY comments.datetime DESC
SELECT comments.userid, users.username, comments.comment, comments.ratings, comments.datetime, comments.record_num , content.uploader , content.anonymous
FROM comments
LEFT JOIN users ON comments.userid = users.record_num
LEFT JOIN content ON comments.content = content.record_num
WHERE comments.content = ? ORDER BY comments.datetime DESC limit ?, ?
I am trying show users the top three comments by their ratings at first and then regular comments order by comments.datetime DESC .
Now how can I join the above two MySQL queries into one?
As the other answer says - you can combine results using union - which simply means to join two results together. What is important to note, however, is that you can't simply union those two queries together directly, as they use order by, so we need to wrap them in outer queries, and use a ranking variable to ensure we can reconstruct the union in the order that we want:
select * from (
SELECT 1 as tbl,
comments.userid,
users.username,
comments.comment,
comments.ratings,
comments.datetime,
comments.record_num,
content.uploader,
content.anonymous,
#rank := #rank + 1 as rank
FROM comments
LEFT JOIN users ON comments.userid = users.record_num
LEFT JOIN content ON comments.content = content.record_num
CROSS JOIN (select #rank := 0 ) q
WHERE comments.content = ? ORDER BY comments.ratings DESC limit 3
) q1
UNION ALL
select * from (
SELECT 2 as tbl,
comments.userid,
users.username,
comments.comment,
comments.ratings,
comments.datetime,
comments.record_num,
content.uploader,
content.anonymous,
#rank := #rank + 1 as rank
FROM comments
LEFT JOIN users ON comments.userid = users.record_num
LEFT JOIN content ON comments.content = content.record_num
CROSS JOIN (select #rank := 0 ) q
WHERE comments.content = ? ORDER BY comments.datetime DESC limit ?, ?
) q2
ORDER BY tbl asc, rank asc;
union by default is distinct meaning that it won't duplicate rows found in both result sets, however there is also no guarantee that the rows will be returned in the order you expect, hence the need to mark each table with its own tbl value, and then order by that field.
If you were certain there would be no duplicates, you could eliminate the duplicate check by using union all, instead of union
To answer your question, yes you can combine your queries using UNION, but
I am trying show users the top three comments by their ratings at first and then regular comments order by comments.datetime DESC
If your are trying to achieve something like the functionality of comments of Stack Overflow, where the top comments (or comments with upvotes) are showed first, then I think separate queries are good enough.
If your query is combined, then the user is required to load all the top and regular comments. Also, it is somehow hard to make or implement a pagination with big query that have UNION (based on my experience because I'm still a noob on coding).
Disclaimer: it is hard, but not impossible
If I'm the one who is going to code that problem, first my program will load the top three comments. After that, there is a 'Show more comments' button that will post to my server using Ajax and append the result (regular comments) to the list of showed comments. With that, queries need to be separate like what you have now.
To stack result sets on top of each other into one set, you need to use a "UNION" - https://dev.mysql.com/doc/refman/5.0/en/union.html
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
I have two tables with like below codes:
Table: Accounts
id | username | email | registered
1 | player1 | 123#asd.ad| 2012-05-03
2 | goodman | 1345#bs.ad| 2012-06-03
3 | goodbat | asdh#asd.d| 2012-06-05
table:characters
guid | account | name | rank
213 | 1 | fres | 2
214 | 2 | sdg2 | 3
215 | 1 | fgax | 4
216 | 3 | zFvx | 8
217 | 3 | zsvx | 2
...
I want to show accounts with their highest rank character with just one Query .
output (Show Accounts with their Highest rank character)
username : player1 | 123#asd.ad | char: fgax(4)
username : goodman | 1345#bs.ad | char: sdg2(3)
username : goodbat | 134s#bs.ad | char: zFvx(8)
...
My Query:
SELECT username,email,id FROM accounts
You can try this:
SELECT a.username, a.email, c.name
FROM Accounts a, characters c
WHERE a.id=c.account
ORDER BY MAX(c.rank) DESC
SELECT a.username, a.email, a.id, c.name
FROM accounts a
JOIN chars c ON a.id = c.account
ORDER BY a.rank DESC
Try this::
SELECT
a.username as userName,
a.email as email,
a.id as id,
c.name as name
FROM accounts a
INNER JOIN chars c ON (a.id = c.accoun)t
ORDER BY a.rank DESC
What you need is a table join. What table joins do is that they query 2 tables, in your case Table1 and table2. Here is the query and it will be followed by the explanation.
SELECT `t1`.`username`, `t1`.`email`, `t2`.account
FROM `Table1` `t1`, `table2` `t2`
WHERE `t1`.`id` = `t2`.`account` ORDER BY `rank` DESC
LIMIT 0,3
LINE 1: SELECT...
t1.username means username column from t1., and so on...and the t1, t2 notation comes from...
LINE 2: FROM...
Table1 t1 means I am querying from Table1, and assigned it an alias or pointer t1. This is the same for how we assign table2 to t2.
LINE 3: WHERE...
This means that you join the tables based on a common column. In this case, both Table1 and table2 share the same set of values - id for Table1 and account for table2. Hence, you used the above line.
ORDER BY...
This means you want to arrange the records based on a certain criteria, in this case rank. There are 2 directions - ASC which means Ascending (smallest top) or DESC which means Descending.
LINE 4: LIMIT 0,3
This means I want to get the first row (0) and only 3 records. Hence, LIMIT 0,3
This is the most comprehensive explanation I could give.
Try this.
SELECT
t1.username as userName,
t1.email as email,
concat(t2.name, '(', t2.rank, ')') as name
FROM table1 t1
INNER JOIN table2 t2 ON t1.id = t2.account
ORDER BY t2.rank DESC
Supposed I have a table posts like below:
| id | type | ref_id |
-----------+--------------+ ---------------
| 1 | 1 | 1 |
| 2 | 1 | 2 |
| 3 | 3 | 3 |
| 4 | 1 | 4 |
| 5 | 2 | 5 |
| 6 | 3 | 6 |
The type field is to join with other tables, so for example 1=blogs, 2=reviews, 3=photos, and each of them have respective fields, while ref_id is the ID of the blog/review/photo.
What i want to do is to show all latest posts (order by id DESC), while joining with all other tables based on the value of the type, but I want to do it with one query if possible. My no-good query is below:
SELECT * FROM posts
LEFT JOIN blogs ON posts.ref_id = blogs.ID AND posts.type = 1
LEFT JOIN reviews ON posts.ref_id = reviews.ID AND posts.type = 2
LEFT JOIN photos ON posts.ref_id = photos.ID AND posts.type = 3
ORDER BY posts.id DESC
The query doesn't result in anything. Another alternative is to loop the SELECT * FROM posts query and do another query based on the type, but if possible, I'd like to do it in 1 go.
Thanks for the help.
To see why your query does not return anything, consider moving the " AND posts.type = " from JOIN to WHERE clause. It will be:
select * from posts ... where type=1 and type=2 and type=3
Obviously, it will return no results. I know it is not the answer to your question, but I would consider a small change in your table definitions:
(table name: fields)
posts: id
blogs: id, post_id
reviews: id, post_id
photos: id, post_id
In this case you will be able to construct simpler and more traditional queries:
select * from posts
left join blogs on blogs.post_id = posts.id
left join reviews on reviews.post_id = posts.id
left join photos on photos.post_id = posts.id
I guess you want something like this:
( SELECT posts.*, blogs.*
FROM posts
JOIN blogs
ON posts.ref_id = blogs.ID
AND posts.type = 1
UNION ALL
SELECT posts.*, reviews.*
FROM posts
JOIN reviews
ON posts.ref_id = reviews.ID
AND posts.type = 2
UNION ALL
SELECT posts.*, photos.*
FROM posts
JOIN photos
ON posts.ref_id = photos.ID
AND posts.type = 3
)
ORDER BY posts.id DESC