1

I have a problem with my mysql query.

My database:

sections
id
section_name

grades
id
user_id
section_id
date
grade

I want my data to be shown like this:

Section_name grade

But i wat want the grade to be the closest to todays date... This is what i have so far but it doesn't show the latest grade. instead it orders by id (I guess)

SELECT *

                        FROM 
                        grades, 
                        sections 

                        WHERE 
                        sections.id = grades.section_id

                        AND
                        grades.user_id = '.$id.'

                        GROUP BY grades.section_id

                        ORDER BY grades.date DESC

EDIT: the $id variable is the user id from a session.

DaDu
  • 103
  • 1
  • 2
  • 9

8 Answers8

1

My solution involves the use of subquery to get the id of the latest grade and then passing it up to the query which then joins both sections and grades table to return section_name and grade. Please consider this more as a psuedocode than a valid sql query as I dont have time to test it. Might come back later to edit it

SELECT section_name, grade FROM sections, grades WHERE sections.id = grades.id AND grades.id = (SELECT id FROM grades WHERE section_id = '$Id' ORDER by date DESC LIMIT 1)

boug
  • 1,859
  • 1
  • 13
  • 13
  • @piotrm - you are right but like i said in my answer, it should be considered a psuedocode. – boug May 27 '11 at 07:47
  • @DaDu: I thought you only wanted to get the latest grade under a particular section. do you also want to show other grades as well? – boug May 27 '11 at 07:48
  • No, I want from every section the newest grade. – DaDu May 27 '11 at 09:15
1

I would pre-query based on the specific user_id you wanted, and find their max date per section.. Then, re-join back to sections and grades (now that this prequery will extremely limit the result set to join against). Then get section name and finally proper grade for the date matching the specific student (user) taking the course.

select STRAIGHT_JOIN
      PreQuery.Section_ID,
      Sections.section_name,
      PreQuery.LastDatePerSection
   from
      ( select section_id, 
               user_id,
               max( date ) as LastDatePerSection 
            from
               grades
            where
               user_id = YourUserIDParameter
            group by
               section_id ) PreQuery

      join sections
         on PreQuery.Section_ID = Sections.ID

      join grades
         on PreQuery.Section_ID = grades.Section_ID
         AND PreQuery.User_ID = grades.User_ID
         AND PreQuery.LastDatePerSection = grades.Date
DRapp
  • 47,638
  • 12
  • 72
  • 142
0

If I get this right you want the latest grade per section. This cannot be done simply with MySQL.

You'd need a window function or a sequence generator, neither of which are available in MySQL. There is a workaround using variables, however. See this question for a similar use-case:

MySQL query: Using UNION and getting row number as part of SELECT

Additional background information here:

SQL/mysql - Select distinct/UNIQUE but return all columns?

What you'll likely end up doing is a big nested query that will look like this:

select t.*
from (select @rownum := @rownum + 1 rownum,
             t.*
      from (select *
              from grades join sections on sections.id = grades.section_id 
             where user_id=$id order by grades.date desc
           ) t,
           (select @rownum := 0) r
      ) t
where t.rownum = 1
Community
  • 1
  • 1
Denis de Bernardy
  • 75,850
  • 13
  • 131
  • 154
0

I recently had to do something similiar, but with T-SQL so you'll have to change it yourself to get it to work in MySQL (which I don't have installed here so I can't check for you I'm afraid).

Add to the WHERE clause:

AND grades.date = (SELECT TOP 1 date 
                     FROM grades 
                       WHERE date > GETDATE() 
                   ORDER BY ABS(CONVERT(FLOAT,GETDATE() - date)))

GETDATE() is equivalent to NOW(), TOP 1 is like LIMIT 1 and the other functions, well I don't know the MySQL equivalents off the top of my head!

dnagirl
  • 20,196
  • 13
  • 80
  • 123
Ian Devlin
  • 18,534
  • 6
  • 55
  • 73
  • Trouble is, there is no `select top 1` in mysql. :-) – Denis de Bernardy May 26 '11 at 13:09
  • Ah thanks @dnagirl for the formatting, I never get that right :-( @Denis I know, hence the LIMIT 1, which should help if the internal query is also ordered by date desc. – Ian Devlin May 26 '11 at 13:37
  • There won't be any grades with date > NOW(), we are interested in grades we already have stored, not future ones. – piotrm May 26 '11 at 14:26
  • And not much will be left, just some order which could be written as `ORDER BY date DESC` and it will restict results to the row with most recent date for the whole table and not for the given user and section. – piotrm May 26 '11 at 16:46
0

If I understand correctly:

SELECT grades.*,sections.* 
  FROM grades INNER JOIN sections ON sections.id = grades.section_id 
  WHERE user_id=$id  ORDER BY grades.date DESC LIMIT 1
luca
  • 36,606
  • 27
  • 86
  • 125
0
SELECT sections.section_name, grades.grade

                    FROM 
                    grades, 
                    sections 

                    WHERE 
                    sections.id = grades.section_id

                    AND
                    grades.user_id = '.$id.'

                    ORDER BY grades.date DESC

                    LIMIT 1

try this?

Vinoth Gopi
  • 734
  • 4
  • 11
0
SELECT grades.*,sections.* 
 FROM grades inner join sections on grades.sections_id = sections.id 
 where grades.user_id = '.$id.' 
 GROUP BY grades.section_id 
 ORDER BY grades.date DESC

you can use this it is working 100%

Jimesh Gajera
  • 612
  • 1
  • 11
  • 32
0

Lets assume we are looking for grades of user with id=1 (just to test query without php). You can replace it with '$Id' later.

If your date column is precise enough to ensure it is unique for every grade you can use:

SELECT s.section_name, g1.grade FROM
  ( SELECT g.section_id, max(g.date) AS last_grade_date
    FROM grades g
    WHERE g.user_id = 1
    GROUP BY g.section_id ) gd
  JOIN grades g1
    ON g1.date = gd.last_grade_date
  JOIN sections s
    ON s.id = gd.section_id

If your date is not unique then you have to join grades back to itself on id found in dependent subquery:

SELECT s.section_name, ga.grade FROM
  ( SELECT g1.section_id, max(g1.date) AS last_grade_date
    FROM grades g1
    WHERE g1.user_id = 1
    GROUP BY g1.section_id ) gmax
  JOIN grades ga
    ON ga.id =
      ( SELECT g2.id
        FROM grades g2
        WHERE g2.user_id = 1
        AND g2.section_id = gmax.section_id
        AND g2.date = gmax.last_grade_date
        LIMIT 1 )
  JOIN sections s
    ON s.id = gmax.section_id

You need index on (user_id, section_id, date) for this query.

ALTER TABLE grades ADD INDEX user_section_date( user_id, section_id, date ) ;

@comment: Ok I'll try to explain the second query as it gives correct results for any case.

In table g1 we take rows from grades for user with id=1, we group them by section and in every section we find a maximum date - which means most recent date. At this point we don't know yet which row exactly holds this most recent date, because we can select only columns that are in the group by or aggregate functions (like max()). Mysql allows to select other columns, like grade, called hidden columns (other dbs would just throw a syntax error), but is free to return any row from each group, usually not the one we want, and we want the one holding most recent date. If all rows have the same value, like user_id in this case, its ok, but we need grades, which can be different in every group. For small tables select can return the right one, thats why some people claim ordering by date can help, because they test it on small tables and see correct results, but it goes wrong once the table grows, there are row updates, deletes and so on.

Basically we now have a list of sections and most recent dates and we need to find out grades. So we have to join this g1 table we just got to the grades table to find a row that holds the most recent date for every section. Id is the only column we are sure to be unique (if we don't join on unique column or unique list of columns we'll get more rows and we want exactly one) so we try to find this id in dependent subquery g2 (its a subquery that references values from outside, from gmax in this case, which is just an alias for g1, explained earlier).

Once we have grade for every section_id the only thing left to do is to join that to sections table on section_id to get section_name instead of its id value.

piotrm
  • 12,038
  • 4
  • 31
  • 28