数据库常用SQL语句大全,从入门到实战全覆盖 轻松搞定日常各类数据操作

2026-09-21 17:23:41 70阅读
这份《数据库常用SQL语句大全》覆盖从入门到实战全阶段内容,可帮助使用者快速掌握并熟练运用SQL,搞定各类日常数据操作需求,内容既包含基础数据库、表结构操作及数据增删改查入门语法,也涵盖联表查询、聚合统计、索引优化、事务控制等实战常用技巧,适配日常开发、数据分析等多场景需求,能让不同基础的使用者快速上手,高效完成各类数据处理任务,解决常见数据库操作痛点。

作为一名和数据库打了近八年交道的后端开发,我见过不少刚入行的朋友踩过同一个坑:拿着厚厚的SQL语法手册死记硬背,真到写业务代码、排查线上数据问题时,翻半天找不到能用的语句,要么写出全表扫描的慢查询拖垮服务,要么漏了条件差点把整表数据改崩。 其实日常工作里90%的数据库操作,翻来覆去就是那二三十条核心语句,今天我就按照数据查询、数据操作、表结构管理、库级与权限控制、高频进阶用法五个维度,把工作中真正高频使用的SQL语句整理清楚,每个语句都配上实用示例和避坑提醒,不管是学生做课设、刚入行的开发写业务,还是运营、产品偶尔需要查个数,都能直接拿来用。

数据查询(DQL):使用频率最高的核心语句

查询语句占了日常SQL使用的70%以上,也是最容易出性能问题的部分,核心就是SELECT相关的语法,从简单查数到多表关联,覆盖绝大多数取数场景。

基础查询:精准取数不做无用功

最基础的查询逻辑,核心是避免无脑写SELECT *——查不需要的字段不仅浪费网络IO,还可能因为覆盖索引失效拖慢查询速度。

数据库常用SQL语句大全,从入门到实战全覆盖 轻松搞定日常各类数据操作

-- 查询指定字段(推荐写法,只查需要的列)
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 <=30WHERE 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 + 

文章版权声明:除非注明,否则均为亚朵原创文章,转载或复制请以超链接形式并注明出处。