资讯详情

SQL练习题全解析:从建表到嵌套查询的避坑指南

📅 2026/10/12 2:03:14 | 华诺云谱 👁 阅读
SQL练习题全解析:从建表到嵌套查询的避坑指南
简介一份针对SQL初学者与数据库备考者的练习题及答案文档内容以“学校数据库”为核心应用场景要求依据给出的表结构创建Student、Course、SC三张表并完成数据插入、删除、更新及多样查询操作。题型覆盖了单表筛选、年龄与性别组合条件、系科分组统计以及总分、平均分、最高分、最低分等聚合统计还包含了连接查询、嵌套子查询、相关子查询以及求平均分前三名、查询每门课程成绩都高于该课程平均分等进阶问题且每类题目均附有参考答案。资源包内为1个doc文档压缩后仅43KB轻量便携可直接打开浏览或打印刷题。目前已有520人学习下载适用场景包括课程实验配套练习、SQL考前冲刺、自学巩固或作为教学辅导材料能帮助读者系统掌握SQL增删改查与多表查询、统计分析的典型写法和解题思路。1. 一套能直接抄的 SQL 练习题从建表到嵌套查询的完整闭环这份《sql语句练习题及答案.doc》是我见过最像课堂实战的 SQL 训练材料没有之一。它不跟你讲抽象理论而是直接甩出 school 数据库里的三张表——学生表 Student、课程表 Course、选课表 SC然后从建表、插数据一路问到嵌套查询、相关子查询和集合运算。换句话说你拿到的不只是几十道题而是一整套数据库课程设计级别的 SQL 语法覆盖方案特别适合准备数据库期末考试、刷面试笔试题、或者刚学完 SQL 语法想找个系统训练场的人。文档里最值钱的部分不是答案本身而是题目设计的层次感先单表查询再统计分组然后连接查询逐步升级到嵌套、EXISTS 相关子查询、TOP/ALL 极值查询和集合差集运算。每个阶段恰好卡在 SQL 学习者最容易混淆的节点上。比如grade is null判断缺考成绩、like _明%匹配第二个字、having与where的过滤时机差异、left join保留未选课男生等等这些坑几乎覆盖了从入门到进阶的所有高频失分点。下面我按实际刷题顺序把这些题目拆开讲透。2. 建表与基础 DML把三张表和增删改一次跑通2.1 数据类型与主键的选择为什么学号用 char(6) 而不是 int打开文档第一页它会先把 Student、Course、SC 的表结构列出来还特别标了数据类型和长度。这里有个容易被忽略的细节Sno 学号字段用的是char(6)而不是整数类型。原因是学号通常是固定位数的字符串可能包含前导零比如0001。如果存成 int前导零会被丢掉查询和显示都会出问题。Sname 用varchar(8)因为姓名长度不固定用变长字符串省空间。Ssex 用char(2)是因为性别就一个字或两个字的固定长度。Sage 用smallint是因为年龄范围很小没必要用 int 浪费空间。Ccredit 学分字段用的是tinyint范围 0 到 255一张课的学分绝对不会超过这个范围比 smallint 更省空间。Grade 成绩字段用decimal(12,2)这个值得注意——它允许两位小数总共 12 位有效数字可以存类似87.50这样的成绩。如果用 float 或 real 存浮点数后续做avg(grade)统计时很容易出现 0.30000000000000004 这类精度误差考试批改时也会因为精度问题被扣分。创建这三张表的完整语句如下create table student ( sno char(6), sname varchar(8), ssex char(2), sage smallint, sdept varchar(15), primary key (sno) ); create table course ( cno char(4), cname varchar(20), cpno char(4), ccredit tinyint, primary key (cno) ); create table sc ( sno char(6), cno char(4), grade decimal(12,2), primary key (sno, cno) );这段代码的逻辑清晰student 和 course 各自主键独立而 sc 表的主键是(sno, cno)复合主键表示一个学生选一门课只能有一条成绩记录。如果要模拟现实中的重修覆盖就得靠应用层处理表结构层面复合主键已经杜绝了同一学生对同一课程的重复选课记录。建表之后顺手插入测试数据。文档里只给了 student 表的两条样例实际刷题时建议把后面题目需要的数据都补齐比如插入几条带grade is null的选课记录来测空值查询insert into student values (4001, 赵茵, 男, 20, SX); insert into student values (4002, 杨华, 女, 21, JSJ); insert into course values (1001, 数据库原理, 1000, 4); insert into course values (1002, 数据结构, 1001, 3); insert into sc values (4001, 1001, 92.50); insert into sc values (4002, 1002, NULL);insert into sc values (4002, 1002, NULL)这行是特意留的空值记录后面第 3 题「查询 1001 课程没有成绩的学生学号」就需要这种数据才能跑出结果。grade is null不等于grade null后者永远匹配不到任何行这个点是 NULL 三值逻辑在 SQL 里的经典陷阱。2.2 表结构的后期维护删除表和添加属性文档里有两道操作题特别像真实业务里的需求删除整个 student 表和给 student 表添加新列。删除表用drop table student这是物理删除表结构、数据、索引全部消失没有后悔药。如果只是想清空数据保留表结构应该用delete from student或者truncate table student。delete是 DML 操作可以加where条件选择性删行删除后可以回滚truncate是 DDL 操作直接释放数据页速度更快但不能回滚。给表添加新列的语句如下alter table student add sbirthdate datetime;这个操作在考试中出现的频率很高但很多人会漏了alter table关键字直接写add column。标准 SQL 里add column的column关键字在 SQL Server 和 MySQL 中可省略但 Oracle 要求必须写add (sbirthdate date)的括号格式。不同数据库方言差异就在这里体现建议以你实际使用的数据库为准。删除所有 JSJ 系男生的操作是两条条件的组合这道题主要考and逻辑和字符串比较。需要注意JSJ是带单引号的字符串字面量不能写成双引号。删除选课记录用子查询嵌套删除where cno in比直接关联删除更稳妥delete from SC where Cno in ( select Cno from Course where Cname 数据库原理 );这种写法的好处是只需要知道课程名称不需要提前查课程号。子查询先算出目标课程号集合外层删除匹配这些课程号的选课记录。如果课程表里有重名课程这个写法会把所有同名课程的选课记录一起删掉需要先通过唯一标识确认目标范围。3. 单表查询与统计分组从基础语法到聚合函数3.1 条件过滤与排序between、like、is null 的边界行为第 3 题「查询年龄在 19 至 21 岁之间的女生的学号、年龄按年龄从大到小排列」是典型的范围查询select sno, sname, sage from student where ssex 女 and sage between 19 and 21 order by sage desc;between 19 and 21是闭区间等价于sage 19 and sage 21。如果题目要求 19 到 21 不含边界就得改成sage 19 and sage 21。order by sage desc是降序默认是升序asc。这里有个隐藏考点order by的列不一定出现在select列表中比如可以select sno from student order by sage这是合法的。第 2 题「查询中第 2 个字为『明』字的学生学号、性别」考的是通配符匹配select sno, ssex from student where sname like _明%;下划线_匹配任意单个字符百分号%匹配任意长度字符串。所以_明%的含义是第一个字符任意第二个字符必须是「明」后面可以跟任意内容。如果想查以「明」开头的名字应该写明%想查包含「明」的名字写%明%。第 3 题查缺考记录必须用is null而不是 nullselect sno, cno from sc where grade is null and cno 1001;NULL 在 SQL 里代表未知值任何与 NULL 的算术比较结果都是 UNKNOWNwhere条件只保留 TRUE 的行UNKNOWN 会被过滤掉。所以查空值只能用is null查非空用is not null。这道题如果数据里没有 null 的成绩查询结果会是空集刷题时最好先插入一条 null 成绩记录。第 4 题多系别过滤用in列表select sno, sname from student where sdept in (JSJ, SX, WL) and sage 25;等价写法是sdept JSJ or sdept SX or sdept WL但in更简洁。注意in列表空值问题如果列表里包含 NULL比如sdept in (JSJ, NULL)匹配行为可能不符合直觉因为NULL匹配不到任何实际值。这道题文档里的答案带了group by sdept但严格来说这个查询没有聚合函数group by不是必需的按系排列应该用order by sdept。3.2 聚合函数的正确姿势group by 与 having 的过滤时机统计题是这份文档的精华区第 2 题计算 JSJ 系的平均年龄和最大年龄是聚合函数的基础用法select avg(sage), max(sage) from student where sdept JSJ;avg会忽略 NULL 值如果 sage 有 NULL计算结果只统计非空行。max同样忽略 NULL。这里如果题目要求「将 NULL 视为 0」就得先coalesce(sage, 0)再聚合。第 4 题「计算每一门课的总分、平均分、最高分、最低分按平均分由高到低排列」是最典型的分组统计select cno, sum(grade), avg(grade), max(grade), min(grade) from sc group by cno order by avg(grade) desc;关键点在于select列表中出现的非聚合列必须出现在group by子句中。这里cno在group by里所以可以出现在select中。sum(grade)对 NULL 值同样忽略如果一门课所有人都缺考sum 会是 NULLavg 也是 NULL排序时 NULL 默认排在最前还是最后取决于数据库设置。第 6 题「查询平均分大于 80 分的学生学号及平均分」引出havingselect sno, avg(grade) from sc group by sno having avg(grade) 80;where是在分组前过滤行having是在分组后过滤组。这里如果写成where avg(grade) 80是语法错误聚合函数不能出现在where里。什么时候用where什么时候用having最简单的判断方式是问自己这个条件是针对原始行的还是针对分组结果的。针对原始行用where针对聚合结果用having。第 10 题「统计有大于两门课不及格的学生学号」是having加count的进阶select sno from sc where grade 60 group by sno having count(*) 2;先通过where grade 60过滤出所有不及格的记录再按学号分组count(*)统计每个学生不及格的门数having count(*) 2只保留超过两门的学生。注意where和having的执行顺序在逻辑上是先where再group by再having先缩小数据范围再做分组运算性能更好。第 8 题「统计有 10 位成绩大于 85 分以上的课程号」特别容易理解错select cno from sc where grade 85 group by cno having count(*) 10;它查的不是某门课有 10 个人且都大于 85而是某门课大于 85 分的人数恰好等于 10。如果一门课有 12 个人大于 85count(*) 10就不成立换成了count(*) 10才能包含。这类题做错通常是因为把题意的「10 位」理解成课程编号相关的条件。3.3 十档分制换算用算术表达式生成新列第 5 题「按 10 分制查询成绩」这道题很多人第一次见会觉得绕实际是考算术表达式和除法运算的类型转换select sno, cno, grade / 10.0 1 as level from sc;这里的关键是写grade / 10.0而不是grade / 10。如果 grade 是整数类型的列整数除以整数会得到整数比如85 / 10 8丢掉小数部分 1就是 9 档。而85 / 10.0 8.5 1就是 9.5 档然后可以配合取整函数或floor处理。原题的参考答案直接用grade/10.01没有处理边界和取整实际使用时建议加一层floor或cast把结果规整到整数档位select sno, cno, floor(grade / 10.0) 1 as level from sc;floor(grade / 10.0)把 85 分变成 8加 1 得到 9 档。100 分的情况是floor(100 / 10.0) 1 11档但 10 分制最高只到 10 档需要再套一层case when处理满分边界。这类算术表达式生成新列在真实报表里非常常见比如把百分制成绩映射为优良中差等级。4. 连接查询实战内连接、左连接与多表条件过滤4.1 等值连接与嵌套子查询的等价互换连接查询部分的第一道题「查询 JSJ 系的学生选修的课程号」是最基础的等值连接select distinct cno from student, sc where student.sno sc.sno and sdept JSJ;这里用了隐式连接写法逗号分隔两表where里写连接条件。现代 SQL 更推荐显式inner join可读性更强select distinct sc.cno from student inner join sc on student.sno sc.sno where student.sdept JSJ;distinct是必须的因为一个 JSJ 系学生可能选修多门课没有distinct会出现重复课程号。这道题也考了列限定符的使用——sno在两个表里都有必须写成student.sno sc.sno才能消歧义。第 2 题「查询选修 1002 课程的学生的学生不用嵌套及嵌套 2 种方法」是连接和子查询的对比题-- 方法一连接 select sname from student, sc where student.sno sc.sno and cno 1002; -- 方法二嵌套 select sname from student where sno in ( select sno from sc where cno 1002 );两种方法结果集相同但执行路径不同。嵌套子查询的思路是先查找到选了 1002 课的学生学号集合再回到 student 表过滤姓名。连接方法是一边扫描一边配对数据量大时连接性能通常优于子查询但子查询的可读性更好。这道题的参考答案里还有第三种用exists的写法属于相关子查询放在后面的嵌套章节。面试时如果能随口说出三种写法会显得对 SQL 执行逻辑理解得更透彻。4.2 三表连接查课程名称条件拿成绩、选课、课程三张表关联第 3 题「查询数据库原理不及格的学生学号及成绩」需要三张表协作select sno, grade from sc, course where sc.cno course.cno and cname 数据库原理 and grade 60;因为要通过课程名查成绩必须先通过sc.cno course.cno把选课表和课程表连接起来才能过滤cname条件并取出grade。这里有个隐式内连接的陷阱如果 sc 表里有某条记录对应课程在 course 表中不存在也就是外键失效内连接会把它丢掉。数据库不强制外键约束时这种数据孤岛会被静默忽略。正规做法是给 sc 的 cno 加外键约束或者审查数据质量。第 4 题要连接三张表才能同时拿到学生姓名、课程名称和成绩select sname from student, sc, course where student.sno sc.sno and sc.cno course.cno and grade 80 and cname 数据库原理;嵌套写法select sname from student where sno in ( select sno from sc where grade 80 and cno in ( select cno from course where cname 数据库原理 ) );外层的连接版本一步到位内层子查询版本层层剥洋葱。性能上连接通常更快因为优化器可以更灵活地调整连接顺序和选择索引。子查询的好处是逻辑隔离每一层只关心一件事。实际项目里如果数据量大我会先把子查询写成临时表或者用with公共表表达式让优化器有更好的执行计划。4.3 left join 保留未匹配行男学生没选课也不能漏第 7 题「查询男学生学号、课程号、成绩一门课程也没有选修的男学生也要列出」是所有连接题里最容易写错的select student.sno, sname, cno, grade from student left join sc on student.sno sc.sno where ssex 男;关键点是left join以左表 student 为基准右侧 sc 表没有匹配的行时用 NULL 填充 cno 和 grade。如果一个男学生一门课都没选sc 表里就没有他的行只有left join才能把他保留下来。如果用 inner join 或等值连接没选课的学生会被过滤掉。这道题的隐藏陷阱在条件的位置where ssex 男放在where里没问题因为它只是过滤左表。但如果是left join sc on student.sno sc.sno and ssex 男语义就变了——右表连接条件里的过滤会影响右表匹配行但不影响左表保留。而把ssex 男放在where里实际上是先过滤了左表再连接结果等价。考试时会区分这两种写法的结果差异比如把ssex男放进on后面会导致左表不是「只保留男学生」而是「男生参与连接女生带 NULL 扩展」结果集完全不同。4.4 平均分不及格的连接版本与子查询版本第 5 题「查询平均分不及格的学生的学号、平均分」需要连接 student 和 scselect student.sno, sname, avg(grade) from sc, student where student.sno sc.sno group by student.sno, sname having avg(grade) 60;这里group by后面必须同时包含student.sno和sname因为select里两个非聚合列都要出现在group by中。如果只按sno分组而sname在 select 里SQL Server 和 MySQL 的行为还不一样MySQL 默认允许这种写法但取哪个姓名不确定SQL Server 直接报错。养成group by写全所有非聚合列的习惯跨数据库运行更安全。嵌套子查询版本只需要在 sc 上分组再回 student 表取姓名select sname from student where sno in ( select sno from sc group by sno having avg(grade) 60 );第 6 题「查询女学生平均分高于 75 分的学生」把性别过滤和平均分过滤拆成两层select sname from student where ssex 女 and sno in ( select sno from sc group by sno having avg(grade) 75 );也可以写成连接版本select sname from sc, student where student.sno sc.sno and ssex 女 group by student.sno, sname having avg(grade) 75;子查询版本先算平均分再过滤性别连接版本先过滤性别再分组。数据量大时先缩小 student 表再做连接往往更快如果 sc 表很大先用子查询把平均分大于 75 的学号集合算出来再 join能减少连接时的笛卡尔积规模。这类优化思路在面试里经常被追问。5. 嵌套查询与 EXISTS 相关子查询避开三类常见陷阱5.1 not in 与 not exists 的语义差异第 2 题「查询没有选修 1002 课程的学生的学生」是一道必考题select sname from student where sno not in ( select sno from sc where cno 1002 );用not in直观易懂。但如果 sc 表的 sno 列存在 NULL 值这个查询结果就会变成空集。原因是 SQL 的三值逻辑not in子查询返回结果集里包含 NULL 时整体判断变成 UNKNOWN所有行都被过滤掉。用not exists可以规避select sname from student where not exists ( select 1 from sc where sc.sno student.sno and cno 1002 );相关子查询not exists对每一行 student 执行一次内部查询只要没有匹配到选课记录就保留该学生。性能上not exists通常优于not in因为子查询命中即可短路而且不受 NULL 影响。这道题的参考答案里贴了一个演示数据sc 表里 0002 学生没有选 1002 课所以只在结果中出现。5.2 全称量词转换没有一门课没选等价于选了所有课第 4 题「查询没有选修 1001、1002 课程的学生」实际上有两层含义是两门课都没选还是至少有一门没选题目的标准答案用的是双重否定逻辑select sname from student where not exists ( select 1 from course where cno in (1001, 1002) and not exists ( select 1 from sc where sc.sno student.sno and sc.cno course.cno ) );内层not exists检查当前学生是否有某门课没选。如果学生对 1001 和 1002 课程都存在选课记录内层not exists对所有课程都返回 FALSE外层not exists就返回 TRUE表示「不存在任何一门课该学生没选」学生被选中。这是一个典型的全称量词所有课都选了转存在量词不存在没选的课的变换SQL 没有直接的for all语法只能通过双重否定实现。这道题还可以用集合差集做select sno from student except select sno from sc where cno in (1001, 1002);except返回左边集合有而右边集合没有的数据语义比双重 NOT EXISTS 直观得多。但except要求两边的列数、类型完全兼容而且性能和兼容性因数据库而异。Oracle 用minusMySQL 的except支持取决于版本。如果考试没限制数据库方言except是最优雅的解法。5.3 极值查询的三种写法top、all、相关子查询第 5 题「查询 1002 课程第一名的学生学号」至少能写出三种方式-- 方法一top select top 1 sno from sc where cno 1002 order by grade desc; -- 方法二all select sno from sc where cno 1002 and grade all ( select grade from sc where cno 1002 ); -- 方法三相关子查询 select sno from sc x where cno 1002 and not exists ( select 1 from sc y where y.cno 1002 and y.grade x.grade );top 1简单直接但只能查第一名并列第一时只返回一行。 all返回所有并列第一不会漏人。相关子查询的not exists写法最通用不依赖top或all关键字任何数据库都能跑。如果 grade 有 NULL all的行为会变得诡异NULL 比较返回 UNKNOWN all可能查不出任何行需要用is not null先过滤。第 6 题「查询平均分前三名的学生学号」把top和group by结合select top 3 sno from sc group by sno order by avg(grade) desc;这里order by avg(grade) desc才能按平均分排序取前三。如果平均分相同top 3会任意返回三行有可能漏掉并列。需要并列时改用窗口函数select sno from ( select sno, avg(grade) as avg_grade, dense_rank() over (order by avg(grade) desc) as rk from sc group by sno ) t where rk 3;窗口函数dense_rank会保留并列名次比如两个第二名时第三名也会被包含。SQL Server 2012 以上支持over子句MySQL 8.0 也有。如果题目明确要求「前三名」通常指的是dense_rank语义只用top 3会被扣分。关于相关子查询的经典考点——「查询大于本系科平均年龄的学生」原题答案用别名 x 区分内外层select sname from student x where sage ( select avg(sage) from student where sdept x.sdept );外层每取一行学生 x内层就计算一次该学生所在系的平均年龄然后比较。这个查询的关键是内层where sdept x.sdept让内外层建立关联。没有这个关联内层 avg 就是全校平均年龄语义完全不同。相关子查询执行次数等于外层行数数据量大时性能可能很差通常可以用窗口函数avg(sage) over (partition by sdept)替代一次扫描出结果select sname from ( select sname, sage, avg(sage) over (partition by sdept) as dept_avg from student ) t where sage dept_avg;窗口函数在select阶段计算分区均值不需要对每一行执行子查询执行计划的 IO 成本更低。6. 避坑与常见问题刷这套题最容易踩的五个坑6.1grade is null写成grade null整条查询白跑现象查询「1001 课程没有成绩的学生学号」返回空结果但表里明明有空成绩记录。原因 null永远返回 UNKNOWNwhere子句只保留 TRUE 行。解决所有判断空值的条件一律写成is null或is not null。顺带记住null参与的算术运算结果都是 null比如grade 1如果 grade 是 null结果也是 null。6.2where和having放错位置聚合条件报错或者结果错现象写where avg(grade) 80报「聚合函数不能出现在 WHERE 子句」。原因where在分组之前执行此时还没有聚合值。解决对单行条件用where对分组统计结果条件用having。我一般会先问自己「这个条件过滤的是原始行还是分组结果」答案直接决定写在哪里。6.3not in遇到子查询结果含 NULL结果集莫名为空现象where sno not in (select sno from sc)返回空集但数据明明有学生没选课。原因子查询结果包含 NULLnot in比较时产生 UNKNOWN。解决要么在子查询里加where sno is not null要么直接改为not exists相关子查询。not exists没有这个坑而且通常执行更快。6.4top不带order by取出的行没有确定顺序现象select top 1 sno from sc where cno 1002每次执行返回不同行。原因top在没有order by时取的行是物理存储顺序不确定。解决极值查询必须配合order bytop 1 ... order by grade desc才能保证取到最高分那行。并列第一名时用top 1 with ties或者 all否则会漏数据。6.5 连接查询里忘了加表别名导致列名歧义现象三表连接时where sno 1001报「列名不明确」。原因sno在 student 和 sc 两个表都有数据库不知道你指哪个。解决连接查询里所有公共列都加表名前缀或别名。这道题还有个隐藏坑用数字字符串查学号时1001是长度为 4 的字符串与学号列char(6)比较时数据库会自动补空格还是报类型不匹配取决于字段类型定义。如果学号定义为char(6)而查询条件是1001某些数据库会补空格成1001 匹配不到的记录就漏了。更稳的做法是把学号列定义为varchar(6)去掉尾部补空格的烦恼。7. 查漏补缺的进阶验证用结果集反向检查你的 SQL 是否正确刷完这套题别急着对答案先做一步验证。我自己的习惯是准备一份固定的小数据集把每个查询跑出来的结果和手工推算的期望结果做对比有差异就优先怀疑是条件边界或连接方向写错。比如「查询平均分前三名」这道题先手工给 sc 表构造 5 个学生、每人 3 门课的成绩平均分分别为 55、72、72、88、91期望结果应该返回 88、91 和两个 72 的并列共 4 行。如果top 3只返回了 3 行说明并列没有处理。验证外键类的题目比如「查询男学生学号、课程号、成绩没选课也要列出」我会故意构造一个没有选课记录的男学生确认结果里该学生出现在第一列有值、后两列为 NULL。如果查询结果里这个学生消失了说明不小心用了 inner join 或等值连接。这个数据驱动的验证方式比看答案更靠谱因为答案也可能有笔误但你自己的数据预期不会说谎。这份文档还提供了几个值得反复练习的变式。第 3 题「查询每门课程成绩都高于该门课程平均分的学生学号」是最难的相关子查询之一参考答案写法是select sno from student where sno not in ( select sno from sc x where grade ( select avg(grade) from sc where cno x.cno ) );逻辑是全称量词转存在量词不存在任何一门课该学生成绩低于这门课的平均分。如果你把not in改成not exists还能避免 NULL 成绩带来的空结果问题。这类题目做一遍可能不够建议隔一周不看答案重做一次确认逻辑链没有断层。还有一点值得留意的是文档里没有但实际考试爱考的操作类考点——建表后加外键约束。原题只要求创建三张表并设定主键但现实项目中 sc 表的外键必须指向 student 和 coursealter table sc add constraint fk_sc_sno foreign key (sno) references student(sno); alter table sc add constraint fk_sc_cno foreign key (cno) references course(cno);加了外键后删除 student 里被 sc 引用的行会被拒绝必须先删 sc 里的参照行或设置级联删除。这个约束关系是数据库课程设计里必考的知识点原题只到建表其实漏了这步。自己补上之后整个 school 库的关系完整性才算闭环。从那以后我每次拿到类似练习题都会先建好数据再动手写 SQL不靠答案反推题意。你可以先把这份文档跑通一遍再按自己的数据结构改出三套变体题那才是真正把 SQL 语法吃进去了。希望帮到你。本文还有配套的精品资源点击获取
📝

华诺云谱内容团队

资深建站顾问 · 行业研究员

10年+企业数字化服务经验,专注智能建站、SEO优化与品牌营销,持续输出建站技巧、行业洞察与营销干货,已帮助5000+企业实现数字化增长。

你可能需要的服务

订阅华诺云谱资讯周报

每周一封,精选建站技巧、SEO与营销干货,直达邮箱。已有 8,000+ 企业主订阅,助你少走弯路。

↑