Group by and order with joining tables - mysql

I am having a problem sorting results from joining tables that have to be grouped in Mysql.
This is my tables setup.
Owners Table
owner_id | owner_name
1 | Test owner 1
2 | Test owner 2
3 | Test owner 3
Images upload table
image_id | image_name | ownerid | upload_date
1 | image1.jpg | 2 | 04-08-2009
2 | image2.jpg | 1 | 04-08-2009
3 | image3.jpg | 3 | 04-08-2009
4 | image4.jpg | 1 | 04-08-2009
5 | image5.jpg | 3 | 04-08-2009
The owner_id field is auto increment and so is the image_id fields.
What I am trying to do is get the owner_name for the last three uploaded images but not by the same owner. So in that example I would like it to return the following results.
Test owner 3
Test owner 1
Test owner 2
In that example the last owner to upload is Test owner 3 then Test owner 1 then Test owner 2.
I am using the following query but it does not return correct results
$sql = "SELECT u.*, s.* FROM UPLOAD_TBL u, OWNER_TBL s WHERE u.ownerid = s.owner_id
GROUP BY s.owner_id ORDER BY u.image_id DESC LIMIT 0, 3";
Any help setting up this query would be greatly appreciated.

See if you can sort on the aggregated image id:
select s.owner_id, s.owner_name, max(u.imag_id) as last_image_id
from UPLOAD_TBL u
inner join OWNER_TBL s on s.ownerid = u.owner_id
group by s.owner_id, s.owner_name
order by last_image_id desc
limit 3

You should simply group by owner_id and sort by upload_date DESC with LIMIT 3

How about something like:
select
distinct(owner_id), owner_name
from
owner
inner join
images on images.ownerid = owner.ownerid
order by
images.upload_date desc limit 3

Related

MYSQL - Order a table by another table

I have a problem
I have two tables
The table "Memes"
id imglink name
----------------------------------
1 img.Png Polite cat
2 img2.png Crying cat
And the table "Vote"
id idmeme vote
---------------------
1 1 5
2 1 2
3 2 4
So basically the table "meme" contains memes with their image and their name
And the table "votes" contains the notes on 5 that users assign to the memes
I would like my sql query to rank by the same the highest rated with the highest rating
I already look at other topic but the problem is that for each vote with the id of the same it duplicates in the result of the SELECT *
thank you in advance
One method is to use a subquery right in the order by:
select m.*
from memes m
order by (select max(v.vote) from vote v where v.idmeme = m.id);
Of course, you can also include this in the from clause (as an aggregation query) and use a join.
The most efficient way is to use a query that returns all the maximum votes from Vote and join it to the table:
select m.*
from Memes m left join (
select idmeme, max(vote) vote
from Vote
group by idmeme
)v on v.idmeme = m.id
order by v.vote desc, m.name
See the demo.
Results:
| id | imglink | name |
| --- | -------- | ---------- |
| 1 | img.Png | Polite cat |
| 2 | img2.png | Crying cat |

MySQL add avg of count by id to existing select with id

Im not even sure what the title of this question should be but lets start out with my data.
I have a table of users who have taken a few lessons while belonging to a particular training center.
lesson table
id | lesson_id | user_id | has_completed
----------------------------------------
1 | asdf3314 | 2 | 1
2 | d13saf12 | 2 | 1
3 | a33adff5 | 2 | 0
4 | a33adff5 | 1 | 1
5 | d13saf12 | 1 | 0
user table
id | center_id | ...
----------------------------------------
1 | 20 | ...
2 | 30 | ...
training center table
id | center_name | ...
----------------------------------------
20 | learn.co | ...
30 | teach.co | ...
I've written a small chunk but am now stuck as I don't know how to proceed. This statement gets the counted total of completed lessons per user. it then figures the average completed value from a center id. if two users belong to a center and have completed 3 lessons and 2 lessons it finds the average of 3 and 2 then returns that.
SELECT
FLOOR(AVG(a.total)) AS avg_completion,
FROM
(SELECT
user_id,
user.center_id,
count(user_id) AS total
FROM lesson
LEFT JOIN user ON user.id = user_id
WHERE is_completed = 1 AND center_id = 2
GROUP BY user_id) AS a;
The question I have is how do I loop through the training centers table and also append average data from similar select statement as above to each center that is queried. I cant seem to pass the center id down to the subquery so there must be a fundamentally different way to achieve the same query but also loop through training centers.
An example of desired result:
center.id | avg_completion | ...training center table
-----------------------------------------------------
20 | 2 | ...
Your main query needs to select a.center_id and then use GROUP BY center_id. You can then join it with the training_center table.
SELECT c.*, x.avg_completion
FROM training_center AS c
JOIN (
SELECT
a.center_id,
FLOOR(AVG(a.total)) AS avg_completion
FROM (
SELECT
user_id
user.center_id,
count(*) AS total
FROM lesson
JOIN user ON user.id = user_id
WHERE is_completed = 1 AND center_id = 2
GROUP BY user_id) AS a
GROUP BY a.center_id) AS x
ON x.center_id = c.id
If I understand correctly:
select u.center_id, count(*) as num_users,
sum(l.has_completed) as num_completed,
avg(l.has_completed) as completed_ratio
from lesson l join
user u
on l.user_id = u.id
group by u.center_id

Getting the most recent row and linking it with another table?

Im trying to get the most recent row of a table
user_quiz:
+--------+-----------+-------------+-------------------+------------+
|quiz_id |userid | module_id |number_of_questions| user_score |
+--------+-----------+-------------+-------------------+-------- ---+
| 1 | 1 | 1 | 5 | 5 |
| 2 | 2 | 2 | 10 | 9 |
| 3 | 1 | 1 | 10 | 9 |
+--------+-----------+-------------+-------------------+------------+
I have used the query:
SELECT * FROM user_quiz WHERE userid = 1 ORDER BY quiz_id DESC LIMIT 1
which correctly retrieves the last row.
However I want to link the module_id with another table:
module:
+---------+------------+
|module_id|module_name |
+---------+------------+
| 1 | Forces |
| 2 | Electricity|
+---------+------------+
And retrieve the module name.
The result of the query will be used to print out the users most recent quiz:
Most recent quiz: Forces - Number of questions: 10 - User Score: 9
Is this possible using just one query?
You just need a JOIN:
SELECT uq.*, m.module_name
FROM user_quiz uq JOIN
modules m
ON uq.module_id = m.module_id
WHERE uq.userid = 1
ORDER BY uq.quiz_id DESC
LIMIT 1;
A more simple query to achieve the same would be
SELECT
user_quiz.quiz_id,
user_quiz.number_of_questions,
user_quiz.user_score,
modules .module_name
FROM user_quiz JOIN modules
ON user_quiz.module_id = modules.module_id
WHERE user_quiz.userid = 1
ORDER BY user_quiz.quiz_id DESC
LIMIT 1
If you want to get the same results for all the users, you could use a bit more sophisticated query
SELECT
user_quiz_virtual_table.userid,
user_quiz_virtual_table.quiz_id,
user_quiz_virtual_table.number_of_questions,
user_quiz_virtual_table.user_score,
modules.module_name
FROM (
SELECT
user_quiz.userid
user_quiz.quiz_id,
user_quiz.module_id
user_quiz.number_of_questions,
user_quiz.user_score
FROM user_quiz
ORDER BY user_quiz.quiz_id DESC
GROUP BY userid
) AS user_quiz_virtual_table
JOIN modules ON user_quiz_virtual_table.module_id = modules.module_id

MySql Preferential Sorting Using FIELD

Student table :
----------------------
id | name
______________________
1 | Name 1
2 | Name 2
Course Table :
id | student_id | ctype | level
__________________________________
1 | 2 | beginner | complete
2 | 2 | advanced | current
3 | 1 | beginner | current
4 | 2 | intermed | skipped
From the above two table i am trying to get the latest user records based on the level from course table . the level should be matched such that it checks for current, complete and skipped in the same order so if the user has a level of current for any course type it should be fetched else check the level complete...
i am using the following query .
SELECT `sc`.`student_id`,
`s`.`name`,
`sc`.`id` as `course_id`,
`sc`.`ctype`,
`sc`.`level`,
FROM `course` `sc`
LEFT JOIN `students` `s` ON `s`.`id` = `sc`.`student_id`
WHERE sc.id = (SELECT ssc.id FROM course ssc WHERE ssc.student_id = sc.student_id
ORDER BY FIELD(`ssc`.`level`,"current","complete","skipped") DESC LIMIT 1,1)
GROUP BY `sc`.`student_id`
ORDER BY `sc`.`id` DESC
LIMIT 20
The problem with the above query is it displays only if there is more than one user id matching in course table . so the final output i get is it displays only the student with id 2 and ignore the student with id 1 as there is no more than one .
Result form above query
student_id | name | course_id | ctype | level |
=====================================================
2 | Name 2 | 2 | advanced | current
Expected Result
student_id | name | course_id | ctype | level |
=====================================================
2 | Name 2 | 2 | advanced | current
1 | Name 1 | 3 | beginner | current
NOTE : I have also tried FIELD_IN_SET and IN instead of FIELD im getting the same result
Change LIMIT 1,1 to LIMIT 0,1 or just LIMIT 1.
Unlike most other things in SQL, the offset field in the LIMIT clause is 0-based, not 1-based. So if there's only 1 matching row, LIMIT 1,1 skips over it. And if there are 2 or more matching rows, you're not getting the top match, you're getting the 2nd match.
Also, the ordering should be ASC, not DESC, since you want to prefer the lowest field (current), not the highest.
SELECT `sc`.`student_id`,
`s`.`name`,
`sc`.`id` as `course_id`,
`sc`.`ctype`,
`sc`.`level`
FROM `course` `sc`
LEFT JOIN `students` `s` ON `s`.`id` = `sc`.`student_id`
WHERE sc.id = (
SELECT ssc.id FROM course ssc
WHERE ssc.student_id = sc.student_id
ORDER BY FIELD(`ssc`.`level`,"current","complete","skipped") ASC
LIMIT 0,1)
GROUP BY `sc`.`student_id`
ORDER BY `sc`.`id` DESC
LIMIT 20
DEMO
There's also no need for GROUP BYsc.student_id`. The query is only returning one course ID per student, so there can't be multiple rows for each student.

SUM of Multiple COUNTs on Different Tables

This topic appears to be a popular one and definitely saturated in terms of the number of related posts, however, I've been working on this for 3 days and I cannot get this figured out.
I've been scouring this site and many others with potential solutions to this and some are executing, but I am not getting the expected results.
Here's what I'm trying to do...
SELECT and COUNT the number of reviews a user has submitted in the reviews table.
SELECT and COUNT the number of up-votes a user has in the reviewVotes table.
GROUP BY username (which is a key in both tables - usernames are unique, but exist in multiple rows).
Order the result set by the SUM of those COUNTs DESC. (This is something I keep trying, but can't get to even execute, so I am ordering by userReviewNum DESC right now.)
LIMIT the result set to the first 10.
The result set should give me the top 10 reviewers which is calculated by the number of reviews plus (+) the number of up-votes.
Here is my latest attempt which executes, but appears to be multiplying userReviewNum * reviewVotesNum and I need it to add them (but I have been extremely unsuccessful at any attempt to include the SUM command - so bad in fact that I am embarrassed to even show my attempts).
SELECT
reviews.username,
count(reviews.username) userReviewNum,
count(reviewVotes.username) reviewVotesNum
FROM reviews
LEFT JOIN reviewVotes ON reviews.username = reviewVotes.username
GROUP by reviews.username
ORDER BY userReviewNum DESC
LIMIT 0, 10
I've tried using a JOIN and a UNION and I can't seem to get either of them to work.
Any help anyone can provide is greatly appreciated!
UPDATE:
Here is the structure and some sample data.
Reviews Table (there are other fields, but these are the important ones):
| username | comment | rating | productID |
| foo | this is awesome! | 5 | xxxx |
| bar | i don't like this | 1 | xxxx |
| foo2 | it's ok | 3 | xxxx |
| foo | bleh - nasty | 1 | xxxx |
reviewVotes Table (again, more fields than this, but these are the important ones):
| username | voterUsername | productID |
| foo | foo2 | xxxx |
| foo2 | foo | xxxx | (the simple idea here is one user is up-voting another user's post)
So I need to count the number of reviews a user has in the Reviews table, then count the number of upvotes a user has in the reviewVotes table, and then order by the sum of those two numbers.
Additional UPDATE:
In the example above, here are the expected results:
Username | # Reviews
foo | 2
bar | 1
foo2 | 1
Username | # Up-Votes
foo | 1
foo2 | 1
Username | Total Sum
foo | 3
bar | 1
foo2 | 2
Try counting distinct reviews and votes like this:
SELECT
reviews.username,
COUNT(DISTINCT reviews.id) AS userReviewNum,
COUNT(DICTINCT reviewVotes.id) AS reviewVotesNum,
COUNT(DISTINCT reviews.id) + COUNT(DICTINCT reviewVotes.id) AS userRating
FROM
reviews
LEFT JOIN reviewVotes ON reviews.username = reviewVotes.username
GROUP by reviews.username
ORDER BY userRating DESC
LIMIT 10
Try this:
SELECT username, SUM(userReviewNum + reviewVotesNum) AS userRank
FROM (
SELECT
reviews.username,
count(reviews.username) userReviewNum,
count(reviewVotes.username) reviewVotesNum
FROM reviews
LEFT JOIN reviewVotes ON reviews.username = reviewVotes.username
GROUP by reviews.username
ORDER BY userReviewNum DESC
LIMIT 0, 10)
AS result_set
GROUP BY username
The group by there is, I think, required for the SUM to work.
Try this:
SELECT Res1.*, SUM(IF(reviewVotes.Username IS NULL, 0, 1)) AS UpVotes,
userReviewNum + SUM(IF(reviewVotes.Username IS NULL, 0, 1)) AS TotalSum FROM (
SELECT username, Count(*) AS userReviewNum
FROM reviews
GROUP BY username) AS Res1
LEFT OUTER JOIN reviewVotes ON res1.username = reviewVotes.username
GROUP BY Res1.username
ORDER BY TotalSum DESC
There result would be this:
foo 2 1 3
foo2 1 1 2
bar 1 0 1