首页 > 数据库 > MySQL > SQL必知必会(一)语法基础篇
作为一名工程师,我非常理解那种“知其然,更想知其所以然”的冲动。看到一堆SQL概念在眼前飘过,是不是特别想撸起袖子,把它们一个个拆解清楚,然后装进自己的知识库里?
别急,咱们今天就来干这件事。我会用工程师的视角,像抽丝剥茧一样,把这些SQL的“必知必会”知识点,用最通俗的语言、最形象的比喻,一个一个给你讲透。
准备好了吗?咱们这就开始,花上一点时间,把这杯SQL的“浓缩咖啡”一口闷下去,保证你以后写起查询来,手中有剑,心里不慌!

嗨,朋友们!作为一名经常跟数据库打交道的工程师,你是不是也遇到过这种情况:有些SQL语法,明明以前用得飞起,但一段时间不用,就变得“最熟悉的陌生人”了?
别担心,这太正常了!为了对抗这种“遗忘曲线”,我把自己在工作中的一些复习笔记整理了出来。这不仅是为了巩固我自己的知识体系,如果能顺便帮到你,那就更棒了!
咱们先快速浏览一下,今天这趟“SQL探险之旅”会经过哪些站点:
ALTER 语句给表“整容”(修改表结构)SELECT 加“常量”这种骚操作DISTINCT 去重,到底该放哪儿?ORDER BY 排序,它心里的小九九% 和
_ 的正确打开方式HAVING 和
WHERE 这对“兄弟”的区别怎么样,是不是感觉内容很扎实?别急,咱们一个一个来!
想象一下,你写了一个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的详细“体检单”
是不是很简单?就像给代码加了个计时器,一目了然。
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 '王z荣耀' AS platform, name FROM heros;
这条语句会返回两列:一列是固定的字符串“王z荣耀”(列名叫platform),另一列是
heros 表里的
name 字段。
注意! 常量是字符串,一定要加单引号
''。不加的话,SQL会以为你是在引用一个列名,然后报错。数字常量则可以直接写。
DISTINCT 的作用是去重,但它有个“小脾气”:
必须放在所有列名的最前面。而且,它是对后面所有列的组合进行去重。
比如
SELECT DISTINCT name, age FROM users,只有当
name 和
age 都相同时,才会被认定为重复行。
ORDER BY 排序,数值是按大小排,文本呢?这取决于数据库的“排序规则”。在 MySQL 里,如果字段创建时加了
BINARY 属性,就区分大小写。
还有一个很实用的技巧:
ORDER BY 可以使用SELECT语句中没有出现的列进行排序。比如
SELECT name FROM users ORDER BY id,这样是合法的。
分页查询,不同数据库的“方言”还真不一样,就像不同地方说“你好”一样。
LIMIT (最常用的)TOPFETCH FIRST 5 ROWS ONLYROWNUM通配符是模糊查询的好帮手。
%:表示零个或多个字符。比如
LIKE '张%',能查到所有姓“张”的。_:表示一个字符。比如
LIKE '张_',只能查到姓“张”且名字只有一个字的。
重要提示: 只有当
LIKE 语句后面
没有通配符时,并且字段有索引,才能避免全表扫描。否则,
LIKE 基本就是“全表扫描”的代名词。
处理时间数据,这些函数你得认识:
SELECT CURRENT_TIME(); -- 当前时间
SELECT CURRENT_DATE(); -- 当前日期
SELECT CURRENT_TIMESTAMP(); -- 当前日期和时间
SELECT EXTRACT(YEAR FROM '2019-04-03'); -- 从日期中提取年份
SELECT DATE('2019-04-01 12:00:05'); -- 提取日期部分
处理数字,这两个函数很实用:
-- CAST:把123.123转换成DECIMAL类型,总长度8,小数点后保留2位 SELECT CAST(123.123 AS DECIMAL(8, 2)); -- COALESCE:返回参数列表中的第一个非NULL值 SELECT COALESCE(NULL, 1, 2); -- 返回 1
HAVING 和
WHERE 很像,但它们是“兄弟”关系,不是“父子”关系。
WHERE:在
分组前对原始数据进行筛选。HAVING:在
分组后对分组后的结果进行筛选。所以,
HAVING 子句支持所有
WHERE 的功能,但它通常和
GROUP BY 一起出现,用来过滤聚合函数的结果(比如
HAVING COUNT(*) > 5)。
子查询,就是“查询中的查询”。它有两种“工作模式”:
关联子查询通常和
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 有两个主要的标准: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…ENDIFCASELOOP,
LEAVE (跳出循环),
ITERATE (进入下一次循环)REPEAT…UNTIL…END REPEATWHILE…DO…END WHILE优缺点:
事务是保证数据一致性的“定海神针”。它遵循 ACID 原则:原子性、一致性、隔离性、持久性。
在 MySQL 里,
InnoDB 存储引擎支持事务,而
MyISAM 不支持。
常用控制语句:
START TRANSACTION 或
BEGIN:开启事务。COMMIT:提交事务。ROLLBACK:回滚事务。SAVEPOINT:设置保存点,方便部分回滚。当多个事务同时操作数据时,可能会产生三个“幽灵”问题:
为了解决这些问题,SQL标准定义了四个隔离级别:
| 隔离级别 | 脏读 | 不可重复读 | 幻读 |
| :--- | :---: | :---: | :---: |
| 读未提交 | 可能 | 可能 | 可能 |
| 读已提交 | 不可能 | 可能 | 可能 |
| 可重复读 | 不可能 | 不可能 | 可能 |
| 可串行化 | 不可能 | 不可能 | 不可能 |
查看和设置隔离级别:
-- 查看当前会话的隔离级别 SHOW VARIABLES LIKE 'transaction_isolation'; -- 设置当前会话的隔离级别为读未提交 SET SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
游标就像C语言里的“指针”,让你能一行一行地处理SQL查询结果。它让面向集合的SQL具备了面向过程的能力。
使用步骤:
DECLARE cursor_name CURSOR FOR select_statement;OPEN cursor_name;FETCH cursor_name INTO var_name;CLOSE cursor_name;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(对象关系映射)就像是一个“翻译官”,它把数据库里的“表”和“行”,翻译成我们编程语言里的“对象”和“属性”。这样,我们就可以用面向对象的方式操作数据库,而不用写一堆SQL。
好处: 开发效率高,屏蔽了不同数据库的差异,换数据库时,改个配置就行。
传统的关系型数据库(如MySQL)是行式存储,适合频繁的增删改(OLTP)。而列式数据库(如ClickHouse)则是按列存储。
好处:
COUNT(*) ≈
COUNT(1) >
COUNT(字段)。尽量用
COUNT(*) 吧,除非你要统计某字段的非空值。LIMIT 1 可以让MySQL在找到一条记录后立即停止扫描,避免全表扫描。ORDER BY 的字段有索引,MySQL会直接利用索引排序,避免“文件排序”(FileSort),效率极高。这个顺序很重要,理解了它,你就能预判SQL的执行结果。
FROM 子句:确定数据来源(包括JOIN)。WHERE 子句:对原始数据进行筛选。GROUP BY 子句:对筛选后的数据进行分组。SUM,
COUNT)。HAVING 子句:对分组后的结果进行筛选。SELECT 后面的列名、别名等。SELECT 子句:选择要输出的列。ORDER BY 子句:对结果进行排序。LIMIT 子句:限制返回的行数。EXISTS 效率高;反之用
IN。varchar(n) 是
n 个
字节,存英文占1字节,中文占2字节;
nvarchar(n) 是
n 个
字符,英文和中文都占2字节(Unicode编码)。BEGIN 用于开启事务。如果你连续使用
BEGIN,数据库会隐式
COMMIT 第一个事务,然后进入第二个事务。好了,朋友们!这趟SQL语法基础之旅到这里就告一段落了。希望这些内容能帮你把那些零散的知识点串起来,形成一个更清晰的知识网络。
学习SQL最关键的还是要练习
SQL全套学习,资料,练习和视频,推荐自学网站,SQL自学网, http://xuesql.cn 入门学习网站, https://www.w3schools.com/
来自 “ ITPUB博客 ” ,链接:https://blog.itpub.net/70049428/viewspace-3176438/ ,如需转载,请注明出处,否则将追究法律责任。