ITPub博客

首页 > 数据库 > MySQL > SQL必知必会(一)语法基础篇

SQL必知必会(一)语法基础篇

原创 MySQL 作者:lanjinwei 时间:2026-07-22 09:18:16 0 删除 编辑

SQL必知必会(一)语法基础篇

作为一名工程师,我非常理解那种“知其然,更想知其所以然”的冲动。看到一堆SQL概念在眼前飘过,是不是特别想撸起袖子,把它们一个个拆解清楚,然后装进自己的知识库里?

别急,咱们今天就来干这件事。我会用工程师的视角,像抽丝剥茧一样,把这些SQL的“必知必会”知识点,用最通俗的语言、最形象的比喻,一个一个给你讲透。

准备好了吗?咱们这就开始,花上一点时间,把这杯SQL的“浓缩咖啡”一口闷下去,保证你以后写起查询来,手中有剑,心里不慌!


7e1ad5515f2f.png

SQL 必知必会(一)语法基础篇:工程师的“庖丁解牛”笔记

嗨,朋友们!作为一名经常跟数据库打交道的工程师,你是不是也遇到过这种情况:有些SQL语法,明明以前用得飞起,但一段时间不用,就变得“最熟悉的陌生人”了?

别担心,这太正常了!为了对抗这种“遗忘曲线”,我把自己在工作中的一些复习笔记整理了出来。这不仅是为了巩固我自己的知识体系,如果能顺便帮到你,那就更棒了!

咱们先快速浏览一下,今天这趟“SQL探险之旅”会经过哪些站点:

  • 如何给MySQL“体检”,分析SQL语句的执行时间
  • 用 ALTER 语句给表“整容”(修改表结构)
  • 用 SELECT 加“常量”这种骚操作
  • DISTINCT 去重,到底该放哪儿?
  • ORDER BY 排序,它心里的小九九
  • 不同数据库里,分页的“方言”怎么说?
  • 通配符 % 和 _ 的正确打开方式
  • 获取时间和转换数值的实用函数
  • HAVING 和 WHERE 这对“兄弟”的区别
  • 子查询的“单打独斗”与“协同作战”
  • SQL 标准那些事儿(SQL92 vs SQL99)
  • 视图:给你的数据加个“快捷方式”
  • 存储过程:把复杂的SQL逻辑“封装”起来
  • 事务处理:保证数据“原子性”的定海神针
  • 事务隔离:解决“脏读”、“幻读”的武林秘籍
  • 游标:像指针一样,逐行处理数据
  • ORM:让面向对象和数据库“握手言和”
  • 列式数据库:为什么它比行式更适合分析?
  • 查询优化小技巧:让你的SQL飞起来

怎么样,是不是感觉内容很扎实?别急,咱们一个一个来!

在 MySQL 中分析 SQL 语句的执行时间

想象一下,你写了一个SQL,跑起来感觉有点慢。怎么定位问题呢?MySQL 自带了一个“体检工具”—— profiling。

第一步:看看这个工具开了没。

SELECT @@profiling;

如果结果是0,说明没开。咱们手动打开它:

SET profiling = 1; -- 1 表示开启,0 表示关闭

第二步:执行你想分析的SQL。 可以是一条,也可以是多条。

第三步:查看“体检报告”。

SHOW PROFILES; -- 查看所有SQL的执行“体检单”
SHOW PROFILE; -- 查看最近一次SQL的详细“体检单”
SHOW PROFILE FOR QUERY 2; -- 查看指定Query ID的详细“体检单”

是不是很简单?就像给代码加了个计时器,一目了然。

DDL 语言中修改表结构的 ALTER 语句

ALTER 就是用来修改表结构的“瑞士N”。比如,你想给 player 表加个年龄字段,或者改个字段名,都可以用它。

-- 给 player 表增加一个 age 字段(整数类型)
ALTER TABLE player
ADD (age INT(11));
-- 把 age 字段改名为 player_age
ALTER TABLE player
RENAME COLUMN age TO player_age;
-- 修改 player_age 字段的类型为 FLOAT,总长度3,小数位1
ALTER TABLE player
MODIFY (player_age FLOAT(3,1));
-- 删除 player_age 字段
ALTER TABLE player
DROP COLUMN player_age;

互动提问: 思考一下,如果表里已经有大量数据,修改字段类型(比如从INT改成FLOAT)会有什么风险?欢迎在评论区留言讨论!

常量搜索 SELECT

有时候,我们想在查询结果里加一列固定的“标签”,比如所有数据都来自同一个平台。这时候就可以用常量搜索。

SELECT '王z荣耀' AS platform, name FROM heros;

这条语句会返回两列:一列是固定的字符串“王z荣耀”(列名叫platform),另一列是 heros 表里的 name 字段。

注意! 常量是字符串,一定要加单引号 ''。不加的话,SQL会以为你是在引用一个列名,然后报错。数字常量则可以直接写。

DISTINCT 去重位置

DISTINCT 的作用是去重,但它有个“小脾气”: 必须放在所有列名的最前面。而且,它是对后面所有列的组合进行去重。

比如 SELECT DISTINCT name, age FROM users,只有当 name 和 age 都相同时,才会被认定为重复行。

ORDER BY 排序规则

ORDER BY 排序,数值是按大小排,文本呢?这取决于数据库的“排序规则”。在 MySQL 里,如果字段创建时加了 BINARY 属性,就区分大小写。

还有一个很实用的技巧: ORDER BY 可以使用SELECT语句中没有出现的列进行排序。比如 SELECT name FROM users ORDER BY id,这样是合法的。

不同数据库系统下的分页关键字

分页查询,不同数据库的“方言”还真不一样,就像不同地方说“你好”一样。

  • MySQL: LIMIT (最常用的)
  • SQL Server、Access: TOP
  • DB2: FETCH FIRST 5 ROWS ONLY
  • Oracle: ROWNUM

SELECT 通配符相关

通配符是模糊查询的好帮手。

  • %:表示零个或多个字符。比如 LIKE '张%',能查到所有姓“张”的。
  • _:表示一个字符。比如 LIKE '张_',只能查到姓“张”且名字只有一个字的。

重要提示: 只有当 LIKE 语句后面 没有通配符时,并且字段有索引,才能避免全表扫描。否则, LIKE 基本就是“全表扫描”的代名词。

SELECT 获取时间函数

处理时间数据,这些函数你得认识:

SELECT CURRENT_TIME();   -- 当前时间
SELECT CURRENT_DATE();   -- 当前日期
SELECT CURRENT_TIMESTAMP(); -- 当前日期和时间
SELECT EXTRACT(YEAR FROM '2019-04-03'); -- 从日期中提取年份
SELECT DATE('2019-04-01 12:00:05'); -- 提取日期部分

SQL 数值转换函数

处理数字,这两个函数很实用:

-- CAST:把123.123转换成DECIMAL类型,总长度8,小数点后保留2位
SELECT CAST(123.123 AS DECIMAL(8, 2));
-- COALESCE:返回参数列表中的第一个非NULL值
SELECT COALESCE(NULL, 1, 2); -- 返回 1

关于 HAVING

HAVING 和 WHERE 很像,但它们是“兄弟”关系,不是“父子”关系。

  • WHERE:在 分组前对原始数据进行筛选。
  • HAVING:在 分组后对分组后的结果进行筛选。

所以, HAVING 子句支持所有 WHERE 的功能,但它通常和 GROUP BY 一起出现,用来过滤聚合函数的结果(比如 HAVING COUNT(*) > 5)。

关于子查询分类

子查询,就是“查询中的查询”。它有两种“工作模式”:

  1. 非关联子查询:子查询只执行一次,然后把结果返回给主查询。就像你一次性查好资料,然后直接用。效率高。
  2. 关联子查询:子查询需要执行多次,每次主查询处理一行数据,都会把当前行的值传给子查询。就像你每处理一个人,都要去查一下他的档案。效率相对低,但功能强大。

关联子查询通常和 EXISTS 一起用。 EXISTS 只关心子查询是否有结果返回,有则 True,无则 False。

-- 查询所有在 player_score 表中有得分的球员
SELECT player_id, team_id, player_name FROM player
WHERE EXISTS (
    SELECT player_id FROM player_score
    WHERE player.player_id = player_score.player_id
);

小贴士: IN 和 EXISTS 可以互换,但效率不同。如果外层表( player)数据量小,用 EXISTS 更快;如果子查询表( player_score)数据量小,用 IN 更快。

常用的 SQL 标准

SQL 有两个主要的标准:SQL92 和 SQL99。你可以把它们想象成 SQL 的“1.0版本”和“2.0版本”。

SQL92 定义了五种连接方式:

  • 笛卡尔积
  • 等值连接
  • 非等值连接
  • 外连接(左连接、右连接)
  • 自连接

左外连接,就是左表为主,右表为从。在 Oracle 里,用 (+) 表示从表,而 MySQL 不支持这种写法。

SQL92 写法:

SELECT * FROM player, team
WHERE player.team_id = team.team_id (+);

SQL99 写法(更推荐):

SELECT * FROM player LEFT JOIN team ON player.team_id = team.team_id;

SQL99 还引入了 NATURAL JOIN(自然连接),它会自动匹配两个表中同名的列,省去了写 ON 条件的麻烦。

视图

视图就像一个“虚拟表”,它不存储实际数据,只存储一个查询定义。你可以把它想象成一个“快捷方式”,把你经常用到的复杂查询结果保存起来,下次直接用这个“快捷方式”就行。

创建视图:

CREATE VIEW view_name AS SELECT column1, column2 FROM table WHERE condition;

修改用 ALTER VIEW,删除用 DROP VIEW。

存储过程

如果说视图是“快捷方式”,那存储过程就是“宏命令”或“小程序”。它是一组为了完成特定功能的SQL语句集,可以接收参数,并返回结果。

创建存储过程:

CREATE PROCEDURE procedure_name ([参数列表])
BEGIN
    -- 执行的语句
END;

来个例子:计算 1 + 2 + ... + n 的值

DELIMITER //  -- 先把结束符改成 //,避免和语句内的 ; 冲突
CREATE PROCEDURE add_num(IN n INT)
BEGIN
    DECLARE i INT;
    DECLARE sum INT;
    SET i = 1;
    SET sum = 0;
    WHILE i <= n DO
        SET sum = sum + i;
        SET i = i + 1;
    END WHILE;
    SELECT sum;
END //
DELIMITER ;  -- 再把结束符改回 ;

常用流控制语句:

  • IF…THEN…ELSE…ENDIF
  • CASE
  • LOOP, LEAVE (跳出循环), ITERATE (进入下一次循环)
  • REPEAT…UNTIL…END REPEAT
  • WHILE…DO…END WHILE

优缺点:

  • 优点:一次编译,多次使用;安全性高;减少网络传输。
  • 缺点:可移植性差(换数据库就得重写);调试困难;不支持高并发。

事务处理

事务是保证数据一致性的“定海神针”。它遵循 ACID 原则:原子性、一致性、隔离性、持久性。

  • 原子性:事务里的操作,要么全部成功,要么全部失败。
  • 一致性:事务执行前后,数据状态都是正确的。
  • 隔离性:并发执行的事务之间互不干扰。
  • 持久性:事务一旦提交,结果就是永J的。

在 MySQL 里, InnoDB 存储引擎支持事务,而 MyISAM 不支持。

常用控制语句:

  • START TRANSACTION 或 BEGIN:开启事务。
  • COMMIT:提交事务。
  • ROLLBACK:回滚事务。
  • SAVEPOINT:设置保存点,方便部分回滚。

事务隔离

当多个事务同时操作数据时,可能会产生三个“幽灵”问题:

  • 脏读:读到另一个事务未提交的数据。
  • 不可重复读:同一事务内,两次读取同一行数据,结果不一样(被其他事务修改了)。
  • 幻读:同一事务内,两次查询返回的行数不一样(被其他事务插入或删除了)。

为了解决这些问题,SQL标准定义了四个隔离级别:

| 隔离级别 | 脏读 | 不可重复读 | 幻读 |

| :--- | :---: | :---: | :---: |

| 读未提交 | 可能 | 可能 | 可能 |

| 读已提交 | 不可能 | 可能 | 可能 |

| 可重复读 | 不可能 | 不可能 | 可能 |

| 可串行化 | 不可能 | 不可能 | 不可能 |

  • 读未提交:最低级别,什么都不防。
  • 读已提交:防脏读,Oracle等数据库的默认级别。
  • 可重复读:防脏读和不可重复读, MySQL的默认级别。
  • 可串行化:最高级别,所有事务串行执行,性能最差。

查看和设置隔离级别:

-- 查看当前会话的隔离级别
SHOW VARIABLES LIKE 'transaction_isolation';
-- 设置当前会话的隔离级别为读未提交
SET SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;

游标

游标就像C语言里的“指针”,让你能一行一行地处理SQL查询结果。它让面向集合的SQL具备了面向过程的能力。

使用步骤:

  1. 定义: DECLARE cursor_name CURSOR FOR select_statement;
  2. 打开: OPEN cursor_name;
  3. 获取: FETCH cursor_name INTO var_name;
  4. 关闭: CLOSE cursor_name;
  5. 释放: DEALLOCATE cursor_name;

使用实例(累加英雄的最大生命值):

CREATE PROCEDURE calc_hp_max()
BEGIN
    DECLARE hp INT;
    DECLARE hp_sum INT DEFAULT 0;
    DECLARE done INT DEFAULT false;
    DECLARE cur_hero CURSOR FOR SELECT hp_max FROM heros;
    DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = true; -- 处理找不到数据的情况
    OPEN cur_hero;
    read_loop: LOOP
        FETCH cur_hero INTO hp;
        IF done THEN
            LEAVE read_loop;
        END IF;
        SET hp_sum = hp_sum + hp;
    END LOOP;
    CLOSE cur_hero;
    SELECT hp_sum;
END;

ORM 的意义

ORM(对象关系映射)就像是一个“翻译官”,它把数据库里的“表”和“行”,翻译成我们编程语言里的“对象”和“属性”。这样,我们就可以用面向对象的方式操作数据库,而不用写一堆SQL。

好处: 开发效率高,屏蔽了不同数据库的差异,换数据库时,改个配置就行。

列式数据库的好处

传统的关系型数据库(如MySQL)是行式存储,适合频繁的增删改(OLTP)。而列式数据库(如ClickHouse)则是按列存储。

好处:

  1. 降低I/O:分析查询通常只涉及少数几个字段,列式存储只需读取相关列,大大减少了I/O。
  2. 压缩率高:同一列的数据类型一致,更容易压缩,节省存储空间。
  3. 适合OLAP:非常适合大数据量的分析型查询。

查询优化相关

  • COUNT(*) vs COUNT(1) vs COUNT(字段):一般来说,执行效率 COUNT(*) ≈ COUNT(1) > COUNT(字段)。尽量用 COUNT(*) 吧,除非你要统计某字段的非空值。
  • LIMIT 1:如果能确定结果只有一条,加上 LIMIT 1 可以让MySQL在找到一条记录后立即停止扫描,避免全表扫描。
  • ORDER BY 加索引:如果 ORDER BY 的字段有索引,MySQL会直接利用索引排序,避免“文件排序”(FileSort),效率极高。

SQL 语句执行顺序

这个顺序很重要,理解了它,你就能预判SQL的执行结果。

  1. FROM 子句:确定数据来源(包括JOIN)。
  2. WHERE 子句:对原始数据进行筛选。
  3. GROUP BY 子句:对筛选后的数据进行分组。
  4. 使用聚合函数:对每个分组进行计算(如 SUM, COUNT)。
  5. HAVING 子句:对分组后的结果进行筛选。
  6. 计算所有表达式:比如 SELECT 后面的列名、别名等。
  7. SELECT 子句:选择要输出的列。
  8. ORDER BY 子句:对结果进行排序。
  9. LIMIT 子句:限制返回的行数。

几个补充知识点

  • EXISTS vs IN:当外层表(左表)小,子查询表(右表)大时,用 EXISTS 效率高;反之用 IN。
  • varchar vs nvarchar: varchar(n) 是 n 个 字节,存英文占1字节,中文占2字节; nvarchar(n) 是 n 个 字符,英文和中文都占2字节(Unicode编码)。
  • 事务补充:在MySQL中, BEGIN 用于开启事务。如果你连续使用 BEGIN,数据库会隐式 COMMIT 第一个事务,然后进入第二个事务。

好了,朋友们!这趟SQL语法基础之旅到这里就告一段落了。希望这些内容能帮你把那些零散的知识点串起来,形成一个更清晰的知识网络。


学习SQL最关键的还是要练习

SQL全套学习,资料,练习和视频,推荐自学网站,SQL自学网, http://xuesql.cn 入门学习网站, https://www.w3schools.com/

来自 “ ITPUB博客 ” ,链接:https://blog.itpub.net/70049428/viewspace-3176438/ ,如需转载,请注明出处,否则将追究法律责任。

请登录后发表评论 登录
全部评论

注册时间:2026-01-27

  • 博文量
    10
  • 访问量
    10797