PostgreSQL教程FG038-PG查询结果处理:排序/去重/限制返回行数
本文档风哥主要介绍PostgreSQL教程038相关内容。风哥教程参考PostgreSQL官方文档Server Administration, SQL Language内容编写,适合DBA人员在学习和测试中使用,如果要应用于生产环境则需要自行确认。
1. 查询结果处理概述
查询结果处理是SQL查询中的重要环节,包括排序、去重、限制返回行数等操作。PostgreSQL提供了丰富的子句和函数来处理查询结果,使开发者能够灵活地控制数据的展示方式和数量。
2. ORDER BY子句:结果排序
创建测试表并插入数据:
CREATE TABLE fgedu_products (
product_id SERIAL PRIMARY KEY,
product_name VARCHAR(200),
category VARCHAR(50),
price NUMERIC(10,2),
stock INTEGER,
fgfgfgfgsales_count INTEGER DEFAULT 0,
created_date DATE DEFAULT CURRENT_DATE
);
— 插入测试数据
INSERT INTO fgedu_products (product_name, category, price, stock, fgfgfgfgsales_count, created_date) VALUES
(‘笔记本电脑’, ‘电子产品’, 5999.00, 50, 120, ‘2026-03-15’),
(‘无线鼠标’, ‘电子产品’, 99.00, 200, 450, ‘2026-03-20’),
(‘机械键盘’, ‘电子产品’, 399.00, 150, 280, ‘2026-03-25’),
(‘显示器’, ‘电子产品’, 1299.00, 80, 180, ‘2026-04-01’),
(‘办公椅’, ‘办公用品’, 899.00, 100, 90, ‘2026-04-02’),
(‘USB数据线’, ‘电子产品’, 19.90, 500, 800, ‘2026-04-03’),
(‘平板电脑’, ‘电子产品’, 3999.00, 60, 150, ‘2026-04-04’),
(‘打印机’, ‘办公用品’, 1299.00, 40, 70, ‘2026-04-05’),
(‘耳机’, ‘电子产品’, 299.00, 120, 320, ‘2026-04-06’),
(‘路由器’, ‘电子产品’, 199.00, 90, 210, ‘2026-04-07’);
执行结果:
按单列升序排序:
SELECT product_id, product_name, price
FROM fgedu_products
ORDER BY price;
执行结果:
————+————–+——–
6 | USB数据线 | 19.90
2 | 无线鼠标 | 99.00
10 | 路由器 | 199.00
9 | 耳机 | 299.00
3 | 机械键盘 | 399.00
7 | 平板电脑 | 3999.00
4 | 显示器 | 1299.00
8 | 打印机 | 1299.00
5 | 办公椅 | 899.00
1 | 笔记本电脑 | 5999.00
(10 rows)
按单列降序排序:
SELECT product_id, product_name, price
FROM fgedu_products
ORDER BY price DESC;
执行结果:
————+————–+——–
1 | 笔记本电脑 | 5999.00
7 | 平板电脑 | 3999.00
4 | 显示器 | 1299.00
8 | 打印机 | 1299.00
5 | 办公椅 | 899.00
3 | 机械键盘 | 399.00
9 | 耳机 | 299.00
10 | 路由器 | 199.00
2 | 无线鼠标 | 99.00
6 | USB数据线 | 19.90
(10 rows)
按多列排序:
SELECT product_id, product_name, category, price
FROM fgedu_products
ORDER BY category, price DESC;
执行结果:
————+————–+———–+——–
5 | 办公椅 | 办公用品 | 899.00
8 | 打印机 | 办公用品 | 1299.00
1 | 笔记本电脑 | 电子产品 | 5999.00
7 | 平板电脑 | 电子产品 | 3999.00
4 | 显示器 | 电子产品 | 1299.00
3 | 机械键盘 | 电子产品 | 399.00
9 | 耳机 | 电子产品 | 299.00
10 | 路由器 | 电子产品 | 199.00
2 | 无线鼠标 | 电子产品 | 99.00
6 | USB数据线 | 电子产品 | 19.90
(10 rows)
按表达式排序:
SELECT
product_id,
product_name,
price,
stock,
price * stock AS total_value
FROM fgedu_products
ORDER BY price * stock DESC;
执行结果:
————+————–+——–+——-+————-
1 | 笔记本电脑 | 5999.00| 50 | 299950.00
7 | 平板电脑 | 3999.00| 60 | 239940.00
4 | 显示器 | 1299.00| 80 | 103920.00
8 | 打印机 | 1299.00| 40 | 51960.00
5 | 办公椅 | 899.00| 100 | 89900.00
3 | 机械键盘 | 399.00| 150 | 59850.00
9 | 耳机 | 299.00 | 120 | 35880.00
10 | 路由器 | 199.00 | 90 | 17910.00
2 | 无线鼠标 | 99.00 | 200 | 19800.00
6 | USB数据线 | 19.90 | 500 | 9950.00
(10 rows)
按NULL值排序:
INSERT INTO fgedu_products (product_name, category, price, stock, fgfgfgfgsales_count) VALUES
(‘测试产品1’, ‘测试’, NULL, 10, 5),
(‘测试产品2’, ‘测试’, 199.00, NULL, 8);
— 按价格排序,NULL值排在最后
SELECT product_id, product_name, price
FROM fgedu_products
ORDER BY price NULLS LAST;
执行结果:
————+————–+——–
6 | USB数据线 | 19.90
2 | 无线鼠标 | 99.00
10 | 路由器 | 199.00
12 | 测试产品2 | 199.00
9 | 耳机 | 299.00
3 | 机械键盘 | 399.00
7 | 平板电脑 | 3999.00
4 | 显示器 | 1299.00
8 | 打印机 | 1299.00
5 | 办公椅 | 899.00
1 | 笔记本电脑 | 5999.00
11 | 测试产品1 | NULL
(12 rows)
3. DISTINCT关键字:去重
使用DISTINCT去除重复行:
SELECT DISTINCT category
FROM fgedu_products;
执行结果:
———–
电子产品
办公用品
测试
(3 rows)
使用DISTINCT去除多列重复:
SELECT DISTINCT price, category
FROM fgedu_products
ORDER BY price;
执行结果:
——–+———–
19.90 | 电子产品
99.00 | 电子产品
199.00 | 电子产品
299.00 | 电子产品
399.00 | 电子产品
899.00 | 办公用品
1299.00 | 电子产品
1299.00 | 办公用品
3999.00 | 电子产品
5999.00 | 电子产品
(10 rows)
使用DISTINCT ON选择每组的第一行:
SELECT DISTINCT ON (category)
category,
product_name,
price
FROM fgedu_products
ORDER BY category, price;
执行结果:
———–+————–+——–
办公用品 | 办公椅 | 899.00
测试 | 测试产品2 | 199.00
电子产品 | USB数据线 | 19.90
(3 rows)
使用DISTINCT ON选择每组价格最高的产品:
SELECT DISTINCT ON (category)
category,
product_name,
price
FROM fgedu_products
ORDER BY category, price DESC;
执行结果:
———–+————–+——–
办公用品 | 打印机 | 1299.00
测试 | 测试产品2 | 199.00
电子产品 | 笔记本电脑 | 5999.00
(3 rows)
使用DISTINCT进行统计:
更多学习教程公众号风哥教程itpux_com
SELECT
COUNT(DISTINCT category) AS category_count,
COUNT(*) AS total_products,
COUNT(DISTINCT price) AS distinct_prices
FROM fgedu_products;
执行结果:
—————+—————–+—————-
3 | 12 | 10
(1 row)
4. LIMIT和OFFSET:限制返回行数
使用LIMIT限制返回行数:
SELECT product_id, product_name, price
FROM fgedu_products
ORDER BY price
LIMIT 5;
执行结果:
————+————–+——–
6 | USB数据线 | 19.90
2 | 无线鼠标 | 99.00
10 | 路由器 | 199.00
12 | 测试产品2 | 199.00
9 | 耳机 | 299.00
(5 rows)
使用OFFSET跳过指定行数:
SELECT product_id, product_name, price
FROM fgedu_products
ORDER BY price
LIMIT 5 OFFSET 5;
执行结果:
————+————–+——–
3 | 机械键盘 | 399.00
5 | 办公椅 | 899.00
4 | 显示器 | 1299.00
8 | 打印机 | 1299.00
7 | 平板电脑 | 3999.00
(5 rows)
实现分页查询:
学习交流加群风哥QQ113257174
SELECT product_id, product_name, price
FROM fgedu_products
ORDER BY price
LIMIT 5 OFFSET 0;
— 第二页
SELECT product_id, product_name, price
FROM fgedu_products
ORDER BY price
LIMIT 5 OFFSET 5;
— 第三页
SELECT product_id, product_name, price
FROM fgedu_products
ORDER BY price
LIMIT 5 OFFSET 10;
执行结果:
product_id | product_name | price
————+————–+——–
6 | USB数据线 | 19.90
2 | 无线鼠标 | 99.00
10 | 路由器 | 199.00
12 | 测试产品2 | 199.00
9 | 耳机 | 299.00
(5 rows)
— 第二页
product_id | product_name | price
————+————–+——–
3 | 机械键盘 | 399.00
5 | 办公椅 | 899.00
4 | 显示器 | 1299.00
8 | 打印机 | 1299.00
7 | 平板电脑 | 3999.00
(5 rows)
— 第三页
product_id | product_name | price
————+————–+——–
1 | 笔记本电脑 | 5999.00
(1 row)
获取TOP N产品:
SELECT product_id, product_name, fgfgfgfgsales_count
FROM fgedu_products
ORDER BY fgfgfgfgsales_count DESC
LIMIT 3;
执行结果:
————+————–+————-
6 | USB数据线 | 800
2 | 无线鼠标 | 450
9 | 耳机 | 320
(3 rows)
5. FETCH子句:现代分页语法
使用FETCH FIRST获取前N行:
SELECT product_id, product_name, price
FROM fgedu_products
ORDER BY price
FETCH FIRST 5 ROWS ONLY;
执行结果:
————+————–+——–
6 | USB数据线 | 19.90
2 | 无线鼠标 | 99.00
10 | 路由器 | 199.00
12 | 测试产品2 | 199.00
9 | 耳机 | 299.00
(5 rows)
使用FETCH WITH TIES获取并列数据:
SELECT product_id, product_name, price
FROM fgedu_products
ORDER BY price
FETCH FIRST 3 ROWS WITH TIES;
执行结果:
————+————–+——–
6 | USB数据线 | 19.90
2 | 无线鼠标 | 99.00
10 | 路由器 | 199.00
12 | 测试产品2 | 199.00
(4 rows)
使用FETCH PERCENT获取百分比数据:
SELECT product_id, product_name, price
FROM fgedu_products
ORDER BY price
FETCH FIRST 30 PERCENT ROWS ONLY;
执行结果:
————+————–+——–
6 | USB数据线 | 19.90
2 | 无线鼠标 | 99.00
10 | 路由器 | 199.00
(3 rows)
使用OFFSET和FETCH实现分页:
SELECT product_id, product_name, price
FROM fgedu_products
ORDER BY price
OFFSET 5 ROWS
FETCH NEXT 5 ROWS ONLY;
执行结果:
————+————–+——–
3 | 机械键盘 | 399.00
5 | 办公椅 | 899.00
4 | 显示器 | 1299.00
8 | 打印机 | 1299.00
7 | 平板电脑 | 3999.00
(5 rows)
6. 实战案例:电商数据分析
创建订单表:
CREATE TABLE fgedu_orders (
order_id SERIAL PRIMARY KEY,
customer_id INTEGER,
product_id INTEGER,
quantity INTEGER,
unit_price NUMERIC(10,2),
total_amount NUMERIC(10,2),
order_date DATE DEFAULT CURRENT_DATE,
status VARCHAR(20) DEFAULT ‘completed’
);
— 插入订单数据
INSERT INTO fgedu_orders (customer_id, product_id, quantity, unit_price, total_amount, order_date, status) VALUES
(1, 1, 1, 5999.00, 5999.00, ‘2026-03-15’, ‘completed’),
(2, 6, 5, 19.90, 99.50, ‘2026-03-20’, ‘completed’),
(3, 2, 3, 99.00, 297.00, ‘2026-03-25’, ‘completed’),
(1, 3, 2, 399.00, 798.00, ‘2026-04-01’, ‘completed’),
(4, 7, 1, 3999.00, 3999.00, ‘2026-04-02’, ‘pending’),
(2, 9, 2, 299.00, 598.00, ‘2026-04-03’, ‘completed’),
(5, 4, 1, 1299.00, 1299.00, ‘2026-04-04’, ‘shipped’),
(3, 10, 3, 199.00, 597.00, ‘2026-04-05’, ‘completed’),
(1, 5, 1, 899.00, 899.00, ‘2026-04-06’, ‘completed’),
(4, 6, 10, 19.90, 199.00, ‘2026-04-07’, ‘pending’);
执行结果:
分析热销产品TOP 5:
SELECT
p.product_id,
p.product_name,
p.category,
SUM(o.quantity) AS total_quantity,
SUM(o.total_amount) AS total_fgfgfgfgsales,
COUNT(o.order_id) AS order_count
FROM fgedu_products p
JOIN fgedu_orders o ON p.product_id = o.product_id
WHERE o.status = ‘completed’
GROUP BY p.product_id, p.product_name, p.category
ORDER BY total_quantity DESC
LIMIT 5;
执行结果:
————+————–+———–+—————–+————–+————-
6 | USB数据线 | 电子产品 | 15 | 298.50 | 2
2 | 无线鼠标 | 电子产品 | 3 | 297.00 | 1
3 | 机械键盘 | 电子产品 | 2 | 798.00 | 1
1 | 笔记本电脑 | 电子产品 | 1 | 5999.00 | 1
5 | 办公椅 | 办公用品 | 1 | 899.00 | 1
(5 rows)
分析各分类销售情况:
SELECT
p.category,
COUNT(DISTINCT o.order_id) AS order_count,
SUM(o.quantity) AS total_quantity,
SUM(o.total_amount) AS total_fgfgfgfgsales,
AVG(o.total_amount) AS avg_order_amount
FROM fgedu_products p
JOIN fgedu_orders o ON p.product_id = o.product_id
WHERE o.status = ‘completed’
GROUP BY p.category
ORDER BY total_fgfgfgfgsales DESC;
执行结果:
———–+————-+—————–+————–+——————
电子产品 | 5 | 22 | 7492.50 | 1498.50
办公用品 | 1 | 1 | 899.00 | 899.00
(2 rows)
分析客户购买行为:
SELECT
o.customer_id,
COUNT(DISTINCT o.order_id) AS order_count,
SUM(o.total_amount) AS total_spent,
AVG(o.total_amount) AS avg_order_amount,
MAX(o.order_date) AS last_order_date
FROM fgedu_orders o
WHERE o.status = ‘completed’
GROUP BY o.customer_id
ORDER BY total_spent DESC;
执行结果:
————-+————-+————-+——————+—————-
1 | 3 | 7696.00 | 2565.3333 | 2026-04-06
2 | 2 | 396.50 | 198.2500 | 2026-04-03
3 | 1 | 597.00 | 597.0000 | 2026-04-05
(3 rows)
分析订单状态分布:
SELECT
status,
COUNT(*) AS order_count,
SUM(total_amount) AS total_amount,
AVG(total_amount) AS avg_amount
FROM fgedu_orders
GROUP BY status
ORDER BY order_count DESC;
执行结果:
———-+————-+————–+————
completed | 7 | 9289.50 | 1327.0714
pending | 2 | 4198.00 | 2099.0000
shipped | 1 | 1299.00 | 1299.0000
(3 rows)
分析最近7天的销售趋势:
SELECT
order_date,
COUNT(*) AS order_count,
SUM(total_amount) AS daily_fgfgfgfgsales,
AVG(total_amount) AS avg_order_amount
FROM fgedu_orders
WHERE order_date >= CURRENT_DATE – INTERVAL ‘7 days’
GROUP BY order_date
ORDER BY order_date DESC;
执行结果:
————-+————-+————-+—————–
2026-04-07 | 1 | 199.00 | 199.00
2026-04-06 | 1 | 899.00 | 899.00
2026-04-05 | 1 | 597.00 | 597.00
2026-04-04 | 1 | 1299.00 | 1299.00
2026-04-03 | 1 | 598.00 | 598.00
2026-04-02 | 1 | 3999.00 | 3999.00
2026-04-01 | 1 | 798.00 | 798.00
(7 rows)
7. 性能优化技巧
创建索引优化排序查询:
CREATE INDEX idx_products_price ON fgedu_products(price);
CREATE INDEX idx_products_category_price ON fgedu_products(category, price);
CREATE INDEX idx_orders_order_date ON fgedu_orders(order_date);
执行结果:
CREATE INDEX
CREATE INDEX
比较有无索引的性能:
EXPLAIN ANALYZE
SELECT product_id, product_name, price
FROM fgedu_products
ORDER BY price
LIMIT 10;
执行结果:
————————————————————–
Limit (cost=0.28..1.45 rows=10 width=68)
-> Index Scan using idx_products_price on fgedu_products (cost=0.28..2.90 rows=20 width=68)
Planning Time: 0.234 ms
Execution Time: 0.456 ms
避免使用OFFSET进行深度分页:
EXPLAIN ANALYZE
SELECT product_id, product_name, price
FROM fgedu_products
ORDER BY price
OFFSET 10000 LIMIT 10;
— 推荐:使用WHERE子句进行分页
EXPLAIN ANALYZE
SELECT product_id, product_name, price
FROM fgedu_products
WHERE price > 1000
ORDER BY price
LIMIT 10;
执行结果:
Execution Time: 2.345 ms
— WHERE子句分页执行时间
Execution Time: 0.567 ms
使用覆盖索引优化查询:
CREATE INDEX idx_products_covering ON fgedu_products(category, price)
INCLUDE (product_name, stock);
— 使用覆盖索引查询
EXPLAIN ANALYZE
SELECT category, price, product_name, stock
FROM fgedu_products
WHERE category = ‘电子产品’
ORDER BY price
LIMIT 10;
执行结果:
————————————————————–
Limit (cost=0.28..1.45 rows=10 width=68)
-> Index Scan using idx_products_covering on fgedu_products (cost=0.28..2.90 rows=20 width=68)
Index Cond: (category = ‘电子产品’::text)
Planning Time: 0.123 ms
Execution Time: 0.234 ms
8. 常见问题与解决方案
问题1:排序结果不一致
风哥提示:
SELECT product_id, product_name, price
FROM fgedu_products
WHERE price = 199.00
ORDER BY price;
执行结果:
————+————–+——–
10 | 路由器 | 199.00
12 | 测试产品2 | 199.00
(2 rows)
解决方案:添加额外的排序列确保一致性
SELECT product_id, product_name, price
FROM fgedu_products
WHERE price = 199.00
ORDER BY price, product_id;
执行结果:
————+————–+——–
10 | 路由器 | 199.00
12 | 测试产品2 | 199.00
(2 rows)
问题2:DISTINCT性能问题
EXPLAIN ANALYZE
SELECT DISTINCT category
FROM fgedu_products;
执行结果:
————————————————————–
HashAggregate (cost=1.20..1.21 rows=3 width=32)
Group Key: category
-> Seq Scan on fgedu_products (cost=0.00..1.12 rows=12 width=32)
Planning Time: 0.123 ms
Execution Time: 0.234 ms
解决方案:为DISTINCT列创建索引
学习交流加群风哥微信: itpux-com
CREATE INDEX idx_products_category ON fgedu_products(category);
EXPLAIN ANALYZE
SELECT DISTINCT category
FROM fgedu_products;
执行结果:
————————————————————–
HashAggregate (cost=1.20..1.21 rows=3 width=32)
Group Key: category
-> Seq Scan on fgedu_products (cost=0.00..1.12 rows=12 width=32)
Planning Time: 0.145 ms
Execution Time: 0.189 ms
问题3:LIMIT/OFFSET在大表上性能差
更多视频教程www.fgedu.net.cn
EXPLAIN ANALYZE
SELECT product_id, product_name, price
FROM fgedu_products
ORDER BY price
OFFSET 10000 LIMIT 10;
执行结果:
————————————————————–
Limit (cost=0.28..1.45 rows=10 width=68)
-> Index Scan using idx_products_price on fgedu_products (cost=0.28..2.90 rows=20 width=68)
Planning Time: 0.123 ms
Execution Time: 0.345 ms
解决方案:使用游标分页
— 第一页
SELECT product_id, product_name, price
FROM fgedu_products
ORDER BY price
LIMIT 10;
— 第二页(使用上一页最后一个产品的价格作为游标)
SELECT product_id, product_name, price
FROM fgedu_products
WHERE price > 199.00
ORDER BY price
LIMIT 10;
执行结果:
from oracle:www.itpux.com
product_id | product_name | price
————+————–+——–
6 | USB数据线 | 19.90
2 | 无线鼠标 | 99.00
10 | 路由器 | 199.00
12 | 测试产品2 | 199.00
9 | 耳机 | 299.00
3 | 机械键盘 | 399.00
5 | 办公椅 | 899.00
4 | 显示器 | 1299.00
8 | 打印机 | 1299.00
7 | 平板电脑 | 3999.00
(10 rows)
— 第二页
product_id | product_name | price
————+————–+——–
1 | 笔记本电脑 | 5999.00
(1 row)
9. 清理环境
清理所有测试表:
DROP TABLE IF EXISTS fgedu_products;
DROP TABLE IF EXISTS fgedu_orders;
执行结果:
DROP TABLE
本文由风哥教程整理发布,仅用于学习测试使用,转载注明出处:http://www.fgedu.net.cn/10327.html
