I am currently running the following query that returns a set of jobs along with their categories and the user name of the person who posted it.
SELECT job_id, user_id, title, profiles.user_name
FROM (jobs)
JOIN profiles ON jobs.user_id = profiles.user_id
JOIN job_categories ON jobs.cat_id = job_categories.cat_id
JOIN job_sub_categories ON jobs.sub_cat_id = job_sub_categories.sub_cat_id
WHERE `status` = 'open'
ORDER BY post_date desc
LIMIT 5
I have a table called feedback that holds rows of feedback for a particular employer based on their previous transactions (much like ebay).
feedback_id|employer_id|job_id|performance_score|quality_score|availability_score|communication_score
What I want to be able to do is to sort and filter my results based on an employers current feedback rating and I'm not sure how to add this into my query. It seems like I have to do some math within my query or run a sub-query perhaps?. Or should I modify my feedback table to include another field such as total feedback given for a particular rating?
Any help would be greatly appreciated.
Rating is calculated at all of the feedback scores added together divided by the number of rows then divided by the number 4 because there are 4 scored fields (performance, quality, availability and communication) so feedback_avg = (feedback_total/num_rows)/4
Let me give it a shot. I will assume you have only two tables, employers: [id, name] and feedback: [id, employer_id, score].
First off, the score subquery:
SELECT employer_id, SUM(score) AS total_score, COUNT(*) AS num_rows
FROM feedback GROUP BY employer_id;
Now the main query:
SELECT name, total_score/num_rows AS avg_score
FROM employers JOIN ([subquery]) AS sq ON(employers.id = sq.employer_id)
WHERE avg_score > 0.5;
Paste the entire subquery into the indicated position.
Tip: Views
If you like, you can make the sub-query a permanent view, and use that in the main query:
CREATE VIEW score_tally AS
SELECT employer_id, SUM(score) AS total_score, COUNT(*) AS num_rows
FROM feedback
GROUP BY employer_id;
SELECT name, total_score/num_rows AS avg_score
FROM employers JOIN score_tally ON(employers.id = score_tally.employer_id)
WHERE avg_score > 0.5;
Tip (again): The above tip was stupid, we should use the built-in AVG:
CREATE VIEW score_tally AS
SELECT employer_id, AVG(score) AS avg_score
FROM feedback
GROUP BY employer_id;
SELECT name, avg_score
FROM employers JOIN score_tally ON(employers.id = score_tally.employer_id)
WHERE avg_score > 0.5;
Let's guess what your complete query might look like:
SELECT job_id,
user_id,
title,
profiles.user_name AS user_name,
avg_score
FROM jobs
JOIN profiles ON(jobs.user_id = profiles.user_id)
JOIN job_categories ON(jobs.cat_id = job_categories.cat_id)
JOIN job_sub_categories ON(jobs.sub_cat_id = job_sub_categories.sub_cat_id)
JOIN (SELECT employer_id, AVG(score) AS avg_score FROM feedback GROUP BY employer_id) AS sq
ON(employers.id = sq.employer_id)
WHERE status = 'open' AND avg_score > 0.5
ORDER BY post_date desc
LIMIT 5
Related
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.
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.
I am a newbie in MYSQL and had a question regarding the use of MAX and COUNT functions together in MYSQL. I have 2 tables worker and assignment and the primary key of worker is a foreign key in assignment table.
I need to show the employees name and id and the total assignment assigned to him, and only show the person with the most assignment that is the employee with the most assignment.
my code is
SELECT worker.Wrk_ID, worker.Wrk_LastName, MAX(a.count_id)
FROM worker,
(SELECT COUNT(assignment.Wrk_ID) as count_ID
FROM worker, assignment
WHERE worker.Wrk_ID = assignment.Wrk_ID
GROUP BY worker.Wrk_ID)as a
GROUP BY worker.Wrk_ID;
The code is giving an error no. #1054.
Please can anyone help me.
Thanking you in anticipation.
Try something like this:
SELECT worker.Wrk_ID, worker.Wrk_LastName, S.Count
FROM worker
JOIN
(SELECT Wrk_ID, COUNT(*) AS Count FROM Assignments
GROUP BY Wrk_Id ORDER BY COUNT(*) DESC LIMIT 1) S
ON worker.Wrk_ID = S.Wrk_ID
If you want a list of employees sorted by their total assignments:
SELECT w.WrkID, w.Wrk_LastName, COUNT(*) AS Assignments
FROM work w left join Assignments a
ON w.WrkID=a.WrkID
GROUP BY w.WrkID
ORDER BY COUNT(*) DESC;
To allow multiple winners:
SELECT s.*, w.Wrk_Lastname FROM
(
SELECT wrk_id , COUNT(*) AS tot_assignments
FROM Assignments
GROUP BY wrk_id
HAVING COUNT(*) =
(
SELECT MAX(tot) FROM
(
SELECT COUNT(*) AS TOT FROM Assignments GROUP BY wrk_id
) counts
)
) winners
INNER JOIN worker w ON s.wrk_id = w.wrk_id;
It can be slow since it does multiple GROUP BY. Doing it in separated steps in a procedure can be better.
Each student has some scores (and each score has student_id column).
I want to calculate student average, compare his average with other student, and find his position in his class.
Is it possible to find his position based on his average with 1 query? (may contains subqueries or joins)
I can sort all students by their average by this query:
SELECT s.*
FROM
scores s LEFT JOIN lessons lesson
ON lesson.id = s.lesson_id
WHERE lesson.display = 1
GROUP BY s.student_id
ORDER BY AVG(s.score) DESC
but it needs processing with PHP array_search function. (I think using MySQL functions is better, in this situation)
select student_id, AVG(scores) as 'average' from lessons as l, scores as s
where lessons.id = s.lesson_id and lesson.display = 1
GROUP BY s.student_id order by average desc`
Try this query
sample http://sqlfiddle.com/#!2/4fb8d/1
Hope this helps
select s.id, AVG(l.scores) as average from lessons as l, student as s
where l.id = s.lessonid and l.display = 1
GROUP BY s.id order by average desc
can solve your problem
Check this link
Thanks to Meherzad's sql query
I think you are looking for something like this:
SELECT COUNT(DISTINCT average)
FROM (
SELECT AVG(score) as average
FROM scores INNER JOIN lessons
ON scores.lesson_id = lessons.id
WHERE display=1
GROUP BY student_id
) sub_scores
WHERE average >= (SELECT AVG(score)
FROM scores INNER JOIN lessons
ON scores.lesson_id = lessons.id
WHERE display=1
AND student_id=1)
In the subquery sub_scores I'm calculating all averages of all students, in the outer query I'm calculating the number of distinct averages bigger than the average of student 1.
This will return the position of student 1.
Depending on what you are after, you might want to remove DISTINCT clause.
See this fiddle.
You need to use a user defined variable over the result set.
Try this:
SELECT *, (#rank := if(#rank is null, 1, #rank + 1)) as rank
FROM (SELECT s.*
FROM scores s
LEFT JOIN lessons lesson ON lesson.id = s.lesson_id
WHERE lesson.display = 1
GROUP BY s.student_id
ORDER BY AVG(s.score) DESC
) x
I want to create a lecturers' monthly attendance report in my course management system. In this report I need to use nested select queries to count number of presence and absence of lecturers in show them in specific records.
I thought the possible way to do it is to use a select statement for each of the attendance status to be counted if the lecturer code is the same as the lecturer code being processed in the main query!
select lecturers.*, sum(distinct courses.credit) as 'Due Credits',(select count(*) from attendances where status='1' ) as Present, (select count(*) from attendances where status='0' ) as Absent from lecturers,courses,attendances
where attendances.lecturer_code=lecturers.code
AND courses.lecturer_code=lecturers.code
group by lecturer_code
[code, Name,Department,Official Position, Degree are fixed attributes]
I need to know if there is a better way to do it!
Thank you so much!
SELECT lecturers.*,
SUM(DISTINCT courses.credit) AS `Due Credits`,
SUM(attendances.status = 1) AS `Present`,
SUM(attendances.status = 0) AS `Absent`,
FROM lecturers
JOIN courses ON courses.lecturer_code = lecturers.code
JOIN attendances ON attendances.lecturer_code = lecturers.code
GROUP BY lecturer_code