PostgreSQL last_value() Function

The PostgreSQL last_value() function returns the evaluated value from the last row of the window frame associated with the current row.

last_value() Syntax

Here is the syntax of the PostgreSQL last_value() function:

last_value(expr)
OVER (
  [PARTITION BY partition_column_list]
  [ORDER BY order_column_list]
)

Parameters

expr

Required. It can be a column name or an expression.

partition_column_list

List of columns for partitioning.

partition_column_list

List of columns for sorting.

Return value

The PostgreSQL last_value() function returns the evaluated value from the last row of the window frame associated with the current row.

last_value() Examples

Preparing Data

Use the following CREATE TABLE statement to create a table named student_grade to store grades of students:

CREATE TABLE student_grade (
  id SERIAL PRIMARY KEY,
  name VARCHAR(50) NOT NULL,
  class CHAR(1) NOT NULL,
  subject VARCHAR(20) NOT NULL,
  grade INT NOT NULL
);

This student_grade table has 5 columns as following:

  • id - The row ID, primary key.
  • name - The name of a student.
  • class - The class a student is in.
  • subject - The name of a subject.
  • grade - The grades for a subject and a student.

Insert some rows into the student_grade table using the following INSERT statement:

INSERT INTO student_grade
  (name, class, subject, grade)
VALUES
  ('Tim', 'A', 'Math', 9),
  ('Tom', 'A', 'Math', 7),
  ('Jim', 'A', 'Math', 8),
  ('Tim', 'A', 'English', 7),
  ('Tom', 'A', 'English', 8),
  ('Jim', 'A', 'English', 7),
  ('Lucy', 'B', 'Math', 8),
  ('Jody', 'B', 'Math', 6),
  ('Susy', 'B', 'Math', 9),
  ('Lucy', 'B', 'English', 6),
  ('Jody', 'B', 'English', 7),
  ('Susy', 'B', 'English', 8);

Use the following SELECT statement to show all rows of this table:

SELECT * FROM student_grade;
 id | name | class | subject | grade
----+------+-------+---------+-------
  1 | Tim  | A     | Math    |     9
  2 | Tom  | A     | Math    |     7
  3 | Jim  | A     | Math    |     8
  4 | Tim  | A     | English |     7
  5 | Tom  | A     | English |     8
  6 | Jim  | A     | English |     7
  7 | Lucy | B     | Math    |     8
  8 | Jody | B     | Math    |     6
  9 | Susy | B     | Math    |     9
 10 | Lucy | B     | English |     6
 11 | Jody | B     | English |     7
 12 | Susy | B     | English |     8
(12 rows)

Example 1

To show the highest-to-lowest grade in each subject and the lowest grade of each subject in each row, use the following statement:

SELECT
  *,
  last_value(grade) OVER (
    PARTITION BY subject
    ORDER BY grade DESC
    RANGE BETWEEN
      UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING
  ) last_grade
FROM student_grade;
 id | name | class | subject | grade | last_grade
----+------+-------+---------+-------+------------
 12 | Susy | B     | English |     8 |          6
  5 | Tom  | A     | English |     8 |          6
 11 | Jody | B     | English |     7 |          6
  4 | Tim  | A     | English |     7 |          6
  6 | Jim  | A     | English |     7 |          6
 10 | Lucy | B     | English |     6 |          6
  1 | Tim  | A     | Math    |     9 |          6
  9 | Susy | B     | Math    |     9 |          6
  7 | Lucy | B     | Math    |     8 |          6
  3 | Jim  | A     | Math    |     8 |          6
  2 | Tom  | A     | Math    |     7 |          6
  8 | Jody | B     | Math    |     6 |          6
(12 rows)

Note that the window function in the SQL statement above:

last_value(grade) OVER (
  PARTITION BY subject
  ORDER BY grade DESC
) last_grade

In the OVER clause,

  • The PARTITION BY subject partitions all rows by subject
  • The ORDER BY grade sorts all rows within each partition in ascending order by grade.
  • The RANGE BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING defines the window frame relative to the current row in each partition, here means from the first row to the last row in each partition.
  • The last_value(grade) returns the grade (grade) in the last row of the window frame that the current row locates. Since the rows is sorted by grade in ascending order, so this function returns the lowest grade in each partition.

Example 2

To show the ranking number of each student in each class in descending order of total grade and the lowest total grade for each class in each row, use the following statement:

SELECT
  t.*,
  last_value(t.sum_grade) OVER (
    PARTITION BY class
    ORDER BY t.sum_grade DESC
    RANGE BETWEEN
      UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING
  ) last_grade
FROM (
    SELECT class,
      name,
      sum(grade) sum_grade
    FROM student_grade
    GROUP BY class, name
  ) t;
 class | name | sum_grade | last_grade
-------+------+-----------+------------
 A     | Tim  |        16 |         15
 A     | Jim  |        15 |         15
 A     | Tom  |        15 |         15
 B     | Susy |        17 |         13
 B     | Lucy |        14 |         13
 B     | Jody |        13 |         13
(6 rows)

Notice this subquery in the above statement:

SELECT class,
  name,
  sum(grade) sum_grade
FROM student_grade
GROUP BY class, name

This subquery uses the GROUP BY clause and the sum() function sums up each student’s total grade by class and student.

 class | name | sum_grade
-------+------+-----------
 A     | Tim  |        16
 A     | Jim  |        15
 A     | Tom  |        15
 B     | Jody |        13
 B     | Lucy |        14
 B     | Susy |        17
(6 rows)

The main statement partitions all rows from this subquery by classes, then sort by total grade in ascending order within each partition, and add the lowest grade of each partition to all rows using last_value(grade).