MYSQL SELECT rank of user (more than x & less than y) - php

I'm a bit stuck with a php/Mysql Query. I got 2 tables :
table_users table_ranks
---------------- ------------------------
| id | points | | name | points_needed |
---------------- ------------------------
| 1 | 2 | | lvl0 | 0 |
| 2 | 10 | | lvl1 | 10 |
| 3 | 21 | | lvl2 | 20 |
| 4 | 29 | | lvl3 | 30 |
---------------- ------------------------
I need an ouput like this :
User_1 = lvl0 (because user has 2 points)
User_2 = lvl1 (because user has just reached 10 points)
...
User_4 = lvl2 (because user has not yet reached 30 points)
Think you :)
Regards.

You can do it like this
SELECT
tu.id,
tr.name,
tu.points
FROM table_ranks as tr
LEFT JOIN (SELECT * FROM table_ranks LIMIT 1,69596585953484) as l
ON l.points_needed = (SELECT MIN(points_needed) FROM table_ranks WHERE points_needed > tr.points_needed limit 1)
LEFT OUTER JOIN table_users AS tu ON tu.points >= tr.points_needed AND tu.points < l.points_needed
WHERE tu.id IS NOT NULL
group by tu.id
Fiddle
Output
-------------------------
| id | points | name |
-------------------------
| 1 | lvl0 | 2 |
| 2 | lvl1 | 10 |
| 3 | lvl2 | 21 |
| 4 | lvl2 | 29 |
-------------------------

give this a try, a little bit messy due to table design,
SELECT u.*, r.name
FROM table_users u
INNER JOIN
(
SELECT x.name,
x.points_needed start_point,
COALESCE(y.points_needed - 1, 2000000) end_point
FROM
(
SELECT name, points_needed, #rank:=#rank+1 ranks
FROM table_ranks a, (SELECT #rank:=0) b
ORDER BY points_needed
) x
LEFT JOIN
(
SELECT name, points_needed, #rank1:=#rank1+1 ranks
FROM table_ranks a, (SELECT #rank1:=0) b
ORDER BY points_needed
) y ON x.ranks+1 = y.ranks
) r ON u.points BETWEEN r.start_point AND r.end_point
SQLFiddle Demo

Related

How can I get total user in a group from database by PHP

I have 2 tables in database:
How can I get total user for each group. i.e: group 1: total are 2 users;
group2: total are 2 users;
group3: total is 1 user
You need normalization and never store comma-separated data.
Consider the following
mysql> select * from user_table ;
+---------+---------------+
| user_id | user_group_id |
+---------+---------------+
| 1 | 1,2 |
| 2 | 2 |
| 3 | 1,3 |
+---------+---------------+
3 rows in set (0.00 sec)
mysql> select * from group_table ;
+----------+------------+
| group_id | group_name |
+----------+------------+
| 1 | a |
| 2 | b |
| 3 | c |
+----------+------------+
3 rows in set (0.00 sec)
The above data is not normalized and to get the desired result out of these you need to use some in-efficient query as
select
g.group_id,
count(*) as total
from group_table g
left join user_table u on find_in_set(g.group_id,u.user_group_id) > 0
group by g.group_id ;
+----------+-------+
| group_id | total |
+----------+-------+
| 1 | 2 |
| 2 | 2 |
| 3 | 1 |
+----------+-------+
Now lets do normalization and store user-group data in a different table as
mysql> select * from user_to_group ;
+---------+----------+
| user_id | group_id |
+---------+----------+
| 1 | 1 |
| 1 | 2 |
| 2 | 2 |
| 3 | 1 |
| 3 | 3 |
+---------+----------+
You can easily write different queries from these tables now and here are some examples
select group_id,count(*) as tot from user_to_group group by group_id ;
+----------+-----+
| group_id | tot |
+----------+-----+
| 1 | 2 |
| 2 | 2 |
| 3 | 1 |
+----------+-----+
Joining the tables would even more easy
select
g.group_id,
g.group_name,
count(*) as tot
from user_to_group ug
join group_table g on g.group_id = ug.group_id
join user_table u on u.user_id = ug.user_id
group by g.group_id
+----------+------------+-----+
| group_id | group_name | tot |
+----------+------------+-----+
| 1 | a | 2 |
| 2 | b | 2 |
| 3 | c | 1 |
+----------+------------+-----+
SELECT group_name, COUNT(*) FROM user_table u, group_table g WHERE u.user_group_id LIKE %g.group_id% GROUP BY g.group_name;
this should work and give you a list of all groups and how many users are in them.
I will recommend you to create a third table which holds the information about which users are in which groups.
CREATE TABLE users_in_groups
(
user_id INT
, group_id INT
);
Then you can join like this:
SELECT
gt.group_id
, count(ut.user_id)
FROM
user_table AS ut
, INNER JOIN users_in_groups AS uig ON uig.user_id = ut.user_id
, INNER JOIN group_table AS gt ON gt.group_id = uig.group_id
GROUP BY
gt.group_id
;
To use the table you have now will you have to do something like this (in mysql):
SELECT
gt.group_id
, count(ut.user_id)
FROM
user_table AS ut
, INNER JOIN group_table AS gt ON LOCATE(gt.group_id, ut.user_group_id) > 0
GROUP BY
gt.group_id
Remember, when using group by, always locate what makes your group unique!
This is not an answer to your specific question but rather an alternative data structure proposal that might be better.
Introduce a new table members that looks like
# members
user_id | group_id
1 | 1
1 | 2
2 | 2
3 | 1
3 | 3
Then you could SELECT group_id, count(*) FROM members GROUP BY group_id
+----------+----------+
| group_id | count(*) |
+----------+----------+
| 1 | 2 |
| 2 | 2 |
| 3 | 1 |
+----------+----------+
This structure might also make it easier for you to manage your memberships. user_id + group_id should be unique. And if supported let them be foreign keys.

MySQL SELECT FROM WHERE COUNT

Hi im looking for an MySQL Select that returns only that rows with doubled entries in column xxx
example:
+------+------+------------+--------------------+
| id | name | work_date | daily_typing_pages |
+------+------+------------+--------------------+
| 1 | John | 2007-01-24 | 250 |
| 2 | Ram | 2007-05-27 | 220 |
| 3 | Jack | 2007-05-06 | 170 |
| 4 | Jack | 2007-04-06 | 100 |
| 5 | Jill | 2007-04-06 | 220 |
| 6 | Zara | 2007-06-06 | 300 |
| 7 | Zara | 2007-02-06 | 350 |
+------+------+------------+--------------------+
Got This Table and i want to read out all entries thats name is listed more than once,
my Query is not working cause it just shows entries with two times the name just once
SELECT id, name, COUNT(name) AS count
FROM table_xy
having count(name) > 1;
what i want to have returned:
+------+------+------------+
| id | name | count |
+------+------+------------+
| 3 | Jack | 2 |
| 4 | Jack | 2 |
| 6 | Zara | 2 |
| 7 | Zara | 2 |
+------+------+------------+
Is there a way to get that?
You could use a subquery for your group by:
SELECT x.id, y.name, y.count
FROM table_xy AS x
INNER JOIN
( SELECT Name, COUNT(*) AS count
FROM table_xy
GROUP BY Name
HAVING COUNT(*) > 1
) AS y
ON y.Name = x.Name;
Alternatively you could use a self join with distinct if you don't need the count:
SELECT DISTINCT x.ID, x.Name
FROM table_xy AS x
INNER JOIN table_xy AS y
ON x.Name = y.Name
AND x.ID != y.ID;
Or a self join with GROUP BY if you do need the count:
SELECT x.ID, x.Name, COUNT(y.ID) + 1 AS count
FROM table_xy AS x
INNER JOIN table_xy AS y
ON x.Name = y.Name
AND x.ID != y.ID
GROUP BY x.ID, x.Name;
Examples on SQL Fiddle
try this...
SELECT x.ID, x.Name, COUNT(y.ID) + 1 AS count
FROM table_xy AS x
INNER JOIN table_xy AS y
ON x.Name = y.Name
AND x.ID != y.ID
GROUP BY x.ID, x.Name;

MySQL Sum multiple column values with conditions

I have the following schema (two tables):
**APPS**
| ID (bigint) | USERID (Bigint) | USAGE_START_TIME (datetime) |
------------------------------------------------------------------
| 1 | 12 | 2013-05-03 04:42:55 |
| 2 | 12 | 2013-05-12 06:22:45 |
| 3 | 12 | 2013-06-12 08:44:24 |
| 4 | 12 | 2013-06-24 04:20:56 |
| 5 | 13 | 2013-06-26 08:20:26 |
| 6 | 13 | 2013-09-12 05:48:27 |
**USAGE**
| ID (bigint) | APPID (bigint) | DEVICEID (bigint) | HIGH_COUNT (bigint) | MEDIUM_COUNT (bigint) |
--------------------------------------------------------------------------------------------------------
| 1 | 1 | 2 | 400 | 200 |
| 2 | 1 | 3 | 200 | 100 |
| 3 | 2 | 3 | 350 | 40 |
| 4 | 3 | 4 | 2 | 400 |
| 5 | 4 | 2 | 4 | 30 |
| 6 | 5 | 3 | 50 | 300 |
Explanation:
So, there are two tables.
Now I want to find the following:
Given a USERID, Get sum of HIGH_COUNT & MEDIUM_COUNT. While counting
the SUM it should be taken care that: If in USAGE, same device is used
more than once, then the record which has the latest info (based on
APPS.USAGE_START_TIME), should be considered while calculating the
sum.
For ex:
For above schema, result should be (for userid=12) :
| HIGH_COUNT (bigint) | MEDIUM_COUNT (Bigint) |
-----------------------------------------------
| 356 | 470 |
SQL Fiddle: http://sqlfiddle.com/#!2/74ae0f
If a user uses multiple APPS on one device, this query will use the APPS row with the highest usage_start_time:
select a.userid
, sum(u.high_count)
, sum(u.medium_count)
from apps a
join `usage` u
on u.appid = a.id
join (
select u.device_id
, a.userid
, max(a.usage_start_time) as max_start_time
from apps a
join `usage` u
on u.appid = a.id
group by
u.device_id
, a.userid
) filter
on filter.device_id = u.device_id
and filter.userid = a.userid
and filter.max_start_time = a.usage_start_time
group by
a.userid
In your dataset, it will select usage rows 5, 3, 4 for user 12.
See it working at SQL Fiddle.
I can't quite get your numbers, but something like this should work...
SELECT a.userid
, SUM(u.high_count)
, SUM(u.medium_count)
FROM apps a
JOIN `usage` u
ON u.appid = a.id
JOIN
( SELECT userid
, deviceid
, MAX(usage_start_time) max_usage_start_time
FROM apps a
JOIN `usage` u
ON u.appid = a.id
GROUP
BY userid
, deviceid
) x
ON x.userid = a.userid
AND x.deviceid = u.deviceid
AND x.max_usage_start_time = a.usage_start_time
GROUP
BY userid;
Note that usage is a reserved word. Therefore, this is a bad name for a column (or a table). Also, note inconsistencies between your question and your fiddle.
I think not had chance to test it but
SELECT SUM(HIGH_COUNT), SUM(MEDIUM_COUNT) FROM `USAGE` INNER JOIN `APPS` ON USAGE.APPID=APPS.ID WHERE APPS.USERID=$input_user_id_to_lookup
will give you your counts.
For yoru other question (homework?) you didn't give us the full schema so we can't guess what you need doing.
Also whoever designed that db should be shot its horrible

Mysql Remove Duplicates on Left Join 3 Tables using id

I have the following tables
hobbies
+----+-------------+---------------+
| id | employeeid | hobby_name |
+----+-------------+---------------+
| 1 | 123 | cooking |
| 2 | 123 | painting |
| 3 | 124 | dancing |
+----+-------------+---------------+
nonacad_recog
+----+-------------+---------------+
| id | employeeid | recog_name |
+----+-------------+---------------+
| 1 | 123 | Award1 |
| 2 | 123 | Award2 |
| 3 | 124 | Award3 |
+----+-------------+---------------+
org_membership
+----+-------------+---------------+
| id | employeeid | recog_name |
+----+-------------+---------------+
| 1 | 123 | Boyscout |
| 2 | 124 | Girlscout |
+----+-------------+---------------+
This is the query I used:
SELECT h.hobby_name,r.recog_name,o.org_name
FROM hobbies as h
JOIN nonacad_recog as r ON (h.employeeid=r.employeeid)
JOIN org_membership as o ON (r.employeeid=o.employeeid)
WHERE h.employeeid='123'
I am getting duplicate outputs:
+----+-------------+---------------------+
| hobby_name | recog_name | org_name |
+----+-------------+---------------------+
| cooking | Award1 | Boyscout |
| cooking | Award2 | Boyscout |
| painting| Award1 | Boyscout |
| painting| Award2 | Boyscout |
+----+-------------+---------------------+
What I want as an output is like this: with no duplicates
+----+-------------+---------------------+
| hobby_name | recog_name | org_name |
+----+-------------+---------------------+
| cooking | Award1 | Boyscout |
| painting| Award2 | NULL |
+----+-------------+---------------------+
also giving a null to the other columns/rows if there is nothing to return.
I might have missed out some things.
Is this possible to achieve using mysql queries?
Any solution to this?
If I get the correct table, it will be easy for me to
mysqli_fetch assoc the results and display it on a table using PHP.
The problem is that in the desired output you want to relate rows in your tables by non-existent column, which is a row number.
You can technically do it like this
SELECT MAX(CASE WHEN source = 1 THEN name END) hobby_name,
MAX(CASE WHEN source = 2 THEN name END) recog_name,
MAX(CASE WHEN source = 3 THEN name END) org_name
FROM
(
SELECT 1 source, id, hobby_name name, #n1 := #n1 + 1 rnum
FROM hobbies CROSS JOIN (SELECT #n1 := 0) i
WHERE employeeid = 123
UNION ALL
SELECT 2 source, id, recog_name, #n2 := #n2 + 1 rnum
FROM nonacad_recog CROSS JOIN (SELECT #n2 := 0) i
WHERE employeeid = 123
UNION ALL
SELECT 3 source, id, org_name, #n3 := #n3 + 1 rnum
FROM org_membership CROSS JOIN (SELECT #n3 := 0) i
WHERE employeeid = 123
ORDER BY source, id
) q
GROUP BY rnum
Output:
| HOBBY_NAME | RECOG_NAME | ORG_NAME |
|------------|------------|----------|
| cooking | Award1 | Boyscout |
| painting | Award2 | (null) |
Here is SQLFiddle demo
Here is an alternative solution. You pack all values as delimited strings with GROUP_CONCAT()
SELECT GROUP_CONCAT(DISTINCT hobby_name ORDER BY h.id) hobby_name,
GROUP_CONCAT(DISTINCT recog_name ORDER BY r.id) recog_name,
GROUP_CONCAT(DISTINCT org_name ORDER BY o.id) org_name
FROM
(
SELECT 123 employeeid
) e LEFT JOIN hobbies h
ON e.employeeid = h.employeeid
LEFT JOIN nonacad_recog r
ON e.employeeid = r.employeeid
LEFT JOIN org_membership o
ON e.employeeid = o.employeeid;
Output:
| HOBBY_NAME | RECOG_NAME | ORG_NAME |
|------------------|---------------|----------|
| cooking,painting | Award1,Award2 | Boyscout |
Here is SQLFiddle demo
and then in your client php code easily explode() column values while iterating over the resultset and build your presentation.
I think "Limit" can work:
SELECT h.hobby_name,r.recog_name,o.org_name
FROM hobbies as h,
nonacad_recog as r,
org_membership as o
WHERE h.employeeid='123'
AND r.employeeid=o.employeeid
AND h.employeeid=r.employeeid
LIMIT 1

Can't get my head around GROUP_CONCAT

I cannot seem to get an GROUP_CONCAT query to work.
I have this tables, which I joined:
stud
id | stud_name |
-----------------
1 | Class1 |
2 | Class2 |
note
id | stud_id | mat_id | Note |
------------------------------------------------
1 | 1 | 1 | 10 |
2 | 1 | 2 | 9 |
3 | 1 | 1 | 10 |
mat
id | mat_name |
----------------
1 | Porc |
2 | Vaca |
Here is what I did to join them.
SELECT
`stud`.`id`
, `mat`.`mat_name`
, `note`.`note`
FROM
`stud`
LEFT JOIN
`note`
ON
(`stud`.`id` = `note`.`id_stud`)
LEFT JOIN
`mat`
ON
(`note`.`id_mate` = `mat`.`id`)
Here is what I want them to look.
mat.id | mat_name | Note |
-----------------------------
1 | Porc | 10,10 |
2 | Vaca | 9 |
Here is how they look.
mat.id | mat_name | Note |
-----------------------------
1 | Porc | 10 |
2 | Vaca | 9 |
1 | Port | 10 |
I tried doing this.
SELECT
`mat`.`mat_name`,
GROUP_CONCAT(`note`.`note`) AS `note`.`note`,
FROM ( "Here is what I did to join them." )attr_groups
GROUP BY `mat`.`mat_name`;
ORDER BY `mat`.`mat_name`;
Any ideas?
EDIT: If I add group by or ORDER by, no results are found. If I don't add them, the query works but its not concatenating them.
SELECT mat.id,
mat.mat_name,
GROUP_CONCAT(note.note) as note
FROM mat
LEFT JOIN note ON mat.id = note.mat_id
LEFT JOIN stud ON note.stud_id = stud.id
GROUP BY mat.id, mat.mat_name
ORDER BY mat.mat_name
SQLFiddle demo
You have error in group by.
GROUP BY `mat`.`mat_name`;
------------------------^^^---
remove ; from query
SELECT
`stud`.`id`
, `mat`.`mat_name`
, GROUP_CONCAT(`note`.`note`) AS Note
FROM
`stud`
LEFT JOIN
`note`
ON
(`stud`.`id` = `note`.`id_stud`)
LEFT JOIN
`mat`
ON
(`note`.`id_mate` = `mat`.`id`)
GROUP BY
`stud`.mat_id
ORDER BY
`mat`.`mat_name`
Try this:
SELECT
m.id,
m.mat_name,
GROUP_CONCAT(n.Note) as grades
FROM
mat m
LEFT JOIN note n
ON n.mat_id = m.id
GROUP BY
m.id

Categories