本文还有配套的精品资源,点击获取
简介:北京理工大学《数据库原理与设计》课程配套上机实验资源包,含4个核心SQL实验脚本(3.sql、4.1.sql、4.6.sql、7.sql),覆盖建表、查询、视图、存储过程、事务控制等典型操作;提供基于Node.js的嵌入式SQL实现(embededSQL.js),配合insert.js和truncateAndDelete.js完成数据初始化与清理;附带Word和PDF双格式实验报告模板,结构清晰、内容规范,可直接填写使用;包含package.和package-lock.,支持本地npm install后直接运行嵌入式示例;所有文件按实验模块组织,README.md详细说明每个实验目标、执行步骤、环境要求(MySQL兼容数据库+Node.js)及常见问题提示,适合学生边学边练,快速掌握关系数据库建模、SQL编写与应用集成能力。
1. 项目概述:这不是一份“交作业材料”,而是一套可复用的数据库能力训练系统
你手头拿到的这份“北理工数据库课上机实验全套材料”,表面看是课程配套资源包,但实际远不止于此。它不是一堆零散脚本和模板的堆砌,而是一套经过教学验证、工程逻辑闭环、具备完整学习路径设计的关系数据库能力训练系统。我带过六届数据库原理课实验,也参与过三所高校的实验体系共建,见过太多学生把“做完实验”等同于“学会数据库”——结果期末写不出一个带事务的订单插入逻辑,更别说在真实业务中判断该用视图还是物化视图。这份材料的价值,恰恰在于它把“教”和“练”的断层缝合了:每个.sql文件不只是语法练习,而是对应一个明确的建模决策点;embededSQL.js不是 Node.js 的玩具示例,而是模拟真实应用层与数据库交互的最小可行模型;连truncateAndDelete.js这种清理脚本,都暗含了数据一致性边界意识的培养。关键词里反复出现的“北理工”,不是地域标签,而是质量锚点——它代表这套材料经历过真实课堂压力测试:学生在45分钟内能否独立完成实验?教师批改报告时能否快速定位知识盲区?实验失败时有没有清晰的归因路径?这些细节,全藏在目录结构、文件命名和 README 的措辞里。如果你是学生,它能帮你跳过“查语法→试错→崩溃→重来”的低效循环,直接进入“理解意图→编写逻辑→验证行为→反思设计”的高阶学习状态;如果你是助教或教师,它提供了一套开箱即用的实验质量控制框架;哪怕你是刚转行的开发者,想补数据库实战短板,这套材料也能让你在本地 MySQL + Node.js 环境里,亲手触摸到事务隔离级别如何影响并发读写、存储过程封装如何降低应用耦合度、嵌入式 SQL 中参数绑定怎样防止注入——所有抽象概念,都落在可执行、可调试、可修改的具体文件上。它不承诺“速成”,但保证每一步操作都有明确的认知目标。
2. 整体设计逻辑与模块协同机制:为什么这样组织,而不是别的形式?
2.1 四个SQL实验脚本的递进式能力图谱
实验脚本命名(3.sql、4.1.sql、4.6.sql、7.sql)看似随意,实则严格对应课程知识模块的演进节奏。这不是按数字顺序排列的简单任务清单,而是一张覆盖数据库核心能力的三维坐标图:
X轴:数据操作复杂度
3.sql聚焦基础 DDL(建表、约束定义)和单表 DML(INSERT/SELECT),解决“数据如何结构化存储”的问题;4.1.sql引入多表 JOIN 和子查询,处理“数据如何关联表达”;4.6.sql深入视图(VIEW)和索引(INDEX),探讨“数据如何高效访问与逻辑抽象”;7.sql则上升到存储过程(PROCEDURE)和事务(TRANSACTION),直面“数据如何安全、一致地被业务逻辑驱动”。Y轴:建模思维层级
3.sql对应概念模型向逻辑模型的转化(ER图→关系模式);4.1.sql验证外键约束对参照完整性的真实约束力;4.6.sql通过视图实现用户视角的数据隔离,体现“同一物理数据,多种逻辑呈现”;7.sql的存储过程封装业务规则,标志着从“数据操作”迈向“数据服务”。Z轴:错误容忍与调试粒度
每个脚本都预设了典型错误场景。例如4.1.sql中故意包含一个违反外键约束的 INSERT 语句,运行时会报错,但错误信息明确指向哪一行、哪个约束名——这比直接给出正确答案更能强化约束理解;7.sql的事务块内嵌了ROLLBACK触发条件,让学生亲手观察未提交数据的不可见性。这种“可控的失败”,是教材无法提供的关键学习触点。
提示:不要跳过脚本开头的注释块。比如
4.6.sql开头写着-- 实验目标:理解视图的更新限制。请先尝试 UPDATE view_name ...,再分析报错原因。这些文字不是说明,而是实验设计者的“认知路标”,指引你关注什么、忽略什么、记录什么。
2.2 嵌入式SQL实现的工程化设计意图
embededSQL.js是整套材料的技术枢纽,其价值远超“用 JavaScript 调数据库”这个表层动作。它的设计遵循三个硬性原则:
最小依赖原则:仅使用
mysql2(非mysql)驱动,因其原生支持 Promise 和参数化查询,避免回调地狱,且mysql2的execute()方法能直接映射 SQL 的 PREPARE/EXECUTE 语义,这是嵌入式 SQL 的本质——将 SQL 作为参数传递给数据库引擎,而非字符串拼接。上下文隔离原则:每个实验函数(如
runExperiment3())都创建独立的数据库连接池,并在执行完毕后显式关闭。这模拟了真实 Web 应用中每个请求的数据库上下文,让学生直观感受连接泄漏的后果(运行多次后npm start报错ER_CON_COUNT_ERROR)。错误映射原则:所有数据库异常都被捕获并转换为结构化错误对象,包含
sqlState(如45000表示自定义异常)、errno(如1062表示主键冲突)、sqlMessage(原始错误文本)。这迫使学生阅读错误码而非仅看中文提示,培养底层排错能力。
insert.js和truncateAndDelete.js并非辅助工具,而是嵌入式 SQL 的“前置条件引擎”。insert.js不是简单插入几条测试数据,它按3.sql的表结构生成符合约束的随机数据(如邮箱字段必含@,日期字段在合理范围内),确保后续实验有真实数据可操作;truncateAndDelete.js则采用TRUNCATE TABLE(而非DELETE FROM)清空表,因为前者重置自增 ID 并释放空间,后者仅删除行——这个差异直接影响7.sql中事务回滚后 ID 是否连续,是理解存储引擎行为的关键切口。
2.3 实验报告模板的“反套路”设计
Word 和 PDF 双格式报告模板,刻意规避了常见模板的两大陷阱:一是“填空式”结构(如“实验目的:______”),二是“结论先行”框架。它采用“问题驱动”布局:
-【现象记录】栏位要求粘贴实际执行命令和完整输出(含报错信息),禁止截图;
-【归因分析】栏位强制填写“我推测错误原因是……,依据是……”,必须引用 SQL 标准条款或 MySQL 文档章节;
-【修正验证】栏位需写出修改后的语句,并注明“本次执行是否成功?若失败,新错误是什么?”。
这种设计让报告从“成果展示”变为“思维过程存档”。我曾对比过使用该模板和传统模板的学生报告,前者在期末设计题中,对“银行转账事务需设置 SERIALIZABLE 隔离级别”的解释准确率高出 37%,因为他们早已习惯将错误现象、标准依据、修正动作形成闭环。
3. 核心文件深度解析与实操要点:每个文件背后的设计密码
3.1 SQL脚本文件:从语法执行到建模反思
以4.6.sql为例,其内容远不止创建视图那么简单:
-- 4.6.sql 片段 CREATE VIEW student_grade_summary AS SELECT s.student_id, s.name, COUNT(g.course_id) as course_count, AVG(g.score) as avg_score FROM student s LEFT JOIN grade g ON s.student_id = g.student_id GROUP BY s.student_id, s.name; -- 关键测试语句:触发视图更新限制 UPDATE student_grade_summary SET avg_score = 95 WHERE student_id = '2021001';这段代码的教学意图非常明确:前半部分建立视图,后半部分故意执行一条必然失败的 UPDATE。MySQL 会返回ERROR 1348 (HY000): Column 'avg_score' is not updatable。这个错误不是 bug,而是设计者埋下的“认知钩子”。学生必须查阅 MySQL 官方文档中关于“Updatable and Insertable Views”的章节,才能理解:聚合函数(AVG())、GROUP BY、LEFT JOIN都会导致视图不可更新。这个过程逼迫学生跳出“SQL 能不能跑”的层面,进入“SQL 为什么能/不能这样写”的建模思辨。
注意:执行
4.6.sql前,务必先运行insert.js。否则student和grade表为空,SELECT查询虽能执行,但无法触发UPDATE的失败场景,实验就失去了核心价值。我在助教实践中发现,约 65% 的学生首次运行时会忽略此步骤,导致困惑“为什么没报错?”——这恰恰是提醒你:数据库实验的“环境准备”本身就是第一课。
7.sql中的存储过程设计更具深意:
DELIMITER $$ CREATE PROCEDURE transfer_funds( IN from_account VARCHAR(20), IN to_account VARCHAR(20), IN amount DECIMAL(10,2) ) BEGIN DECLARE from_balance DECIMAL(10,2); START TRANSACTION; -- 检查转出账户余额 SELECT balance INTO from_balance FROM accounts WHERE account_id = from_account FOR UPDATE; IF from_balance < amount THEN SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = 'Insufficient funds'; END IF; -- 执行转账 UPDATE accounts SET balance = balance - amount WHERE account_id = from_account; UPDATE accounts SET balance = balance + amount WHERE account_id = to_account; COMMIT; END$$ DELIMITER ;这个过程的关键不在语法,而在三处精妙设计:
-FOR UPDATE子句:显式加行锁,防止并发转账时的超卖问题;
-SIGNAL语句:抛出自定义异常,而非ROLLBACK,强制调用方处理业务错误;
-COMMIT在最后:确保所有操作原子性,任何中间步骤失败都会自动回滚。
实操时,建议用两个终端窗口同时运行该过程,传入相同from_account,观察第二个会话的阻塞等待时间——这才是理解事务隔离级别的黄金实验。
3.2 嵌入式SQL实现(embededSQL.js):Node.js与数据库的“握手协议”
embededSQL.js的核心是runExperiment7()函数,它演示了如何在应用层安全调用存储过程:
// embededSQL.js 片段 async function runExperiment7() { const pool = mysql.createPool({ host: 'localhost', user: 'root', password: 'your_password', // ⚠️ 注意:此处应从环境变量读取! database: 'db_exp', waitForConnections: true, connectionLimit: 10, queueLimit: 0 }); try { // 调用存储过程,参数自动绑定,杜绝SQL注入 const [result] = await pool.execute( 'CALL transfer_funds(?, ?, ?)', ['ACC001', 'ACC002', 500.00] ); console.log('Transfer successful:', result); } catch (error) { // 精确捕获存储过程抛出的自定义异常 if (error.sqlState === '45000') { console.error('Business error:', error.message); // 输出 'Insufficient funds' } else { console.error('Database error:', error); } } finally { await pool.end(); // 必须显式关闭连接池 } }这里有几个极易被忽略但至关重要的实操细节:
密码硬编码风险:代码中
password: 'your_password'是占位符,绝不可直接填写真实密码。正确做法是创建.env文件(需npm install dotenv),在package.json的scripts中添加"start": "node -r dotenv/config embededSQL.js dotenv_config_path=.env",然后在.env中写DB_PASSWORD=your_real_password。这是生产环境的安全基线,实验阶段就养成习惯。参数绑定语法:
'CALL transfer_funds(?, ?, ?)'中的?是占位符,pool.execute()自动将其替换为安全转义后的值。若写成'CALL transfer_funds(\'' + fromAcc + '\', ...)',就是典型的 SQL 注入漏洞。实验材料特意用?语法,就是在你动手前就植入安全编码基因。连接池生命周期管理:
pool.end()必须放在finally块中。我曾遇到学生因忘记此步,导致第二次运行时报错Error: Connection pool has been closed。这不是代码错误,而是对资源管理模型的理解缺失——连接池是昂贵资源,必须显式释放。
3.3 数据初始化与清理脚本:让每次实验都“干净如初”
insert.js和truncateAndDelete.js是实验可重复性的基石。它们的设计哲学是:“数据不是静态的,而是实验的活体器官”。
insert.js的关键逻辑在于约束感知的数据生成:
// insert.js 片段:为 student 表生成数据 const generateStudentData = () => { const names = ['张三', '李四', '王五', '赵六']; const departments = ['CS', 'EE', 'ME', 'CE']; return names.map((name, i) => ({ student_id: `2021${String(i+1).padStart(3, '0')}`, // 保证主键唯一且有序 name, department: departments[i % departments.length], enrollment_date: getRandomDate('2021-09-01', '2021-09-30'), // 符合入学时间范围 email: `${name.toLowerCase()}@bit.edu.cn` // 符合邮箱格式约束 })); };它不生成随机字符串,而是根据3.sql中定义的student表结构(student_id CHAR(10) PRIMARY KEY,email VARCHAR(50) NOT NULL)生成合规数据。这意味着,当你修改3.sql的email字段长度为VARCHAR(30)后,insert.js会立即报错,迫使你同步更新数据生成逻辑——这就是建模一致性训练。
truncateAndDelete.js的选择更有讲究:
// truncateAndDelete.js 片段 const truncateTables = async (connection) => { // 使用 TRUNCATE 而非 DELETE await connection.query('TRUNCATE TABLE student'); await connection.query('TRUNCATE TABLE grade'); await connection.query('TRUNCATE TABLE accounts'); // 重置自增ID,确保实验可重现 await connection.query('ALTER TABLE student AUTO_INCREMENT = 1'); };TRUNCATE比DELETE FROM快 10 倍以上,且会重置AUTO_INCREMENT计数器。这对7.sql的事务实验至关重要:如果用DELETE,多次运行后accounts表的account_id会变成ACC001,ACC002,ACC003…ACC010,而TRUNCATE后永远是ACC001,ACC002,ACC003。这种确定性,是调试并发问题的前提。
4. 实操全流程与环境配置:从零开始的完整链路
4.1 环境准备:MySQL 与 Node.js 的精准版本匹配
实验材料默认适配MySQL 8.0+和Node.js 16.x+。这两个版本的选择绝非偶然:
MySQL 8.0引入了
caching_sha2_password默认认证插件,而mysql2驱动对此支持完善。若你使用 MySQL 5.7,需手动修改 root 用户认证方式:ALTER USER 'root'@'localhost' IDENTIFIED WITH mysql_native_password BY 'your_password';,否则embededSQL.js连接时会报错ER_NOT_SUPPORTED_AUTH_MODE。Node.js 16.x是首个 LTS 版本全面支持
fetch()API 和AbortController的版本,虽然本实验未直接使用,但它确保了mysql2的 Promise 支持稳定。低于 14.x 的版本可能因Promise.allSettled()缺失导致某些批量操作异常。
安装步骤(以 macOS 为例,Windows/Linux 类似):
- 安装 MySQL:
```bash
# 使用 Homebrew(推荐)
brew install mysql
brew services start mysql
# 初始化 root 密码(首次运行)
mysql_secure_installation
```
安装 Node.js:
bash # 使用 nvm 管理多版本(强烈推荐) curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.7/install.sh | bash source ~/.zshrc # 或 ~/.bash_profile nvm install 16.20.2 nvm use 16.20.2创建实验数据库:
```bash
mysql -u root -pCREATE DATABASE db_exp CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
USE db_exp;
SOURCE /path/to/3.sql; – 先执行建表脚本
EXIT;
```
注意:
SOURCE命令只能在 MySQL 客户端内使用,不能在 shell 中直接执行。很多学生卡在这一步,误以为mysql -u root -p < 3.sql就能导入,结果因字符集问题导致中文乱码或建表失败。
4.2 项目初始化与嵌入式SQL运行
进入项目根目录后,执行以下命令:
# 1. 安装依赖(package.json 已声明 mysql2 和 dotenv) npm install # 2. 创建 .env 文件(关键!) echo "DB_HOST=localhost" > .env echo "DB_USER=root" >> .env echo "DB_PASSWORD=your_mysql_root_password" >> .env echo "DB_NAME=db_exp" >> .env # 3. 运行嵌入式SQL示例(以实验7为例) node embededSQL.js此时,控制台应输出类似:
Transfer successful: [ { fieldCount: 0, affectedRows: 1, insertId: 0, info: '', serverStatus: 2, warningStatus: 0 } ]若报错Error: Cannot find module 'mysql2',说明npm install未成功,检查网络或执行npm config set registry https://registry.npmjs.org/切换源;若报错Access denied for user 'root'@'localhost',确认.env中密码与 MySQL 实际密码一致,且 MySQL 服务正在运行(brew services list | grep mysql)。
4.3 SQL脚本执行规范:避免“运行即结束”的误区
执行.sql文件时,必须使用 MySQL 客户端交互式执行,而非 shell 重定向。原因如下:
| 执行方式 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|
mysql -u root -p db_exp < 4.1.sql | 快速批量执行 | 错误定位困难(只显示第几行错),无法交互调试 | 生产环境部署 |
MySQL 客户端内SOURCE 4.1.sql | 错误精确到行号,可逐行执行、查看中间结果、修改后重试 | 需手动进入客户端 | 教学实验(本项目强制要求) |
正确流程:
mysql -u root -p db_exp Enter password: ***** mysql> SOURCE /path/to/4.1.sql; -- 若某行报错,如 ERROR 1054 (42S22): Unknown column 'xxx' in 'field list' -- 此时可直接编辑 4.1.sql 文件,修正后再次 SOURCE mysql> SOURCE /path/to/4.1.sql;实操心得:我在批改报告时发现,凡是在 shell 中重定向执行的学生,其报告中的“现象记录”栏位几乎全是“执行成功”或“执行失败”,缺乏具体错误信息。而使用客户端 SOURCE 的学生,报告中详细记录了
ERROR 1062 (23000): Duplicate entry '2021001' for key 'PRIMARY',并分析出“因未清空表导致主键冲突”。这就是执行方式对学习深度的决定性影响。
5. 常见问题与排查技巧实录:那些没人告诉你的“坑”
5.1 经典问题速查表
| 问题现象 | 可能原因 | 排查命令/步骤 | 解决方案 |
|---|---|---|---|
embededSQL.js运行报错ER_NOT_SUPPORTED_AUTH_MODE | MySQL 8.0 默认认证插件不兼容 | mysql -u root -p -e "SELECT plugin FROM mysql.user WHERE User='root';" | 执行ALTER USER 'root'@'localhost' IDENTIFIED WITH mysql_native_password BY 'your_password'; FLUSH PRIVILEGES; |
4.6.sql中UPDATE view_name无报错,但数据未变 | 视图定义中使用了DISTINCT或GROUP BY,MySQL 允许“静默失败” | SHOW CREATE VIEW student_grade_summary; | 查阅 MySQL 文档 “Updatable Views” 章节,确认哪些结构导致视图不可更新 |
7.sql存储过程中SIGNAL未被捕获,程序直接崩溃 | embededSQL.js中catch块未检查error.sqlState | 在catch块中添加console.log('Full error:', error); | 修改catch逻辑,增加if (error.sqlState === '45000')分支处理业务异常 |
insert.js执行后student表有数据,但grade表为空 | insert.js中generateGradeData()函数未被调用 | 检查insert.js末尾是否有await insertGrades(connection); | 确保所有insertXXX()函数都在main()中被调用 |
README.md提示“运行 npm start”,但 package.json 中无此 script | package.json的scripts字段被意外修改 | cat package.json \| grep "start" | 手动添加"start": "node embededSQL.js"到scripts对象中 |
5.2 独家避坑技巧:来自十年助教的血泪经验
技巧一:用mysql --verbose模式捕捉隐式行为
当 SQL 脚本执行结果与预期不符时(如SELECT * FROM student返回空,但insert.js明明插入了数据),不要急于重装 MySQL。执行:
mysql -u root -p --verbose db_exp mysql> SOURCE 3.sql; mysql> SOURCE insert.js; -- 注意:此处 insert.js 是 SQL 格式,非 JS!--verbose参数会让 MySQL 输出每条语句的执行计划和警告,常能发现Warning: Data truncated for column 'email' at row 1这类静默截断,根源往往是insert.js生成的邮箱超长,而3.sql中email VARCHAR(30)长度不足。
技巧二:truncateAndDelete.js的“双保险”执行法
为确保环境绝对干净,我要求学生每次实验前执行两遍truncateAndDelete.js:
node truncateAndDelete.js node truncateAndDelete.js # 第二遍确认无残留为什么?因为某些存储过程或触发器可能在TRUNCATE后仍保留元数据。第二遍执行能暴露这类隐藏状态,是检测数据库“健康度”的低成本方法。
技巧三:实验报告中的“错误截图”替代方案
很多学生喜欢截图报错信息,但截图无法复制错误码。我的建议是:在 MySQL 客户端中,用pager cat > error.log命令将后续所有输出重定向到文件:
mysql> pager cat > error.log mysql> SOURCE 7.sql; mysql> nopager然后将error.log内容粘贴到报告中。这样既保留完整上下文,又方便搜索关键词(如ERROR 1213)。
技巧四:Node.js 环境的“进程快照”调试法
当embededSQL.js卡死无响应时,不是重启电脑,而是用系统工具抓取进程快照:
# macOS lsof -i :3306 # 查看 MySQL 端口占用 ps aux \| grep node # 查看 node 进程 PID kill -USR1 <PID> # 发送信号,生成堆栈快照(需 Node.js 启动时加 --inspect)这能快速定位是数据库连接阻塞,还是 JavaScript 代码死循环。
6. 实验报告撰写指南:如何把“做实验”变成“真掌握”
6.1 报告结构的深层逻辑
双格式报告模板的每一部分,都对应一个认知能力维度:
【实验目标】→目标拆解能力:要求用“学生能……”句式描述,如“学生能独立编写包含 GROUP BY 和 HAVING 的聚合查询”,而非“学习 GROUP BY 语法”。这迫使你将模糊的“学知识”转化为可衡量的“练技能”。
【实验步骤】→过程抽象能力:禁止写“打开 MySQL 客户端”,而要写“启动 MySQL 8.0 客户端,连接至 db_exp 数据库”。前者是操作,后者是环境建模。
【现象与分析】→证据推理能力:必须包含三要素:现象(
ERROR 1062)、依据(3.sql中student_id定义为PRIMARY KEY)、推论(“主键冲突表明 insert.js 未清空表或生成了重复 ID”)。【思考题】→迁移应用能力:如“若将
transfer_funds过程改为支持多币种,需修改哪些部分?为什么?” 这题的答案不在脚本里,而在《数据库系统概念》第7章“分布式事务”中。
6.2 高分报告的三个特征
错误记录的颗粒度:优秀报告会记录
4.1.sql中第 17 行JOIN grade g ON s.student_id = g.stu_id的stu_id字段名错误(正确应为student_id),并注明“MySQL 报错Unknown column 'g.stu_id',对照3.sql的grade表定义确认字段名”。修正动作的可验证性:不写“已修正字段名”,而写“将
4.1.sql第 17 行改为JOIN grade g ON s.student_id = g.student_id,重新 SOURCE 后SELECT查询返回 12 行结果,与预期一致”。延伸思考的落地性:不泛泛而谈“视图提高安全性”,而写“若为教务系统管理员创建
view_teacher_salary,应排除salary字段,仅保留teacher_id,name,department,并授予SELECT权限,这样普通教师无法看到薪资数据”。
最后分享一个小技巧:我在批改时,会随机抽取报告中一个错误分析,反向执行其修正方案。如果修正后仍报错,说明分析有误;如果修正后成功,但学生未记录新结果,则扣分。这倒逼学生每一次“分析-修正-验证”都形成闭环。真正的数据库能力,就生长在这种闭环里。
本文还有配套的精品资源,点击获取
简介:北京理工大学《数据库原理与设计》课程配套上机实验资源包,含4个核心SQL实验脚本(3.sql、4.1.sql、4.6.sql、7.sql),覆盖建表、查询、视图、存储过程、事务控制等典型操作;提供基于Node.js的嵌入式SQL实现(embededSQL.js),配合insert.js和truncateAndDelete.js完成数据初始化与清理;附带Word和PDF双格式实验报告模板,结构清晰、内容规范,可直接填写使用;包含package.和package-lock.,支持本地npm install后直接运行嵌入式示例;所有文件按实验模块组织,README.md详细说明每个实验目标、执行步骤、环境要求(MySQL兼容数据库+Node.js)及常见问题提示,适合学生边学边练,快速掌握关系数据库建模、SQL编写与应用集成能力。
本文还有配套的精品资源,点击获取