02 - Top N Records
Last modified on Thu 17 Mar 2022

Problem

Write a query that will return 3 most recent images for each post.

Input Data

id post_id image_url created_at updated_at
1 1 images.com/1 2021-03-12 00:00:00 2022-02-11 10:26:06.634393
2 2 images.com/2 2021-03-15 00:00:00 2022-02-11 10:26:06.634393
3 2 images.com/3 2021-03-20 00:00:00 2022-02-11 10:26:06.634393
4 3 images.com/4 2021-04-13 00:00:00 2022-02-11 10:26:06.634393
5 1 images.com/5 2021-04-15 00:00:00 2022-02-11 10:26:06.634393
6 3 images.com/6 2021-04-17 00:00:00 2022-02-11 10:26:06.634393
7 3 images.com/7 2021-04-18 00:00:00 2022-02-11 10:26:06.634393
8 1 images.com/8 2021-05-01 00:00:00 2022-02-11 10:26:06.634393
9 1 images.com/9 2021-05-02 00:00:00 2022-02-11 10:26:06.634393
10 3 images.com/0 2021-05-13 00:00:00 2022-02-11 10:26:06.634393

Expected Result

id post_id image_url created_at updated_at
9 1 images.com/9 2021-05-02 00:00:00 2022-02-11 10:26:06.634393
8 1 images.com/8 2021-05-01 00:00:00 2022-02-11 10:26:06.634393
5 1 images.com/5 2021-04-15 00:00:00 2022-02-11 10:26:06.634393
3 2 images.com/3 2021-03-20 00:00:00 2022-02-11 10:26:06.634393
2 2 images.com/2 2021-03-15 00:00:00 2022-02-11 10:26:06.634393
10 3 images.com/0 2021-05-13 00:00:00 2022-02-11 10:26:06.634393
7 3 images.com/7 2021-04-18 00:00:00 2022-02-11 10:26:06.634393
6 3 images.com/6 2021-04-17 00:00:00 2022-02-11 10:26:06.634393

Setup Script

DROP TABLE IF EXISTS post_images;
DROP SEQUENCE IF EXISTS post_images_id_seq;

CREATE SEQUENCE post_images_id_seq;

CREATE TABLE post_images (
  id BIGINT NOT NULL DEFAULT NEXTVAL('post_images_id_seq'::regclass),
  post_id BIGINT,
  image_url VARCHAR,
  created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
  updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
  PRIMARY KEY (id)
);

INSERT INTO post_images (post_id, image_url, created_at) VALUES
(1, 'images.com/1', '2021-03-12'),
(2, 'images.com/2', '2021-03-15'),
(2, 'images.com/3', '2021-03-20'),
(3, 'images.com/4', '2021-04-13'),
(1, 'images.com/5', '2021-04-15'),
(3, 'images.com/6', '2021-04-17'),
(3, 'images.com/7', '2021-04-18'),
(1, 'images.com/8', '2021-05-01'),
(1, 'images.com/9', '2021-05-02'),
(3, 'images.com/0', '2021-05-13');

Solution

SELECT
  id, post_id, image_url, created_at, updated_at

FROM (
  SELECT
    post_images.*,
    ROW_NUMBER() OVER (
      PARTITION BY post_id ORDER BY created_at DESC
    ) AS rn

  FROM
    post_images
) post_images

WHERE rn <= 3;

Keywords