MySQL GROUP BY and sort the group by column - mysql

I want to sort a group by a specific column.
I tried the following:
SELECT u.level, max(u.score),n.nick
FROM database.userdata u, database.nicks n
WHERE n.user = u.id
GROUP BY level
But this only prints the max score, and not the username for this score..
How can I fix this?
Thanks!

Try this way:
SELECT n.nick, u1.score, u1.level
FROM database.nicks AS n
JOIN database.userdata AS u1 ON n.user = u1.id
JOIN (
SELECT level, max(score) AS score
FROM database.userdata
GROUP BY level
) AS u2 ON u1.level = u2.level AND u1.score = u2.score

Related

I need to get specific ids from db if these are in current and last quarter using SQL

[DB Table]
SELECT b.first_name, b.last_name, a.pod_name, a.category, c.user_id,
SUM(IF(QUARTER(CURDATE())-1 OR (QUARTER(CURDATE())-2) AND a.user_id, 1, 0)) AS flag FROM kudos a
INNER JOIN users b ON a.user_id = b.id INNER JOIN users_groups c ON a.user_id = c.user_id
INNER JOIN groups d ON c.group_id = d.id WHERE a.group_name = 'G2' AND d.id IN (7,8,9,11,12,13,14,15,16,17,21,22,23,24,25,26,27,28)
AND QUARTER(CURDATE())-1 = a.quarter ORDER BY a.final_score+0 DESC
I need to get the user_ids of those users which are both in quarter 1 and 2 from table.
Tried above query but failed to get expected results.
Can someone please guide me on this?
if you only need user_id then you can do this :
select user_id
from tablename
where quarter in (1,2)
group by user_id
having count(distinct quarter) = 2
another way is to use window function, assuming you have one user id in each quarter:
select * from (
select * , count(*) over (partition by user_id) cn
from tablename
where quarter in (1,2)
) t where cn = 2

sum of 2 different queries result

I have to do sum from 2 different tables and show it using MySQL:
total comments from table 1, total comments from table 2: What i have tried so far is,
SELECT u.name as name, u.username as username,
( SELECT SUM(total) FROM (SELECT (COUNT(nc.id)) as total FROM table1 as nc WHERE nc.user_id = u.id) UNION ALL (SELECT COUNT(pc.id) AS total FROM table2 pc WHERE pc.user_id = u.id) as finalTotal ) as total_comments
FROM user as u
GROUP BY u.id
It is giving me this error:
Every derived table must have its own alias
If I understand what you need, you have to modify the query like this :
select u.name,u.username,total as total_comments
from user as u
left join (
select id,sum(total) as total
from(
select nc.id,count(1) as total
from table1 as nc
group by nc.id
union all
select pc.id,count(1) as total
from table2 as pc
group by pc.id
) as t group by id
) comments on comments.id = u.id
Before UNION ALL put alias like you giving for all..
SELECT u.name as name, u.username as username,
( SELECT SUM(total) FROM (SELECT (COUNT(nc.id)) as total FROM table1 as nc WHERE nc.user_id = u.id) as vr UNION ALL (SELECT COUNT(pc.id) AS total FROM table2 pc WHERE pc.user_id = u.id) as finalTotal ) as total_comments
FROM user as u
GROUP BY u.id
SELECT u.name as name, u.username as username,
( SELECT SUM(total) FROM
(SELECT (COUNT(nc.id)) as total FROM table1 as nc WHERE nc.user_id = u.id
UNION ALL SELECT COUNT(pc.id) AS total FROM table2 pc WHERE pc.user_id = u.id) as finalTotal ) as total_comments
FROM user as u
GROUP BY u.id
Remove brackets before and after UNION ALL and check.

mysql: group functions on subqueries with limits

I have a group of users who perform tasks on which they are scored. I'm trying to create a report showing the average of each user's last 50 tasks.
user table: userid, username, usertype
task table: taskid, score, tasktype, userid
If I do:
SELECT u.userid, u.username, (SELECT AVG(score)
FROM task t
WHERE t.userid = u.userid AND t.tasktype = 'task1'
ORDER BY t.taskid DESC LIMIT 50) AS avgscore
FROM user u
WHERE u.usertype = 'utype';
that doesn't work because it does the limit 50 after it does the average of everything.
What I need is this:
SELECT u.userid, u.username, AVG(SELECT t.score
FROM task t
WHERE t.userid = u.userid AND t.tasktype = 'task1'
ORDER BY t.taskid DESC LIMIT 50) AS avgscore
FROM user u
WHERE u.usertype = 'utype';
but that is not valid syntax
I've tried sub-sub queries, but can't get it that way either, as I always get a problem with the limit, or a join, or unknown fields when I reference u.userid in the sub-subquery.
Is there a way to do this?
Use a subquery within the subquery:
SELECT u.userid, u.username,
(SELECT AVG(score)
FROM (select t.*
from task t
WHERE t.userid = u.userid AND t.tasktype = 'task1'
ORDER BY t.taskid DESC
LIMIT 50
) t
) AS avgscore
FROM user u
WHERE u.usertype = 'utype';
EDIT:
I didn't realize that MySQL would not recognize the u.userid. It should according to ANSI rules for the scoping of table aliases.
You can take a different approach which is find the 50th taskid value, and then take everything above that:
select ut.userid, ut.username, avg(t.score)
from (SELECT u.userid, u.username,
(SELECT substring_index(substring_index(group_concat(taskid order by taskid desc
), ',', 50), ',', -1)
from task t
WHERE t.userid = u.userid AND t.tasktype = 'task1'
) + 0 as taskid50
FROM user u
WHERE u.usertype = 'utype'
) ut join
task t
on ut.userid = t.userid and
ut.taskid50 >= t.taskid and t.tasktype = 'task1'
group by ut.userid, ut.username;
try this
SELECT u.userid, u.username, AVG(t.score ) AS avgscore
FROM user u
INNER JOIN task t
ON t.userid = u.userid
WHERE u.usertype = 'utype' AND t.tasktype = 'task1'
GROUP BY u.userid
ORDER BY t.taskid DESC LIMIT 50;

issue with joins

I have the following query, in which I used JOINs. It says:
unknown column m.bv ..
Could you please take a look and tell me what I'm doing wrong?
$query4 = 'SELECT u.*, SUM(c.ts) AS total_sum1, SUM(m.bv) AS total_sum
FROM users u
LEFT JOIN
(SELECT user_id ,SUM(points) AS ts FROM coupon GROUP BY user_id) c
ON u.user_id=c.user_id
LEFT JOIN
(SELECT user_id ,SUM(points) AS bv FROM matching GROUP BY user_id) r
ON u.user_id=m.user_id
where u.user_id="'.$_SESSION['user_name'].'"
GROUP BY u.user_id';
You are selecting SUM(points) AS bv from the table with the alias r, there is no tables with the alias m. So that it has to be r.bv instead like so:
SELECT
u.*,
SUM(c.ts) AS total_sum1,
SUM(r.bv) AS total_sum
FROM users u
LEFT JOIN
(
SELECT
user_id,
SUM(points) AS ts
FROM coupon
GROUP BY user_id
) c ON u.user_id=c.user_id
LEFT JOIN
(
SELECT
user_id,
SUM(points) AS bv
FROM matching
GROUP BY user_id
) r ON u.user_id = m.user_id
where u.user_id="'.$_SESSION['user_name'].'"
GROUP BY u.user_id
Replace m., with r. Look at second Join
You have aliased the derived table with r and you reference that table (twice) with m. Correct one or the other.
Since you group by user_id in the two subqueries and user_id is (I assume) the primary key of table user, you don't really need the final GROUP BY.
I would write it like this, if it was meant for all (many) users:
SELECT u.*, COALESCE(c.ts, 0) AS total_sum1, COALESCE(m.bv, 0) AS total_sum
FROM users u
LEFT JOIN
(SELECT user_id, SUM(points) AS ts FROM coupon GROUP BY user_id) c
ON u.user_id = c.user_id
LEFT JOIN
(SELECT user_id, SUM(points) AS bv FROM matching GROUP BY user_id) m
ON u.user_id = m.user_id
and like this in your (one user) case:
SELECT u.*, COALESCE(c.ts, 0) AS total_sum1, COALESCE(m.bv, 0) AS total_sum
FROM users u
LEFT JOIN
(SELECT SUM(points) AS ts FROM coupon
WHERE user_id = "'.$_SESSION['user_name'].'") c
ON TRUE
LEFT JOIN
(SELECT SUM(points) AS bv FROM matching
WHERE user_id = "'.$_SESSION['user_name'].'") m
ON TRUE
WHERE u.user_id = "'.$_SESSION['user_name'].'"
The last query can also be simplified to:
SELECT u.*,
COALESCE( (SELECT SUM(points) FROM coupon
WHERE user_id = u.user_id)
, 0) AS total_sum1,
COALESCE( (SELECT SUM(points) FROM matching
WHERE user_id = u.user_id)
, 0) AS total_sum
FROM users u
WHERE u.user_id = "'.$_SESSION['user_name'].'"

MySQL INNER JOIN select only one row from second table

I have a users table and a payments table, for each user, those of which have payments, may have multiple associated payments in the payments table. I would like to select all users who have payments, but only select their latest payment. I'm trying this SQL but i've never tried nested SQL statements before so I want to know what i'm doing wrong. Appreciate the help
SELECT u.*
FROM users AS u
INNER JOIN (
SELECT p.*
FROM payments AS p
ORDER BY date DESC
LIMIT 1
)
ON p.user_id = u.id
WHERE u.package = 1
You need to have a subquery to get their latest date per user ID.
SELECT u.*, p.*
FROM users u
INNER JOIN payments p
ON u.id = p.user_ID
INNER JOIN
(
SELECT user_ID, MAX(date) maxDate
FROM payments
GROUP BY user_ID
) b ON p.user_ID = b.user_ID AND
p.date = b.maxDate
WHERE u.package = 1
SELECT u.*, p.*
FROM users AS u
INNER JOIN payments AS p ON p.id = (
SELECT id
FROM payments AS p2
WHERE p2.user_id = u.id
ORDER BY date DESC
LIMIT 1
)
Or
SELECT u.*, p.*
FROM users AS u
INNER JOIN payments AS p ON p.user_id = u.id
WHERE NOT EXISTS (
SELECT 1
FROM payments AS p2
WHERE
p2.user_id = p.user_id AND
(p2.date > p.date OR (p2.date = p.date AND p2.id > p.id))
)
These solutions are better than the accepted answer because they work correctly when there are multiple payments with same user and date. You can try on SQL Fiddle.
SELECT u.*, p.*, max(p.date)
FROM payments p
JOIN users u ON u.id=p.user_id AND u.package = 1
GROUP BY u.id
ORDER BY p.date DESC
Check out this sqlfiddle
SELECT u.*
FROM users AS u
INNER JOIN (
SELECT p.*,
#num := if(#id = user_id, #num + 1, 1) as row_number,
#id := user_id as tmp
FROM payments AS p,
(SELECT #num := 0) x,
(SELECT #id := 0) y
ORDER BY p.user_id ASC, date DESC)
ON (p.user_id = u.id) and (p.row_number=1)
WHERE u.package = 1
You can try this:
SELECT u.*, p.*
FROM users AS u LEFT JOIN (
SELECT *, ROW_NUMBER() OVER(PARTITION BY userid ORDER BY [Date] DESC) AS RowNo
FROM payments
) AS p ON u.userid = p.userid AND p.RowNo=1
There are two problems with your query:
Every table and subquery needs a name, so you have to name the subquery INNER JOIN (SELECT ...) AS p ON ....
The subquery as you have it only returns one row period, but you actually want one row for each user. For that you need one query to get the max date and then self-join back to get the whole row.
Assuming there are no ties for payments.date, try:
SELECT u.*, p.*
FROM (
SELECT MAX(p.date) AS date, p.user_id
FROM payments AS p
GROUP BY p.user_id
) AS latestP
INNER JOIN users AS u ON latestP.user_id = u.id
INNER JOIN payments AS p ON p.user_id = u.id AND p.date = latestP.date
WHERE u.package = 1
#John Woo's answer helped me solve a similar problem. I've improved upon his answer by setting the correct ordering as well. This has worked for me:
SELECT a.*, c.*
FROM users a
INNER JOIN payments c
ON a.id = c.user_ID
INNER JOIN (
SELECT user_ID, MAX(date) as maxDate FROM
(
SELECT user_ID, date
FROM payments
ORDER BY date DESC
) d
GROUP BY user_ID
) b ON c.user_ID = b.user_ID AND
c.date = b.maxDate
WHERE a.package = 1
I'm not sure how efficient this is, though.
SELECT U.*, V.* FROM users AS U
INNER JOIN (SELECT *
FROM payments
WHERE id IN (
SELECT MAX(id)
FROM payments
GROUP BY user_id
)) AS V ON U.id = V.user_id
This will get it working
Matei Mihai given a simple and efficient solution but it will not work until put a MAX(date) in SELECT part so this query will become:
SELECT u.*, p.*, max(date)
FROM payments p
JOIN users u ON u.id=p.user_id AND u.package = 1
GROUP BY u.id
And order by will not make any difference in grouping but it can order the final result provided by group by. I tried it and it worked for me.
My answer directly inspired from #valex very usefull, if you need several cols in the ORDER BY clause.
SELECT u.*
FROM users AS u
INNER JOIN (
SELECT p.*,
#num := if(#id = user_id, #num + 1, 1) as row_number,
#id := user_id as tmp
FROM (SELECT * FROM payments ORDER BY p.user_id ASC, date DESC) AS p,
(SELECT #num := 0) x,
(SELECT #id := 0) y
)
ON (p.user_id = u.id) and (p.row_number=1)
WHERE u.package = 1
This is quite simple do The inner join and then group by user_id and use max aggregate function in payment_id assuming your table being user and payment query can be
SELECT user.id, max(payment.id)
FROM user INNER JOIN payment ON (user.id = payment.user_id)
GROUP BY user.id
If you do not have to return the payment from the query you can do this with distinct, like:
SELECT DISTINCT u.*
FROM users AS u
INNER JOIN payments AS p ON p.user_id = u.id
This will return only users which have at least one record associated in payment table (because of inner join), and if user have multiple payments, will be returned only once (because of distinct), but the payment itself won't be returned, if you need the payment to be returned from the query, you can use for example subquery as other proposed.