1-9 SELECT 查询基础:where、group by、having、order by、limit offset

哈喽,各位码友!在数据库开发中,SELECT 查询 是使用率最高的核心语法,无论是日常业务开发、数据统计,还是接口数据查询,都离不开它。很多初学者能写简单查询,但对 where、group by、having、order by、limit offset 的执行顺序、使用场景、区别以及分页性能问题一知半解。

本篇博客将从零梳理 SELECT 基础查询核心知识点,包含完整语法、关键字用法、distinct 去重、offset 分页性能缺陷及优化思路,全程搭配可运行 SQL 案例,适合零基础入门、查漏补缺。

适用数据库:PostgreSQL 12+

说明:PostgreSQL 的 limit/offset 语法和 MySQL 写法很接近,但分组、聚合、严格模式、分页底层实现有区别,下文会标注差异点。

测试表说明 :本文所有案例基于学生成绩表 stu_score

sql 复制代码
-- 测试数据表 PostgreSQL
CREATE TABLE stu_score (
    id INT PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY COMMENT '主键ID',
    stu_name VARCHAR(20) NOT NULL COMMENT '学生姓名',
    subject VARCHAR(20) NOT NULL COMMENT '考试科目',
    score INT NOT NULL COMMENT '分数',
    class_id INT COMMENT '班级ID'
) COMMENT '学生成绩表';

-- 测试数据
INSERT INTO stu_score (stu_name, subject, score, class_id) VALUES
('张三','语文',88,1),
('张三','数学',95,1),
('李四','语文',76,2),
('李四','数学',82,2),
('王五','语文',92,1),
('王五','数学',89,1),
('赵六','语文',85,2),
('赵六','数学',90,2);

一、SELECT 完整基础语法结构

首先记住 SELECT 标准执行顺序(重中之重),这是区分各类查询关键字的核心:

  • 语法书写顺序:select → from → where → group by → having → order by → limit
  • 数据库真实执行顺序:from → where → group by → having → select → order by → limit

很多语法报错、查询结果异常,都是因为混淆了执行顺序!下面我们逐个拆解每个关键字的用法。

完整通用语法:

sql 复制代码
SELECT [DISTINCT] 字段列表
FROM 表名
[WHERE 行过滤条件]
[GROUP BY 分组字段]
[HAVING 分组后过滤条件]
[ORDER BY 排序字段 [ASC/DESC]]
[LIMIT 条数 OFFSET 偏移量];

PG 小提示:PostgreSQL 推荐 LIMIT size OFFSET offset 写法;不支持 MySQL LIMIT offset,size 逗号写法!这点和 MySQL 不一样,写 SQL 时容易踩坑。

二、核心关键字详解

1、WHERE:分组前行过滤

where 的作用是筛选原始数据表中的单行数据 ,在 group by 分组之前 执行,过滤掉不符合条件的原始数据。

✅ 支持条件:比较运算(>、<、=)、逻辑运算(and/or/not)、模糊查询(like)、范围查询(between and、in) ❌ 不支持:聚合函数(sum、count、avg 等),因为执行 where 时还未分组,没有聚合结果

案例:查询语文分数大于 80 分的学生成绩

sql 复制代码
SELECT * FROM stu_score 
WHERE subject = '语文' AND score > 80;

执行逻辑:先从数据表中筛选出科目为语文、分数大于 80 的原始数据,再返回结果。

2、GROUP BY:数据分组统计

group by 用于将查询结果按照指定字段分组,通常搭配聚合函数使用(count、sum、avg、max、min),实现分组统计。

使用规则(PostgreSQL 强制严格校验):

  • SELECT 后面出现的非聚合字段,必须全部出现在 GROUP BY 子句中,PG 默认不允许 MySQL 那种宽松分组模式,否则直接报错。
  • 可以单个字段分组,也可以多个字段联合分组。

案例:统计每个学生的总成绩、平均成绩

sql 复制代码
SELECT 
    stu_name,
    SUM(score) AS total_score,
    AVG(score) AS avg_score
FROM stu_score
GROUP BY stu_name;

3、HAVING:分组后结果过滤

having 是分组后的过滤条件 ,在 group by 分组、聚合计算完成后执行,专门用于筛选聚合后的统计结果。

✅ 支持:聚合函数、普通字段条件 ❌ 和 where 的核心区别:where 过滤原始数据(分组前),having 过滤统计数据(分组后)

案例:统计总成绩大于 170 分的学生

sql 复制代码
SELECT 
    stu_name,
    SUM(score) AS total_score
FROM stu_score
GROUP BY stu_name
HAVING SUM(score) > 170;

PG 小提示:having 里不能直接使用 select 中定义的别名 total_score,需要写原始聚合表达式,这点和 MySQL 不同!

4、ORDER BY:结果排序

order by 用于对最终查询结果进行排序,在所有筛选、分组、聚合完成后执行。

排序规则:

  • ASC:升序(默认,可省略)
  • DESC:降序

支持单字段排序、多字段优先级排序。

案例:按学生总成绩降序排序

sql 复制代码
SELECT 
    stu_name,
    SUM(score) AS total_score
FROM stu_score
GROUP BY stu_name
ORDER BY total_score DESC;

PG 这里支持使用 select 别名排序,和 MySQL 一致。

5、LIMIT & OFFSET:分页查询

PostgreSQL 使用 LIMIT 限制返回行数,OFFSET 设置跳过多少行,没有 MySQL 逗号简写语法。

标准写法:

sql 复制代码
-- PG 标准写法:LIMIT 返回条数 OFFSET 跳过行数
LIMIT size OFFSET offset
  • offset:偏移量,跳过前 N 条数据
  • size:每页展示的数据条数

案例:分页查询,第 2 页,每页 2 条数据(页码从第 0 页开始)

sql 复制代码
SELECT * FROM stu_score
ORDER BY id ASC
LIMIT 2 OFFSET 2;

三、DISTINCT 去重用法详解

DISTINCT 用于去除查询结果中的重复数据,保留唯一值,写在 SELECT 之后。

1、单字段去重

sql 复制代码
-- 查询所有不重复的班级ID
SELECT DISTINCT class_id FROM stu_score;

2、多字段联合去重

重点 :多字段去重时,只有所有字段值完全一致才会判定为重复,单一字段重复不会去重。

sql 复制代码
-- 姓名+科目联合去重
SELECT DISTINCT stu_name, subject FROM stu_score;

、DISTINCT 注意事项

  • DISTINCT 作用于后续所有字段,而非单个字段
  • 底层会执行排序(HashAggregate / Sort),大数据量下会消耗 CPU 和内存,慎用
  • PostgreSQL 还提供 DISTINCT ON(),可以实现按指定字段只取第一条,是 PG 特有语法,后续进阶篇讲解

四、OFFSET 分页核心性能问题(面试高频)

很多人以为只有 MySQL 存在 offset 深分页问题,PostgreSQL 的大 offset 分页同样有严重性能问题,后端面试高频考点。

1、性能问题根源

PostgreSQL 在执行 OFFSET M LIMIT N 时:数据库需要扫描并丢弃前面 M 行数据,再返回 N 条。

LIMIT 10 OFFSET 10000 PG 需要读取 10010 行数据,丢弃前 10000 行,只返回最后 10 条。offset 越大,扫描行数越多,IO 和 CPU 开销越高,分页越深查询越慢。

2、案例对比

sql 复制代码
-- 浅分页:性能几乎无影响
SELECT * FROM stu_score LIMIT 10 OFFSET 10;

-- 深分页:百万级数据下极度卡顿
SELECT * FROM stu_score LIMIT 10 OFFSET 1000000;

3、优化方案(PostgreSQL 落地)

方案 1:主键书签分页(推荐,也叫 keyset 分页)

利用有序主键索引,用 where 条件代替 offset,不需要扫描丢弃前面数据。

sql 复制代码
-- 已知上一页最后一条ID为10,查询下一页10条数据
SELECT * FROM stu_score WHERE id > 10 ORDER BY id LIMIT 10;

优点:走索引查询,速度稳定,不受分页深度影响 缺点:不适合 ID 存在删除、不连续场景;不支持直接跳页(跳转到第 1000 页)

方案 2:延迟关联优化

先从索引取出主键,再关联回表拿完整字段,减少回表开销。

sql 复制代码
SELECT s.* FROM stu_score s
INNER JOIN (SELECT id FROM stu_score ORDER BY id LIMIT 10 OFFSET 10000) temp
ON s.id = temp.id
ORDER BY s.id;
方案 3:业务层限制深分页

产品层面限制用户最大分页页数(例如最多允许查前 1000 页),从业务上规避大 offset,企业项目最常用方案。

PG 额外补充:PostgreSQL 13+ 支持 FETCH FIRST ... ROWS ONLY,是 SQL 标准语法,等价 LIMIT

sql 复制代码
SELECT * FROM stu_score ORDER BY id FETCH FIRST 2 ROWS ONLY OFFSET 2;

五、高频易错知识点总结

  1. 执行顺序:where(原始过滤)→ group by(分组)→ having(聚合过滤)→ order by(排序)→ limit(分页)
  2. Where 和 Having 区别 :where 不能用聚合函数,过滤原始行;having 过滤分组聚合结果。PostgreSQL 的 having 不能直接引用 select 别名
  3. Distinct :多字段联合去重,全部字段一致才去重;底层排序开销大。PG 特有 DISTINCT ON
  4. Offset 分页坑:PG 同样存在大偏移量性能衰减,优先使用主键书签分页优化

六、文末总结

SELECT 查询是 SQL 的基石,where、group by、having、order by、limit offset 的组合使用,覆盖了 90% 的数据查询、统计、分页场景。

掌握执行顺序 和性能痛点,不仅能写出正确的 SQL,更能规避线上慢查询问题,也是面试、开发的核心加分点。后续会持续更新 PostgreSQL 进阶语法、索引优化、联表查询等干货!

相关推荐
唐老板31 分钟前
Postgres 慢查询,先取证再动手
数据库·ai编程
笃行3507 小时前
数据库性能优化的五处内核改造,KingbaseES 配了四件诊断工具
数据库
非优秀程序员9 小时前
第02章 杭电的英语老师与一台服务器|《互联网产品恩仇录》
数据库·node.js·代码规范
SelectDB14 小时前
SelectDB 是哪个公司的?是国产数据库吗?一篇讲清飞轮科技与 Apache Doris 的关系
数据库
这个DBA有点耶14 小时前
库内计算推理:一条SQL背后的算子融合,和外挂方案差了几次网络往返
数据库·人工智能·sql
这个DBA有点耶16 小时前
函数索引的“完全一致”有多苛刻:写法、字符集、类型,差一处就失效
数据库·mysql·代码规范
星恒随风16 小时前
MySQL 从零入门(三):表的约束详解
linux·服务器·数据库·笔记·mysql
Alluxio17 小时前
Alluxio + OCI:为 AI 工作负载实现亚毫秒访问延迟与可扩展吞吐
运维·数据库·人工智能·分布式·缓存·ai·语言模型
数据库急诊室_阿哲21 小时前
一次加索引建议的复核记录:从报告到签字
数据库·ai数据库