news 2026/9/17 5:13:14

MySQL基本查询全解析:SELECT、JOIN与WHERE的实战避坑指南

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
MySQL基本查询全解析:SELECT、JOIN与WHERE的实战避坑指南

数据库这行干久了,你会发现在业务代码里写 SQL 的时间,往往比写 Java、Python 还要多。尤其是“基本查询”这四个字,看着简单,真到面试、上线、排查线上问题的时候,多少人栽在它上面。我见过写了好几年代码的开发,愣是把 LEFT JOIN 当 WHERE 用;也见过把 ORDER BY 条件放到子查询里,结果排序完全失效。所以这次我打算把 MySQL 基本查询从头到尾捋一遍,不讲花架子,直接讲那些你每天都会碰到的语句、原理和坑。

这篇内容适合三类人:刚接触数据库的学生或转行新人;写过增删改查但没系统整理过查询逻辑的初级开发;准备面试想快速再过一遍基础的人。内容会覆盖 SELECT、WHERE、ORDER BY、GROUP BY、JOIN、子查询,以及我在实际操作中遇到的性能陋习和排查方法。你会看到不少可以直接抄走的语句模板,也会看到我踩过的坑。下面我们直接开始。

1. 从零开始的 SELECT:查询的骨架

1.1 先记住 SQL 的执行顺序,别被书写顺序骗了

很多初学者以为 SQL 是从 SELECT 开始执行的,这其实是最大的误解。SQL 的书写顺序和执行顺序并不一致。一条标准查询语句写出来是SELECT → FROM → WHERE → GROUP BY → HAVING → ORDER BY → LIMIT,但在 MySQL 内部的执行顺序却是FROM → WHERE → GROUP BY → HAVING → SELECT → ORDER BY → LIMIT

这个顺序为什么重要?因为它直接决定了你能不能在 WHERE 里用别名。比如:

SELECT age + 1 AS new_age FROM user WHERE new_age > 20;

这条语句会报错,因为 WHERE 在 SELECT 之前执行,此时new_age这个别名还不存在。正确做法是把age + 1 > 20直接写进 WHERE,或者在外面套一层子查询。理解执行顺序还有一个好处:写复杂语句时,你会天然知道过滤条件该放在哪里。比如分组前的过滤用 WHERE,分组后的过滤用 HAVING,原因也是因为 WHERE 在 GROUP BY 之前执行。

1.2 SELECT 与 FROM 的使用细节

SELECT 是查询的投影操作,决定最终返回哪些列。基本规则很简单,但有两个细节经常有人忽略。

第一,能用SELECT *吗?我建议尽量别用。尤其是生产环境的表,动辄几十个字段,SELECT *会把无关字段全部查出来,增加网络传输压力;如果表结构后面加了字段,线上代码拿到的结果集也跟着变,极容易埋雷。第二,多表查询时,所有字段最好都带表名或别名前缀,比如SELECT u.id, u.name FROM user u。这样写的好处是查询语义清晰,MySQL 优化器也能少一步“猜字段”的环节,更重要的是避免两个表存在同名字段时返回结果错乱。

FROM 里还支持子查询作为临时表,MySQL 规定这种派生表必须起别名,比如FROM (SELECT ...) t。这个技巧在拿到中间结果、继续统计时非常常用,后面讲子查询时我会再展开。

1.3 DISTINCT、别名与基础表达式

DISTINCT 的作用是对结果集去重,但很多人没注意到它是“整行去重”,不是“单列去重”。

SELECT DISTINCT department, city FROM employee;

上面这句话的意思是department + city这两个字段的组合不能重复,而不是 department 唯一。如果你只想看有哪些部门,应该用SELECT department FROM employee GROUP BY department或者直接对 department 用 DISTINCT。DISTINCT 还有一个性能小坑:它本质上是排序或哈希去重,数据量大时会额外消耗内存和 CPU,能不用就不要滥用。

别名和表达式也是日常查询的高频操作。SELECT amount * price AS total能直接算金额,CASE WHEN能做条件转换,比如:

SELECT name, CASE WHEN score >= 60 THEN '及格' ELSE '不及格' END AS result FROM student;

看到这里你应该已经明白:基本查询不是把 SELECT 背下来就完事,它是有执行顺序、有语义边界、有设计取舍的。后面所有复杂写法,都是在这些问题的基础上叠加。

2. 把数据框进你的范围:WHERE 条件过滤

2.1 比较运算与逻辑组合

WHERE 的核心作用是对表数据进行逐行筛选,保留满足条件的行。最常用的是比较运算符:=!=<>><>=<=。逻辑组合用 AND、OR、NOT,注意 OR 的优先级低于 AND,所以WHERE a = 1 OR b = 2 AND c = 3实际是WHERE a = 1 OR (b = 2 AND c = 3)。如果拿不准,就加括号,别让读到代码的人猜。

这里有个特别容易踩的坑:字符串和数字比较。如果你在字符串字段上写WHERE phone = 13800001111,MySQL 会把字段值和数字都转成浮点数再比较。一旦字段内容不是纯数字,转换过程就可能让索引失效,甚至出现诡异的结果。正确做法是给字符串加引号:WHERE phone = '13800001111'

NULL 的过滤也是重灾区。WHERE name = NULL永远查不出数据,因为 NULL 不是一个值,它表示“未知”。判断空值必须用IS NULLIS NOT NULL。很多人写条件过滤时栽在 NULL 上,往往就是没理解这一点。

2.2 模糊查询 LIKE 和字符集陷阱

LIKE 是做模糊搜索最直接的写法,但它的性能和个人习惯密切相关。LIKE '张%'匹配以“张”开头的字符串,这种写法在字段有普通索引时是有机会走索引的;但LIKE '%张%'因为通配符在最前面,优化器只能全表扫描,数据量一大就会卡。

实际项目里,如果需要频繁做前后模糊匹配,一般建议配合全文索引或者用第三方搜索组件,而不是硬扛 LIKE。如果只是临时查数据,用LIKE '%关键词%'也能接受,但要心里有数。

字符集和排序规则(Collation)也会影响 LIKE 的结果。比如表是utf8mb4_general_ci时,LIKE 匹配一般不区分大小写;如果改成utf8mb4_bin,它就会区分。有些项目从别的数据库迁到 MySQL,发现SELECT * FROM user WHERE name = 'Zhang'能查出zhang,第一反应以为数据出错了,其实是排序规则的问题。判断这个问题很简单:执行SHOW TABLE STATUS LIKE 'user';查看表的 Collation,或者用SHOW FULL COLUMNS FROM user;看字段的 Collation。

2.3 IN、BETWEEN 与 NULL 处理

IN 适合匹配一组固定值,比如WHERE status IN ('open', 'closed')。它的语义清楚,但有两个使用细节:IN 列表里的值太多会降低性能,通常超过几百个就应该考虑用连接或临时表;如果 IN 子查询返回的结果集特别大,优化器可能选择不友好,后面会单独讲 EXISTS。

BETWEEN 是范围过滤的语法糖,WHERE age BETWEEN 18 AND 30等价于age >= 18 AND age <= 30。这里要注意 BETWEEN 是包含边界值的,跟有些语言里的半开区间不一样。边界是日期时更要小心:BETWEEN '2024-01-01' AND '2024-01-31'不包含当天的 23:59:59,需要判断是否应该用datetime < '2024-02-01'这种方式。

NULL 和空字符串是两个概念。NULL 表示没有值,空字符串''是有效值。在统计时,COUNT(col)会忽略 NULL 但不会忽略空字符串,很多数据不一致的问题就从这里来。写过滤条件时,最好明确需求到底是排除 NULL,还是排除空串,还是两个都排除。

3. 让数据有秩序:ORDER BY 排序与 LIMIT 分页

3.1 ORDER BY 多字段排序和字符集问题

排序是查询里最容易“想当然”的一个环节。ORDER BY created_at DESC很简单,但多字段排序时经常有人写反。比如先按部门升序,再按工资降序,正确写法是:

SELECT name, department, salary FROM employee ORDER BY department ASC, salary DESC;

这里的关键是:只有当前面字段值相同时,后面的字段才参与排序。如果你写ORDER BY salary DESC, department ASC,结果就是把工资高的排最前,部门字段只在工资相同时起作用,跟预期完全不同。

中文排序的坑也不少。MySQL 的默认排序规则对中文字段通常按拼音排序,但如果你用的是gbk_chinese_ciutf8mb4_general_ci,不同版本的 MySQL 表现可能不完全一致。更麻烦的是,如果字段里既有中文又有英文、数字,排序结果会很“迷”。遇到严格的排序需求,可以在 ORDER BY 里显式指定:ORDER BY name COLLATE utf8mb4_bin ASC。这个操作能强制按字节序排,但要注意它会失去拼音排序功能。

3.2 LIMIT 分页与性能优化

分页最常见的写法是LIMIT offset, size,也就是跳过前 offset 条数据取 size 条。它的性能问题出现在 offset 很大的时候,比如查第 100 万页,MySQL 还是要先扫描并丢弃前 1000 万条数据,这个过程非常浪费。

一个经典优化方案是“记录上一页最后一条 ID”:

SELECT id, name, created_at FROM user WHERE id > 100000 ORDER BY id ASC LIMIT 20;

它用 WHERE 条件缩小范围,让查询能利用主键索引直接定位,速度比LIMIT 100000, 20快得多。如果业务必须用传统分页且表很大,可以考虑“延迟关联”:先只查主键SELECT id FROM user ORDER BY id LIMIT 100000, 20,拿到 20 个 ID 后再 JOIN 回原表取完整数据。这样做能减少第一阶段的回表数量,实测提升非常明显。

3.3 排序与索引的联动

很多人以为 ORDER BY 无非就是最后加一句,其实排序是否走索引,直接决定查询是毫秒级还是秒级。当排序字段和 WHERE 过滤字段能组成联合索引时,MySQL 可以直接按索引顺序读取,不需要额外排序操作,执行计划里的 Extra 列就不会出现Using filesort

举例子:表里有索引idx_status_created(status, created_at),查询WHERE status = 1 ORDER BY created_at DESC就能从索引里同时完成过滤和排序。但如果写WHERE status = 1 ORDER BY name,name 字段不在索引里,MySQL 就得先把数据查出来,再用临时文件排序,这就是 filesort。

想验证也非常简单,执行EXPLAIN SELECT ...,看到 Extra 列里有Using filesort就要警觉了。这不是说 filesort 一定慢,数据量大时它一定不轻松。所以遇到排序慢,优先想能不能调整索引,而不是一味加内存。

4. 聚合与分组:从明细到统计

4.1 常用聚合函数

聚合函数就是把多行数据计算成一个结果。最常用的是COUNTSUMAVGMAXMIN。它们各自的细节值得单独说:

  • COUNT(*)统计的是行数,不管字段是不是 NULL;COUNT(字段)统计的是该字段非 NULL 的行数。
  • SUM(字段)遇到所有值都是 NULL 时,返回 NULL,不是 0。算业绩时最好用IFNULL(SUM(amount), 0)包一层。
  • AVG(字段)会自动忽略 NULL 行,也就是说它不是拿总行数做分母,而是拿非 NULL 行数做分母。
  • MAXMIN对字符串、日期类型也能用,日期最大就是最晚的日期,字符串比较规则由排序规则决定。

聚合函数配合 WHERE 时执行顺序是:先 WHERE 过滤,再聚合。所以SELECT COUNT(*) FROM orders WHERE status = 'paid'统计的是已支付订单数量,逻辑很清楚。

4.2 GROUP BY 与 HAVING 的使用场景

GROUP BY 的作用是分组统计,它会把同一字段值的行合并成一组,然后每组输出一行结果。分组之后,SELECT 后面能出现的字段只分两类:分组字段本身,或者聚合函数计算结果。比如:

SELECT department, AVG(salary) AS avg_salary FROM employee WHERE status = 'active' GROUP BY department HAVING AVG(salary) > 8000;

这条语句先过滤在职员工,再按部门分组算平均工资,最后只保留平均工资大于 8000 的部门。注意HAVING是对分组后的结果做过滤,能用 WHERE 提前过滤的条件就尽量不放在 HAVING 里,因为 HAVING 是在分组后执行,能处理的数据范围已经没那么“友好”了。

MySQL 的sql_mode如果开启了ONLY_FULL_GROUP_BY,你 SELECT 的非聚合列必须出现在 GROUP BY 中,否则直接报错。很多老项目从宽松模式迁到严格模式时会冒出一堆错误,原因就在这里。建议新项目直接保持默认严格模式,强行让自己写出标准 SQL。

4.3 分组统计常见坑

我见过最多的问题是统计“每个用户最近一次登录时间”时,想当然写成:

SELECT user_id, MAX(login_time), login_ip FROM login_log GROUP BY user_id;

在严格模式下这直接报错,因为login_ip不在 GROUP BY 里。但即使不报错,返回的 login_ip 也不一定是最近一次登录的那条记录的 IP,因为 MySQL 只保证MAX(login_time)的结果正确,不保证同行其他字段也是“最大值对应的值”。这个语义问题非常隐蔽,正确做法是用子查询先查出每个用户的最大登录时间,再 JOIN 回原表取完整记录。

分组统计另一个坑是 NULL 值会被单独分成一组。比如GROUP BY department,所有 department 为 NULL 的数据会聚合成一组,看起来就像凭空多了一个“空部门”。如果你不想统计 NULL,记得先加WHERE department IS NOT NULL

5. 多表连接 JOIN:从一行看到另一张表

5.1 理解 JOIN 的几种类型

JOIN 是基本查询里最考验逻辑能力的部分,核心就是“把两张表按某种条件拼起来”。最常见的三种:

  • INNER JOIN:只返回两表都匹配上的行,其他行丢掉。
  • LEFT JOIN:返回左表全部行,右表没有匹配时用 NULL 填充。
  • RIGHT JOIN:反过来,返回右表全部行,左表没有匹配时用 NULL 填充。

举一个订单和用户的例子。订单表有 user_id,用户表有 id。我想查所有订单以及对应的用户姓名:

SELECT o.order_id, u.name FROM orders o LEFT JOIN users u ON o.user_id = u.id;

这里的语义是“订单是主角,即使某些订单的 user_id 在用户表里不存在,订单行也要出现在结果里,只是用户姓名为 NULL”。如果用INNER JOIN,那些没有匹配用户的订单就会消失。两种写法就差几个字母,结果差出好几行,这也是联表查询最容易出 bug 的地方。

MySQL 本身不支持FULL OUTER JOIN,如果业务需要“合并两张表的所有数据,匹配得上就匹配,匹配不上就补 NULL”,可以同时 LEFT JOIN 再 RIGHT JOIN,然后用 UNION 去重。不过这种情况比较少,一般通过表结构设计就能避免。

5.2 ON 与 WHERE 的区别

这个知识点我几乎每次培训都要强调。ON 后面的条件负责“如何连接两张表”,WHERE 负责“连接完成后过滤结果”。两者在 LEFT JOIN 中有着本质区别。

SELECT o.order_id, u.name FROM orders o LEFT JOIN users u ON o.user_id = u.id AND u.status = 'active';

上面的查询会返回所有订单,即使关联到的用户 status 不是 active,用户字段照样返回,只是不满足 ON 条件的用户行会被置为 NULL。但如果写成:

SELECT o.order_id, u.name FROM orders o LEFT JOIN users u ON o.user_id = u.id WHERE u.status = 'active';

那 LEFT JOIN 的结果还要经过 WHERE 过滤,一旦用户 status 不满足条件,整行订单都被过滤掉,LEFT JOIN 就变成了 INNER JOIN 的效果。很多线上数据异常,排查到最后都是这个原因。

对于 INNER JOIN,ON 和 WHERE 在结果上等价,但为了让语义清楚,最好还是把连接条件放 ON,业务过滤条件放 WHERE。

5.3 JOIN 性能注意事项

多表 JOIN 的性能,比单表查询容易失控。我总结几个基本注意事项:

  • 连接字段类型要一致。如果一张表 id 是 INT,另一张表 user_id 是 VARCHAR,JOIN 时 MySQL 要做隐式类型转换,索引大概率失效。
  • 小表驱动大表。MySQL 优化器会尽量用小表作为驱动表,但如果你在 JOIN 条件上用了函数或表达式,优化器就没法准确估算。
  • 关注执行计划里的 type 和 Extra。最优是eq_refref,如果出现ALL且是大表,就要考虑加索引。
  • 不要 JOIN 太多张表。三张以上大表关联时,数据量会呈指数级膨胀,可以先在应用层拆查询,或者提前汇总。

JOIN 本身没有罪,关键是要让每一步都走索引。每次写 JOIN 之前,先看连接字段是否有索引,这比事后调优省事得多。

6. 子查询与进阶技巧:让查询更灵活

6.1 子查询的三种常见写法

子查询就是嵌套在查询里的查询,常见有三种位置,各有各的用途。

第一种在 WHERE 里:

SELECT name FROM users WHERE id IN (SELECT user_id FROM orders WHERE amount > 100);

这种写法适合“先查出符合条件的集合,再用集合过滤外层”。第二种在 FROM 里:

SELECT department, AVG(amount) FROM ( SELECT user_id, department, amount FROM orders o JOIN users u ON o.user_id = u.id ) t GROUP BY department;

FROM 子查询相当于先构建一张临时结果表,外层继续做统计。注意 MySQL 对派生表有严格要求:每个派生表都必须有别名,上面例子里的t就是干这个用的。第三种在 SELECT 后面作为标量子查询:

SELECT name, (SELECT COUNT(*) FROM orders WHERE user_id = users.id) AS order_count FROM users;

这种写法适合给每一行补充一个聚合值,但要小心:如果子查询返回多行,MySQL 会直接报错,所以通常都会配合聚合函数或者直接限制返回一行。

6.2 EXISTS 与 IN 的选择

WHERE id IN (SELECT ...)WHERE EXISTS (SELECT ...)并不总是可以互相替代,它们的执行思路不同。

IN 子查询通常会先把子查询结果集算出来,再和外层逐行比较。如果子查询结果集很小,比如只有几十个 id,IN 很高效。但子查询结果集很大时,内存和比较成本都会上升。EXISTS 则是相关子查询,对外层的每一行,都会去判断“是否存在满足条件的行”,只要找到一行就会停止。如果子表上有合适的索引,EXISTS 在外层表数据量大的时候往往表现更好。

实际项目中我一般这样选:子查询结果集小且稳定,用 IN;外层表数据量很大,并且能在子查询关联字段上走索引,用 EXISTS。当然,最靠谱的不是拍脑袋,而是用 EXPLAIN 看执行计划,数据说话。

MySQL 8.0 之后的优化器已经能把不少 IN 子查询改写为更优执行方式,所以也不用过度焦虑,掌握基本取舍原则就够用了。

6.3 UPDATE / DELETE 中的子查询实践

MySQL 查数据的能力强,更新和删除也经常要结合子查询完成。比如要把“没有下过任何订单的用户”标记为无效:

UPDATE users SET status = 'inactive' WHERE id NOT IN (SELECT user_id FROM orders);

这个需求很常见,但 MySQL 有个著名的限制:不能在修改一张表的同时直接查询这张表。也就是说,如果你试图删除某张表里某个条件的记录,而子查询也来自同一张表,就会报错:

DELETE FROM t WHERE id IN (SELECT id FROM t WHERE ...); -- ERROR 1093: You can't specify target table 't' for update in FROM clause

解决办法是给子查询套一层临时表:

DELETE FROM t WHERE id IN ( SELECT id FROM ( SELECT id FROM t WHERE ... ) tmp );

这个“你要改表 t,先从表 t 里把目标 id 捞出来放临时表里,再去更新”的思路,我几乎每个月都会用上。遇到类似报错不要慌,套一层派生表就能绕过去,这也再次印证了 FROM 子查询的重要性。

7. 新手避坑指南:MySQL 基本查询的实战心得

7.1 慢查询的排查思路

基本查询写得再漂亮,跑得慢一样白搭。我遇到查询性能问题,第一步不是猜,而是看执行计划:

EXPLAIN SELECT ...;

重点看几个关键列:

  • type:至少达到range,最好到refeq_ref,如果出现ALL,基本可以判定是全表扫描。
  • key:实际用到的索引,如果为 NULL,说明没走索引。
  • rows:预估扫描行数,这个数字越大越危险。
  • Extra:出现Using filesortUsing temporary,都要警惕。

排查出全表扫描的原因后,优先考虑两个方向:WHERE 条件里的字段有没有索引;查询语句有没有在字段上做函数、运算、隐式转换导致索引失效。加索引也不是越多越好,但针对高频查询建联合索引收益非常明显。比如这个查询:

SELECT * FROM orders WHERE status = 'paid' ORDER BY created_at DESC;

建议索引idx_status_created(status, created_at),能同时覆盖过滤和排序。

7.2 常见错误速查表

新手阶段遇到的报错,很多都有固定解法。我整理了一份速查表,遇到类似问题可以直接对号入座。

错误/表现常见原因解决办法
Unknown column 'xxx' in 'where clause'字段名写错,或者表别名没写对检查字段是否存在,多表查询显式加别名
Error 1055: Expression #... not in GROUP BY开启了 ONLY_FULL_GROUP_BY,SELECT 了非分组字段把多余字段移出 SELECT,或改用聚合函数
Error 1093: You can't specify target table for update in FROM clause同一张表出现在 UPDATE/DELETE 和子查询中子查询外层再套一层临时表
WHERE name = NULL 查不到数据NULL 不能用等号比较改成 IS NULL
字符串字段和数字比较结果诡异隐式类型转换导致索引失效字符串加引号,字段类型保持一致
ORDER BY 没生效子查询内部排序被外层覆盖排序放到最外层,或改用 JOIN / 聚合
分页到后面越来越慢offset 过大用 ID 条件分页或延迟关联优化

这些错误不是背下来就行,最好自己在本地复现一遍。我当年就是故意把这些错误 SQL 写出来,逐个看报错信息,后来遇到类似问题一眼就能定位。

7.3 练习建议和扩展方向

基本查询想练扎实,光看文章不够,必须上手敲。我比较推荐三个练习素材:官方文档里的示例库 employees 和 sakila,数据量适中,关联关系齐全;自己随便建两张表,插入几千条测试数据,然后疯狂改写查询;去公司真实业务场景里做只读查询,但要注意权限和敏感数据保护。

练习时要有目的地“换着写”。同一个需求,先用 JOIN 写一版,再用子查询写一版,然后对比执行计划;同一张表,先统计总数,再分组,再带条件分组,再排序分页,把一个场景吃透,远比机械地刷一百道题有用。

如果这篇文章看到这里还觉得不过瘾,下一步值得研究的方向是:索引的工作原理、EXPLAIN 各列含义、慢查询日志分析、窗口函数(MySQL 8.0)。这些知识和基本查询是一脉相承的,查询语法是招式,索引和执行计划是内功,二者叠加才能真正告别“小白”。

最后分享一个我自己的习惯:写完任何一条查询,我都会顺手执行一次 EXPLAIN,哪怕数据量很小。这个动作坚持半年以后,你再写 SQL,就会本能地避开那些全表扫描、超大分页、隐形类型转换的写法。不是因为它多高级,而是因为养成习惯,才能让基础真正变成肌肉记忆。

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

戴尔准系统爆改低功耗NAS:90元老机器的工业级重生

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/17 5:10:01

UniApp集成Towxml实现Markdown渲染与分包优化

1. 项目背景与需求分析在小程序开发中&#xff0c;Markdown内容的展示一直是个痛点。传统的文本展示方式无法完美呈现代码块、数学公式、表格等结构化内容。Towxml作为一款专为微信小程序设计的渲染引擎&#xff0c;能够将Markdown/HTML转换为小程序原生组件&#xff0c;支持丰…

作者头像 李华
网站建设 2026/9/17 5:09:37

纠结 Agent 和 Skills 区别?用 TaoToken 走通 Claude Code 的长会话

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/17 5:08:09

桌面应用开发框架选型:Electron、Tauri、JavaFX 与 Qt 对比

"桌面应用"这四个字&#xff0c;在过去十几年里被反复宣告过"要凉了"&#xff0c;结果每次都被现实捞了回来。浏览器能干的活越来越多&#xff0c;可一旦碰到本地文件批处理、设备调试、音视频处理、本地数据库管理、离线内网办公这类场景&#xff0c;开发…

作者头像 李华