Limiting Rows


Limiting Rows: Top-N Queries

In Oracle 19c SQL, retrieving a subset of rows from a larger result set is a common and crucial task. This process, often referred to as "Top-N Queries," allows you to display only the first few rows that meet certain criteria, or even a specific range of rows. Understanding how to effectively limit rows is essential for performance optimization, data presentation, and building efficient applications. Oracle provides several powerful mechanisms for achieving this, each with its own advantages and ideal use cases. We'll explore the traditional ROWNUM pseudocolumn, the modern SQL standard-compliant FETCH FIRST / OFFSET FETCH clause, and touch upon the LIMIT clause's general concept in the context of Oracle 19c. Mastering these techniques will significantly enhance your ability to manipulate and present data precisely.

 

ROWNUM Pseudocolumn (Traditional Oracle approach)

The ROWNUM pseudocolumn is a classic Oracle feature used to assign a sequential number to each row returned by a query, before the ORDER BY clause is applied. This means ROWNUM values are assigned as rows are fetched, and their order can be unpredictable if an ORDER BY clause is not used in a subquery or inline view. While still functional in Oracle 19c, it's generally recommended to use the FETCH FIRST / OFFSET FETCH clause for new development due to its SQL standard compliance and more intuitive behavior, especially when dealing with paging. However, understanding ROWNUM is vital for working with legacy code and for specific scenarios where its unique behavior is desired.

Example 1: Oracle 19c SQL ROWNUM for basic Top-N

SELECT ROWNUM AS rn, -- Assigns a sequential number to each row
       employee_id,
       first_name,
       last_name,
       salary
FROM employees
WHERE ROWNUM <= 5 -- Limits the result set to the first 5 rows
ORDER BY salary DESC; -- This ORDER BY clause is applied *after* ROWNUM, so it might not give the highest salaries if not used in a subquery.

Explanation This SQL query demonstrates a basic use of the ROWNUM pseudocolumn in Oracle 19c to retrieve the first 5 rows from the employees table. The ROWNUM <= 5 condition filters the rows as they are being fetched, effectively limiting the output. It's crucial to note that the ORDER BY salary DESC clause in this example is applied after the ROWNUM condition. This means ROWNUM is assigned before the rows are sorted by salary, which might not yield the employees with the absolute highest salaries if the table is not already sorted in that manner. For accurate Top-N results based on a specific order, ROWNUM is typically used within an inline view or subquery.

Example 2: Oracle 19c SQL ROWNUM with an inline view for accurate Top-N

SELECT rn,
       employee_id,
       first_name,
       last_name,
       salary
FROM (
    SELECT employee_id,
           first_name,
           last_name,
           salary
    FROM employees
    ORDER BY salary DESC -- Sorts the data first
)
WHERE ROWNUM <= 3; -- Then applies ROWNUM to the sorted result

Explanation This example showcases the recommended way to use ROWNUM for reliable Top-N queries in Oracle 19c, especially when you need the "top" based on a specific order. By nesting the SELECT statement that includes the ORDER BY salary DESC clause within an inline view (the subquery in parentheses), we ensure that the data is first sorted by salary in descending order. Once the data is sorted, the outer SELECT statement applies the ROWNUM <= 3 condition to the already ordered rows. This guarantees that we truly retrieve the 3 employees with the highest salaries. This pattern is fundamental for accurate Top-N queries using ROWNUM.

Example 3: Oracle 19c SQL ROWNUM to find rows within a range (Paging)

SELECT employee_id,
       first_name,
       last_name,
       salary
FROM (
    SELECT ROWNUM AS rn, -- Assign ROWNUM to the sorted data
           employee_id,
           first_name,
           last_name,
           salary
    FROM (
        SELECT employee_id,
               first_name,
               last_name,
               salary
        FROM employees
        ORDER BY last_name ASC -- Sort by last name for consistent paging
    )
    WHERE ROWNUM <= 10 -- Get up to the 10th row
)
WHERE rn > 5; -- Then filter to get rows from 6 to 10

Explanation This Oracle 19c SQL query demonstrates how to use ROWNUM to retrieve a specific range of rows, a common requirement for implementing pagination (or "paging") in applications. It employs a double-nested subquery structure. The innermost subquery sorts the employees table by last_name to ensure a consistent ordering for paging. The middle subquery then assigns ROWNUM to these sorted rows, but only up to a certain limit (in this case, ROWNUM <= 10). Finally, the outermost query filters this intermediate result set using the assigned rn (ROWNUM) to select rows within the desired range (e.g., rn > 5 to get rows from 6 to 10). This technique is crucial for efficient data display in user interfaces where only a subset of data is shown at a time.

Example 4: Oracle 19c SQL ROWNUM with GROUP BY

SELECT ROWNUM AS dept_rn,
       department_id,
       COUNT(employee_id) AS num_employees
FROM (
    SELECT department_id, COUNT(employee_id) AS num_employees
    FROM employees
    GROUP BY department_id
    ORDER BY COUNT(employee_id) DESC -- Order by the count of employees
)
WHERE ROWNUM <= 3; -- Get the top 3 departments by employee count

Explanation This Oracle 19c SQL example illustrates how ROWNUM can be combined with GROUP BY to find the "top N" groups based on an aggregate function. The inner query first groups employees by department_id and counts the number of employees in each department. It then orders these grouped results by the num_employees in descending order. The outer query then applies ROWNUM <= 3 to this ordered, grouped result set. This effectively retrieves the top 3 departments with the highest number of employees, demonstrating ROWNUM's utility beyond simple row-level filtering.

Example 5: Oracle 19c SQL ROWNUM for finding the Nth largest value

SELECT salary
FROM (
    SELECT salary
    FROM employees
    ORDER BY salary DESC -- Sort salaries in descending order
)
WHERE ROWNUM = 5; -- Get the 5th highest salary

Explanation This Oracle 19c SQL query utilizes ROWNUM to efficiently find the Nth largest value (in this case, the 5th highest salary) from the employees table. The inner query sorts all salaries in descending order. By applying ROWNUM = 5 to this sorted result set in the outer query, we directly pinpoint and retrieve the fifth highest salary. This is a common and effective pattern for specific ranking requirements using the ROWNUM pseudocolumn.

 

FETCH FIRST / OFFSET FETCH (SQL Standard compliant, for Oracle 12c+ including 19c)

The FETCH FIRST and OFFSET FETCH clauses were introduced in Oracle 12c and are fully supported and recommended in Oracle 19c for limiting rows. These clauses adhere to the SQL standard, making your queries more portable across different database systems. They offer a much cleaner and more intuitive syntax for Top-N queries and pagination compared to the traditional ROWNUM approach, especially when dealing with offsets. FETCH FIRST is used to retrieve a specific number of rows from the beginning of the result set, while OFFSET FETCH allows you to skip a certain number of rows before fetching. This combination provides robust and flexible control over row limiting and is the preferred method for modern Oracle SQL development.

Example 1: Oracle 19c SQL FETCH FIRST for Top-N

SELECT employee_id,
       first_name,
       last_name,
       salary
FROM employees
ORDER BY salary DESC -- Orders the data first
FETCH FIRST 5 ROWS ONLY; -- Retrieves only the first 5 rows after sorting

Explanation This Oracle 19c SQL query is a straightforward and modern way to perform a Top-N query. It first sorts the employees table by salary in descending order using the ORDER BY clause. Crucially, the FETCH FIRST 5 ROWS ONLY clause is then applied to this already sorted result set. This guarantees that you accurately retrieve the 5 employees with the highest salaries. This syntax is highly readable, SQL standard compliant, and the recommended approach for Top-N queries in Oracle 19c, superior to ROWNUM for its clarity and precise behavior.

Example 2: Oracle 19c SQL OFFSET FETCH for Pagination (skipping rows)

SELECT employee_id,
       first_name,
       last_name,
       hire_date
FROM employees
ORDER BY hire_date ASC -- Consistent ordering for pagination
OFFSET 10 ROWS -- Skips the first 10 rows
FETCH NEXT 5 ROWS ONLY; -- Then retrieves the next 5 rows

Explanation This Oracle 19c SQL example demonstrates how to implement pagination using the OFFSET FETCH clause. This is a common pattern for displaying data in chunks (e.g., showing page 2 of results). The query first sorts the employees table by hire_date in ascending order to ensure a consistent page order. The OFFSET 10 ROWS clause then instructs the database to skip the first 10 rows of the sorted result set. Following that, FETCH NEXT 5 ROWS ONLY retrieves only the subsequent 5 rows. This effectively fetches rows 11 through 15, making it ideal for displaying the second "page" of results where each page has 5 records.

Example 3: Oracle 19c SQL FETCH FIRST with WITH TIES

SELECT department_id,
       department_name,
       location_id
FROM departments
ORDER BY location_id ASC
FETCH FIRST 2 ROWS WITH TIES; -- Gets the first 2 rows, and any additional rows that tie with the 2nd row's location_id

Explanation This Oracle 19c SQL query introduces the WITH TIES option with FETCH FIRST. When used, WITH TIES will include all rows that have the same values in the ORDER BY columns as the last row retrieved by the FETCH FIRST clause, even if it exceeds the specified row limit. In this example, it will fetch the first 2 departments based on location_id in ascending order. If there are other departments with the same location_id as the 2nd department, those "tied" rows will also be included in the result set. This is useful when you want to retrieve a Top-N set but also want to ensure that all equally ranked items are included.

Example 4: Oracle 19c SQL OFFSET FETCH with PERCENT

SELECT employee_id,
       first_name,
       last_name,
       salary
FROM employees
ORDER BY salary DESC
OFFSET 0 ROWS FETCH FIRST 10 PERCENT ROWS ONLY; -- Retrieves the top 10% of employees by salary

Explanation This Oracle 19c SQL example utilizes the PERCENT option with FETCH FIRST to retrieve a percentage of the total rows. The OFFSET 0 ROWS is used here simply to allow the FETCH FIRST clause to be specified after an offset, even if no rows are skipped. The FETCH FIRST 10 PERCENT ROWS ONLY clause then calculates 10% of the total number of rows in the employees table (after ordering by salary descending) and returns that many rows. This is a convenient way to get a proportional subset of your data without hardcoding a specific number of rows, especially useful for statistical analysis or sampling.

Example 5: Oracle 19c SQL OFFSET FETCH with a large offset for deep paging

SELECT product_id,
       product_name,
       list_price
FROM products
ORDER BY product_name ASC
OFFSET 1000 ROWS FETCH NEXT 50 ROWS ONLY; -- Skips 1000 rows and gets the next 50

Explanation This Oracle 19c SQL query showcases OFFSET FETCH for "deep paging," where you need to retrieve rows far down into a large result set. After ordering the products table by product_name for consistency, OFFSET 1000 ROWS instructs Oracle to skip the first 1000 records. Subsequently, FETCH NEXT 50 ROWS ONLY retrieves the subsequent 50 records. This pattern is crucial for applications dealing with very large datasets, allowing users to navigate through thousands of records efficiently without having to load the entire dataset into memory. It's a highly optimized approach for retrieving specific data windows from extensive result sets.

 

LIMIT Clause (General SQL, though FETCH FIRST is preferred in Oracle 19c)

While the LIMIT clause is a common way to restrict the number of rows returned by a query in many SQL databases (like MySQL and PostgreSQL), it is not directly supported in Oracle 19c as a standalone clause for limiting rows in the same manner. Oracle's equivalent and preferred method for limiting rows, especially for Top-N queries and pagination, is the SQL standard-compliant FETCH FIRST and OFFSET FETCH clauses. Although you might see "LIMIT" discussed in a general SQL context, remember that for Oracle 19c, you should leverage FETCH FIRST and OFFSET FETCH for modern and efficient row limiting. It's important to clarify this distinction to avoid confusion for users familiar with other SQL dialects.

Example 1: Oracle 19c SQL (Simulating LIMIT with FETCH FIRST)

-- This is how you would achieve the equivalent of a "LIMIT 5" in Oracle 19c
SELECT customer_id,
       customer_name,
       email
FROM customers
ORDER BY customer_id ASC -- Essential for consistent results
FETCH FIRST 5 ROWS ONLY; -- Oracle's way to limit to the first 5 rows

Explanation This Oracle 19c SQL query demonstrates how to achieve the functionality typically provided by a LIMIT clause in other SQL databases. While LIMIT itself is not a native Oracle 19c keyword for this purpose, the FETCH FIRST 5 ROWS ONLY clause provides the exact same result: it retrieves only the first 5 rows from the customers table after it has been sorted by customer_id. This is the recommended and standard-compliant approach in Oracle 19c for obtaining a fixed number of initial rows, effectively serving as Oracle's "limit" mechanism.

Example 2: Oracle 19c SQL (Simulating LIMIT with an offset)

-- This simulates "LIMIT 5 OFFSET 10" in Oracle 19c
SELECT product_name,
       category,
       price
FROM products
ORDER BY product_name
OFFSET 10 ROWS -- Skip the first 10 rows
FETCH NEXT 5 ROWS ONLY; -- Then fetch the next 5 rows

Explanation This Oracle 19c SQL example illustrates how to simulate a LIMIT clause combined with an OFFSET (e.g., LIMIT 5 OFFSET 10 in other databases). The query first sorts the products table by product_name. The OFFSET 10 ROWS clause then instructs Oracle to skip the initial 10 rows of the sorted result set. Subsequently, FETCH NEXT 5 ROWS ONLY retrieves the following 5 rows. This effectively fetches a "page" of 5 results starting from the 11th record, providing the same functionality as a combined LIMIT and OFFSET found in other database systems.

Example 3: Oracle 19c SQL (Simulating LIMIT without an ORDER BY - not recommended for production)

-- While possible, using FETCH FIRST without ORDER BY for "LIMIT" is not recommended for consistent results
SELECT employee_id,
       first_name
FROM employees
FETCH FIRST 3 ROWS ONLY; -- Retrieves 3 arbitrary rows, order is not guaranteed

Explanation This Oracle 19c SQL query demonstrates the use of FETCH FIRST without an accompanying ORDER BY clause to simulate a simple LIMIT of 3 rows. While the query will indeed return 3 rows, it's crucial to understand that the specific rows returned are not guaranteed to be consistent across multiple executions if an ORDER BY clause is omitted. Without a defined order, Oracle might return rows in an arbitrary or internal order, which can vary. For any production environment or scenario where the exact rows returned matter, an ORDER BY clause should always be used in conjunction with FETCH FIRST for predictable and reliable results.

Example 4: Oracle 19c SQL (Simulating LIMIT with a derived table, less efficient than FETCH FIRST)

-- This is an older, less efficient way to simulate LIMIT using ROWNUM,
-- not preferred over FETCH FIRST in Oracle 19c
SELECT *
FROM (
    SELECT customer_id, customer_name
    FROM customers
    ORDER BY customer_name ASC
)
WHERE ROWNUM <= 4; -- Simulates "LIMIT 4" using ROWNUM

Explanation This Oracle 19c SQL example shows an older method of simulating LIMIT functionality using a derived table (inline view) and the ROWNUM pseudocolumn. The inner query sorts the customers table by customer_name. The outer query then applies WHERE ROWNUM <= 4 to this sorted result set, effectively limiting the output to the first 4 rows. While this approach works and was common in older Oracle versions, it is generally less efficient and less readable than the FETCH FIRST clause introduced in Oracle 12c and fully available in 19c. For modern Oracle development, FETCH FIRST is the preferred and more performant way to achieve LIMIT functionality.

Example 5: Oracle 19c SQL (Incorrect LIMIT syntax and how to correct it)

-- INCORRECT SYNTAX for LIMIT in Oracle 19c:
-- SELECT product_id, product_name FROM products LIMIT 5;

-- CORRECT SYNTAX for limiting rows in Oracle 19c:
SELECT product_id,
       product_name
FROM products
ORDER BY product_id -- Always order for predictable results when limiting
FETCH FIRST 5 ROWS ONLY;

Explanation This example highlights a common mistake for developers coming from other SQL databases: attempting to use the LIMIT clause directly in Oracle 19c. The first commented line shows the incorrect syntax as LIMIT is not a native Oracle keyword for this purpose. The subsequent SELECT statement demonstrates the correct and recommended Oracle 19c syntax to achieve the same result. By using FETCH FIRST 5 ROWS ONLY (preferably with an ORDER BY clause for consistent output), you effectively limit the number of rows returned, providing the equivalent functionality to a LIMIT 5 clause found in other SQL dialects. This distinction is crucial for writing correct and efficient Oracle SQL.