Query to get grouped items ranked? - mysql

I have a table of hits for various users:
HITS:
id | userid
1 | 1
2 | 1
3 | 2
4 | 1
5 | 2
6 | 2
I want the fastest possible way to get a list of these items ranked by ID. So this:
HITS RANKED:
id | userid | ranks
1 | 1 | 1
2 | 1 | 2
3 | 2 | 1
4 | 1 | 3
5 | 2 | 2
6 | 2 | 3
I want to avoid joining two tables to each other, as this takes forever when the tables get big. Any other suggestions?

SELECT ID,
UserID,
Ranks
FROM
(
SELECT id,
userid,
#group:=CASE WHEN #temp <> userid THEN 1 ELSE #group+1 END AS ranks,
#temp:=userid AS clset
FROM (SELECT #group:= 0) s,
(SELECT #temp:= 0) c,
(SELECT * FROM hits ORDER BY userid, id) t
) x
ORDER BY ID
SQLFiddle Demo

Related

MySQL - Count number of rows in each group

I have a SQL table user_game which contains the games that a user owns:
| id | user_id | game_id |
|----|---------|---------|
| 83 | 1 | 1 |
| 84 | 1 | 2 |
| 85 | 1 | 3 |
| 86 | 2 | 2 |
| 87 | 2 | 3 |
| 88 | 2 | 4 |
| 89 | 3 | 2 |
I am trying to count the number of users which have 1 game, 2 games, 3 games.. etc.
User 1 has 3 games, User 2 has 3 games, and User 3 has 1 game. Therefore these are the results I want to achieve:
| no_of_games | COUNT(no_of_games) |
|-------------|--------------------|
| 1 | 1 |
| 2 | 0 |
| 3 | 2 |
COUNT(no_of_games) is the number of users that have that number of games.
I can individually get the number of users for each no_of_games with this query:
-- Select no. of users with 1 game
SELECT no_of_games, COUNT(no_of_games)
FROM
(
-- Select no. of games each user has
SELECT user_id, COUNT(1) as no_of_games
FROM user_game
GROUP BY user_id
) as A
WHERE no_of_games = 1;
which gives the results:
| no_of_games | COUNT(no_of_games) |
|-------------|--------------------|
| 1 | 1 |
However I have to change the no_of_games = 1 to 2, 3, 4... manually and UNION them with this solution and I can't do it for ~60 cases.
Is there a simpler way to achieve this?
Your problem is a bit tricky, because groups of games which do not appear in your data with a certain frequency (e.g. 2) will not appear in the result set just using your original table. In the query below, I use a second table called nums which simply contains the sequence 1 through 10 representing counts of number of games. By using a LEFT JOIN we can retain each game count in the final result set.
SELECT t1.no_of_games,
COALESCE(t2.no_of_games_count, 0) AS no_of_games_count
FROM nums t1
LEFT JOIN
(
SELECT t.no_of_games, COUNT(*) AS no_of_games_count
FROM
(
SELECT COUNT(*) AS no_of_games
FROM user_game
GROUP BY user_id
) t
GROUP BY t.no_of_games
) t2
ON t1.no_of_games = t2.no_of_games
ORDER BY t1.no_of_games
And here is the definition I used for nums:
CREATE TABLE nums (`no_of_games` int);
INSERT INTO nums (`no_of_games`)
VALUES
(1),(2),(3),(4),(5),(6),(7),(8),(9),(10);
Demo here:
SQLFiddle
You can find count of games for each user and then find count of users for each count of games.
select cnt no_of_games, count(*) cnt_no_of_games
from(
select user_id, count(*) cnt
from your_table
group by user_id
) t group by cnt;

Check multiple columns for duplicate and list all records

I have a table with columns ID, Content and Day. I am trying to find all rows that have duplicate Content and Day values and display all rows
SELECT ID,Content, `Day`, Count(*)
FROM table
GROUP BY Content,`Day`
HAVING COUNT(*) > 1
The current code will return a list of duplicate Content and 'Day' values for instance:
ID|Content|Day
1 | a | 1
2 | a | 1
3 | a | 1
4 | b | 2
5 | b | 2
6 | c | 3
7 | c | 4
Will result in:
ID|Content|Day|Count
1 | a | 1 | 3
4 | b | 2 | 2
But I want to display all the unique IDs as well;
ID|Content|Day
1 | a | 1
2 | a | 1
3 | a | 1
4 | b | 2
5 | b | 2
Just make a Sub-Query
select *
from table
where `day` in
(
SELECT ID
FROM table
GROUP BY Content,`Day`
HAVING COUNT(*) > 1
) A
Use that query as a subquery to join against the table again:-
SELECT table.ID, table.Content, table.`Day`
FROM table
INNER JOIN
(
SELECT Content, `Day`, Count(*)
FROM table
GROUP BY Content,`Day`
HAVING COUNT(*) > 1
) sub0
ON sub0.Content = table.Content
AND sub0.`Day` = table.`Day`

Group by gives wrong title

I've got this database structure:
id | parent_id | name | num_begin | num_changes
====|===========|============|===========|=============
1 | 1 | 0001-0001 | 10 | 0
2 | 2 | 0001-0002 | 15 | 2
3 | 3 | 0001-0003 | 20 | 0
4 | 3 | testgroup | 5 | 1
The user can make groups and subgroups. The subgroup get's a parent_id. The testgroup in the above table is a subgroup of 3.
Now I want to select the current numbers (num_begin - num_changes). So I made this query:
SELECT
name,
( SUM( num_begin ) - SUM( num_changes ) ) AS num_now
FROM
groups
GROUP BY
parent_id
ORDER BY
id DESC
The result is:
name | num_now
============|===========
0001-0001 | 10
0001-0002 | 13
testgroup | 24
But I would like to have the name of the original group. So instead of testgroup it should be 0001-0003
Any thoughts?
Do a join on parents:
select p.name,
sum(c.num_begin) - sum(c.num_changes) as num_now
from groups c
join groups p on c.parent_id = p.id
group by p.id, p.name
When joining on parent you are getting the following results:
---------------------c-------------------------- | ----------p-------------...
id parent_id name num_begin num_changes | id parent_id name
1 1 0001-0001 10 0 | 1 1 0001-0001
2 2 0001-0002 15 2 | 2 2 0001-0002
3 3 0001-0003 20 0 | 3 3 0001-0003
4 3 testgroup 5 1 | 3 3 0001-0003
No you are grouping by p and calculating c.

Get other user_ids which have same mid

I want to search user_id of 1 in table below and get other user_ids related to same mid ( in this case mid of 1 & 3)
mid | user_id
1 1
1 2
1 3
2 2
2 3
2 5
3 1
3 5
3 2
The result must be :
mid | user_id
1 1
1 2
1 3
3 1
3 5
3 2
How is it done with MySQL query ?
Assuming I understood correctly, you want to first find all mid values that have a user_id value of 1, then get all user_id values from all those previously gotten mid values.
SELECT mid, user_id from table
where mid IN (SELECT mid FROM table WHERE user_id = 1)
Assuming that you want to get all rows for those mid that matches a user_id this should do what you want:
select * from your_table t1
where exists (
select 1 from your_table t2
where user_id = 1
and t1.mid = t2.mid
)
Sample SQL Fiddle
Result given your sample data:
| MID | USER_ID |
|-----|---------|
| 1 | 1 |
| 1 | 2 |
| 1 | 3 |
| 3 | 1 |
| 3 | 5 |
| 3 | 2 |

Remove duplicates from one column keeping whole rows

id | userid | total_points_spent
1 | 1 | 10
2 | 2 | 15
3 | 2 | 50
4 | 3 | 5
5 | 1 | 15
With the above table, I would first like to remove duplicates of userid keeping the rows with the largest total_points_spent, like so:
id | userid | total_points_spent
3 | 2 | 50
4 | 3 | 5
5 | 1 | 15
And then I would like to sum the values of total_points_spent, which would be the easy part, resulting in 70.
I am not really sure the "remove" you meant is to delete or to select. Here is the query for select only max totalpointspend record respectively.
SELECT tblA.*
FROM ( SELECT userid, MAX(totalpointspend) AS maxtotal
FROM tblA
GROUP BY userid ) AS dt
INNER JOIN tblA
ON tblA.userid = dt.userid
AND tblA.totalpointspend = dt.maxtotal
ORDER BY tblA.userid