尧图网络 高端网站定制 · 原创设计
免费咨询热线
400-888-6620
免费获取方案
C/C++与MySQL图书管理系统设计与实现:从表结构到事务处理
又到了课程设计扎堆交作业的时候不知道你有没有发现图书管理系统几乎是数据库课的“保留题目”。我第一次拿到这个题目是在数据库原理的下半学期要求说得很直接数据必须用 MySQL 存界面和逻辑用 C/C 写。一开始我也觉得“不就是增删改查嘛”等真正动手才发现表结构怎么设计、连接 API 怎么封装、借书还书怎么保证数据不错乱、编译环境怎么配置每一个环节都有值得琢磨的地方。这篇文章就围绕“MySQL C/C 实现图书管理系统”这个组合把完整的设计思路、表结构、工程分层、核心业务代码、编译踩坑和调试经验一次讲清楚。先亮个底这不是一个花哨的项目但它是把数据库原理和 C/C 两门课串起来最直接的训练场。你要是正在被这个题目折磨或者想找一个能放进简历的课程设计这篇可以当一份完整参考路线看。1. 为什么是C/C配MySQL选型背后的真实考量1.1 这个组合到底解决了什么问题图书管理系统的数据模型很典型读者、图书、借阅记录天然就是一对多关系。用关系型数据库存储这些数据再用 C/C 写一个控制台客户端通过 MySQL 官方提供的 C API 和服务器交互能让你把大学里两门核心课的知识完整落在同一个项目里。相比 Java 那一整套框架C/C 方案更“裸”也更考验基础。你要自己管理连接、执行 SQL、处理结果集、释放内存。这个过程中理解的连接生命周期、事务边界、SQL 语句怎么被服务器执行都是后面做其他后端开发时随时要用的底层知识。我做这个项目时最直接的感受是数据库课上学的那套“范式、事务、索引”如果不在真实代码里用一次永远只是背概念。而图书管理系统正好把这些概念全部逼着你用一遍。1.2 和其他方案差在哪很多同学会犹豫要不要换别的技术栈这里我把自己对比过的方案列出来方案优点缺点适合人群C/C MySQL底层链路清晰能真实理解 SQL 执行和 API 调用开发量略大编译环境要折腾学过 C/C 和数据库原理的学生Java JDBC生态成熟面向对象封装好需要额外理解框架工程体积重系统学完 JavaWeb 的同学Python SQLite写起来最快代码量小SQLite 是嵌入式数据库没有独立数据库服务只想快速跑通演示纯文件存储不依赖数据库完全偏离题目要求数据一致性难保证不推荐不是说 Java/Python 不好而是回到题目本身老师想看的是你会不会用数据库解决实际问题。C/C MySQL 这套组合把“客户端/服务端结构”“数据库连接”“SQL 设计与事务处理”全部显式地暴露给你项目做完你能说清楚每一行关键代码在干什么。1.3 动手前需要垫的三块基础如果你基础偏弱先把这三件事过一遍再动工会顺畅很多关系型数据库核心概念表、主键、外键、事务、索引。不要求多深但要知道这些工具各自解决什么问题。MySQL C API 的基本用法mysql_init、mysql_real_connect、mysql_query、mysql_store_result、mysql_fetch_row、mysql_free_result这是整个项目的调用主线。C 封装思想把连接管理和数据库访问包成类避免每个功能都裸写一遍。这三块准备到位后剩下的就是把业务需求一步步翻译成表格和 SQL。2. 数据建模先行四张表怎么定、边界卡在哪2.1 四张核心表的字段设计很多初学者上手就写代码结果写到一半发现缺字段、表关系理不清又回来改表结构非常浪费。正确顺序是先建模再写功能。我的做法是四张表图书表、读者表、借阅表、管理员表。图书表book字段类型说明book_idINT 自增主键图书唯一标识isbnVARCHAR(20) 唯一ISBN 编号一本书的不同副本共享titleVARCHAR(100)书名authorVARCHAR(50)作者publisherVARCHAR(80)出版社categoryVARCHAR(30)分类total_countINT总册数available_countINT当前可借册数shelf_locationVARCHAR(50)馆藏位置create_timeDATETIME 默认当前时间入库时间这里有个关键设计我要单独解释available_count 为什么单独存一个字段而不是每次对借阅明细做 SUM 统计。原因是高频查询场景是“这本书还剩几本”如果每次都去借阅表聚合数据量一大性能就明显下降。单独维护一个冗余字段借书时减一、还书时加一查询直接读字段这是典型的“冗余换性能”思路课程设计里用得上面试也常问。读者表reader字段类型说明reader_idINT 自增主键读者唯一标识reader_noVARCHAR(20) 唯一读者证号nameVARCHAR(50)姓名phoneVARCHAR(20)联系电话statusTINYINT 默认 11 正常 0 冻结max_borrowINT 默认 5最大可借数量register_dateDATETIME 默认当前时间注册日期max_borrow 放在读者表而不是写死在代码里这样以后要给不同等级读者开放不同额度直接改字段就行不用动逻辑。借阅表borrow字段类型说明borrow_idINT 自增主键借阅记录唯一标识reader_idINT 外键读者book_idINT 外键图书borrow_dateDATETIME借出时间due_dateDATETIME应还时间return_dateDATETIME NULL实际归还时间statusTINYINT 默认 11 借出 2 已归还 3 逾期归还fineDECIMAL(6,2) 默认 0逾期费用借阅表是整个系统的核心流转表。注意一个重要原则借阅记录是流水还书后不能删除记录只改状态。这样保留历史借阅轨迹后面要做借阅排行榜、罚款统计、图书热门度分析都有数据支撑。管理员表admin字段类型说明admin_idINT 自增主键管理员标识usernameVARCHAR(30) 唯一登录名password_hashVARCHAR(64)密码哈希值不存明文2.2 外键、索引与约束怎么取舍外键在课程设计阶段我建议加上不要嫌它麻烦。数据库层面保证不能插入一个不存在的读者或图书比你在应用层写一堆判断要可靠得多。索引方面借阅表是查询压力最大的表。实际项目里主要两种查询查“某读者当前借了哪些书”、查“某本书的借阅历史”。所以我在 borrow 表建立了 (reader_id, status) 联合索引以及单独的 book_id 索引。图书表的 isbn 和读者表的 reader_no 本身有唯一约束查询时会自动走唯一索引不需要额外处理。还有一个小提醒建表时给 book 表加 CHECK 约束保证 available_count 不小于 0 且不超过 total_count。MySQL 8.0 之前 CHECK 约束不强制但我建议还是写上配合应用层校验双保险。2.3 建库建表 SQL 参考下面这组 SQL 是我调过一遍后可以直接落地的版本字符集统一用 utf8mb4避免中文乱码DROP DATABASE IF EXISTS library_db; CREATE DATABASE library_db DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci; USE library_db; CREATE TABLE book ( book_id INT AUTO_INCREMENT PRIMARY KEY, isbn VARCHAR(20) NOT NULL UNIQUE, title VARCHAR(100) NOT NULL, author VARCHAR(50) NOT NULL, publisher VARCHAR(80) DEFAULT NULL, category VARCHAR(30) DEFAULT NULL, total_count INT NOT NULL DEFAULT 1, available_count INT NOT NULL DEFAULT 1, shelf_location VARCHAR(50) DEFAULT NULL, create_time DATETIME DEFAULT CURRENT_TIMESTAMP, CHECK (available_count 0), CHECK (available_count total_count) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; CREATE TABLE reader ( reader_id INT AUTO_INCREMENT PRIMARY KEY, reader_no VARCHAR(20) NOT NULL UNIQUE, name VARCHAR(50) NOT NULL, phone VARCHAR(20) DEFAULT NULL, status TINYINT NOT NULL DEFAULT 1, max_borrow INT NOT NULL DEFAULT 5, register_date DATETIME DEFAULT CURRENT_TIMESTAMP ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; CREATE TABLE borrow ( borrow_id INT AUTO_INCREMENT PRIMARY KEY, reader_id INT NOT NULL, book_id INT NOT NULL, borrow_date DATETIME NOT NULL, due_date DATETIME NOT NULL, return_date DATETIME DEFAULT NULL, status TINYINT NOT NULL DEFAULT 1, fine DECIMAL(6,2) DEFAULT 0.00, FOREIGN KEY (reader_id) REFERENCES reader(reader_id), FOREIGN KEY (book_id) REFERENCES book(book_id), INDEX idx_reader_status (reader_id, status), INDEX idx_book_id (book_id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; CREATE TABLE admin ( admin_id INT AUTO_INCREMENT PRIMARY KEY, username VARCHAR(30) NOT NULL UNIQUE, password_hash VARCHAR(64) NOT NULL ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;导入方式mysql -uroot -p library.sql搞定后直接进库验证一下表结构。注意这里所有表都指定了 InnoDB原因后面会讲——MyISAM 不支持事务借书流程会出大问题。3. 工程代码怎么组织才不烂尾3.1 目录与模块划分课程设计虽然只是一个控制台程序但如果所有代码堆在 main.cpp 里写到后面你会心态爆炸。我用的是简单三层组织连接管理、DAO 数据访问、界面主逻辑。library_system/ ├── include/ │ ├── db.h │ ├── book_dao.h │ ├── reader_dao.h │ └── borrow_dao.h ├── src/ │ ├── db.cpp │ ├── book_dao.cpp │ ├── reader_dao.cpp │ └── borrow_dao.cpp ├── sql/ │ └── library.sql ├── main.cpp └── Makefilemain.cpp 只负责菜单循环和基本的输入输出所有 SQL 逻辑都收进 DAO 层。这样以后想把控制台换成 Qt 界面或者改成一个简单的网页后端DAO 层和数据库连接层不需要大改。我实际改过一次深有体会。3.2 连接管理用类把 MYSQL 资源的生命周期管住直接用 C API 时最容易犯的错误是某个分支提前 return忘了调用 mysql_close连接泄漏。我这个项目里用一个 MyDB 类把连接生命周期管起来析构函数保证释放// include/db.h #pragma once #include mysql/mysql.h #include string class MyDB { public: MyDB() : conn_(nullptr) {} ~MyDB() { close(); } bool connect(const std::string host, const std::string user, const std::string pwd, const std::string db, unsigned int port 3306); void close(); bool execute(const std::string sql); std::string escape(const std::string input); MYSQL* getConn() { return conn_; } private: MYSQL* conn_; };// src/db.cpp #include db.h #include cstdio bool MyDB::connect(const std::string host, const std::string user, const std::string pwd, const std::string db, unsigned int port) { conn_ mysql_init(nullptr); if (conn_ nullptr) return false; mysql_options(conn_, MYSQL_SET_CHARSET_NAME, utf8mb4); if (mysql_real_connect(conn_, host.c_str(), user.c_str(), pwd.c_str(), db.c_str(), port, nullptr, 0) nullptr) { fprintf(stderr, MySQL 连接失败: %s\n, mysql_error(conn_)); close(); return false; } // 统一使用 utf8mb4避免中文乱码 execute(SET NAMES utf8mb4); return true; } void MyDB::close() { if (conn_) { mysql_close(conn_); conn_ nullptr; } } bool MyDB::execute(const std::string sql) { if (mysql_query(conn_, sql.c_str()) ! 0) { fprintf(stderr, SQL 执行失败: %s\nSQL: %s\n, mysql_error(conn_), sql.c_str()); return false; } return true; } std::string MyDB::escape(const std::string input) { if (input.empty()) return input; std::string out; out.resize(input.size() * 2 1); unsigned long len mysql_real_escape_string( conn_, out[0], input.c_str(), input.size()); out.resize(len); return out; }这里有两个细节值得说。第一连接成功后立刻执行SET NAMES utf8mb4这一步不做中文乱码几乎是必然的。第二连接对象不要跨线程共享MySQL 的 MYSQL* 指针不是线程安全的。课程设计一般是单线程菜单但面试问到多线程架构时这个点很重要。3.3 DAO 层把 SQL 从业务逻辑里抽出来以图书查询为例我封装一个 BookDAO外部调用者只传关键词不接触任何 SQLbool BookDAO::queryByTitle(MyDB db, const std::string keyword, std::vectorBook out) { std::string esc db.escape(keyword); char sql[4096]; snprintf(sql, sizeof(sql), SELECT book_id, isbn, title, author, publisher, category, total_count, available_count, shelf_location FROM book WHERE title LIKE %%%s%% ORDER BY book_id LIMIT 50, esc.c_str()); if (mysql_query(db.getConn(), sql) ! 0) { fprintf(stderr, 查询图书失败: %s\n, mysql_error(db.getConn())); return false; } MYSQL_RES* res mysql_store_result(db.getConn()); if (res nullptr) return false; MYSQL_ROW row; while ((row mysql_fetch_row(res)) ! nullptr) { Book b; b.book_id atoi(row[0]); b.isbn row[1] ? row[1] : ; b.title row[2] ? row[2] : ; b.author row[3] ? row[3] : ; b.publisher row[4] ? row[4] : ; b.category row[5] ? row[5] : ; b.total_count atoi(row[6]); b.available_count atoi(row[7]); b.shelf_location row[8] ? row[8] : ; out.push_back(b); } mysql_free_result(res); return true; }三个重点提醒用户输入必须转义。我调用了 db.escape内部是 mysql_real_escape_string。如果你直接把用户输入的 keyword 拼进 SQL遇到书名里带单引号的内容轻则查询报错重则被构造出 SQL 注入语句。字段可能为 NULL。mysql_fetch_row 返回的是 char* 数组NULL 字段对应 nullptr转 string 之前先判断一下不然程序直接崩。结果集用完记得 mysql_free_result。这个函数我见过太多人忘写内存泄漏在课程设计里短时间看不出但养成习惯后面对更大项目就不会踩。4. 核心功能实现细节借书还书的完整链路4.1 图书查询从模糊匹配到结果解析图书查询是使用频率最高的功能。模糊匹配用LIKE %keyword%但关键词里的特殊字符必须先转义这个前面已经说过了。实际操作时我建议加一个分页逻辑。控制台程序虽然不像网页一样需要真正分页但数据量一大一次查出几百条堆在屏幕上根本没法看。简单做法SELECT book_id, title, author, available_count FROM book WHERE title LIKE %xx% ORDER BY book_id LIMIT 10 OFFSET 0;每页显示 10 条按回车加载下一页。解析结果集时先用 mysql_num_rows 判断是不是空结果再循环 mysql_fetch_row。如果返回空结果不要直接访问 row[0]会越界。4.2 借书状态检查、库存扣减、借阅记录三步一体借书是整个项目里最有含金量的部分。这不仅是写几条 SQL 的事而是要把“多步操作必须同时成功”这个事务思想落实到位。借一本书要完成四件事确认读者存在且状态正常确认这本图书还有可借库存扣减图书 available_count插入一条借阅记录。这四步之间如果一步步裸写第三步成功但第四步失败时库存少了却没有借阅记录数据就永久不对了。正确做法是把它们包在一个事务里任何一步失败整体回滚。START TRANSACTION; -- 1. 检查读者状态应用层先查一次这里再查一次兜底 SELECT status FROM reader WHERE reader_id 10001; -- 如果 status ! 1 或查不到ROLLBACK -- 2. 锁定图书行防止并发超借 SELECT available_count FROM book WHERE book_id 1 FOR UPDATE; -- 如果 available_count 0ROLLBACK -- 3. 扣减库存必须检查影响行数 UPDATE book SET available_count available_count - 1 WHERE book_id 1 AND available_count 0; -- affected_rows 0 说明没扣到ROLLBACK -- 4. 写入借阅记录 INSERT INTO borrow(reader_id, book_id, borrow_date, due_date, status) VALUES(10001, 1, NOW(), DATE_ADD(NOW(), INTERVAL 30 DAY), 1); COMMIT;这里最容易被忽略的是FOR UPDATE。如果没有它两个管理员同时操作时可能两个人都查到 available_count 为 1然后都执行 UPDATE结果库存变成 -1。加锁以后第二个人必须等第一个人提交后才能继续而且 UPDATE 语句里的available_count 0条件会保证第二个人扣减失败从而触发回滚。C 侧配合的代码逻辑大概是if (!db.execute(START TRANSACTION)) return false; // 查读者状态并检查 if (!db.execute(SELECT status FROM reader WHERE reader_id ...)) { db.execute(ROLLBACK); return false; } // 解析结果状态不对就回滚 // 查库存并加锁 if (!db.execute(SELECT available_count FROM book WHERE book_id ... FOR UPDATE)) { db.execute(ROLLBACK); return false; } // 解析结果库存不足就回滚 // 扣减库存 if (!db.execute(UPDATE book SET available_count available_count - 1 WHERE book_id ... AND available_count 0)) { db.execute(ROLLBACK); return false; } // 检查 affected_rows if (mysql_affected_rows(db.getConn()) 0) { std::cout 库存不足本次借书取消 std::endl; db.execute(ROLLBACK); return false; } // 插入借阅记录 if (!db.execute(INSERT INTO borrow(...) VALUES(...))) { db.execute(ROLLBACK); return false; } db.execute(COMMIT);上面每一条 execute 失败后都做 ROLLBACK这是事务代码的基本素养。少写一个回滚下一次测试时数据就可能卡在中间状态。4.3 还书逾期计算与状态回写还书流程和借书反过来更新借阅记录状态、计算逾期费用、恢复图书库存。核心 SQLSTART TRANSACTION; UPDATE borrow SET return_date NOW(), status CASE WHEN NOW() due_date THEN 3 ELSE 2 END, fine CASE WHEN NOW() due_date THEN ROUND(TIMESTAMPDIFF(DAY, due_date, NOW()) * 0.5, 2) ELSE 0.00 END WHERE borrow_id 50001 AND status 1; -- 受影响行数为 0 说明这条借阅记录不存在或已归还回滚 UPDATE book SET available_count available_count 1 WHERE book_id 1; COMMIT;TIMESTAMPDIFF(DAY, due_date, NOW()) 计算逾期天数乘 0.5 元得到违约金。这里我把逾期状态和正常归还区分开status 分别用 2 和 3便于后面统计“有多少本书是逾期归还的”。借阅记录本身不删除只改状态和回填 return_date。这是“流水留痕”原则对账和统计全靠它。4.4 规则边缘情况重复借、超量借、冻结读者课程设计演示时最容易翻车的不是“正常路径”而是这些边界场景。我把测试时遇到过的情况列一下同一读者反复借同一本书如果这本书还没还应该禁止。可以在借书逻辑里加一句SELECT COUNT(*) FROM borrow WHERE reader_id? AND book_id? AND status IN (1,3)结果大于 0 就拒绝。超过最大借阅数借书前统计当前在借数量和 reader 表的 max_borrow 比较。这个字段设计初期放在读者表的好处这时候就体现了。读者被冻结status 为 0 的读者不允许借书。我一开始忘了这个判断测试时创建了一个冻结读者还能正常借书翻车翻得很难看。图书可借数为 0查询时要能查到这本书馆藏还在只是借书时提示无库存。还一本不存在的借阅记录UPDATE 的 WHERE 条件带上 status 1防止重复归还带来库存多扣。这些判断不需要很复杂的代码都是简单的 COUNT 或状态检查但能把系统从“能跑的 demo”变成“被挑不出毛病的作业”。5. 编译与运行让程序在 Linux 和 Windows 上都跑起来5.1 Linux 下 g 编译我这边的环境是 Ubuntu需要先装 MySQL 客户端开发库sudo apt install build-essential libmysqlclient-dev mysql-client装完以后用 mysql_config 获取头文件和库路径参数g main.cpp src/db.cpp src/book_dao.cpp src/reader_dao.cpp src/borrow_dao.cpp \ -Iinclude -o library $(mysql_config --cflags --libs)如果提示找不到 mysql_config先which mysql_config确认路径找不到就把参数写死比如-I/usr/include/mysql -L/usr/lib/mysql -lmysqlclient。编译通过后运行报error while loading shared libraries: libmysqlclient.so的话说明动态库路径没找到。临时解决export LD_LIBRARY_PATH/usr/lib/x86_64-linux-gnu ./library长期方案就是写完 Makefile 后把export LD_LIBRARY_PATH加进启动脚本或者使用 root 权限执行 ldconfig。课程设计阶段临时 export 就够。5.2 Windows 下 Visual Studio 的配置Windows 上我用的是 Visual Studio 创建控制台项目。需要的步骤安装 MySQL Connector/C注意版本和位数x64/x86项目属性 → VC 目录 → 包含目录加上 Connector 的 include 目录库目录加上 lib 目录链接器 → 输入 → 附加依赖项添加 libmysql.lib运行时把 libmysql.dll 放到 exe 同级目录新版 Connector 可能还依赖 libssl一并拷过来。最容易踩的坑是位数不一致项目是 x64 却链接了 x86 的库编译时会报 LNK2019提示找不到 mysql_init 这类函数符号。先检查平台是不是对齐了不要先怀疑代码。5.3 运行期必踩的乱码和字符集问题中文乱码是这类项目出现频率最高的运行期问题而且很多同学找半天不知道问题在哪。乱码本质是字符集不一致要检查三个地方建库建表字符集SQL 文件里已经指定了 utf8mb4如果你的库是默认 latin1中文一定乱连接字符集程序连接后执行SET NAMES utf8mb4这一步必须放在第一次查询之前控制台显示编码Windows 控制台默认中文代码页是 GBK在 main 函数开头setlocale(LC_ALL, )让 C 标准库按本地化方式输出多字节字符串。三个环节都对齐了中文才能正常显示。我见过一个同学的代码数据库和连接都正常就是控制台输出乱码最后发现 main 里没加 setlocale。排错时先按这个顺序查。6. 复盘调试过程中最有价值的几个问题6.1 数据库连接失败的排查顺序如果你遇到“连接失败”不要盯着代码猜按顺序查先命令行验证mysql -ulib_user -p -h127.0.0.1 -P3306 library_db能连说明网络和服务没问题问题在代码参数服务有没有启动systemctl status mysql没启动就先启动账号能不能从当前主机连MySQL 的账号是“用户名 主机”绑定的程序里要单独建账号并授权CREATE USER lib_userlocalhost IDENTIFIED BY 你的密码; GRANT ALL ON library_db.* TO lib_userlocalhost; FLUSH PRIVILEGES;代码参数别传反mysql_real_connect 的参数顺序是 host、user、password、dbname、port我曾经把 dbname 和 user 传反查了半小时才醒悟端口默认是 3306如果你的 MySQL 改过端口代码里要对应改。6.2 事务没生效的典型原因借书流程里我一开始用 MyISAM 引擎测试发现一个问题开始事务后两条 SQL 执行完其中一条报错时前面的数据并没有回滚。当时第一反应是代码写错了查了半天发现是表引擎不对。MyISAM 不支持事务它执行每条语句都是自动提交根本没有 ROLLBACK 这回事。解决办法所有业务表改成 InnoDB。你可以用SHOW TABLE STATUS查引擎建表 SQL 里指定 ENGINEInnoDB 最直接。另一个常见问题是“忘记 COMMIT”。有些同学用 START TRANSACTION 但事务结束后没有显式 COMMIT程序退出后数据看起来像丢了一部分。实际上数据在事务里只是没提交。写代码时每个事务分支结束后要想清楚这个分支到底该 COMMIT 还是 ROLLBACK。还有个容易被误会的点InnoDB 的事务回滚后自增主键不会回退。比如刚才插入到 100事务失败回滚下一条记录还是从 101 开始。这是正常行为不要当成 bug。6.3 做完这个项目之后还能往哪走如果时间和精力允许这个项目有几个不错的扩展方向也比较加分加预约功能一本书全部借出时读者可以预约书归还得下一顺位通知。需要新增预约表把借书流程变成“预约校验 借出闭环”把借书事务封装成存储过程客户端只调用CALL borrow_book(reader_id, book_id)进一步把业务逻辑和调用层解耦界面升级控制台换成 Qt 或简单网页前端DAO 层可以整体复用这是验证你分层设计好不好的机会定时任务提醒逾期每天扫描 borrow 表中 due_date 小于当前时间且 status 为借出的记录生成逾期列表备份策略mysqldump crontab 定期备份数据库养成运维意识。最后再分享一个我做项目时的习惯功能写完别急着交给自己列一份测试清单把借书、还书、重复借、超量借、冻结读者、无库存借书这些场景挨个跑一遍。很多演示翻车就是只测了正常路径。图书馆系统这类业务用户操作错得越离谱越能暴露设计上的短板。把表结构和核心事务流程扎扎实实走一遍这个课程设计不会差到哪里去。
RELATED

相关推荐

SQL窗口函数CUME_DIST与PERCENT_RANK分布分析详解

SQL窗口函数CUME_DIST与PERCENT_RANK分布分析详解

1. 窗口函数中的分布分析利器在数据分析领域,了解数据分布特征往往比单纯计算聚合值更有价值。SQL窗口函数中的CUME_DIST()和PERCENT_RANK()就是专门用于分布分析的两个重要工具。虽然它们都返回0到1之间的值,但计算逻辑和应用场景存在本质区别。CUME_DI…

📅 2026/10/24 8:31:06
Python+Django构建租房管理系统的实践与优化

Python+Django构建租房管理系统的实践与优化

1. 项目概述:Python租房管理系统的核心价值这个Python毕业设计项目本质上是一个典型的CRUD管理系统,但特别选择了租房这个垂直领域作为切入点。我选择开发这个系统是因为发现市面上大多数教学用的管理系统都集中在学生成绩、图书管理等传统场景&#xff…

📅 2026/10/24 8:26:06
Deskflow模仿学习

Deskflow模仿学习

我想要先实现一版: 我在电脑a(服务端)输入键盘上的a---z,1---9时,在电脑b上,我的客户端上,都可以接收到我输入的信息,并且打印出来。 电脑 A(服务端):监听键盘输入,比如…

📅 2026/10/24 8:26:06
MORE NEWS

更多资讯

📰

PDAL点云处理入门:零代码实现读滤写全流程

简介:本资源为PDAL点云数据抽象库官方完整文档(Release 2.2.0),面向三维地理信息、遥感测绘、自动驾驶及点云算法研发领域的工程师与科研人员,解决点云多格式读写、管道化处理、坐标转换与特征分析等核心工程问题。压缩…

📰

Python可复用工具模块包:100个开箱即用的工程化脚本

简介:这是一份面向Python初学者与进阶学习者的综合性源码实践合集,涵盖100个典型功能模块的完整实现,适用于算法练习、项目参考及教学演示等场景。资源包共2003个文件,主体为1751个.py源码文件,辅以133个说明性txt文档…

📰

C语言终端中文表格自动对齐:UTF-8显示宽度计算与渲染

简介:本资源是一份面向C语言初学者的终端对齐打印实战方案,聚焦解决含中文字符的字符串在Linux终端中难以整齐表格化显示的典型痛点,适用于Ubuntu等类Unix系统下的命令行开发与调试场景。压缩包共7个文件,包含核心源码文件&#x…

📰

WebSocket长连接实战:心跳检测、断线重连与房间广播

简介:面向 WebSocket 即时通信入门与跨语言联调场景的实例源码包,包含 HTML5 客户端与 Java 服务端两个可运行工程,适合前端、Java 后端初学者及需要快速验证长连接通信的开发者。客户端基于浏览器 WebSocket API,运行需 Tomcat 7…

📰

ADHD执行功能优化:能量流管理与三层系统搭建指南

1. 项目概述:这不是一个诊断标签,而是一份可操作的自我认知工具包“i-have-adhd”这个短语最近在多个内容平台高频出现,它既不是医学报告的抬头,也不是社交平台上的自嘲梗图配文,而是一类高度结构化、强实操导向的个人…

📰

文件系统绝对路径详解与跨平台处理技巧

1. 全文件名(绝对路径)的概念解析在文件系统中,全文件名(绝对路径)是一个基础但至关重要的概念。简单来说,它就像是一个文件的"身份证号码"——无论你在系统的哪个位置,通过这个完整的…

TODAY

今日更新

THIS WEEK

本周精选

THIS MONTH

本月热门

读完文章,想聊聊您的网站?

告诉我们您的行业与需求,资深顾问一对一梳理方案与报价,全程免费。

📞 💬