news 2026/10/6 13:34:28

MySQL多表查询实战:JOIN、子查询与性能优化全解析

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
MySQL多表查询实战:JOIN、子查询与性能优化全解析

做后端开发的兄弟基本都绕不开MySQL,业务一复杂,单表查询根本撑不住场面。订单要关联用户,商品要关联分类,报表要从三四张表里捞数据,这时候多表查询就是基本功中的基本功。网上关于多表查询的教程一搜一大把,但大多停留在“三种join各抄一遍”的程度,真到了生产环境,索引怎么建、驱动表怎么选、为什么明明加了条件还是慢,照样一堆人栽跟头。这篇东西我按自己的实战经验来写,把多表查询的内在逻辑和完整案例都过一遍,既给刚入行的同学看,也写给写了一段时间SQL但总觉得心里没底的人。

1. 先想明白:为什么要多表查询,单表不香吗

很多人刚开始学SQL的时候都有个疑问:把数据全放一张表里不是更方便吗?非要拆成好几张表,查询的时候还得连来连去,这不是自己给自己找事吗?这个问题的答案,其实就是多表查询存在的根本原因。

1.1 范式设计把数据拆开,查询就得把它们拼回去

关系型数据库的核心设计思想之一就是“数据建模”,而建模过程中最重要的约束就是范式。简单理解,范式就是在教你别把数据重复存。比如第三范式要求“非主键字段之间不能存在依赖关系”,翻译成人话就是:一张表只描述一件事。

拿电商系统举例。一张订单表应该只记录订单本身的信息:订单号、下单时间、订单金额、下单用户ID。至于这个用户叫什么名字、手机号是多少,那是用户表的事,不该出现在订单表里。为什么?因为同一个用户的姓名和手机号可能会在几百张订单里重复出现,一旦用户改了手机号,你就得去更新几百条甚至几千条数据。万一漏了一条,数据就矛盾了,同一笔订单记录的手机号和用户表里的手机号对不上,这种脏数据在业务上是致命的。

所以设计阶段大家都会把数据拆开,用户表、订单表、商品表、分类表各管一摊。但业务需求是复杂的,前台要展示一个订单列表,列表里得显示用户名、商品名、订单金额,这些字段分布在三张表里,怎么办?只能靠多表查询把它们重新拼回去。所以说白了,多表查询就是数据库世界里的“拼图游戏”,范式设计负责把图拆成碎片,查询负责把碎片拼成完整的业务视图。

1.2 连接的本质:笛卡尔积、连接条件、过滤条件

理解了“为什么拆”,接下来得弄明白“怎么拼”。多表查询底层干的事情叫笛卡尔积,这个名词听起来高大上,其实特别好理解:两张表的数据做全排列组合。拿表A(3行)去连接表B(4行),如果没有任何限制条件,结果就是3乘4等于12行。每一行A都会去配一遍所有的B。

但现实中你根本不需要这种毫无意义的大杂烩。订单表的每一行只应该和它对应的用户行拼在一起,这就需要一个“连接条件”,告诉MySQL:订单表的user_id等于用户表的id时才算是有效匹配。没有连接条件的多表查询,要么是刚学SQL还没搞明白的新手写的,要么就是真的有跨表全组合的特殊需求。

这里有一个非常关键但新手容易混淆的点:连接条件(ON子句)和过滤条件(WHERE子句)是两码事。连接条件负责定义两张表怎么拼,过滤条件负责在拼完之后筛掉不想要的行。虽然有些场景下把连接条件写到WHERE里也能得到一样的结果(比如内连接),但放到外连接里就会产生完全不同的语义。这个坑后面讲LEFT JOIN的时候还会详细说,这里先留个印象。

2. 多表查询的三种姿势:JOIN、子查询、集合运算

MySQL里做多表查询,正经路子就三条:JOIN连接、子查询、集合运算(UNION系列)。很多人一提到多表查询就想JOIN,其实子查询在某些场景下可读性更好,UNION则专门解决“多张结构相同的表纵向拼数据”的问题。下面挨个拆开讲。

2.1 JOIN家族:INNER、LEFT、RIGHT,别再傻傻分不清

JOIN家族是使用频率最高的,先给个直白的定义:

  • INNER JOIN(内连接):只保留两张表都能匹配上的行,匹配不上的两边都不要。
  • LEFT JOIN(左连接):左表(写在LEFT JOIN左边的表)的所有行都保留,右表只有匹配得上的才拼进来,拼不上的用NULL填充。
  • RIGHT JOIN(右连接):跟LEFT JOIN反过来,右表全保留,左表只留匹配上的。
  • FULL OUTER JOIN(全连接):两边都全保留,MySQL原生不支持,需要用UNION把LEFT JOIN和RIGHT JOIN的结果合起来模拟。

举个生活化的例子。你现在有两张表:学生表和选课表。学生表有张三、李四、王五,选课表记的是张三选了数学、李四选了英语。内连接查出来就是张三和李四这俩有选课记录的人,王五直接消失;左连接用学生表做左表,查出来是张三、李四、王五三个人,王五没选课那课的字段就是NULL。

这里要特别强调一个经典大坑:ON子句里的附加条件和WHERE子句里的条件,在LEFT JOIN里结果是完全不一样的。比如你想查所有用户以及他们金额大于100的订单,两种写法:

-- 写法A:金额条件放在ON里 SELECT u.name, o.order_id, o.amount FROM users u LEFT JOIN orders o ON o.user_id = u.id AND o.amount > 100; -- 写法B:金额条件放在WHERE里 SELECT u.name, o.order_id, o.amount FROM users u LEFT JOIN orders o ON o.user_id = u.id WHERE o.amount > 100;

写法A会返回所有用户,没下过单或者订单金额不足100的用户也会出现,订单字段是NULL。写法B就变成了:先算出所有用户的订单,然后再筛掉金额不大于100的,结果里没订单的用户全被过滤了,因为它们的订单字段是NULL,NULL大于100这个比较是假值。这个现象我第一次踩坑的时候懵了很久,后来总结出一个好记的口诀:对LEFT JOIN而言,ON里的条件是“附带条件”,不决定左表的去留;WHERE里的条件是“最终筛选”,决定一切生死。

2.2 子查询:嵌套在括号里的另一个世界

子查询说白了就是“查询套查询”,把一个SELECT的结果当作另一个SELECT的数据来源。它的价值在于可以把复杂的多步查询拆成逻辑清晰的多个层次,有时候比连环JOIN好读得多。

子查询按返回结果可以粗暴分成三类:

标量子查询:返回单行单列,比如“找工资高于平均工资的员工”,平均工资就是个标量:

SELECT emp_name, salary FROM employees WHERE salary > (SELECT AVG(salary) FROM employees);

这里子查询只执行一次,效率很高,但要注意如果子查询返回了多行,整个语句直接报错。

表子查询:返回多行多列,放在FROM后面当作临时表用。典型场景是“先在一个小范围内算好数据,再拿出去跟大表 JOIN”:

SELECT d.dept_name, t.total_salary FROM departments d JOIN ( SELECT dept_id, SUM(salary) AS total_salary FROM employees GROUP BY dept_id ) t ON t.dept_id = d.id;

这种写法的好处是逻辑分层很清楚,先确定要算“各部门工资总额”,再确定“跟部门表拼接”,排错的时候一层一层看就行。

IN / EXISTS 子查询:专门做存在性判断。比如“找出选过课的学生”:

-- 用 IN SELECT * FROM students WHERE id IN (SELECT student_id FROM course_selection); -- 用 EXISTS SELECT * FROM students s WHERE EXISTS (SELECT 1 FROM course_selection c WHERE c.student_id = s.id);

IN和EXISTS的选择曾经被当作面试经典题。以前MySQL的优化器对IN支持不太好,数据量大的时候IN很慢,大家总结出“大表用EXISTS,小表用IN”的规律。但现在MySQL 5.6以后优化器做了大量改进,IN的性能已经大幅提升。我个人的习惯是:优先用IN,可读性好;只有当子表数据量极其庞大、且子查询的结果集无法被索引覆盖时,才考虑EXISTS。不过你可以在EXPLAIN里看一眼执行计划,数据不会骗人。

2.3 UNION与UNION ALL:纵向拼接的正确姿势

JOIN和子查询都是横向拼列,两张表拼成一张更宽的表。但有时候你需要的恰恰是纵向拼行:一张表存了今年上半年的订单,一张表存了下半年的订单,你想把两个表的数据合并成一个结果集展示,这就得靠UNION了。

UNION会自动去重,UNION ALL不会。去重意味着需要对结果做排序比较,数据量大时非常消耗性能。举个我踩过的坑:某次导数据统计,源库和目标库有两张结构一样的订单表,中间有一批重复数据,我想当然地用了UNION去重。结果两张表加起来30万行,UNION跑了40多秒,换成UNION ALL瞬间变成2秒。后来一想,业务上两张表的ID本来就用不同的生成策略,压根不可能重复,白白花了几十秒去重。所以我的原则是:能确定不重复或者允许重复就不要用UNION,数据量大以后,UNION ALL加应用层逻辑去重往往比数据库硬去重划算得多。

另外还有个注意点:UNION要求每个SELECT出来的列数量必须一致,且对应列的数据类型要兼容。这个没注意的话,MySQL会直接报错,不用猜。

3. 实战案例拆解:从需求到SQL的完整推演

理论说得再多,不如让案例落地。下面几个场景都是我在项目里实际写过的,从需求出发一步步推演到最终SQL,你照着敲一遍基本就能掌握多表查询的核心套路。

3.1 电商订单报表:用户、订单、订单明细三表联查

需求是这样的:运营要一张报表,展示下单用户的名字、手机号、每个订单的订单号、订单总金额,以及每个订单里的商品名和购买数量。涉及三张表:

  • users(id、name、phone)
  • orders(id、user_id、order_no、total_amount)
  • order_items(id、order_id、product_name、quantity、price)

业务关系是:一个用户可以有多个订单,一个订单可以有多条商品明细。

先想一下连接顺序。最自然的思考方式是“从主表出发逐步扩张”。以orders为核心,向左连接users拿到用户名和手机号,向右连接order_items拿到商品明细:

SELECT u.name AS 用户姓名, u.phone AS 手机号, o.order_no AS 订单号, o.total_amount AS 订单金额, oi.product_name AS 商品名称, oi.quantity AS 购买数量 FROM orders o JOIN users u ON u.id = o.user_id JOIN order_items oi ON oi.order_id = o.id;

用INNER JOIN是合理的,报表场景只需要有完整链路的数据,缺了用户或者缺了明细的订单运营也看不上,直接过滤掉。

但这里有个细节,为什么我先JOIN users再JOIN order_items?顺序上有没有讲究?MySQL优化器在绝大多数情况下会自己决定最优的连接顺序,并不一定按你SQL里写的顺序执行,所以其实不用太纠结。真正该操心的是让每个连接字段都有索引。这个表里orders.user_id、order_items.order_id都应该建索引,否则数据量一上来,JOIN会变成灾难。

3.2 员工-部门-领导:自连接的典型场景

员工表里每一行都有一个manager_id指向自己上级的员工ID,这种“表中有关联自己”的情况就叫自连接。比如要查“每个员工及其领导的姓名”,最直观的做法是对同一张表做两次查询,用别名区分:

SELECT e.emp_name AS 员工姓名, m.emp_name AS 领导姓名 FROM employees e LEFT JOIN employees m ON m.id = e.manager_id;

这里用的是LEFT JOIN,因为老板没有上级,用INNER JOIN的话老板这行就没了。自连接的本质就是表的两份拷贝做连接,理解这个之后就没什么玄乎的了。你在SQL里看到的employees e和employees m,其实是“员工视角的表”和“领导视角的表”两份数据,MySQL底层会读两次员工表,开销翻倍,所以自连接一定要保证连接字段(id、manager_id)都有索引,不然表大一点就会慢得离谱。

再扩展一个场景:查每个部门里薪水最高的员工。这个需求用窗口函数更简单,但早期的MySQL版本(5.7及以下)不支持,只能用自连接加聚合做:

SELECT d.dept_name, e.emp_name, e.salary FROM employees e JOIN departments d ON d.id = e.dept_id WHERE e.salary = ( SELECT MAX(salary) FROM employees e2 WHERE e2.dept_id = e.dept_id );

这个写法的核心就是用关联子查询找到“本部门最高薪水”,再在外层做匹配。它的执行逻辑是:外层每一行员工,都会去子查询里算一遍本部门的最高工资,然后对比当前行的工资是否相等。所以这SQL在部门数量多、员工表大的时候会非常吃紧,优化方向是给(dept_id, salary)建联合索引,让子查询能快速命中。

3.3 聚合统计:JOIN + GROUP BY的组合拳

统计报表是业务方最爱提的需求,玩法一般就一个套路:连表拿到明细,再用GROUP BY汇总。比如按分类统计商品销量和销售额:

SELECT c.category_name AS 分类名称, COUNT(DISTINCT p.id) AS 商品数, SUM(oi.quantity) AS 总销量, SUM(oi.quantity * oi.price) AS 总销售额 FROM categories c LEFT JOIN products p ON p.category_id = c.id LEFT JOIN order_items oi ON oi.product_id = p.id GROUP BY c.id, c.category_name;

这里用LEFT JOIN是故意为之。如果某个分类下没商品,或者商品从没有卖出过,这个分类依然要出现在报表里,而且计数是0。如果用INNER JOIN,这种“空分类”会被直接干掉,运营看到的就是缺失的行,那肯定不行。

这个SQL里有两个坑,我在这上面栽过不止一次:

第一,GROUP BY后面到底该跟哪些列。只写GROUP BY c.id,SELECT里又带了c.category_name,这是MySQL特有的“功能依赖”特性,严格模式下可以这么写,但为了跨数据库兼容性,建议GROUP BY里把SELECT出来非聚合的非聚合列都写上,也就是c.id, c.category_name。

第二,COUNT和SUM遇到NULL的坑。LEFT JOIN以后,分类没有商品时,p.id是NULL,oi.quantity也是NULL。COUNT(DISTINCT p.id)会忽略NULL,所以商品数显示0,没问题。但SUM(oi.quantity)碰到NULL也不会报错,返回NULL,而NULL在报表里显示出来就是一个空值,很多后端代码一拿这个值直接转数字就炸了。稳妥做法是用IFNULL包一层:IFNULL(SUM(oi.quantity), 0) AS 总销量。这种细节不写进去,接口联调的时候就是事故现场。

4. 性能优化:多表查询快不快的命门

不少开发写多表查询,功能上没问题,SQL也不复杂,但一上线数据量几十万、几百万后直接慢成老牛拉破车。多表查询的性能问题,说穿了就三个关键点:连接字段有没有索引、驱动表选得对不对、有没有避免不必要的全表扫描。下面一个一个拆。

4.1 用EXPLAIN看穿MySQL的执行计划

MySQL提供了EXPLAIN命令来展示一条SQL的执行计划,这是性能分析的第一入口。用法极其简单,直接在SQL前面加EXPLAIN关键字就能看到一张结果表。这张表里的关键字段需要重点关注:

  • type:连接类型,从好到坏依次是system、const、eq_ref、ref、range、index、all。看到all就代表全表扫描,多表查询最怕这个。
  • key:实际用到的索引名。有可能是NULL,说明没走索引。
  • rows:预估扫描的行数,这个数字越小越好。多表连接时,这个数字的乘积大概就是最终的扫描量。
  • Extra:如果出现Using temporary和Using filesort,就要警惕了,说明SQL内部建了临时表或者做了文件排序,数据量大时是性能黑洞。

举个实际例子。某次线上订单分页查询的SQL,某天突然从几十毫秒变成两秒多。我EXPLAIN一看,orders表的连接字段user_id那行的type是all,rows显示30多万。马上查了用户表的索引,发现user_id字段上的索引还在,但优化器居然选择了先扫订单表再做连接。核心原因是统计信息过期,MySQL以为用索引需要扫描更多的行,干脆选择全表扫描。用ANALYZE TABLE orders强制更新统计信息后,type变成ref,查询时间降到80毫秒。所以遇到SQL突然变慢,先别急着加索引,EXPLAIN看一眼执行计划,很可能只是统计数据太老了。

4.2 驱动表(小表驱动大表)是怎么个道理

多表连接时,MySQL会选一张表作为“驱动表”,先读这张表的数据,然后用它的每一行去另一张表里找匹配的数据。驱动表扫描多少行,决定了连接的总次数。所以理论上“小表驱动大表”能让扫描次数更少,这也是早期开发和DBA们反复强调的优化思路。

但现代MySQL优化器已经能做基于成本的智能选择,不再需要你手动去暗示“哪张表做驱动”。真正能起到决定性作用的,是让被驱动表的连接字段有索引。举个例子,A表3万行,B表300万行,连接条件是A.id = B.a_id。如果B.a_id有索引,MySQL每拿A的一行去B里查,通过索引扫描的代价很低;反过来如果没索引,那就变成A的每一行都去B表做一次全表扫描,3万乘300万,这数据量是天文数字。

实际操作中我们很少直接干预驱动表,但可以通过控制过滤条件让优化器做出更优选择。比如在A表上加一个状态字段过滤,把A表扫描的行数从3万降到几千,优化器自然会选择这个更小的结果集做驱动。与其纠结驱动表,不如先把每张表的过滤条件做足,把每张表的连接字段索引建好。

4.3 索引设计的常见误区

多表查询先给连接字段建索引,这基本是常识,但实际项目里还有几个容易忽视的细节。

误区一,从表连接字段没索引,主表反而建了。连接的方向是驱动表一行一行去被驱动表匹配,所以真正需要索引的是被驱动表的连接字段。拿JOIN users u ON u.id = o.user_id来说,orders表往往是驱动表,users表是被驱动表,重点应该保证users.id有主键索引(这个天然有)。如果换成LEFT JOIN orders o ON o.user_id = u.id这种,左表是users,被驱动表是orders,那么orders.user_id一定要建索引,否则就是灾难。

误区二,索引建了但查询没走。最常见的原因是在连接字段上用了函数或者隐式类型转换,比如WHERE u.id = '123',如果u.id是整型,MySQL会把字符串转成数字去比较,这种隐式转换有时候会导致索引失效,最好的做法是保持字段类型统一,查询参数类型保持一致。

误区三,盲目建多列索引。多表查询的GROUP BY、ORDER BY字段如果和连接字段一起建联合索引,有时能省掉filesort。但索引也不是越多越好,每个索引都会拖慢写入速度。我的习惯是先用EXPLAIN观察,确认瓶颈之后再针对性地建,绝不为了“可能用到”去建一堆用不上的索引。

5. 常见问题与排查实录

这一节整理几个在实际开发里高频出现的坑和对应的排查方法。很多问题看起来毫无头绪,但其实顺着“连接条件-执行计划-索引”这条线走,几分钟内就能定位。

5.1 结果集数量不对:多了少了的排查思路

多表查询结果莫名多出一堆重复行,这是新手入职第一周最常被骂的Bug。我见过一个很经典的例子:统计每个用户的订单总金额,但用户表跟订单表关系是1对N,写完下面这段就把数据翻了好几倍:

SELECT u.id, u.name, SUM(o.total_amount) FROM users u JOIN orders o ON o.user_id = u.id GROUP BY u.id, u.name;

这个SQL表面上没错,但如果orders表里存在“用户和订单是多条关系”的同时,你又额外JOIN了一张订单明细表,比如:

SELECT u.id, u.name, SUM(o.total_amount) FROM users u JOIN orders o ON o.user_id = u.id JOIN order_items oi ON oi.order_id = o.id GROUP BY u.id, u.name;

那么问题来了:一个订单有多条商品明细,JOIN完后订单金额会被明细条数放大。比如订单金额100元,有3条商品明细,结果是这个订单被算成300元。解决思路是先算明细再合总,或者用子查询先聚合订单金额,再去关联用户。这类问题的核心教训是:多表JOIN产生的行数倍增关系没理清,聚合函数算出来的就是错的数据。碰到这种情况,先把JOIN去掉单独跑一遍每个表的数据量,再带条件看JOIN后的行数变化,立刻就能发现是哪里扩散的。

5.2 慢查询定位三板斧

线上SQL慢的时候,别急着优化SQL语句本身,先把慢查询日志开起来,确认到底哪些SQL是真正吃时间的。MySQL的慢查询日志默认是关闭的,可以在my.cnf里配置:

slow_query_log = 1 slow_query_log_file = /var/log/mysql/slow.log long_query_time = 2

设置long_query_time为2秒,任何执行超过2秒的查询都会落日志。拿到慢SQL后,第一件是EXPLAIN,第二件看扫描行数和索引使用情况,第三件再考虑改写SQL。

我排过最诡异的一个慢查询是这样的:一条三表连接的报表SQL在测试环境完全没问题,上了生产成了10秒。EXPLAIN一看,生产环境的统计信息落后,优化器选了一张大表做驱动,直接扫了几百万行。解决办法就是执行ANALYZE TABLE刷新统计信息,顺便检查是否因为碎片太多导致扫描代价被高估。所以慢SQL优化最忌讳上来就重写SQL,一定要先揪出执行计划的异常。

5.3 多表查询故障速查表(精华版)

现象可能原因排查方法
结果行数暴增多张1对多表直接JOIN产生笛卡尔扩散分别计算每张表的行数,一步步还原JOIN过程
LEFT JOIN后左表有行丢失WHERE里加了右表的过滤条件把右表过滤条件移到ON子句
某个字段显示NULLRIGHT JOIN或LEFT JOIN的补位行为确认业务是否需要该行数据,用函数处理NULL
SQL突然变慢统计信息过期或索引失效跑EXPLAIN,执行ANALYZE TABLE
排序字段导致filesortORDER BY字段没索引考虑联合索引覆盖排序字段
查询结果重复连接条件不够精确检查连接字段是否唯一,考虑用DISTINCT或改写连接逻辑

排查问题一定要养成一个习惯:手动拿真实数据的子集跑一遍SQL,边跑边用EXPLAIN验证自己的每一步猜测。这个习惯救了我很多次,不光是多表查询,任何数据库问题都适用。

写在最后的个人心得

多表查询这块,我踩过的坑比写对的代码多得多。有一阵子我对各种高级写法特别上瘾,凡是能JOIN绝不子查询,能嵌套绝不扁平,结果同事接我代码的时候一脸痛苦。后来想明白了,SQL首先是写给人看的,其次才是给机器跑的。一段逻辑清晰、可读性强的多表查询,比一段看起来炫技但让人琢磨半天的SQL有价值得多。

最后分享一个小技巧:写复杂的多表查询,可以先在脑子里或者草稿纸上画出表的关联关系图,标清哪张表是主表、哪些字段是连接字段、业务上要保留哪些无效行。画清楚之后再去写SQL,大概率一遍过,而且不容易出现行数翻倍或者数据缺失的经典问题。这个方法和ORM设计时的思路完全一致,只是很多人写SQL的时候太着急,省了这一步,后面花在调试上的时间往往是十倍。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/6 13:34:00

SF授权系统源码V3.7全开源无加密:授权码生成校验与安全加固实战

简介:这是一套面向授权站搭建者与程序开发者的SF授权系统源码,版本为V3.7,全开源无加密,适合希望自建授权平台、开展副站长或合作商分站运营的技术人员。源码基于layuiadmin框架开发,集成盗版入库、快捷登录、易支付认…

作者头像 李华
网站建设 2026/10/6 13:33:25

基于HTML5的响应式网站设计与实现:从布局到避坑的完整指南

简介:这是一份基于HTML5的响应式网站设计与实现方向的完整毕业论文正文,面向计算机相关专业的毕业生以及需要了解响应式建站流程的前端开发者。文档以企业官网为应用场景,系统梳理了HTML5、CSS3、JavaScript技术组合,配合Eclipse开…

作者头像 李华
网站建设 2026/10/6 13:33:16

Agent-Reach:量化智能体触达能力的评估框架与实践

1. 为什么需要“Agent-Reach”:从“能做”到“够得着”做智能体(Agent)开发这大半年,我最大的感受是:模型能力早就不是瓶颈了,真正卡脖子的是“触达”。你可能已经有一套基于大模型的Agent框架,…

作者头像 李华
网站建设 2026/10/6 13:33:08

信道与频率到底是什么关系?从电磁波传播到频谱规划全解析

搞无线通信的,不管你是做系统设计、射频前端还是网优,迟早都要面对同一个问题:信道和频率到底是什么关系。我入行头几年一直觉得这就是个“查表”的活儿——频段定好了、信道参数照着填就行。后来开始自己拉链路预算、跑现场测试、在城中村楼…

作者头像 李华
网站建设 2026/10/6 13:33:07

C语言读取文件指定内容:API选型、定位策略与实战解析

开头部分我要用场景切入:日志里有大量数据,但要的是每个"ERROR"后面的第一行内容;配置文件里一堆参数,但程序启动只关心某一个key。我相信干过C语言文件处理的,都遇到过这种问题。文件的打开、读取本身不难&…

作者头像 李华
网站建设 2026/10/6 13:31:36

SQLServer深分页优化实战:从ROW_NUMBER到键集分页

前阵子帮同事做 SQLServer 的慢查询审核,发现系统里一条很常见的分页语句成了头号性能瓶颈。用的是很多团队都在用的 ROW_NUMBER() 写法:从一张 3000 万行的订单表里取第 100 万行附近的 10 条数据,单次查询跑了 9 秒多。业务那边反馈列表页越…

作者头像 李华