数据库基础SQL必会语法
刚入职那会儿,我把数据库当成Excel使
第一天到岗,师兄丢给我一台测试服务器,说“数据都在库里,自己挑”。我打开命令行,看着黑底白字的光标发呆,心里想着:不就是查查、删删、改改嘛。结果一条SELECT * FROM users跑下去,三百多兆的数据直接刷屏,终端卡了三分钟。那一刻我才明白,数据库不是文件柜,它是活的生产线。你得先学会怎么“敲门”,而不是一脚把门踹开。
写SQL像点外卖,语法是菜单,需求是口味
后来我开始接业务需求,才算真正踩进坑里。产品经理一句“我要看最近七天下单但没支付的用户,按城市分组”,听起来简单,写起来却容易跑偏。最怕的不是写错,是写得慢。我见过同事把子查询套了三层,最后自己都忘了哪层是过滤、哪层是聚合。其实SQL没那么玄乎,它就像点外卖:你要先说清楚“吃什么”(字段),再说“在哪吃”(表),然后交代“有什么忌口”(条件),最后决定“分几份”(分组与聚合)。顺序乱了,味道就不对。

别让NULL背锅,先搞清它是“不知道”还是“没有”
有次做报表,我发现总数对不上。排查半天,才发现是NULL在捣乱。一条WHERE status != 'failed'居然把状态为NULL的订单全漏了。那一刻我才记住:NULL不是空字符串,也不是0,它更像一个“我不知道”的占位符。比较、计算、分组,NULL都有自己的脾气。后来我养成习惯,重要字段上来先COALESCE兜底,哪怕只是补个0或空字符串,也好过让逻辑凭空消失。你以为是小细节,往往是线上故障的开头。
索引不是银弹,但没它容易走夜路
系统上线前那周,查询越来越慢。DBA让我加索引,我随手在时间字段上建了一个,以为万事大吉。结果写入慢了,磁盘也涨了。后来才懂,索引像路灯,走夜路确实亮堂,可路灯太多,路也不好修。B-Tree、复合索引、覆盖索引,这些词听着吓人,其实核心就一句话:让查询少走路。你得知道业务最常问哪几个问题,再决定把灯打在哪儿。索引建得好,SQL跑得轻;建得乱,维护更头疼。
数据库基础SQL必会语法
在真正开始写业务之前,很多人都以为数据库只是“存东西的地方”。等数据量涨起来,才发现它其实是整个系统的节拍器。SQL作为和数据库对话的语言,不需要背太多关键字,但必须把最基础、最常用的几类语法吃透。它们决定了你是“能跑通”,还是“跑得稳”。
一、查:SELECT不只是把数据拉出来
最基础的查询从SELECT开始,但很多人停在第一层。
SELECT id, name, create_time
FROM users
WHERE status = 'active';
这句话已经包含了字段选择、表来源和行过滤。真正拉开差距的,往往是细节。比如时间范围,很多新手会写WHERE create_time LIKE '2024-05%',看着能用,却会让索引失效。更稳妥的做法是用区间:
WHERE create_time >= '2024-05-01'
AND create_time < '2024-06-01';
边界清楚,数据库才好发力。
当需要模糊匹配时,LIKE要分情况。开头带通配符的写法(如'%error')通常无法命中索引,数据量大时得小心。更复杂的匹配可以交给全文检索,而不是让SQL硬扛。
再看排序和分页。ORDER BY如果不加索引字段,大数据量下会触发文件排序,拖慢整个库。分页也是,LIMIT 10000, 20看着只取20条,实际上数据库要数过一万条再扔走。业务允许时,用“游标分页”——记住上一页最后一条的ID,从那里继续往下拿,会稳得多。
二、条件:WHERE里的逻辑要克制
条件写得太随性,是SQL变慢的常见原因。
WHERE amount > 0 AND status IN ('paid', 'refund')
AND (type = 'online' OR type = 'mobile')
这种写法可读性不错,但要注意括号。SQL的优先级并不像我们直觉那么友好,少一对括号,结果就可能完全不一样。
对NULL的判断,永远用IS NULL或IS NOT NULL,别用等号。函数也别轻易用在条件字段上,比如WHERE DATE(create_time) = '2024-05-01',数据库很难用到索引。更推荐把函数用在常量一侧,或者提前计算好范围。
多个条件组合时,先想清楚“必选项”和“可选项”。必选项尽量放在前面,让数据库尽早缩小范围;可选项用动态拼接,而不是写死一条大而全的SQL。业务复杂时,拆成多条小查询,往往比一条巨型查询更可控。
三、聚合:SUM、COUNT不只是数字游戏
当我们从“看明细”变成“看统计”,聚合就登场了。
SELECT city, COUNT(*) AS order_cnt, SUM(amount) AS total_amount
FROM orders
WHERE pay_time IS NOT NULL
GROUP BY city;
这里的关键是GROUP BY。数据库会按城市把数据切片,然后在每个切片里计算。字段出现在SELECT里却没出现在GROUP BY里,大多数数据库会直接报错,这是好事,强迫你理清逻辑。
COUNT(*)和COUNT(字段)有区别。前者数行,后者忽略NULL。如果你关心“有值的记录数”,明确写字段更保险。SUM遇到NULL也会忽略,但有时你希望NULL当0处理,就需要COALESCE(amount, 0)兜底。
去重统计用COUNT(DISTINCT user_id),但要注意性能。大数据量下,去重很吃资源。如果业务允许,用近似算法或提前汇总表,能省不少力气。
四、连接:JOIN是把表“拼”在一起,不是“叠”在一起
单表查久了,总会遇到跨表需求。
SELECT u.name, o.order_no, o.amount
FROM users u
JOIN orders o ON u.id = o.user_id
WHERE u.city = 'Shanghai';
这里用JOIN把用户和订单拼起来,靠的是关联字段。写连接时,先想清楚“主表”和“从表”。主表决定结果集的骨架,从表负责补充信息。
INNER JOIN只保留两边都有的记录,LEFT JOIN则保留左边全部,哪怕右边是NULL。很多时候业务说的“所有用户”其实是LEFT JOIN,写成了INNER JOIN就会莫名其妙少数据。
连接条件尽量用等值连接,范围连接(如ON a.date >= b.start_date)要小心,容易产生笛卡尔积,数据膨胀极快。连接太多层时,先检查是否真的需要,或者能否把部分逻辑下推到子查询里。
五、子查询和CTE:让复杂逻辑有章可循
当一条SQL变得太长,维护成本就会飙升。子查询和公共表表达式(CTE)是解法。
WITH city_orders AS (
SELECT city, COUNT(*) AS cnt
FROM orders
WHERE create_time >= CURRENT_DATE - INTERVAL '7 days'
GROUP BY city
)
SELECT *
FROM city_orders
WHERE cnt > 100;
CTE像给中间结果起了个名字,让主查询更干净。子查询用在WHERE里时,注意性能。有些数据库会重复执行子查询,必要时可以先物化成临时表。
子查询不是“高级技巧”,而是“组织技巧”。把大问题拆成小问题,每一步只做一件事,SQL就不容易失控。
六、改与删:UPDATE和DELETE的敬畏心
查询写错了最多慢一点,改和删写错了,可能就是事故。
UPDATE orders
SET status = 'cancelled'
WHERE order_id = 12345;
永远先写WHERE,哪怕是手误测试,也要在事务里跑。很多数据库默认开启自动提交,一句DELETE FROM logs不加条件,可能就真没了。
批量更新时,别一次性锁太多行。分批次、限流、加索引条件,是基本操作。DELETE更是如此,大量删数据会撑大事务日志,影响备库同步。业务允许时,软删除(标记状态)比物理删除更安全。
七、索引意识:写SQL时要“看见”它
基础语法熟练后,要学会“顺着索引写SQL”。比如复合索引(city, status, create_time),查询条件里先写city,再写status,范围条件放在最后,索引利用率才高。
避免对索引字段做计算或函数转换,避免隐式类型转换(如字符串和数字混用)。这些细节看着小,但在高并发时就是压垮性能的稻草。
结语
SQL不需要背得多全,但要把最常用的查、条件、聚合、连接、改删这几块练熟。它们像地基,地基稳了,后面学优化、学执行计划、学分布式,才有落脚点。
真正的好SQL,不是炫技,而是清晰、稳定、可维护。写完一条SQL,多问自己一句:如果数据涨十倍,它还能稳吗?如果同事半夜接手,他能看懂吗?把这些习惯带进日常,数据库就不再是黑盒,而是你手里最稳的工具。