数据库常用SQL语句大全,从入门到实战全覆盖 轻松搞定日常各类数据操作
这份《数据库常用SQL语句大全》覆盖从入门到实战全阶段内容,可帮助使用者快速掌握并熟练运用SQL,搞定各类日常数据操作需求,内容既包含基础数据库、表结构操作及数据增删改查入门语法,也涵盖联表查询、聚合统计、索引优化、事务控制等实战常用技巧,适配日常开发、数据分析等多场景需求,能让不同基础的使用者快速上手,高效完成各类数据处理任务,解决常见数据库操作痛点。
作为一名和数据库打了近八年交道的后端开发,我见过不少刚入行的朋友踩过同一个坑:拿着厚厚的SQL语法手册死记硬背,真到写业务代码、排查线上数据问题时,翻半天找不到能用的语句,要么写出全表扫描的慢查询拖垮服务,要么漏了条件差点把整表数据改崩。 其实日常工作里90%的数据库操作,翻来覆去就是那二三十条核心语句,今天我就按照数据查询、数据操作、表结构管理、库级与权限控制、高频进阶用法五个维度,把工作中真正高频使用的SQL语句整理清楚,每个语句都配上实用示例和避坑提醒,不管是学生做课设、刚入行的开发写业务,还是运营、产品偶尔需要查个数,都能直接拿来用。
数据查询(DQL):使用频率最高的核心语句
查询语句占了日常SQL使用的70%以上,也是最容易出性能问题的部分,核心就是SELECT相关的语法,从简单查数到多表关联,覆盖绝大多数取数场景。
基础查询:精准取数不做无用功
最基础的查询逻辑,核心是避免无脑写SELECT *——查不需要的字段不仅浪费网络IO,还可能因为覆盖索引失效拖慢查询速度。
-- 查询指定字段(推荐写法,只查需要的列) SELECT user_id, user_name, register_time FROM user_info; -- 给字段起别名,适合多表关联时重名字段、或者计算字段的场景 SELECT user_name AS 用户名, register_time AS 注册时间 FROM user_info; -- 查询结果去重,比如查所有下过单的用户ID,避免同一个用户出现多次 SELECT DISTINCT user_id FROM order_info; -- 条件查询:用WHERE过滤符合要求的数据,支持多条件组合 -- 比如查2024年之后注册、且账号状态正常的用户 SELECT user_id, user_name FROM user_info WHERE register_time >= '2024-01-01' AND account_status = 1;
WHERE条件里常用的判断符除了=、>、<、>=、<=、!=,还有几个高频场景的写法:
- 模糊查询:
WHERE user_name LIKE '张%'(匹配姓张的用户,注意前面加比如'%张%'会导致索引失效,大数据量下要慎用) - 范围查询:
WHERE age BETWEEN 18 AND 30等价于age >=18 AND age <=30;WHERE city IN ('北京','上海','广州')匹配在指定集合内的数据 - 空值判断:
WHERE phone IS NULL(注意不能写phone = NULL,NULL值和任何值做等于判断结果都是未知,查不出来结果)排序与分页:解决列表展示的核心需求
做后台列表、前端分页展示几乎都要用到排序和分页语法,这里最容易踩的坑是大数据量下的深度分页问题。
-- 排序:ORDER BY 字段,ASC是升序(默认可以不写),DESC是降序 -- 比如按注册时间倒序,最新注册的用户排在最前面 SELECT user_id, user_name, register_time FROM user_info WHERE account_status = 1 ORDER BY register_time DESC; -- 多字段排序:先按是否付费用户降序,同级别再按注册时间升序 ORDER BY is_paid DESC, register_time ASC; -- 分页:LIMIT 偏移量, 每页条数 -- 比如查第1页,每页10条数据(偏移量从0开始) SELECT * FROM user_info LIMIT 0, 10; -- 查第3页,每页10条:偏移量 = (页码-1)*每页条数 = 2*10=20 SELECT * FROM user_info LIMIT 20, 10; -- MySQL里LIMIT后的偏移量超过1万时性能会急剧下降,上百万数据的表推荐用“主键定位”的方式优化: SELECT * FROM user_info WHERE user_id > 10000 LIMIT 10;
聚合与分组:做数据统计的必备语法
算总数、算平均值、按维度分组统计(比如每天的新增用户、每个城市的订单量)都要靠聚合函数和
GROUP BY实现。-- 常用聚合函数:COUNT统计数量、SUM求和、AVG求平均、MAX最大值、MIN最小值 -- 比如统计user_info表总用户数 SELECT COUNT(user_id) AS total_user FROM user_info; -- 统计2024年订单的总金额、平均客单价、最高单笔金额 SELECT COUNT(order_id) AS total_order, SUM(pay_amount) AS total_money, AVG(pay_amount) AS avg_pay, MAX(pay_amount) AS max_pay FROM order_info WHERE pay_status = 1 AND pay_time >= '2024-01-01'; -- 分组统计:GROUP BY 按维度拆分统计值 -- 比如统计2024年每个月的订单量和总金额 SELECT DATE_FORMAT(pay_time,'%Y-%m') AS pay_month, -- 把支付时间格式化为“年-月”维度 COUNT(order_id) AS month_order, SUM(pay_amount) AS month_money FROM order_info WHERE pay_status = 1 GROUP BY pay_month; -- 分组后过滤:HAVING 注意和WHERE的区别:WHERE在分组前过滤原始数据,HAVING在分组后过滤分组结果 -- 比如统计2024年累计订单金额超过1000元的用户 SELECT user_id, SUM(pay_amount) AS total_pay FROM order_info WHERE pay_status = 1 GROUP BY user_id HAVING total_pay >= 1000; -- 这里不能用WHERE,因为total_pay是分组后计算出来的聚合值
多表关联:解决跨表取数问题
业务数据通常存在不同的表里,比如用户信息在user_info,订单信息在order_info,要查“下单用户名对应的订单金额”就需要关联查询,常用的关联方式有3种:
-- 先准备测试表说明:user_info(用户表:user_id, user_name, city);order_info(订单表:order_id, user_id, pay_amount) -- 1. 内连接INNER JOIN:只返回两张表能匹配上关联条件的数据 -- 场景:查所有已支付订单对应的用户名、订单金额 SELECT o.order_id, u.user_name, o.pay_amount FROM order_info o -- 给表起别名简写,o代表订单表 INNER JOIN user_info u -- u代表用户表 ON o.user_id = u.user_id -- 关联条件:两张表通过user_id匹配 WHERE o.pay_status =1; -- 2. 左连接LEFT JOIN:以左表为基准,返回左表全部数据,右表匹配不上的字段显示为NULL -- 场景:查所有用户的下单情况,包括从来没下过单的用户 SELECT u.user_id, u.user_name, o.order_id, o.pay_amount FROM user_info u LEFT JOIN order_info o ON u.user_id = o.user_id; -- 3. 右连接RIGHT JOIN:以右表为基准,和左连接逻辑相反,实际工作中因为可读性差很少用,基本都可以改写成左连接实现 -- 多表关联注意:关联字段一定要建索引,不要超过3张表关联,否则很容易出现慢查询
子查询与合并查询:处理复杂逻辑
-- 子查询:把一个查询的结果当另一个查询的条件,比如查“所有下过单的用户信息” SELECT * FROM user_info WHERE user_id IN (SELECT DISTINCT user_id FROM order_info WHERE pay_status=1); -- 合并查询:UNION/UNION ALL把多个SELECT的结果合并成一个结果集 -- 注意:UNION会对合并后的结果去重,UNION ALL直接拼接不去重(性能更高,没有去重开销,确定没有重复数据优先用) -- 场景:把北京用户和上海用户的列表合并成一个结果 SELECT user_id, user_name FROM user_info WHERE city='北京' UNION ALL SELECT user_id, user_name FROM user_info WHERE city='上海';
数据操作(DML):增删改数据一定要谨慎
数据操作语句是网上常说“从删库到跑路”的重灾区,核心原则是:执行UPDATE/DELETE之前,一定要先用SELECT把WHERE条件查一遍,确认是目标数据再执行,不要在生产环境不带WHERE条件执行写操作。
插入数据INSERT
-- 插入单条数据:字段和值要一一对应 INSERT INTO user_info(user_id, user_name, phone, register_time, account_status) VALUES(1001, '张三', '13800138000', '2024-05-01 12:00:00', 1); -- 批量插入多条数据(比单条循环插入性能高10倍以上,推荐优先用) INSERT INTO user_info(user_id, user_name, phone, register_time, account_status) VALUES (1002, '李四', '13800138001', '2024-05-02 10:00:00', 1), (1003, '王五', '13800138002', '2024-05-03 09:30:00', 0), (1004, '赵六', '13800138003', '2024-05-04 14:20:00', 1); -- 从其他表查询数据插入目标表:比如把历史表的2023年用户数据导入归档表 INSERT INTO user_info_history(user_id, user_name, register_time) SELECT user_id, user_name, register_time FROM user_info WHERE register_time < '2024-01-01';
更新数据UPDATE
-- 基本更新语法:一定要加WHERE条件!!! -- 场景:把user_id=1001的用户手机号更新为新号码 UPDATE user_info SET phone = '13900139000' WHERE user_id = 1001; -- 不加这句话会把全表所有用户的手机号都改成这个值! -- 多字段同时更新 UPDATE user_info SET account_status = 0, update_time = NOW() WHERE last_login_time < '2023-01-01'; -- 把超过一年没登录的账号设置为冻结状态 -- 关联更新:用另一张表的数据更新当前表 -- 场景:把用户表的总消费金额字段,更新为订单表该用户的累计支付金额 UPDATE user_info u LEFT JOIN ( SELECT user_id, SUM(pay_amount) AS total_pay FROM order_info WHERE pay_status=1 GROUP BY user_id ) o ON u.user_id = o.user_id SET u.total_consume = IFNULL(o.total_pay,0);
删除数据DELETE/TRUNCATE
-- 按条件删除数据:同样必须加WHERE条件!!! DELETE FROM user_info WHERE user_id = 1004; -- 整表数据清空:两种方式区别极大 -- 1. DELETE是逐行删除,可以回滚(如果开了事务),速度慢,会返回删除的行数,自增主键不会重置 DELETE FROM user_info_history; -- 2. TRUNCATE是直接清空表数据,不可回滚,速度极快,自增主键会重置为初始值,不会返回删除行数 TRUNCATE TABLE user_info_history; -- 生产环境禁止用TRUNCATE,删除核心业务数据前一定要先备份!
表结构管理(DDL):建表改表的常用操作
日常开发里建表、加字段、加索引是常事,这部分语句不同数据库语法略有差异,以下以最常用的MySQL为例:
建表CREATE TABLE
-- 建表示例,字段类型、注释、主键、默认值、字符集一次写全,避免后续返工 CREATE TABLE `user_info` ( `user_id` bigint NOT NULL AUTO_INCREMENT COMMENT '用户ID,主键自增', `user_name` varchar(50) NOT NULL DEFAULT '' COMMENT '用户名', `phone` char(11) NOT NULL DEFAULT '' COMMENT '手机号', `city` varchar(20) NOT NULL DEFAULT '' COMMENT '所在城市', `register_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '注册时间', `account_status` tinyint NOT NULL DEFAULT '1' COMMENT '账号状态:1正常0冻结', `total_consume` decimal(10,2) NOT NULL DEFAULT '0.00' COMMENT '累计消费金额', PRIMARY KEY (`user_id`), -- 设置主键 KEY idx_phone(`phone`), -- 给手机号建普通索引 KEY idx_register_time(`register_time`) -- 给注册时间建索引 ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用户信息表'; -- 常用字段类型说明:整数用int/bigint、字符串用varchar、金额用decimal(不要用float会丢精度)、时间用datetime
修改表结构ALTER
-- 给表添加字段:比如给用户表加一个“最后登录时间”字段 ALTER TABLE user_info ADD COLUMN last_login_time datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '最后登录时间' AFTER register_time; -- 修改字段属性:比如把user_name的长度从50改成100 ALTER TABLE user_info MODIFY COLUMN user_name varchar(100) NOT NULL DEFAULT '' COMMENT '用户名'; -- 添加索引:给city字段加索引,提高按城市查询的速度 ALTER TABLE user_info ADD INDEX idx_city(city); -- 删除字段、删除索引(生产环境操作要谨慎,避免锁表影响业务) ALTER TABLE user_info DROP COLUMN test_column; ALTER TABLE user_info DROP INDEX idx_city; -- 删除表 DROP TABLE IF EXISTS user_info_history; -- 表结构和数据全部删除,不可恢复,生产环境慎用
库级与权限控制:运维常用基础语句
这部分语句开发日常用的不多,但做测试环境搭建、服务部署时经常接触:
-- 查询当前数据库有哪些表 SHOW TABLES; -- 查看表结构 DESC user_info; -- 查看建表语句 SHOW CREATE TABLE user_info; -- 创建数据库,指定字符集(推荐用utf8mb4,支持emoji表情) CREATE DATABASE my_blog DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci; -- 创建数据库用户,限制只能从本地登录 CREATE USER 'blog_user'@'localhost' IDENTIFIED BY 'your_password123'; -- 给用户授权:把my_blog库的所有表的全部权限授予blog_user GRANT ALL PRIVILEGES ON my_blog.* TO 'blog_user'@'localhost'; -- 刷新权限让授权生效 FLUSH PRIVILEGES;
进阶实用语句:解决常见高频需求
除了基础的增删改查,工作中还有不少场景需要用到进阶SQL,掌握了可以少写很多代码:
-- 1. CASE WHEN条件判断:在SQL里做逻辑分支,相当于代码里的if-else -- 场景:把账号状态转成中文释义,消费金额分等级统计 SELECT user_name, CASE account_status WHEN 1 THEN '正常' WHEN 0 THEN '冻结' ELSE '未知' END AS status_text, CASE WHEN total_consume >=1000 THEN '高价值用户' WHEN total_consume >=100 THEN '普通用户' ELSE '新用户' END AS user_level FROM user_info; -- 2. 常用日期函数:做时间统计非常方便 SELECT NOW(); -- 获取当前时间,返回2024-05-20 14:30:00 SELECT DATE_FORMAT(NOW(),'%Y-%m-%d'); -- 格式化日期,返回2024-05-20 SELECT DATE_ADD(NOW(), INTERVAL 7 DAY); -- 当前时间加7天,算一周后的时间 SELECT DATEDIFF('2024-05-20','2024-05-01'); -- 计算两个日期相差的天数 -- 3. 常用字符串函数 SELECT CONCAT(user_name,'-',city) FROM user_info; -- 字符串拼接,返回“张三-北京” SELECT LENGTH(phone) FROM user_info; -- 计算字符串长度 SELECT REPLACE(phone,SUBSTRING(phone,4,4),'****') FROM user_info; -- 手机号脱敏:把中间4位替换成* -- 4. 查看SQL执行计划(排查慢查询必备):看SQL有没有走索引、有没有全表扫描 EXPLAIN SELECT * FROM user_info WHERE phone = '13800138000'; -- 5. 事务控制:写多步操作时保证数据一致性,比如转账场景:扣A的钱、加B的钱,要么都成功要么都失败 START TRANSACTION; -- 开启事务 UPDATE account SET balance = balance - 100 WHERE user_id = 1001; UPDATE account SET balance = balance +
文章版权声明:除非注明,否则均为亚朵原创文章,转载或复制请以超链接形式并注明出处。

