开篇直接讲个我自己的体会:做了这么多年的数据查询和报表开发,CASE WHEN这玩意几乎是每天都要碰的东西,但真正把它用明白的人远远比想象中少。很多人对它的理解停留在"SQL里的 if else",写起来倒是挺顺,一碰到统计口径、行列转换、空值处理就开始翻车。这篇就把CASE WHEN从用法到实战,从执行逻辑到性能影响,一次说透。
CASE WHEN是标准 SQL 里的条件表达式,能做的事情非常广:字段值翻译、范围分组、条件聚合、行转列、数据清洗,甚至还能参与UPDATE和ORDER BY。不管你是做数据分析、后端开发,还是偶尔写查询取数的运营,只要你碰 SQL,这个东西就绕不开。文中我会给大量可以直接抄的示例,同时把每一步背后的为什么讲清楚,让你下次遇到类似需求的时候不用再靠百度拼 SQL。
1. 先搞清楚:CASE WHEN 到底是什么,两种写法有什么不同
1.1 简单 CASE 表达式:本质是个等值映射器
CASE WHEN有两种写法,第一种是简单 CASE 表达式,也叫简单 CASE:
CASE 字段 WHEN 值1 THEN 结果1 WHEN 值2 THEN 结果2 ELSE 默认结果 END比如常见的性别翻译:
SELECT user_id, CASE gender WHEN 'M' THEN '男' WHEN 'F' THEN '女' ELSE '未知' END AS gender_name FROM users;这个写法看着简洁,但它有一个很大的局限:它只能做等值比较。也就是说,CASE 后面的那个字段,只能和 WHEN 后面的值做=判断,不能写范围、不能写模糊匹配、不能写多字段组合条件,甚至当字段值为NULL时它也不会匹配任何WHEN,而是直接落到ELSE。
我见过不少新人在这上面踩坑:想用CASE score WHEN 90 THEN '优秀'表达"90分以上",结果发现大于等于 90 和正好等于 90 完全不是一回事——简单 CASE 只会匹配精确相等的行,一旦分数是 91、95,全部落到 ELSE 去了。
所以记住了:简单 CASE 适合"一个字段、多个定值、翻译输出"的场景,比如状态码转中文、枚举值转含义。一旦条件稍微复杂,就该换第二种写法。
1.2 搜索 CASE 表达式:真正完整的判断逻辑
搜索 CASE 才是大家日常说的"SQL 里的 if else":
CASE WHEN 条件1 THEN 结果1 WHEN 条件2 THEN 结果2 ELSE 默认结果 END注意,CASE后面不跟字段,WHEN后面直接跟一个完整的布尔表达式,可以是范围、比较、LIKE、IN,甚至多个条件用AND/OR组合。
举个例子,把订单金额划分为几个档位:
SELECT order_id, order_amount, CASE WHEN order_amount < 100 THEN '小额订单' WHEN order_amount < 1000 THEN '中额订单' WHEN order_amount < 10000 THEN '大额订单' ELSE '超大额订单' END AS order_level FROM orders;这段 SQL 的逻辑很直观:从上往下依次判断每个WHEN条件,遇到第一个为真的条件就返回对应的THEN,后面的条件不再判断。如果所有WHEN都不满足,就返回ELSE的内容;ELSE不写的话默认返回NULL。
这里最重要的一点是求值顺序。SQL 标准规定了 CASE 表达式会按照书写顺序逐个判断条件,遇到第一个为真的就停下。这意味着你在写区间条件时要注意顺序,比如上面这个例子,如果我把< 10000写在< 1000前面,那所有小于 10000 的订单都会被打成"大额订单",后面的条件永远不会执行。这种坑不是语法错误,是逻辑错误,查出问题的时候往往已经影响了一批数据了。
1.3 一个执行逻辑上的冷知识:WHEN 和 THEN 的求值时机
大部分 SQL 开发者不知道的是,CASE 表达式的THEN分支不一定都会被求值。数据库引擎在遇到第一个为真的WHEN后,会直接跳过后续所有分支的求值。
这个特性在某些场景下可以利用。比如你想避免除零错误:
SELECT product_id, CASE WHEN sale_count > 0 THEN revenue / sale_count ELSE 0 END AS avg_price FROM products;当sale_count为 0 时,第一个条件为假,走 ELSE 返回 0,根本不会执行除法,自然就不会报除零错误。但如果把条件反过来写,WHEN revenue / sale_count > 0,那这条 SQL 在sale_count为 0 的行上就会直接报错,因为表达式在判断条件时就执行了除法。
这个细节平时不一定会遇到,但一旦遇到就是让很多人排查半天的疑难杂症。你只要记住:CASE 表达式不是所有分支都会执行,数据库会短路。
2. 四个高频实战场景:从字段翻译到条件聚合
2.1 场景一:字段值翻译与范围分组
最基础的用法就不展开了,上面已经给过例子。这里补充一个更贴近真实业务的场景——把考试分数或者用户积分映射成等级。
假设有一张用户积分表user_points,字段是user_id、points,现在要统计各等级的用户数:
SELECT CASE WHEN points >= 10000 THEN '钻石会员' WHEN points >= 5000 THEN '黄金会员' WHEN points >= 1000 THEN '白银会员' ELSE '普通会员' END AS member_level, COUNT(*) AS user_cnt FROM user_points GROUP BY CASE WHEN points >= 10000 THEN '钻石会员' WHEN points >= 5000 THEN '黄金会员' WHEN points >= 1000 THEN '白银会员' ELSE '普通会员' END ORDER BY user_cnt DESC;这里必须提醒一个新手非常容易犯的错误:GROUP BY后面不能直接写 SELECT 里的别名member_level,在大多数数据库(MySQL 比这个宽松一点)里,GROUP BY的执行顺序早于 SELECT 的别名解析,所以你需要把整段CASE WHEN再写一遍。
很多初学者在这段代码上反复报错,然后扭头去问"为什么 GROUP BY 不认别名",实际上就是没搞懂 SQL 各子句的执行顺序。这里不展开讲执行顺序的全貌,你只需要记住一个结论:SELECT 里的别名通常只能在 ORDER BY 里直接使用,GROUP BY 里要用原始表达式。
2.2 场景二:条件计数与条件求和,一行 SQL 顶好几条
这是CASE WHEN实战价值最高的场景,也是我日常写报表最依赖的功能。
比如我们要统计一张订单表里各种状态的数量,传统写法是:
SELECT COUNT(*) FROM orders WHERE order_status = 'PAID'; SELECT COUNT(*) FROM orders WHERE order_status = 'SHIPPED'; SELECT COUNT(*) FROM orders WHERE order_status = 'COMPLETED';三张报表三行查询,性能上要扫三遍表。用CASE WHEN可以一次搞定:
SELECT COUNT(CASE WHEN order_status = 'PAID' THEN 1 END) AS paid_cnt, COUNT(CASE WHEN order_status = 'SHIPPED' THEN 1 END) AS shipped_cnt, COUNT(CASE WHEN order_status = 'COMPLETED' THEN 1 END) AS completed_cnt FROM orders;这里的运行原理值得说透:COUNT(表达式)只统计"表达式不为 NULL"的行数。当order_status = 'PAID'条件满足时,CASE返回 1,不满足时返回NULL(因为没有 ELSE)。COUNT会跳过 NULL,于是只统计了满足条件的行数。
同理,条件求和只需要把COUNT换成SUM:
SELECT SUM(CASE WHEN order_status = 'PAID' THEN order_amount ELSE 0 END) AS paid_amount, SUM(CASE WHEN order_status = 'SHIPPED' THEN order_amount ELSE 0 END) AS shipped_amount FROM orders;注意COUNT和SUM对 ELSE 的处理习惯不太一样。COUNT写THEN 1就行,不写 ELSE 让它返回 NULL,正好被跳过;SUM则建议写ELSE 0,因为SUM会把 NULL 当 0 来处理,逻辑上更明确。
还有一个我自己常用的技巧:条件聚合里加上DISTINCT,可以做条件去重计数。比如统计"支付过且金额超过 100 元的用户数":
SELECT COUNT(DISTINCT CASE WHEN order_amount > 100 THEN user_id END) AS high_value_user_cnt FROM orders;第一次用这个写法的同事往往会愣一下,因为没想到COUNT、DISTINCT和CASE WHEN还能组合到一起。实际上这种组合在数据统计里极其顺手,遇到类似的去重条件计数需求都可以这样处理。
2.3 场景三:行转列,把明细数据变成一张交叉表
报表需求里经常遇到这样的情况:现在有一张明细表,每个用户每天的订单一条记录,要把它整理成一张以用户为行、以日期为列的宽表。这在数据分析里叫作"行转列"或者"透视"。
用CASE WHEN可以优雅地实现:
SELECT user_id, MAX(CASE WHEN stat_date = '2024-05-01' THEN order_amount END) AS day_20240501, MAX(CASE WHEN stat_date = '2024-05-02' THEN order_amount END) AS day_20240502, MAX(CASE WHEN stat_date = '2024-05-03' THEN order_amount END) AS day_20240503 FROM orders WHERE stat_date BETWEEN '2024-05-01' AND '2024-05-03' GROUP BY user_id;这里的思路稍微绕一点,我拆开讲:先用CASE WHEN把匹配到特定日期的金额挑出来,不匹配的置为 NULL;然后用MAX对同一用户分组内的这一列取值。因为每组里只有一行匹配时是非 NULL,其余全是 NULL,MAX取到的就是那个非 NULL 值。
实际使用中有一个问题:如果同一个用户在同一天有多笔订单,MAX只会取到最大金额,相当于丢数据了。解决方法是先把明细聚合到"用户-日期"粒度,再做行转列:
WITH daily_orders AS ( SELECT user_id, stat_date, SUM(order_amount) AS daily_amount FROM orders GROUP BY user_id, stat_date ) SELECT user_id, MAX(CASE WHEN stat_date = '2024-05-01' THEN daily_amount END) AS day_20240501, MAX(CASE WHEN stat_date = '2024-05-02' THEN daily_amount END) AS day_20240502 FROM daily_orders GROUP BY user_id;这种"先聚合再透视"的模式我几乎每周都要用,涉及销售日报、渠道日报、学员出勤统计等场景。它的通用思路是:任何需要"以某个字段为行、另一个字段为列、第三个字段为值"的报表,都可以用 CASE WHEN + 分组 + 聚合来拼。
2.4 场景四:UPDATE 定向更新与数据清洗
很多人以为CASE WHEN只能用在查询里,其实它在UPDATE中同样非常好用,最典型的场景是批量更新字段值。
比如订单表里负责人字段写的是中文名,现在要统一替换成工号:
UPDATE orders SET owner_id = CASE owner_name WHEN '张伟' THEN 'ZW001' WHEN '李娜' THEN 'LN002' WHEN '王强' THEN 'WQ003' ELSE owner_id END;这个写法的精髓在于ELSE owner_id。如果没有 ELSE,所有不匹配的人会被更新为 NULL,那基本等于数据事故。批量做这种映射更新时,加上 ELSE 兜底是最基本的职业素养。
另一个常见场景是"删除数据前做标记"而不是直接 DELETE。比如清理一批无效订单,直接 DELETE 会影响后面排查问题,不如先把状态字段改为废弃:
UPDATE orders SET order_status = CASE WHEN order_status = 'PENDING' AND create_time < '2024-01-01' THEN 'ABANDONED' WHEN order_status = 'PENDING' AND is_coupon_used = 1 THEN 'NEED_REVIEW' ELSE order_status END;用CASE WHEN在一条 UPDATE 里按不同条件更新成不同的值,比逐条写 UPDATE 或者写一堆初级 SQL 要高效得多。这也体现了 CASE WHEN 的另一个特质:它是表达式,可以出现在任何"表达式可以被使用"的位置,不仅是 SELECT、WHERE,还包括 UPDATE 的 SET 子句。
3. 进阶组合:CASE WHEN 和 GROUP BY、JOIN、窗口函数怎么配合
3.1 动态分组维度:GROUP BY 里的 CASE WHEN
前面已经提过 GROUP BY 后要重复写 CASE 表达式,这里详细展开一个综合分组的需求。
比如我们要看每个月的订单,按"工作日/周末"分组统计销售额,原始订单表里只有create_time一个时间字段:
SELECT DATE_FORMAT(create_time, '%Y-%m') AS month, CASE WHEN DAYOFWEEK(create_time) IN (1, 7) THEN '周末' ELSE '工作日' END AS day_type, SUM(order_amount) AS total_amount FROM orders WHERE create_time >= '2024-01-01' GROUP BY month, CASE WHEN DAYOFWEEK(create_time) IN (1, 7) THEN '周末' ELSE '工作日' END;这个查询的核心思路是:先用 CASE 把非标准的时间值映射成有序的分组值,然后用 GROUP BY 按这个映射值分组统计。这种操作比先查出来所有数据再在应用层分组要高效得多,也省去了大量数据传输。
实际上"动态分组"的价值远不止这一种。比如可以按金额档位分组、按年龄段分组、按地域分组,只要你能用 CASE WHEN 写出分组规则,GROUP BY 就可以按这个规则聚合。数据探查阶段,我经常用这种方式快速确认数据的分布情况。
3.2 JOIN 条件里的 CASE WHEN:能用,但要想清楚
CASE WHEN也可以用于JOIN的关联条件,比如一些业务系统里的"余额表"和"交易流水表",主外键逻辑比较复杂时:
SELECT a.user_id, b.balance FROM user_balance a LEFT JOIN account_snapshot b ON CASE WHEN a.account_type = 'MAIN' THEN a.main_account_id WHEN a.account_type = 'SUB' THEN a.sub_account_id ELSE NULL END = b.account_id;这种写法的灵活性很强,但代价也很明显:连接条件里写 CASE WHEN,往往会导致索引失效。因为在关联之前数据库没法预先确定你要对 b.account_id 做什么操作,必须在每一行上执行函数或者表达式匹配,这就可能引发全表扫描。
我的建议是:能拆成 UNION ALL 就拆成 UNION ALL,能加中间映射表就加中间映射表,尽量别在 JOIN 条件里塞复杂表达式。这不是说你不能用,而是要想清楚"这样写会不会导致查询慢一个量级"。生产环境里为了这个把查询时间从几十毫秒拖到几秒甚至几十秒的情况,我见过不止一次。
3.3 窗口函数里的条件排序与条件移动计算
窗口函数(分析函数)配CASE WHEN也是个黄金组合,常见的需求是"按指定规则取 Top N"。
比如点外卖场景里,要查每个骑手评分最高(去掉差评)的订单:
SELECT rider_id, order_id, score, ROW_NUMBER() OVER ( PARTITION BY rider_id ORDER BY CASE WHEN is_refund = 1 THEN 0 ELSE score END DESC ) AS rn FROM delivery_orders;这段 SQL 的意义在于:用 CASE 把权重高的字段强行排在前面,然后用ROW_NUMBER()取每个分组里的第一名。这类需求如果你不用窗口函数,就得写自连接或者多条子查询,代码量和可读性都会差很多。
还有一类常见需求是算"移动平均",比如要排除极端值再计算最近 N 天的平均单量,同样可以在窗口函数里用 CASE:
SELECT stat_date, AVG(order_cnt) OVER ( ORDER BY stat_date ROWS BETWEEN 6 PRECEDING AND CURRENT ROW ) AS avg_7d FROM daily_stats;如果要在窗口函数内排除异常值,可以结合 CASE 做条件聚合:AVG(CASE WHEN is_abnormal = 0 THEN order_cnt END) OVER (...),这比"先清洗数据再开窗"要少走一个中间步骤。实际业务里我只推荐在简单场景下这么干,逻辑一复杂,维护成本会快速上升,到时候你宁愿多建一张清洗后的中间表。
4. 容易翻车的细节:NULL、性能、代码可读性
4.1 永远的"NULL"问题:不写 ELSE 的后果
CASE WHEN 里不写 ELSE,所有不满足条件的行都会返回 NULL。这在很多需求里就是错的结果。
举个真实的例子。运营要一张"高价值用户明细表",让你判断每个用户是不是高价值:
SELECT user_id, CASE WHEN total_amount > 10000 THEN 'Y' END AS is_high_value FROM user_stats;执行完了运营说怎么用户表里一半以上的人没有标记,一问才知道:不满足条件的人全部返回 NULL,不是 "N"。这就是典型的 NULL 使用错误。
处理这种问题有两个思路:
- 在 CASE 表达式里显式写
ELSE 'N'; - 用
COALESCE包一层,比如COALESCE(CASE WHEN ... THEN 'Y' END, 'N') AS is_high_value。
还有一个更加隐蔽的 NULL 陷阱:CASE 判断条件本身涉及 NULL。比如判断一个订单是否完成:
CASE WHEN ship_time = NULL THEN '未发货' ELSE '已发货' END这段 SQL 永远不会返回"未发货",因为ship_time = NULL的结果是 NULL,不是 TRUE,NULL 判断必须用IS NULL。这个知识点在 CASE WHEN 里的错误率极高,尤其是从 Java 或者其他编程语言转过来的人,特别容易把 SQL 的"三段逻辑"当成别的语言的习惯来用。SQL 里,NULL 和 NULL 是不相等的,任何常规比较运算符遇到 NULL,结果都是"未知",只有IS NULL和IS NOT NULL能明确地判断。
4.2 性能视角:CASE 写在 WHERE、JOIN 和 SELECT 里的差别
CASE WHEN 在不同的位置,性能影响完全不一样。很多人鼓吹"SQL 很智能,只要写完就行",但事实上数据库优化器不是万能的。
第一种情况:CASE 写在 WHERE 里,导致索引失效。
SELECT * FROM orders WHERE CASE WHEN pay_channel = 'WECHAT' THEN order_status ELSE 'DONE' END = 'PAID';这么写数据库没法直接用 order_status 上的索引,因为它要先对每一行计算 CASE 的结果再判断。正确的写法是把它转换成等价的普通过滤条件:
SELECT * FROM orders WHERE (pay_channel = 'WECHAT' AND order_status = 'PAID') OR (pay_channel <> 'WECHAT' AND 'DONE' = 'PAID');看起来复杂一点点,但至少维护性和索引可用性都保住了。
第二种情况:CASE 写在 JOIN 条件里,前面已经强调过了,尽量别用,尤其是在大表上,很容易触发全表扫描加嵌套循环。
第三种情况:CASE 写在 SELECT 里。只要结果集不是特别大,一般没什么大影响。但如果一个查询返回百万行,SELECT 里的 CASE 表达式仍然会被逐行计算。这里我能给的建议是:如果 CASE 分支里调用了重量级函数(正则、字符串解析、子查询),先看看能不能把结果集缩小到必要范围,再做 CASE 计算。数据过滤的优先级永远高于字段加工。
4.3 可读性:别写十层嵌套给自己挖坑
CASE WHEN 可以嵌套,这是语法允许的:
SELECT CASE WHEN channel = 'APP' THEN CASE WHEN is_new_user = 1 THEN 'APP新客' ELSE 'APP老客' END ELSE '非APP' END AS user_type FROM orders;嵌套两层还能接受,三层以上基本就是在给自己挖坑。我接手过一个老脚本,里面一个 CASE WHEN 嵌套了五层,加上括号和缩进混乱,我花了将近一个小时才理清逻辑。后来我直接把它拆成了两个子查询,用 WITH 一层一层做,每个中间结果都很好解释,后面修改一次只动一层,维护成本瞬间降下来。
这里给出一个我自己的经验判断标准:如果一段 CASE 表达式需要写注释才能解释清楚,说明它已经复杂到应该拆分了。可以用 WITH 抽中间表,也可以用多个表达式分步计算,总之一条铁律——可读性优先,能写别人看得懂的 SQL,就不要展示你写复杂表达式的能力。
5. 一个完整综合演练:从需求到 SQL 的思考过程
说再多也不如做一个完整的实战案例。假设我们现在收到一个报表需求,需要做"2024年第一季度销售复盘",重点是按渠道和支付状态分析订单情况。源表sales_order字段为:
order_id:订单号channel:渠道,值为APP、WEB、OFFLINEorder_status:状态,值为PAID(已支付)、PENDING(待支付)、CANCELLED(已取消)user_type:用户类型,值为NEW(新用户)、OLD(老用户)order_amount:订单金额
需求是之前没有 CASE WHEN 的话,可能要写五六条查询再加好几个 UNION,现在我全部用 CASE WHEN 在一个查询里解决。
第一部分:渠道维度的订单数量、有效订单金额、取消订单金额:
SELECT channel, COUNT(*) AS total_order_cnt, SUM(CASE WHEN order_status = 'PAID' THEN order_amount ELSE 0 END) AS valid_amount, SUM(CASE WHEN order_status = 'CANCELLED' THEN order_amount ELSE 0 END) AS cancelled_amount FROM sales_order WHERE create_time >= '2024-01-01' AND create_time < '2024-04-01' GROUP BY channel;这已经比分别写三条 COUNT 和三条 SUM 高效很多了。但运营还要知道支付转化率,所以要算出支付率。支付率 = 支付订单数 / 总订单数:
SELECT channel, COUNT(*) AS total_order_cnt, SUM(CASE WHEN order_status = 'PAID' THEN 1 ELSE 0 END) AS paid_cnt, ROUND( SUM(CASE WHEN order_status = 'PAID' THEN 1 ELSE 0 END) / COUNT(*) * 100, 2 ) AS paid_rate FROM sales_order WHERE create_time >= '2024-01-01' AND create_time < '2024-04-01' GROUP BY channel;这里要注意,paid_cnt用COUNT(CASE WHEN ... THEN 1 END)也可以,效果等价;我用SUM(CASE WHEN ... THEN 1 ELSE 0 END)是为了在后面直接参与比率计算,逻辑上更直观。两个写法没有对错,关键是自己统一风格,团队里保持一致。
除了渠道看板,换个维度再看看:每个渠道的新老用户贡献金额。这里就需要先有一个渠道维度和用户类型维度的矩阵,用 GROUP BY 加上两次 CASE 可以实现:
SELECT channel, SUM(CASE WHEN user_type = 'NEW' THEN order_amount ELSE 0 END) AS new_user_amount, SUM(CASE WHEN user_type = 'OLD' THEN order_amount ELSE 0 END) AS old_user_amount FROM sales_order WHERE create_time >= '2024-01-01' AND create_time < '2024-04-01' GROUP BY channel;如果运营想看的是一个完整的矩阵报表——行是渠道,列是新老用户和支付状态——那就可以用行转列的方式拼,前置条件是把明细聚合成每组的金额和单量。这类需求在我平时做业务复盘的时候非常常见,核心思路都是一样的,只是情况多了几个需要排列组合的维度。
我在实际项目中还经常遇到一个需求变体:按订单金额区间统计转化率。这只要把 GROUP BY 的维度换成 CASE WHEN 金额映射出来的等级就行了。整套思路展开其实就是一个核心思想:CASE WHEN 的价值不是让你写出绕来绕去的炫技 SQL,而是让你灵活地把业务判断直接嵌到数据查询的各个阶段里。从取数、汇总、透视到更新清洗,它都是首选表达式。
最后说点实际的:几条我在项目里反复验证的原则
CASE WHEN 用多了以后,我自己总结了几条铁律,现在基本每次写 SQL 都会过一遍。
第一,能写 ELSE 就写 ELSE。哪怕逻辑上确信不会命中 ELSE,也写上。这不只是防 NULL 的问题,它是给别人(包括三个月后的自己)传达一种"我想清楚了所有分支"的信号。查过的问题里面,有一大类就是 CASE 没写 ELSE 导致数据少了一块,排查起来非常痛苦。
第二,写 CASE 之前先想想有没有更简单的写法。有些需求本质是等值映射,可以直接用 JOIN 一张映射表;有些需求本质是区间判断,也可以用专门的函数(比如分段函数)。CASE WHEN 是通用方案,但不是所有场景的最佳方案。我之前遇到过一个把产品分类用 CASE 字符串匹配硬写了几十个分支的,后面分类口径一调整,整个人崩溃。换成映射表之后,一行配置解决。
第三,写完必测边界。一支 SQL 里 CASE 的分支如果超过三个,我会构造边界数据执行一遍:完全没有匹配的行会返回什么?多个条件同时匹配时第一个命中的是什么?NULL 的数据进去了会落到哪个分支?这些测试看起来费时间,但能挡住绝大部分线上数据事故。
第四,嵌套超过两层,立刻重构。你要相信我,凡是让你写出三层 CASE 嵌套的需求,都可以用拆子查询的方式写出更容易维护的版本。代码是给业务和团队看的,不是给自己比赛用的。能用简单的数据一字一句讲清楚,就不要兜兜转转嵌套着说。
作为一个和 SQL 打了十多年交道的人,我可以说CASE WHEN是 SQL 里最有价值、又最容易被低估的表达式之一。它不复杂,但每一个细节,从写法选择到执行逻辑,从 NULL 处理到性能影响,都有值得深挖的地方。希望这篇能让你把它的用法真正吃透,在项目里写得顺手、查得放心。