1

I have a table named grade with 4 columns: student_id, subject_id, grade, and date.

student_id subject_id grade date
Jenny math 90 2021-12-08
Susan math 60 2021-12-08
Jenny math 80 2021-12-07
Susan math 50 2021-12-07
Jenny science 80 2021-12-08
Susan science 90 2021-12-08
Jenny science 76 2021-12-06
Susan science 85 2021-12-06

I would like to select all rows with only the last grade of each student for each subject. Basically, I want to select all rows with unique student_id, subject_id, like this:

student_id subject_id grade date
Jenny math 90 2021-12-08
Susan math 60 2021-12-08
Jenny science 80 2021-12-08
Susan science 90 2021-12-08

Here is what I have tried:

await Grade.findAll({
    attributes: ['student_id', 'subject_id', 'grade', 'date'],
    raw: true,
    group: ['student_id', 'subject_id']
})

However, I get the following error:

SequelizeDatabaseError: column "grade.grade" must appear in the GROUP BY clause or be used in an aggregate function
Anatol
  • 3,720
  • 2
  • 20
  • 40
  • I don't know how to write in the Sequelize.JS but in postgres you need to use window function over (student_id and subject_id ) order by date DESC and – sia Dec 08 '21 at 16:36

1 Answers1

2

You are close. You are grouping by student_id and subject_id and you just need MAX('date').

await Grade.findAll({
    attributes: ['student_id', 'subject_id', 'grade', [Sequelize.fn('max', Sequelize.col('date')), 'date']],
    raw: true,
    group: ['student_id', 'subject_id']
})

An array in attributes can perform function and alias to a given name [function, alias].

For example:

[Sequelize.fn('max', Sequelize.col('date')), 'new_name']]

this syntax in attributes create SQL as

MAX(`date`) as `new_name`

========================================================

Update:

The above query doesn't work in Postgres.

ref: https://dba.stackexchange.com/a/194352

To achieve the same query in Postgres, 1 alternative solution is to use DISTINCT ON.

await Grade.findAll({
    attributes: [Sequelize.literal('DISTINCT ON ("student_id", "subject_id") *'),
    'id', 'student_id', 'subject_id', 'date', 'grade'],
    order: ['student_id', 'subject_id', ['date', 'DESC']]
})

More about DISTINCT ON query, please check https://zaiste.net/posts/postgresql-distinct-on/

This article also touches upon the use of window function as @sia mentioned.

Some interesting benchmark of DISTINCT ON and ROW_NUMBER https://stackoverflow.com/a/34715134/2956135

Emma
  • 8,518
  • 1
  • 18
  • 35
  • Thanks @Emma! I tried to add `id` to the attributes and got the following error: `SequelizeDatabaseError: column "grade.id" must appear in the GROUP BY clause or be used in an aggregate function`. Any ideas? – Anatol Dec 09 '21 at 12:29
  • Could you add your current `findAll` function in your original question? Please keep the original code. – Emma Dec 09 '21 at 15:23
  • Your answer didn't work and I got the following error: `SequelizeDatabaseError: column "grade.grade" must appear in the GROUP BY clause or be used in an aggregate function`. It only worked when I removed the `grade` attribute. – Anatol Dec 09 '21 at 16:12
  • my bad. I was testing on `sqlite` but this SQL is not allowed in postgres. I will update. – Emma Dec 09 '21 at 16:26
  • Thanks @Emma for your very helpful answer! – Anatol Dec 10 '21 at 12:02