MySQL: get average of value from query - mysql

I have 3 table.
Users --> UID, Name, Lat, Longg, Pic
Profile --> pid, uid,City, State, About
Feedback--> fid, uid, ratingby, txnid, rating, feedback_type
Feedback_type can be 1-4 from same txnid. So, if user is giving feedback for all the questions then there will be 4 records for same.
Now i need to show the user details along with the average feedback.
Below is the query i have written so far.
SELECT
a.name,
a.uid,
b.city,
a.pic,
b.state,
b.about
FROM
users AS a
INNER JOIN profile AS b
ON
a.uid = b.uid
I am not sure how can i get the average value from feedback table.
I need show user average feedback and to be more specific. Average of all 4 feedback separately.
Also advise if my approach is good or is there any other best practice that i need to follow.
Edit
I can fetch the single record from feedback.
SELECT uid, avg(rating) FROM `feedback` WHERE uid= 8
But not sure how can i get the average for different feedback_type.

You probably need to create a sub-table to find the average ratings of each type from the Feedbackback table (you can categorize feedback_type for calculating average by using group by f.uid, f.feedback_type). After that, you just need to join the resulting query table with the Users and Profile table to get additional data such as Name, City, etc.
SELECT u1.Name, ar.uid, ar.average_rating, ar.feedback_type, p.City, u1.Pic, p.State, p.About
FROM (
SELECT f.uid, f.feedback_type, AVG(f.rating) AS average_rating
FROM Feedback AS f
WHERE f.uid=8
GROUP BY f.uid, f.feedback_type
) AS ar
INNER JOIN Users AS u1 ON ar.uid=u1.UID
INNER JOIN Profile AS p ON ar.uid=p.uid;
Update: If alias is not working, an alternative approach would be to create a temporary table to calculate user's average rating and use the table to join with Users and Profile tables like above
CREATE TEMPORARY TABLE ar
SELECT uid, feedback_type, AVG(rating) AS average_rating
FROM Feedback
WHERE uid=8
GROUP BY uid, feedback_type;
SELECT Users.Name, ar.uid, ar.average_rating, ar.feedback_type, Profile.City, Users.Pic, Profile.State, Profile.About
FROM ar
INNER JOIN Users ON ar.uid=Users.UID
INNER JOIN Profile ON ar.uid=Profile.uid;
Update: If you need to put the records of 4 feedback types in different columns, you only need to group by uid in ar table and use CASE in AVG to filter out the feedback_type to calculate the average in each column
CREATE TEMPORARY TABLE ar
SELECT
uid,
AVG(CASE WHEN feedback_type = 1 THEN rating END) AS average_rating_1,
AVG(CASE WHEN feedback_type = 2 THEN rating END) AS average_rating_2,
AVG(CASE WHEN feedback_type = 3 THEN rating END) AS average_rating_3,
AVG(CASE WHEN feedback_type = 4 THEN rating END) AS average_rating_4
FROM Feedback
WHERE uid=8
GROUP BY uid;
SELECT
Users.Name,
ar.uid,
ar.average_rating_1,
ar.average_rating_2,
ar.average_rating_3,
ar.average_rating_4,
ar.feedback_type,
Profile.City,
Users.Pic,
Profile.State,
Profile.About
FROM ar
INNER JOIN Users ON ar.uid=Users.UID
INNER JOIN Profile ON ar.uid=Profile.uid;

You can get the avg value using this query SELECT AVG(column_name) FROM table_name WHERE condition;.
To get more columns you should try
SELECT AVG(column_name_1) AS a, AVG(column_name_2) AS b, AVG() AS c, AVG() AS d FROM table_name WHERE condition
I hope i was helpful.

Related

MYSQL: retrieve all the users details with the details of the first sale for each user by date, 2 tables

I'm trying to retrieve all the users details with the details of the first sale for each user by date.
Mysql tables:
enter image description here
my code:
SELECT u.id,u.name,u.email,s.amount,s.date
FROM users u
INNER JOIN sales s ON u.id=s.user_id
ORDER BY u.id,s.date
what its return:
[{"id":"1","name":"aaa","email":"aaa#gmail.com","amount":"5600","date":"2019-11-11"},{"id":"1","name":"aaa","email":"aaa#gmail.com","amount":"3000","date":"2020-01-08"},{"id":"2","name":"bbb","email":"bbb#gmail.com","amount":"6000","date":"2019-12-15"},{"id":"2","name":"bbb","email":"bbb#gmail.com","amount":"1000","date":"2020-06-05"},{"id":"3","name":"ccc","email":"ccc#gmail.com","amount":"7500","date":"2019-09-02"},{"id":"4","name":"ddd","email":"ddd#gmail.com","amount":"5000","date":"2019-03-12"},{"id":"4","name":"ddd","email":"ddd#gmail.com","amount":"4000","date":"2020-04-21"}]
I want to get the earliest date row of each id,like that:
[{"id":"1","name":"aaa","email":"aaa#gmail.com","amount":"5600","date":"2019-11-11"},{"id":"2","name":"bbb","email":"bbb#gmail.com","amount":"6000","date":"2019-12-15"},{"id":"3","name":"ccc","email":"ccc#gmail.com","amount":"7500","date":"2019-09-02"},{"id":"4","name":"ddd","email":"ddd#gmail.com","amount":"5000","date":"2019-03-12"}]>
help someone?
You can use a query that finds the first sales ID for each user as an inline view (a query in the from clause) and join that inline view to the two tables in question. Like this:
select u.*,
s.amount,
s.date
from users u
join (
select user_id,
min(date) as min_date
from sales
group by user_id
) v
join sales s
on u.user_id = v.user_id
and s.date = v.min_date;

Creating a SQL view with personal best records

I have the following SQL Database structure:
Users are the registered users. Maps are like circuits or race tracks. When a user is driving a time a new time record will be created including the userId, mapId and the time needed to finish the racetrack.
I wish to create a view where all the users personal bests on all maps are listed.
I tried creating the view like this:
CREATE VIEW map_pb AS
SELECT MID, UID, TID
FROM times
WHERE score IN (SELECT MIN(score) FROM times)
ORDER BY registered
This does not lead to the wished result.
Thank you for your help!
I hope that you have 'times' table created as the above diagram and 'score' column in the table that you use to measure the best record.
(MIN(score) is the best record).
You can simply create a view to have the personal best records using sub-queries like this.
CREATE VIEW map_pb AS
SELECT a.MID, a.UID, a.TID
FROM times a
INNER JOIN (
SELECT TID, UID, MIN(score) score
FROM times
GROUP BY UID
) b ON a.UID = b.UID AND a.score= b.score
-- if you have 'registered' column in the 'times' table to order the result
ORDER BY registered
I hope this may work.
You probably need to use a query that will first return the minimum score for each user on each map. Something like this:
SELECT UID,
MID,
MIN(score) AS best_time
FROM times
GROUP BY UID, MID
Note: I used MIN(score) as this is what is shown in your example query, but perhaps it should be MIN(time) instead?
Then just use the subquery JOINed to your other tables to get the output:
SELECT *
FROM (
SELECT UID,
MID,
MIN(score) AS best_time
FROM times
GROUP BY UID, MID
) a
INNER JOIN users u ON u.UID = a.UID
INNER JOIN maps m ON m.MID = a.MID
Of course, replace SELECT * with the columns you actually want.
Note: code untested but does give an idea as to a solution.
Start with a subquery to determine each user's minimum score on each map
SELECT UID, TID, MIN(time) time
FROM times
GROUP BY UID, TID
Then join that subquery into a main query.
SELECT times.UID, times.TID,
mintimes.time
FROM times
JOIN (
) mintimes ON times.TID = mintimes.TID
AND times.UID = mintimes.UID
AND times.time = mintimes.time
JOIN maps ON times.MID = maps.MID
JOIN users ON times.UID = users.UID
This query pattern uses a GROUP BY function to find the outlying (MIN in this case) value for each combination. It then uses that subquery to find the detail record for each outlying value.

SQL Query for getting maximum value from a column Joining from Another Table

This is a slight variant of the question I asked here
SQL Query for getting maximum value from a column
I have a Person Table and an Activity Table with the following data
-- PERSON-----
------ACTIVITY------------
I have got this data in the database about users spending time on a particular activity.
I intend to get the data when every user has spent the maximum number of hours.
My Query is
Select p.Id as 'PersonId',
p.Name as 'Name',
act.HoursSpent as 'Hours Spent',
act.Date as 'Date'
From Person p
Left JOIN (Select MAX(HoursSpent), Date from Activity
Group By HoursSpent, Date) act
on act.personId = p.Id
but it is giving me all the rows for Person and not with the Maximum Numbers of Hours Spent.
This should be my result.
You have several issues with your query:
The subquery to get hours is aggregated by date, not person.
You don't have a way to bring in other columns from activity.
You can take this approach -- joins and group by, but it requires two joins:
select p.*, a.* -- the columns you want
from Person p left join
activity a
on a.personId = p.id left join
(select personid, max(HoursSpent) as max_hoursspent
from activity a
group by personid
) ma
on ma.personId = a.personId and
ma.max_hoursspent = a.hoursspent;
Note that this can return duplicates for a given person -- if there are ties for the maximum.
This is written more colloquially using row_number():
select p.*, a.* -- the columns you want
from Person p left join
(select a.*,
row_number() over (partition by a.personid order by a.hoursspent desc) as seqnum
from activity a
) a
on a.personId = p.id and a.seqnum = 1
ma.max_hoursspent = a.hoursspent;

Mysql get sum of two tables columns grouped

I have 3 tables:
I would like to select the difference of the total gain and total spent per user. So my hypothetical table could be:
I tried this:
SELECT g.total - s.total AS quantity, id FROM
(SELECT SUM(quantity) AS total FROM gain GROUP BY user) AS g,
(SELECT SUM(quantity) AS total FROM spent GROUP BY user) AS s, users
But it doesn't work...
You need to use the users table as base table, to be able to consider all the users, and then LEFT JOIN to the sub queries computing the total spent and total gain. This is because some user may not have any entry in either gain or spent table(s). Also, Coalesce() function handles the NULL (in case of no matching row)
SELECT
u.id AS user,
COALESCE(tot_gain, 0) - COALESCE(tot_spent, 0) AS balance
FROM users AS u
LEFT JOIN (SELECT user, SUM(quantity) as tot_spent
FROM spent
GROUP BY user) AS s ON s.user = u.id
LEFT JOIN (SELECT user, SUM(quantity) as tot_gain
FROM gain
GROUP BY user) AS g ON g.user = u.id
Madhur's solution is fine. An alternative is union all and group by:
select user, sum(gain) as gain, sum(spent) as spent
from ((select user, quantity as gain, 0 as spent
from gain
) union all
(select user, 0, quantity as spent
from spent
)
) u
group by user;
You can join to user if you want users that are not in either table or you need additional columns. However, that join may not be necessary.

How to join 3 tables and get sum from result of individual joins?

I have the following scenario:
Table: users : user_id, username ,...
Table: login: user_id, login_date, ...
Table: point: user_id, points, point_time
Joins will be on the basis of users.user_id with other tables.
Now, I want to get count of all the logins as well as sum of all the points earned by the user.
Now, when I do:
select users.user_id,count(*) from users
inner join login on users.user_id=login.user_id
group by users.user_id
It returns count as 36(for example).
Whenever I run:
select users.user_id,count(*),sum(points) from users
inner join point on users.user_id=point.user_id
group by users.user_id
It returns sum as 400(for example) and count as 2.
But if I combine both the queries:
select users.user_id,count(*),sum(points) from users
inner join login on users.user_id=login.user_id
inner join point on users.user_id=point.user_id
group by users.user_id
It returns count as 72 (36 * 2) and sum as 800 (400 *2).
Twice because of multiple userIds present.
I tried several things like combining with distincts but nothing seems to work. Please help.Better if it's possible with joins alone. Thanks in advance. I am using mysql with Php.
You can sum the points in a subquery and select distinct logins in the count
select users.user_id,l.login,p.points from users
inner join (select user_id, count(1) login from login
group by login) as l on users.user_id=login.user_id
inner join (select user_id, sum(point) as point
from point group by user_id ) as p on users.user_id=point.user_id
You should be able to do your count by joining in your login table and then including a subquery to get your count of points:
select users.user_id, count(*) as login_count,
(select sum(points) from point
where point.user_id = users.user_id) as points_sum
from users
inner join login on users.user_id=login.user_id
group by users.user_id