mysql 有很多高级但实用的功能,能让你的查询变得更简洁、更高效。
今天分享 10 个我在工作中经常使用的 sql 技巧,不用死记硬背,掌握了就能立刻提升你的数据库操作水平!
1. cte(with子句)——让复杂查询变清晰
-- 传统子查询,难以阅读
select nickname
from system_users
where dept_id in (
select id from system_dept where `name` = 'it部'
);
-- 使用cte,逻辑清晰
with ny_depts as (
select id from system_dept where `name` = 'it部'
)
select u.nickname
from system_users u
join ny_depts nd on u.dept_id = nd.id;
解释:
with ny_depts as (...):先创建一个临时结果集,叫ny_depts,里面只包含“it部”的部门名称。select u.nickname from system_users u join ny_depts...:再从用户表中找出那些部门id在ny_depts里的员工昵称。
好处:把找部门和找人分成两步,逻辑更清楚,比嵌套子查询好读多了。
2. 窗口函数 —— 不分组也能统计
select
name,
department,
salary,
rank() over (partition by department order by salary desc) as rank_in_dept,
avg(salary) over (partition by department) as avg_salary
from employees;
解释:
partition by department:按部门“分组”,但不合并行,每行仍然保留。rank() over (...):在每个部门内部,按薪水从高到低排名(相同薪水并列)。avg(salary) over (...):计算每个部门的平均工资,并显示在每一行里。
对比 group by:group by 会把多行合并成一行,而窗口函数保留原始行,同时加上统计值。
3. 条件聚合 —— 一行查出多个统计
select
year(created_at) as year,
count(*) as total,
count(case when status = 'completed' then 1 end) as completed,
sum(case when status = 'completed' then amount else 0 end) as revenue
from orders
group by year(created_at);
解释:
year(created_at):提取订单年份。count(*):该年总订单数。count(case when status = 'completed' then 1 end): 如果状态是'completed',就返回1,否则返回null;count()只统计非null值,所以这行就是“完成的订单数”。sum(case when ... then amount else 0 end):只对完成的订单求金额总和。
关键:不用写多个子查询,一条语句搞定全年报表!
4. 自连接 —— 同一张表自己连自己
select e1.name, e2.name from employees e1 join employees e2 on e1.department = e2.department and e1.id < e2.id and abs(e1.salary - e2.salary) <= e1.salary * 0.1;
解释:
employees e1 join employees e2:把员工表当成两个副本(e1 和 e2)来连接。e1.department = e2.department:只找同一个部门的人。e1.id < e2.id:避免重复配对(比如 alice-bob 和 bob-alice 只保留一个)。abs(...):计算两人薪水差是否 ≤ 10%。
用途:找“相似记录”“配对关系”“上下级”等场景非常有用。
5.exists替代in—— 更高效的存在判断
select name from customers c
where exists (
select 1 from orders o
where o.customer_id = c.id and o.amount > 1000
);
解释:
- 对每一位客户
c,检查是否存在一笔订单满足:- 订单的
customer_id等于这个客户的id - 订单金额 > 1000
- 订单的
select 1:这里不需要返回具体字段,只要知道“有没有”就行,所以用1最轻量。- 为什么快?:一旦找到一条匹配订单,就立刻停止搜索,不像
in可能要加载全部订单 id。
注意:如果子查询可能返回 null,in 会失效(因为 x in (..., null) 永远为 unknown),而 exists 不受影响。
6. json 函数 —— 轻松读取 json 字段
select
name,
profile->>'$.address.city' as city,
json_extract(profile, '$.age') as age
from users
where profile->>'$.city' = 'beijing';
解释:
profile是一个 json 类型字段,比如:{"address": {"city": "beijing"}, "age": 30}profile->>'$.address.city':->>是简写,等价于json_unquote(json_extract(...))- 返回字符串
"beijing"(去掉引号)
json_extract(profile, '$.age'):返回30(带类型,可能是数字)where profile->>'$.city' = 'beijing':筛选城市是北京的用户。
适用场景:用户偏好、动态表单、日志等结构不固定的字段。
7. 生成列 —— 数据库自动帮你算
create table products (
id int primary key,
width decimal(10,2),
height decimal(10,2),
area decimal(10,2) as (width * height) stored
);
insert into products (id, width, height) values (1, 5, 10);
解释:
area decimal(...) as (width * height) stored:- 这是一个“存储型生成列”,数据库会自动计算
width * height并存下来。 - 如果不加
stored,就是“虚拟列”(每次查询时计算,不占存储)。
- 这是一个“存储型生成列”,数据库会自动计算
- 插入时只需给
width和height,area自动变成50。
优势:避免应用层重复计算,还能给 area 加索引加速查询!
8. 多表更新 —— 一条语句更新关联数据
update customers c
join (
select customer_id, sum(amount) as total
from orders
group by customer_id
) o on c.id = o.customer_id
set c.total_spent = o.total;
解释:
- 子查询
o:先按客户 id 统计每个人的总消费。 update customers c join o ...:把客户表和统计结果连接起来。set c.total_spent = o.total:直接把统计值写回客户表。
好处:不用在程序里循环“查一个、改一个”,减少网络开销,保证原子性。
9.group_concat—— 多行变一行
select
department,
group_concat(name order by salary desc separator ', ') as members
from employees
group by department;
解释:
group by department:按部门分组。group_concat(name ...):把每个部门的所有员工名字拼成一个字符串。order by salary desc:按薪水从高到低排序后再拼接。separator ', ':用逗号加空格分隔名字。
典型用途:导出名单、展示标签、汇总明细等。
默认最多拼 1024 字符,可通过 set session group_concat_max_len = 1000000; 调大。
10.insert ... on duplicate key update—— 智能插入/更新
insert into page_views (page_url, view_date, view_count)
values ('/home', curdate(), 1)
on duplicate key update
view_count = view_count + 1;
解释:
- 尝试插入一条新记录:页面
/home,今天日期,访问次数为 1。 - 如果因为唯一索引冲突(比如
(page_url, view_date)是唯一键)导致插入失败:- 就执行
on duplicate key update部分 - 把原有的
view_count加 1
- 就执行
- 效果:第一次访问创建记录,之后每次访问自动 +1,完美实现计数器!
前提:表必须有主键或唯一索引,否则不会触发更新。
到此这篇关于一文分享10个常用的mysql高级用法的文章就介绍到这了,更多相关mysql高级用法内容请搜索代码网以前的文章或继续浏览下面的相关文章希望大家以后多多支持代码网!
发表评论