PostgreSQL LIMIT子句

概述

LIMIT子句用于限制查询返回的行数。它通常与OFFSET配合使用实现分页功能。

基本语法

SELECT column1, column2, ...
FROM table_name
[WHERE condition]
[ORDER BY column]
LIMIT number;

简单示例

-- 返回前10条记录
SELECT * FROM users LIMIT 10;

-- 返回前5个产品
SELECT name, price FROM products LIMIT 5;

LIMIT与ORDER BY

重要:使用LIMIT时通常需要配合ORDER BY,否则返回的行是不确定的。

-- 获取价格最高的5个产品
SELECT name, price
FROM products
ORDER BY price DESC
LIMIT 5;

-- 获取最新的10个订单
SELECT id, created_at, total_amount
FROM orders
ORDER BY created_at DESC
LIMIT 10;

OFFSET子句

OFFSET用于跳过指定数量的行,常用于分页。

语法

SELECT column1, column2, ...
FROM table_name
ORDER BY column
LIMIT number OFFSET skip;

分页示例

-- 第1页(每页10条)
SELECT * FROM products ORDER BY id LIMIT 10 OFFSET 0;

-- 第2页
SELECT * FROM products ORDER BY id LIMIT 10 OFFSET 10;

-- 第3页
SELECT * FROM products ORDER BY id LIMIT 10 OFFSET 20;

-- 通用分页公式:OFFSET = (page - 1) * page_size

FETCH子句(SQL标准写法)

PostgreSQL也支持SQL标准的FETCH语法:

-- 等价于 LIMIT 10
SELECT * FROM products
ORDER BY price DESC
FETCH FIRST 10 ROWS ONLY;

-- 等价于 LIMIT 10 OFFSET 5
SELECT * FROM products
ORDER BY price DESC
OFFSET 5 ROWS
FETCH NEXT 10 ROWS ONLY;

FETCH语法变体

-- 以下写法等价
FETCH FIRST 10 ROWS ONLY
FETCH NEXT 10 ROWS ONLY
FETCH FIRST 10 ROW ONLY
FETCH NEXT 10 ROW ONLY

实际应用示例

获取Top N记录

-- 销售额最高的10个产品
SELECT p.name, SUM(oi.quantity * oi.unit_price) as total_sales
FROM products p
JOIN order_items oi ON p.id = oi.product_id
GROUP BY p.id, p.name
ORDER BY total_sales DESC
LIMIT 10;

-- 最活跃的5个用户
SELECT u.username, COUNT(o.id) as order_count
FROM users u
LEFT JOIN orders o ON u.id = o.user_id
GROUP BY u.id, u.username
ORDER BY order_count DESC
LIMIT 5;

API分页实现

-- 假设每页20条,获取指定页
-- page: 页码(从1开始)
-- page_size: 每页条数

SELECT id, name, email, created_at
FROM users
ORDER BY created_at DESC
LIMIT 20 OFFSET ((3 - 1) * 20);  -- 第3页

-- 同时获取总数用于分页信息
SELECT COUNT(*) FROM users;

配合子查询

-- 获取每个类别中价格最高的3个产品
SELECT * FROM (
    SELECT
        p.*,
        ROW_NUMBER() OVER (
            PARTITION BY category_id
            ORDER BY price DESC
        ) as rn
    FROM products p
) ranked
WHERE rn <= 3;

LIMIT ALL

LIMIT ALL等同于不使用LIMIT:

-- 以下两个查询等价
SELECT * FROM products LIMIT ALL;
SELECT * FROM products;

注意事项

性能考虑

-- 大OFFSET值性能较差
SELECT * FROM logs ORDER BY id LIMIT 10 OFFSET 1000000;
-- 需要扫描前1000010行

-- 优化方案:使用游标或键集分页
SELECT * FROM logs
WHERE id > 1000000
ORDER BY id
LIMIT 10;

不确定性结果

-- 不推荐:没有ORDER BY时结果不确定
SELECT * FROM users LIMIT 10;

-- 推荐:明确排序
SELECT * FROM users ORDER BY id LIMIT 10;

NULL值排序

-- NULL值默认排在最后(ASC)或最前(DESC)
SELECT * FROM products ORDER BY price NULLS FIRST LIMIT 10;
SELECT * FROM products ORDER BY price NULLS LAST LIMIT 10;

与其他子句的顺序

SELECT column_list
FROM table_name
WHERE condition
GROUP BY grouping_columns
HAVING group_condition
ORDER BY sorting_columns
LIMIT number
OFFSET skip;

键集分页(Keyset Pagination)

对于大数据量,键集分页比OFFSET更高效:

-- 传统OFFSET分页(大数据量时慢)
SELECT * FROM products ORDER BY id LIMIT 20 OFFSET 10000;

-- 键集分页(更快)
SELECT * FROM products
WHERE id > 10020  -- 上一页最后一条的ID
ORDER BY id
LIMIT 20;

键集分页优势

  1. 性能稳定,不随页数增加而变慢
  2. 避免了OFFSET大值的性能问题
  3. 适合实时数据和无限滚动场景