Mysql Group by showing multiple rows - mysql

Database structure
Table 'Org_f_a_list'
id org_id user_id date_added
1 1 1 2017-01-01 05:05:05
Table 'Users'
id username last_login
1 Testuser 2017-01-01 05:05:05
Table 'Users_pa'
id user_id summoner_id rank_solo
1 1 1 15
2 1 2 17
My current query
select max(rank_solo) as rank,last_login,username,o.user_id,date_added
from org_f_a_list o
join users u on o.user_id = u.id
join users_pa as p on u.id = p.user_id
where org_id = :org
group by u.id,rank_solo,date_added
order by rank desc
What I want the result to be
user_id user_name rank date_added last_login
1 Testuser 17 date date
My current result
user_id user_name rank date_added last_login
1 Testuser 15 date date
1 Testuser 17 date date
For some reason the group by u.id is not doing anything and I still pull both rows and not just the max rank
Edit : That fixed it. Thanks guys!

remove rank_solo from group by
select max(rank_solo) as rank,last_login,username,o.user_id,date_added
from org_f_a_list o
join users u on o.user_id = u.id
join users_pa as p on u.id = p.user_id
where org_id = :org
group by u.id,date_added
order by rank desc

Rank Should not be part of group by as it is causing forming of two different groups

You can Remove the Rank_solo from the Group By Like this:
select max(rank_solo) as rank,last_login,username,o.user_id,date_added
from org_f_a_list o
join users u on o.user_id = u.id
join users_pa as p on u.id = p.user_id
where org_id = :org
group by u.id,date_added
order by rank desc
You can Use Having with the same Query
select max(rank_solo) as rank,last_login,username,o.user_id,date_added
from org_f_a_list o
join users u on o.user_id = u.id
join users_pa as p on u.id = p.user_id
where org_id = :org
group by u.id,date_added
Having rank_solo = max(ranl_solo)
order by rank desc

Related

Mysql 3 table join and update

I am attempting to join 3 tables and update some values based on the data in the tables.
The table and columns affected are:
users
id | user_registered
members
user_id | status
user_meta
user_id | key | value
I basically want to get all users from the users table that registered more than 7 days ago by the user_registered column and then join their id with the user_id of the members table where the status is active and then join them with the user_id of the user_meta table where the key is = to 'capability' and the value is = to pending. And then finally update the value to active.
I had the following but I don't think it's correct as it is not grouping the records together and not joining and I'm not sure of the correct syntax:
SELECT u.id, u.user_registered, p.user_id, p.status, um.user_id, um.key, um.value
FROM users u, members p, user_meta um
WHERE u.user_registered < NOW() - INTERVAL 1 WEEK AND p.status = "active" AND um.key='capability' AND value='pending'
Any help is greatly appreciated.
You can update user_meta table based on your logic in two ways.
Query 1:
UPDATE user_meta
SET user_meta.value = 'active'
WHERE user_meta.user_id IN
(SELECT id
FROM (SELECT u.id
FROM users u JOIN members p ON (u.id = p.user_id)
JOIN user_meta um ON (u.id = um.user_id)
WHERE u.user_registered < NOW() - INTERVAL 1 WEEK
AND p.status = "active"
AND um.key = 'capability'
AND um.value = 'pending'
)tmp)
Query 2:
UPDATE user_meta
JOIN (SELECT u.id
FROM users u
JOIN members p ON (u.id = p.user_id)
JOIN user_meta um ON (u.id = um.user_id)
WHERE u.user_registered < NOW() - INTERVAL 1 WEEK
AND p.status = "active"
AND um.key = 'capability'
AND um.value = 'pending') tmp
ON (tmp.id = user_meta.user_id)
SET user_meta.value = 'active'
The Query 1 will take long time if the number of select query row is large and column is not properly indexed as it use IN statement.

Display only top sum-up results using group by

My table records three different scores (site, park, division) for the same group of user.
A user's total score is "sum of all the site scores" + "sum of all the park scores" + "sum of all the division scores".
The code is the following and I am using MySQL:
select sum(points)as points, user_name
from
(SELECT sum(site_point) as points, user_name from visits v join users u on
u.user_id = v.user_id group by user_name
union
select sum(park_point) as points , user_name from visits v join users u on
u.user_id = v.user_id group by user_name
union
select sum(division_point) as points , user_name from visits v join users u
on u.user_id = v.user_id group by user_name
) V group by user_name order by sum(points) DESC ;
I want to display only the users whose score is in top 5 and their scores.
Ten users may have the same highest score. I need all of them being displayed.
I appreciate any help.
I think this is what you want:
select user_name,
sum(site_point + park_point + division_point) as points
from visits v join
users u
on u.user_id = v.user_id
group by user_name
order by points desc
limit 5;
A single aggregation simplifies the calculation.
You may not realize it, but the union is going to return incorrect results under some circumstances. Union removes duplicates, so if a user has the same partial scores, then the two rows becomes one.
EDIT:
It is a little trickier to get the top 5 scores, but possible:
select user_name,
sum(site_point + park_point + division_point) as points
from visits v join
users u
on u.user_id = v.user_id
group by user_name
having points >= (select distinct points
from (select sum(site_point + park_point + division_point)
from visits v join
users u
on u.user_id = v.user_id
group by user_name
) vu
order by points desc
limit 1 offset 4
)
order by points desc
limit 5;
Assuming that a user_name is unique for a given id you can simplify this a wee bit:
having points >= (select distinct points
from (select sum(site_point + park_point + division_point) as points
from visits v
group by user_id
) vu
order by points desc
limit 1 offset 4
)
And, if you want anyone who matches the top 5 users -- but to return more than 5 rows if there are ties -- then change the select distinct to select in the subquery.
Use the LIMIT command. More info here
So to tack it onto your SQL would result in:
select sum(points)as points, user_name
from
(SELECT sum(site_point) as points, user_name from visits v join users u on
u.user_id = v.user_id group by user_name
union
select sum(park_point) as points , user_name from visits v join users u on
u.user_id = v.user_id group by user_name
union
select sum(division_point) as points , user_name from visits v join users u
on u.user_id = v.user_id group by user_name
) V group by user_name order by sum(points) DESC LIMIT 5

MySQL query to get last 4 records of a related table

I have a database with two tables: users and payments.
Each user has many payments and each payment can be successful or failed.
I need to write a query to get all the users who failed the last 4 payments.
This is what I tried so far:
select *
from users u
where u.id in(
select p.user_id
from payments
where p.status = 'failed'
group by p.user_id
having count(p.id) = 4
);
But as you can see this is not only checking for the last 4 payments, but all of them. So, it is returning the users that have failed 4 payments (in global, not only the last 4).
I don't know if it is important but the fields on the tables are:
users:
id | name | email | password
payment:
id | date | status | user_id
| | (can be success or failed) | (FK)
Update:
This sqlfiddle will help to understand what I need.
The query is returning all users with 4 failed payments. But I only need the users whose 4 most recent payments failed. In this case it will be only user with id 5
This works
SELECT x.user_id, count(*) as cnt
FROM (
SELECT a.user_id, a.date, a.status FROM payment AS a WHERE
(SELECT COUNT(*) FROM payment AS b
WHERE b.user_id = a.user_id AND b.date >= a.date) <= 4
ORDER BY a.user_id ASC, a.date DESC) AS x
WHERE x.status = 'failed'
GROUP BY x.user_id
HAVING cnt >=4;
If you want the users, whose last 4 transactions were failed (only last 4, not total 4) then following query should get the job done:
select u.* from users u
where
id in
(select p.user_id from payment p
where (select count(*) from payment p1
where p.user_id = p1.user_id
and p.date <= p1.date
order by p1.user_id asc,p1.date desc
) <= 4
and p.status <> 'success'
group by p.user_id
having count(*)>=4);
check the sqlfiddle
Hope it helps!
You want to use the LIMIT keyword, and specify an ORDER.
Try this
select *
from users u
where u.id in(
select p.user_id
from payments
where p.status = 'failed'
group by p.user_id
having count(p.id) = 4
) ORDER BY p.id DESC LIMIT 4;
Not entirely sure what you are trying to do inside the WHERE statement, but ORDER BY p.id DESC LIMIT 4 will retrieve the four most recent rows.
I think you can use a query like this:
select users.id, users.name, users.email, users.password
from users
left join (
select p1.id, p1.date, p1.status, p1.user_id,
count(p2.id) seq -- this count() creates a sequence number for each user ordered by date
from payment p1
left join payment p2
on p1.user_id = p2.user_id -- here I set sequence for each user
and p1.date <= p2.date -- here I set sequence is ordered by data
group by p1.id, p1.date, p1.status, p1.user_id
) t
on users.id = t.user_id
where t.seq < 5 -- Now filter last 4 sequences of each user's payments
and t.status = 'failed'
group by users.id, users.name, users.email, users.password
having count(*) = 4; -- At last filter those have 4 failed in last 4 sequences
[ SQL Fiddle Demo ]

Order by desc in MySQL

I have the following query:
SELECT u.first_name, o.created_at
FROM user AS u
INNER JOIN order AS o ON o.user_id = u.id
GROUP BY u.id
The data structure looks like this:
user 1
order 1
order 2
order 3
user 2
order 1
user 3
order 1 order 2
user 4
order 1 order 2 order 3 order 4 order 5
Currently the query is returning data as follow:
user 1 > order 1
user 2 > order 1
user 3 > order 1
user 4 > order 1
but I would like to have all last items of user order, as example below:
user 1 > order 3
user 2 > order 1
user 3 > order 2
user 4 > order 5
Is there a way to get this nicely?
I need to retrieve last order of each user to generate the report.
Try this query :-
SELECT u.first_name, o.created_at
FROM user AS u
INNER JOIN order AS o ON o.user_id = u.id
Where o.created_at=(SELECT MAX(o2.created_at)
FROM order o2
WHERE o.user_id = o2.user_id);
GROUP BY u.id
this SELECT MAX(o2.created_at) use for get MAX created_at
SELECT u.first_name, max(o.created_at) as max_created_at
FROM user u
INNER JOIN order AS o ON o.user_id = u.id
GROUP BY u.id

mysql - php . How to find

i have 2 table
users : id , name
order : id , price, user_id
a users have more than one order. And i want to select a users
who have total price >100 . how to do ?
EX : user John have 3 orders with price : 30$, 40$ , 50$
SELECT * FROM user u
INNER JOIN order o ON o.user_id = u.id
WHERE sum(o.price) >100
this query will get the user is john ? 30+40+50 = 110 >100
?
First of all you are not allowed to use aggregate functions (i.e. SUM) in your WHERE clause. They should be used in the HAVING clause. To compute aggregates you need to GROUP BY something, in your case by u.id.
So:
SELECT *
FROM user u
INNER JOIN order o ON u.user_id = u.id
GROUP BY u.id
HAVING sum(o.price) > 100
Note that if you would need users with no order you would have to use LEFT JOIN!
SELECT u.Id, Sum(o.price) FROM user u
INNER JOIN order o ON o.user_id = u.id
GROUP BY u.Id
HAVING sum(o.price) > 100
Thank for all answer but i catch problem
now i have a new table order_file
users : id , name
order : id , price, user_id
order_file : id , file , order_id
users, order is one-many
order , order_file is one-many
a user John have 1 order with price is 200$
and this order link to order_file with two record
SELECT *
FROM user u
INNER JOIN order o ON u.user_id = u.id
INNER JOIN order_file of ON of.order_id = o.id
WHERE u.name = 'John'
we will get two rows.Only diff value in order_file
now to get once i use DISTINCT , and i get 1 row
SELECT DISTINCT *
FROM user u
INNER JOIN order o ON u.user_id = u.id
INNER JOIN order_file of ON of.order_id = o.id
WHERE u.name = 'John'
then i do SUM , oh la la , i get 400$ , not 200$
SELECT DISTINCT *, sum(o.price)
FROM user u
INNER JOIN order o ON u.user_id = u.id
INNER JOIN order_file of ON of.order_id = o.id
WHERE u.name = 'John'
how to i can get extracty 200$ , not duplicate row :(