PostgreSQL: PostgreSQL SELECT查询基础
最后更新:2026-08-26
1. 你将学到
- 使用
SELECT查询列、表达式与别名 - 用
DISTINCT去除重复行 - 用
WHERE过滤数据 - 用
ORDER BY排序(含 PostgreSQL 独有的NULLS FIRST/LAST) - 用
LIMIT/OFFSET与FETCH FIRST分页
2. 故事
Bob 是一家电商平台的数据库工程师。平台每天产生约 10,000 笔订单,过去 30 天累积了 100,000 条记录。运营团队要求他:
- 查询最近 30 天所有订单,按金额从高到低排序
- 每页展示 50 条,支持翻页
- 过滤掉已取消的订单
- 展示时去掉重复的客户 ID
Bob 需要用 SELECT、WHERE、ORDER BY、LIMIT/OFFSET 和 DISTINCT 来完成任务。
3. Concept:SELECT 基础查询
(1) SELECT 语法概览
SELECT column1, column2, ...
FROM table_name
[WHERE condition]
[ORDER BY column [ASC|DESC] [NULLS FIRST|NULLS LAST]]
[LIMIT count [OFFSET start]];
SELECT 是 SQL 中最常用的语句,用于从一个或多个表中检索数据。
(2) 查询所有列 vs 指定列
| 写法 | 说明 | 性能 | 推荐场景 |
|---|---|---|---|
SELECT * |
返回所有列 | 较差,传输冗余数据 | 快速探索表结构 |
SELECT col1, col2 |
只返回指定列 | 较好,减少 I/O | 生产环境查询 |
▶ 示例:查询所有列
SELECT * FROM orders;
输出:
id | name | value
----+----------+-------
1 | example | 42
(1 row)
▶ 示例:查询指定列
SELECT order_id, customer_id, total_amount, order_date
FROM orders;
输出:
id | name | value
----+----------+-------
1 | example | 42
(1 row)
(3) 列别名
使用 AS 关键字(可省略)为列或表达式取别名,让结果更具可读性。
| 用法 | 示例 | 说明 |
|---|---|---|
AS 显式别名 |
price * qty AS subtotal |
推荐写法,清晰 |
省略 AS |
price * qty subtotal |
合法但可读性差 |
| 双引号别名 | total_amount AS "Order Total" |
别名含空格/大小写时必须 |
▶ 示例:使用列别名
SELECT
order_id,
total_amount AS amount,
order_date AS "Order Date",
total_amount * 0.08 AS tax
FROM orders;
输出:
id | name | value
----+----------+-------
1 | example | 42
(1 row)
(4) 表达式与计算列
SELECT 不仅能查列,还能写算术表达式、函数调用等,生成计算列。
▶ 示例:计算列
SELECT
product_name,
unit_price,
unit_price * 1.1 AS price_with_tax,
ROUND(unit_price * 1.1, 2) AS rounded_price
FROM products;
输出:
id | name | value
----+----------+-------
1 | example | 42
(1 row)
4. Concept:DISTINCT 去重
(1) 基本去重
DISTINCT 去除结果集中的重复行,常用于统计唯一值数量。
▶ 示例:查询不重复的客户 ID
SELECT DISTINCT customer_id
FROM orders;
输出:
id | name | value
----+----------+-------
1 | example | 42
(1 row)
(2) 多列去重
DISTINCT 作用于所有指定列的组合,而非单列。
| 语句 | 去重范围 |
|---|---|
SELECT DISTINCT a |
按 a 去重 |
SELECT DISTINCT a, b |
按 (a, b) 组合去重 |
SELECT DISTINCT ON (a) a, b |
PostgreSQL 特有,按 a 去重并取每组第一行 |
▶ 示例:多列组合去重
SELECT DISTINCT customer_id, order_status
FROM orders;
输出:
id | name | value
----+----------+-------
1 | example | 42
(1 row)
(3) DISTINCT ON:PostgreSQL 独有语法
DISTINCT ON (expr) 可以按指定表达式去重,并返回每组中的第一行(需配合 ORDER BY 决定取哪行)。
▶ 示例:每个客户最新一笔订单
SELECT DISTINCT ON (customer_id)
customer_id, order_id, order_date, total_amount
FROM orders
ORDER BY customer_id, order_date DESC;
输出:
id | name | value
----+----------+-------
1 | example | 42
(1 row)
5. Concept:WHERE 条件过滤
(1) 基本比较运算符
| 运算符 | 含义 | 示例 |
|---|---|---|
= |
等于 | order_status = 'completed' |
<> 或 != |
不等于 | total_amount <> 0 |
> |
大于 | total_amount > 100 |
< |
小于 | total_amount < 50 |
>= |
大于等于 | total_amount >= 100 |
<= |
小于等于 | total_amount <= 500 |
▶ 示例:过滤已完成的订单
SELECT order_id, customer_id, total_amount
FROM orders
WHERE order_status = 'completed';
输出:
id | name | value
----+----------+-------
1 | example | 42
(1 row)
(2) 日期过滤
日期比较是电商场景最常见的过滤需求之一。
▶ 示例:查询最近 30 天订单
SELECT order_id, customer_id, total_amount, order_date
FROM orders
WHERE order_date >= CURRENT_DATE - INTERVAL '30 days';
输出:
id | name | value
----+----------+-------
1 | example | 42
(1 row)
(3) 多条件组合
用 AND、OR、NOT 组合多个条件。优先级:NOT > AND > OR,建议用括号显式控制。
▶ 示例:组合条件过滤
SELECT order_id, total_amount, order_status
FROM orders
WHERE order_status = 'completed'
AND total_amount > 500
AND order_date >= '2025-01-01';
输出:
id | name | value
----+----------+-------
1 | example | 42
(1 row)
6. Concept:ORDER BY 排序
(1) 基本排序
ORDER BY 默认升序(ASC),可指定降序(DESC)。支持按列名、别名、列位置排序。
| 排序方式 | 语法 | 说明 |
|---|---|---|
| 升序 | ORDER BY col ASC |
默认,可省略 ASC |
| 降序 | ORDER BY col DESC |
从大到小 |
| 按别名 | ORDER BY amount DESC |
使用 SELECT 中的别名 |
| 按位置 | ORDER BY 3 DESC |
按 SELECT 列表中第 3 列(不推荐) |
▶ 示例:按金额降序排列
SELECT order_id, customer_id, total_amount
FROM orders
ORDER BY total_amount DESC;
输出:
id | name | value
----+----------+-------
1 | example | 42
(1 row)
(2) 多列排序
多列排序时,先按第一列排,第一列相同的按第二列排,以此类推。
▶ 示例:先按状态再按金额排序
SELECT order_id, order_status, total_amount
FROM orders
ORDER BY order_status ASC, total_amount DESC;
输出:
id | name | value
----+----------+-------
1 | example | 42
(1 row)
(3) NULLS FIRST / NULLS LAST:PostgreSQL 特色
PostgreSQL 默认将 NULL 视为最大值(升序在末尾,降序在开头)。可以用 NULLS FIRST 和 NULLS LAST 显式控制 NULL 的位置。
| 排序 | NULL 默认位置 | 可用调整 |
|---|---|---|
ASC |
NULL 在末尾 | NULLS FIRST 将 NULL 移到开头 |
DESC |
NULL 在开头 | NULLS LAST 将 NULL 移到末尾 |
▶ 示例:NULLS FIRST/LAST 控制
SELECT product_name, discount_rate
FROM products
ORDER BY discount_rate DESC NULLS LAST;
输出:
count
-------
5
(1 row)
7. Concept:LIMIT 与 FETCH FIRST 分页
(1) LIMIT / OFFSET
LIMIT 限制返回行数,OFFSET 跳过指定行数,二者配合实现分页。
▶ 示例:每页 50 条,第 3 页
SELECT order_id, customer_id, total_amount, order_date
FROM orders
WHERE order_status = 'completed'
ORDER BY total_amount DESC
LIMIT 50 OFFSET 100;
输出:
id | name | value
----+----------+-------
1 | example | 42
(1 row)
(2) FETCH FIRST:SQL 标准语法
PostgreSQL 同时支持 SQL 标准的 FETCH FIRST 语法,语义与 LIMIT 相同。
| 语法 | 等价写法 | 说明 |
|---|---|---|
LIMIT 10 |
FETCH FIRST 10 ROWS ONLY |
标准写法 |
LIMIT 10 OFFSET 20 |
OFFSET 20 ROWS FETCH FIRST 10 ROWS ONLY |
标准分页写法 |
▶ 示例:FETCH FIRST 分页
SELECT order_id, customer_id, total_amount
FROM orders
ORDER BY total_amount DESC
OFFSET 100 ROWS
FETCH FIRST 50 ROWS ONLY;
输出:
id | name | value
----+----------+-------
1 | example | 42
(1 row)
(3) 分页计算公式
| 参数 | 计算方式 |
|---|---|
页码 page |
从 1 开始 |
每页条数 size |
如 50 |
OFFSET |
(page - 1) * size |
LIMIT |
size |
▶ 示例:动态分页(伪代码)
page=3
size=50
offset=$(( (page - 1) * size ))
psql -c "SELECT order_id, total_amount FROM orders ORDER BY total_amount DESC LIMIT $size OFFSET $offset;"
输出:
# psql 命令执行成功
8. 流程图:SELECT 查询执行顺序
flowchart TD
A[FROM orders] --> B[WHERE filter]
B --> C[SELECT columns / expressions]
C --> D[DISTINCT]
D --> E[ORDER BY sort]
E --> F[LIMIT / OFFSET paginate]
F --> G[Result Set]
style A fill:#e1f5fe
style B fill:#fff3e0
style C fill:#e8f5e9
style D fill:#f3e5f5
style E fill:#fce4ec
style F fill:#e0f2f1
style G fill:#c8e6c9
9. 综合示例
Bob 完成了运营的需求:查询最近 30 天已完成订单,按金额降序分页,展示客户 ID 去重统计。
-- Step 1: Recent 30-day completed orders, paginated by amount DESC
SELECT
order_id,
customer_id,
total_amount,
total_amount * 0.08 AS tax_amount,
order_date AS "Order Date",
order_status
FROM orders
WHERE order_status = 'completed'
AND order_date >= CURRENT_DATE - INTERVAL '30 days'
ORDER BY total_amount DESC NULLS LAST
LIMIT 50 OFFSET 0;
-- Step 2: Count distinct customers in those orders
SELECT COUNT(DISTINCT customer_id) AS unique_customers
FROM orders
WHERE order_status = 'completed'
AND order_date >= CURRENT_DATE - INTERVAL '30 days';
-- Step 3: Each customer's latest order (DISTINCT ON)
SELECT DISTINCT ON (customer_id)
customer_id,
order_id,
total_amount,
order_date
FROM orders
WHERE order_status = 'completed'
AND order_date >= CURRENT_DATE - INTERVAL '30 days'
ORDER BY customer_id, total_amount DESC;
-- Step 4: Top 10 orders with computed total (price + tax)
SELECT
order_id,
customer_id,
total_amount,
ROUND(total_amount * 1.08, 2) AS total_with_tax,
order_date
FROM orders
WHERE order_status = 'completed'
AND order_date >= CURRENT_DATE - INTERVAL '30 days'
ORDER BY total_with_tax DESC
FETCH FIRST 10 ROWS ONLY;
❓ 常见问题
📖 小节
SELECT是 SQL 查询的基础,支持列名、表达式、别名DISTINCT去除重复行,DISTINCT ON是 PostgreSQL 特有语法WHERE用比较运算符过滤行,支持AND/OR/NOT组合ORDER BY排序支持ASC/DESC和 PostgreSQL 独有的NULLS FIRST/LASTLIMIT/OFFSET与FETCH FIRST实现分页,大偏移量场景需考虑 keyset pagination- SQL 逻辑执行顺序:FROM → WHERE → SELECT → DISTINCT → ORDER BY → LIMIT
📝 作业
-
⭐ 编写查询,从
products表中选择product_name和unit_price,按价格升序排列,只返回前 10 条。 -
⭐⭐ 编写查询,从
orders表中选择最近 7 天的订单,过滤出total_amount > 200的记录,按order_date DESC排列,使用FETCH FIRST 20 ROWS ONLY。 -
⭐⭐⭐ 使用
DISTINCT ON查询每个customer_id金额最大的订单(提示:需要ORDER BY customer_id, total_amount DESC),并统计这些客户中有多少位的最大单笔金额超过 1,000 USD。