Listing top 5 users measured by most common rows in foreign key table - mysql

Here's a picture of my database structure:
When I input an Observation, I'm entering a row into Observations but also 0, 1 or more rows into Criteria.
I'm trying to write an SQL statement which allows me to select the Strongest 5 teachers in a specific criteria.
So for example I'd click on Questioning (which might have an ID of 5 in Criteria_Labels), I'd want to return a list of 5 teachers (Teacher_ID from Observations) who have the most rows of Criteria_ID = 5 in Criteria.
The statement that I've attempted to write is as follows:
SELECT t.Name AS Teacher_Name
FROM observations o
LEFT JOIN teachers t ON o.Teacher_ID = t.Teacher_ID
LEFT JOIN criteria c ON o.ID = c.Observation_ID
WHERE c.Criteria_ID = 5
ORDER BY COUNT(c.Criteria_ID) DESC
LIMIT 0,5
However, it only appears to return one member of staff. I'm not sure I've got this right at all, but hopefully I'm along the right lines.
Can anyone help? Thanks in advance,

SELECT t.Teacher_ID, t.Name AS Teacher_Name, count(*) as total
FROM observations o
LEFT JOIN teachers t ON o.Teacher_ID = t.Teacher_ID
LEFT JOIN criteria c ON o.ID = c.Observation_ID
WHERE c.Criteria_ID = 5
group by t.Teacher_ID, t.Name
order by total desc
limit 5

Related

MySQL View in place of subquery does not return the same result

The query below is grabbing some information about a category of toys and showing the most recent sale price for three levels of condition (e.g., Brand New, Used, Refurbished). The price for each sale is almost always different. One other thing - the sales table row id's are not necessarily in chronological order, e.g., a toy with a sale id of 5 could have happened later than a toy with a sale id of 10).
This query works but is not performant. It runs in a manageable amount of time, usually about 1s. However, I need to add yet another left join to include some more data, which causes the query time to balloon up to about 9s, no bueno.
Here is the working but nonperformant query:
SELECT b.brand_name, t.toy_id, t.toy_name, t.toy_number, tt.toy_type_name, cp.catalog_product_id, s.date_sold, s.condition_id, s.sold_price FROM brands AS b
LEFT JOIN toys AS t ON t.brand_id = b.brand_id
JOIN toy_types AS tt ON t.toy_type_id = tt.toy_type_id
LEFT JOIN catalog_products AS cp ON cp.toy_id = t.toy_id
LEFT JOIN toy_category AS tc ON tc.toy_category_id = t.toy_category_id
LEFT JOIN (
SELECT date_sold, sold_price, catalog_product_id, condition_id
FROM sales
WHERE invalid = 0 AND condition_id <= 3
ORDER BY date_sold DESC
) AS s ON s.catalog_product_id = cp.catalog_product_id
WHERE tc.toy_category_id = 1
GROUP BY t.toy_id, s.condition_id
ORDER BY t.toy_id ASC, s.condition_id ASC
But like I said it's slow. The sales table has about 200k rows.
What I tried to do was create the subquery as a view, e.g.,
CREATE VIEW sales_view AS
SELECT date_sold, sold_price, catalog_product_id, condition_id
FROM sales
WHERE invalid = 0 AND condition_id <= 3
ORDER BY date_sold DESC
Then replace the subquery with the view, like
SELECT b.brand_name, t.toy_id, t.toy_name, t.toy_number, tt.toy_type_name, cp.catalog_product_id, s.date_sold, s.condition_id, s.sold_price FROM brands AS b
LEFT JOIN toys AS t ON t.brand_id = b.brand_id
JOIN toy_types AS tt ON t.toy_type_id = tt.toy_type_id
LEFT JOIN catalog_products AS cp ON cp.toy_id = t.toy_id
LEFT JOIN toy_category AS tc ON tc.toy_category_id = t.toy_category_id
LEFT JOIN sales_view AS s ON s.catalog_product_id = cp.catalog_product_id
WHERE tc.toy_category_id = 1
GROUP BY t.toy_id, s.condition_id
ORDER BY t.toy_id ASC, s.condition_id ASC
Unfortunately, this change causes the query to no longer grab the most recent sale, and the sales price it returns is no longer the most recent.
Why is it that the table view doesn't return the same result as the same select as a subquery?
After reading just about every top-n-per-group stackoverflow question and blog article I could find, getting a query that actually worked was fantastic. But now that I need to extend the query one more step I'm running into performance issues. If anybody wants to sidestep the above question and offer some ways to optimize the original query, I'm all ears!
Thanks for any and all help.
The solution to the subquery performance issue was to use the answer provided here: Groupwise maximum
I thought that this approach could only be used when querying a single table, but indeed it works even when you've joined many other tables. You just have to left join the same table twice using the s.date_sold < s2.date_sold join condition and make sure the where clause looks for the null value in the second table's id column.

query result taking time to load

I have a query that fetches data from Six tables but it takes too much time to fetch data.The browser loads and shows sometimes nothing as a result.When I run this query in the MySQL database, it takes a long time to execute.
SELECT SQL_CALC_FOUND_ROWS movies.*,
curriculums.name AS curriculum,
teachers.name AS teacher,
movie_sub_categories.name AS sub_cat_name,
movie_categories.name AS cat_name
FROM movies
LEFT JOIN curriculums on movies.curriculum_id = curriculums.id
LEFT JOIN teachers on movies.teacher_id = teachers.id
LEFT JOIN movies_movie_sub_categories on movies.id = movies_movie_sub_categories.movie_id
LEFT JOIN movie_sub_categories on movies_movie_sub_categories.movie_sub_category_id = movie_sub_categories.id
LEFT JOIN movie_categories on movie_sub_categories.movie_category_id = movie_categories.id
ORDER BY id LIMIT 0, 50
Here all of my table structure
That's not a very exciting query -- it simply delivers the first 50 rows of whichever table id belongs to. When JOINing, please qualify columns so we know what is going on.
Do you really need LEFT?
Assuming you need LEFT and id belongs to movies, then this should run a lot faster:
Meanwhile, find how many rows there are in movies only once, so you don't have to compute it every time.
SELECT movies.*, curriculums.name AS curriculum,
teachers.name AS teacher, movie_sub_categories.name AS sub_cat_name,
movie_categories.name AS cat_name
FROM ( SELECT id FROM movies ORDER BY id LIMIT 0, 50 ) AS m
JOIN movies USING(id)
LEFT JOIN curriculums AS c ON movies.curriculum_id = c.id
LEFT JOIN teachers AS t ON movies.teacher_id = t.id
LEFT JOIN movies_movie_sub_categories AS mmsc ON movies.id = mmsc.movie_id
LEFT JOIN movie_sub_categories AS msc ON mmsc.movie_sub_category_id = msc.id
LEFT JOIN movie_categories AS mc ON msc.movie_category_id = mc.id
ORDER BY m.id
Please use SHOW CREATE TABLE; we need to see if you have sufficient indexes, such as
mmsc: INDEX(movie_id)
the table movies_movie_sub_categories needs to have an index on movie_id and a separate index on movie_sub_category_id. Without those two indexes the query builder will be forced to scan every record twice (since the query has two separate join clauses that reference that table)

Mysql table join records from 2 tables but get all records from main table

I have a students table and a results table, both with studentId. There can be one or no results for each student in the results table. There can be more but with parameters i just want one or none, but i still want all the students even if they have no result, results are multiple columns pivoted. What join do i need, i've tried them all. Or do i need a subquery?
Thanks, sql below returns only students with results, assessment values, i want all students in classId 3 even if they don't have and entry. Its going in a data grid so i can insert/edit assessment results
select s.studentId, r.resultID as ThisResultID, r.*, arv.*, u.fullname as TeacherName from (Results R
left join AssessmentResultValues arv on r.resultID = arv.resultID)
left join users u on u.userID = R.userID
left join students as s on s.studentId = R.studentId
where arv.resulttypeID = 32
and assessmentID = 181
and yearID = 21
and Month = 0
and Term = 0
and Week = 0
and Semester = 1
and s.studentClassId = 3
order by dateentered
Put the students table first in the list of tables after FROM. When you do a left join, all records will be included from the first table, but from the second table you will only get records matching the join condition. In your query, the students table is the second table in a left join, and that is why all students are not included in the result.

mysql join query - QUERY

i have two tables one paper and one attempts. What i am trying ot do is to show the list of papers and the attempts made on each paper by a particular candidate
query is
SELECT
p . * ,
GROUP_CONCAT( a.attempt_id ORDER BY a.date_time DESC ) AS ATT_ID
FROM papers AS p
LEFT JOIN attempts AS a ON p.prod_id = a.paper_id
WHERE (a.user_id =15 OR a.user_id IS NULL)
GROUP BY p.prod_id
The query works partially where it will show all the papers the client has not attempted but it will not show the paper if the client has not attempted but another HAS attempted, so if the attempt of paper 2 has user id 14 in the attempts table and another client has user id 15 who has not attempted paper 2 then paper 2 does not show up on the list ...
thanks in advance.
An outer join still gives you records when there are none. With paper 2, there is nothing outer-joined, however, because a record is found (for user 14). Later, in your WHERE clause, you remove that record, as you say you only want to keep user 15 or null.
Move the criteria to the ON clause to make it work as desired.
SELECT
p . * ,
GROUP_CONCAT( a.attempt_id ORDER BY a.date_time DESC ) AS ATT_ID
FROM papers AS p
LEFT JOIN attempts AS a ON p.prod_id = a.paper_id and a.user_id = 15
GROUP BY p.prod_id

MySql query to get count of days spent in each country for each purpose? (Get count of all record in second table present in first table)

I have three tables tl_log, tl_geo_countries,tl_purpose. I am trying to get the count of number of days spent in each country in table 'tl_log' for each purpose in table 'tl_purpose'.
I tried below mysql query
SELECT t.country_id AS countryID,t.reason_id AS reasonID,count(t.reason_id) AS
days,c.name AS country, p.purpose AS purpose
FROM `tl_log` AS t
LEFT JOIN tl_geo_countries AS c ON t.country_id=c.id
LEFT JOIN tl_purpose AS p ON t.reason_id=p.id
GROUP BY t.reason_id,t.country_id ORDER BY days DESC
But landed up with.
I am not able to get the count for purpose for each country in 'tl_log' that is not present in table 'tl_log'. Any help is greatly appreciated. Also, Please let me know if the question is difficult to understand.
Expected Output:
Below is the structure of these three tables
tl_log
tl_geo_countries
tl_purpose
If you want all possible combination of countries and purposes, even those that do not appear on the log table (these will be shown with a count of 0), you can do first a cartesian product of the two tables (a CROSS join) and then LEFT join to the log table:
SELECT
c.id AS countryID,
p.id AS reasonID,
COUNT(t.reason_id) AS days,
c.name AS country,
p.purpose AS purpose
FROM
tl_geo_countries AS c
CROSS JOIN
tl_purpose AS p
LEFT JOIN
tl_log AS t
ON t.country_id = c.id
AND t.reason_id = p.id
GROUP BY
p.id,
c.id
ORDER BY
days DESC ;
If you want the records for only the countries that are present in the log table (but still all possible reason/purposes), a slight modification is needed:
SELECT
c.id AS countryID,
p.id AS reasonID,
COUNT(t.reason_id) AS days,
c.name AS country,
p.purpose AS purpose
FROM
( SELECT DISTINCT
country_id
FROM
tl_log
) AS dc
JOIN
tl_geo_countries AS c
ON c.id = dc.country_id
CROSS JOIN
tl_purpose AS p
LEFT JOIN
tl_log AS t
ON t.country_id = c.id
AND t.reason_id = p.id
GROUP BY
p.id,
c.id
ORDER BY
days DESC ;
LEFT JOIN should be replaced by RIGHT JOIN