Home SQL Pagination
Post
Cancel

SQL Pagination

SQL Pagination with Materialized View and Pipelined Table Function in Oracle

Overview

This guide explains how to implement snapshot-based pagination in Oracle using:

  • A Materialized View (MV) to store a consistent snapshot of the employees table.
  • A pipelined table function to return paginated results as a table type, enabling easy querying with SELECT * FROM TABLE(...).

This approach ensures that all pages are based on the same snapshot, even if the underlying employees table changes during pagination.


Step 1: Create the Materialized View

The MV will hold a static snapshot of the employees table.

1
2
3
4
5
6
7
CREATE MATERIALIZED VIEW mv_employees_snapshot
BUILD IMMEDIATE
REFRESH ON DEMAND
AS
SELECT employee_id, first_name, last_name, email, hire_date
FROM employees
ORDER BY employee_id;
  • REFRESH ON DEMAND: You control when the snapshot updates.
  • ORDER BY employee_id: Ensures stable ordering for pagination.

Step 2: Refresh the Materialized View

Before starting pagination, refresh the MV to capture the latest data.

1
EXEC DBMS_MVIEW.REFRESH('MV_EMPLOYEES_SNAPSHOT', 'COMPLETE');

This ensures the MV contains a consistent snapshot of the employees table.


Step 3: Create Object and Table Types

Define an object type for a single row and a table type for multiple rows.

1
2
3
4
5
6
7
8
9
CREATE OR REPLACE TYPE employee_obj AS OBJECT (
    employee_id NUMBER,
    first_name  VARCHAR2(50),
    last_name   VARCHAR2(50),
    email       VARCHAR2(100),
    hire_date   DATE
);

CREATE OR REPLACE TYPE employee_tab AS TABLE OF employee_obj;

Step 4: Create the Pipelined Table Function

This function reads from the MV and returns paginated rows using PIPE ROW.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
CREATE OR REPLACE FUNCTION get_paginated_mv_pipe (
    p_page_number IN NUMBER,
    p_page_size   IN NUMBER
) RETURN employee_tab PIPELINED
AS
BEGIN
    FOR rec IN (
        SELECT employee_id, first_name, last_name, email, hire_date
        FROM mv_employees_snapshot
        ORDER BY employee_id
        OFFSET ((p_page_number - 1) * p_page_size) ROWS
        FETCH NEXT p_page_size ROWS ONLY
    )
    LOOP
        PIPE ROW(employee_obj(rec.employee_id, rec.first_name, rec.last_name, rec.email, rec.hire_date));
    END LOOP;

    RETURN;
END;
/

Example Usage

Query the pipelined function like a table:

1
2
3
4
5
6
-- Refresh the MV first
EXEC DBMS_MVIEW.REFRESH('MV_EMPLOYEES_SNAPSHOT', 'COMPLETE');

-- Fetch page 2 with 10 rows per page
SELECT *
FROM TABLE(get_paginated_mv_pipe(2, 10));

Notes

  • Always refresh the MV before starting pagination to ensure consistency.
  • This approach guarantees a stable snapshot across all pages.
  • For very large datasets, consider indexing the MV for better performance.
This post is licensed under CC BY 4.0 by the author.