Delphi+SQL Server单车管理系统实战开发
简介本资源是一套基于Delphi与SQL Server开发的单车管理系统完整工程面向高校计算机专业学生及桌面应用初学者用于实践数据库应用开发、GUI界面设计与前后端数据交互等核心技能。项目覆盖用户注册/登录、单车租赁/归还、维修管理、骑行记录查询等典型业务模块融合VCL组件拖放开发、ADO数据库连接、SQL语句编写、DBGrid数据绑定及异常处理等关键技术点适合作为课程设计、毕业实训或Delphi进阶实验参考。压缩包含52个文件以9个.pas源码文件、9个.dfm界面文件、3个.sql数据库脚本及1个.exe可执行程序为主体辅以详细文档说明如SQL代码说明、页面功能解析、主代码注释总大小4.81MB。已有335人学习下载提供可直接运行的完整系统、结构清晰的模块化代码组织、配套数据库备份与配置说明便于快速理解分层逻辑并开展二次开发。1. 为什么用 Delphi 写单车管理系统不是“怀旧”而是工程选择很多人看到“Delphi”第一反应是“老技术”但当你需要快速交付一个带图形界面、强数据绑定、本地部署、无需运行时依赖的桌面端单车管理工具——比如高校后勤处要管200辆教学用自行车社区物业要登记300台共享电单车的维修/停放/责任人或者职校数据库课程设计要求“可运行可演示可答辩”——Delphi 依然是最省力的选项。它不是靠语法新潮取胜而是靠 VCL 组件天然支持拖拽式 UI 构建、TDataSet 系列对 SQL Server / SQLite 的开箱即用、DataModule 提供清晰的数据层隔离以及编译后单文件发布能力。尤其在 Windows 内网环境如学校机房、物业办公室不依赖 .NET Framework 或 Java JRE双击即用运维零成本。本篇不讲 Delphi 历史只聚焦如何用 Delphi 10.4 或 11兼容主流版本 SQL Server 2019或 2012/2016 DataModule 模式从零写出一个真实可用的单车管理系统——包含车辆登记、借还记录、状态查询、报表导出四个核心模块所有代码可复制粘贴、参数可调、错误可定位。2. 用 TADOConnection DataModule 搭建稳定的数据通道Delphi 中连接 SQL Server 不再推荐使用已淘汰的 BDE 或早期 ADOExpress而是采用原生TADOConnection配合TADOQuery/TADOTable这是当前 Delphi 社区维护最完善、兼容性最强的方案。关键不在“能连”而在“连得稳、断得明、错得清”。DataModule 是 Delphi 实现三层分离的基石它把数据库连接、查询逻辑、事务控制全部封装在独立单元中UI 窗体只负责展示和交互彻底避免窗体间数据耦合。2.1 创建 DataModule 并配置 TADOConnection新建一个 DataModuleFile → New → Other → Delphi Projects → Data Module命名为dmMain。在对象查看器Object Inspector中选中ADOConnection1设置以下关键属性属性名推荐值说明ConnectionStringProviderSQLOLEDB.1;Integrated SecuritySSPI;Persist Security InfoFalse;Initial CatalogBikeSystem;Data Source.\SQLEXPRESS使用 Windows 身份验证最安全若用 SQL 登录改为User IDsa;PasswordyourpassData Source根据实际 SQL Server 实例名调整如localhost\SQLEXPRESS、192.168.1.100LoginPromptFalse禁用弹窗登录框避免部署时卡住ConnectedTrue设计时即测试连接失败会报错提示注意SQL Server 必须启用 TCP/IP 协议并在 SQL Server Configuration Manager 中确认SQL Server (SQLEXPRESS)服务正在运行。若连接失败先用 SSMS 连接同一实例验证网络与权限。2.2 在 DataModule 中定义单车核心数据集添加TADOQuery组件命名为qBikes设置其Connection属性指向ADOConnection1SQL.Text输入建表语句并执行CREATE TABLE BikeInfo ( BikeID VARCHAR(10) PRIMARY KEY, Brand NVARCHAR(20) NOT NULL, Model NVARCHAR(30), PurchaseDate DATE, Status TINYINT DEFAULT 1, -- 1:可用, 2:维修中, 3:报废 Location NVARCHAR(50), Remark NVARCHAR(100) ); CREATE TABLE BorrowRecord ( RecordID INT IDENTITY(1,1) PRIMARY KEY, BikeID VARCHAR(10) NOT NULL, BorrowerName NVARCHAR(20) NOT NULL, BorrowTime DATETIME DEFAULT GETDATE(), ReturnTime DATETIME NULL, Operator NVARCHAR(20) );执行后在qBikes上右键 → “Execute” 运行建表。接着设置qBikes.SQL.Text为SELECT b.BikeID, b.Brand, b.Model, b.PurchaseDate, CASE b.Status WHEN 1 THEN 可用 WHEN 2 THEN 维修中 ELSE 报废 END AS StatusText, b.Location, b.Remark FROM BikeInfo b ORDER BY b.PurchaseDate DESC再添加TDataSource命名为dsBikes将其DataSet指向qBikes。这样DataModule 就完成了单车主表的读取封装。2.3 处理常见连接异常Cannot perform this operation on an open dataset该错误高频出现在未关闭数据集就执行Close或Open时。正确做法是所有TADOQuery操作前加保护判断。在dmMain单元中为qBikes添加方法procedure TdmMain.OpenBikeQuery; begin if not qBikes.Active then qBikes.Open else if qBikes.State in [dsEdit, dsInsert] then qBikes.Post; // 确保编辑状态已提交 end; procedure TdmMain.CloseBikeQuery; begin if qBikes.Active then qBikes.Close; end;并在窗体OnCreate中调用dmMain.OpenBikeQueryOnDestroy中调用dmMain.CloseBikeQuery。这比依赖AutoOpenTrue更可控。3. 用 DBGrid TDataSource 实现单车列表的增删改查VCL 的TDBGrid是 Delphi 数据显示的“瑞士军刀”但它不是万能的——直接绑定TADOQuery易导致编辑冲突。最佳实践是用TDataSource作中间层配合TADODataSet而非TADOQuery处理编辑再通过TDBGrid可视化呈现。3.1 配置可编辑的单车数据集在dmMain中删除qBikes新增TADODataSet命名为dsBikeEdit设置Connection→ADOConnection1CommandType→cmdTableCommandText→BikeInfoActive→True添加TDataSource命名为dsBikeSourceDataSet指向dsBikeEdit。此时dsBikeEdit支持Insert/Edit/Post/Cancel全流程操作。3.2 在主窗体中拖拽构建单车管理界面新建窗体frmBikeList从 Tool Palette 拖入TDBGridDataSource→dmMain.dsBikeSourceTButtonCaption 新增OnClick 事件在frmBikeList.OnCreate中procedure TfrmBikeList.FormCreate(Sender: TObject); begin dmMain.dsBikeEdit.Open; // 打开数据集 // 设置 DBGrid 列宽与标题 DBGrid1.Columns[0].Width : 80; // BikeID DBGrid1.Columns[1].Width : 100; // Brand DBGrid1.Columns[2].Width : 120; // Model DBGrid1.Columns[3].Width : 90; // PurchaseDate DBGrid1.Columns[4].Width : 80; // StatusText只读 DBGrid1.Columns[5].Width : 150; // Location end;3.3 实现新增单车逻辑避免空值与重复主键点击“新增”按钮时不能直接dsBikeEdit.Insert必须校验必填字段并拦截非法输入procedure TfrmBikeList.btnNewClick(Sender: TObject); var sID: string; begin sID : InputBox(新增单车, 请输入单车编号如B001:, ); if sID then Exit; // 检查是否已存在 dmMain.qCheckID.Close; dmMain.qCheckID.SQL.Text : SELECT COUNT(*) FROM BikeInfo WHERE BikeID :ID; dmMain.qCheckID.Parameters.ParamByName(ID).Value : sID; dmMain.qCheckID.Open; if dmMain.qCheckID.Fields[0].AsInteger 0 then begin ShowMessage(编号 sID 已存在); Exit; end; dmMain.dsBikeEdit.Insert; dmMain.dsBikeEdit.FieldByName(BikeID).AsString : sID; dmMain.dsBikeEdit.FieldByName(Brand).AsString : 未知品牌; dmMain.dsBikeEdit.FieldByName(PurchaseDate).AsDateTime : Date; dmMain.dsBikeEdit.FieldByName(Status).AsInteger : 1; dmMain.dsBikeEdit.Post; // 提交插入 end;提示qCheckID是 DataModule 中预置的TADOQuery仅用于校验不参与主数据流。这种“查重用 Query增删改用 DataSet”的分工是 Delphi 数据操作的黄金组合。3.4 删除与状态修改用 TActionList 统一管理为避免按钮事件散落创建TActionList命名为actBikeActions添加三个TActionactDeleteCaption 删除OnExecute → 调用DeleteCurrentBikeactRepairCaption 标记维修OnExecute →SetStatus(2)actAvailableCaption 设为可用OnExecute →SetStatus(1)核心删除方法procedure TfrmBikeList.DeleteCurrentBike; begin if Application.MessageBox(确定删除当前单车, 确认, MB_YESNO or MB_ICONQUESTION) ID_YES then begin try dmMain.dsBikeEdit.Delete; // 物理删除 ShowMessage(删除成功); except on E: Exception do ShowMessage(删除失败 E.Message); end; end; end;TActionList关联到工具栏或菜单实现 UI 与逻辑解耦。4. 用 TADOQuery 执行借还业务逻辑与跨表关联查询单车管理的核心价值不在“存”而在“用”——借车、还车、统计周转率。这些操作涉及多表写入、时间戳更新、状态联动必须用TADOQuery手动编写 SQL而非依赖TADODataSet的自动映射。4.1 借车操作插入记录 更新单车状态在dmMain中添加TADOQuery命名为qBorrowConnection指向ADOConnection1。借车逻辑需同时完成两件事向BorrowRecord插入新记录并将对应BikeInfo.Status改为 2维修中或保持 1可用中。但注意借车不改变单车状态只有“故障报修”才改状态借车只是登记行为。因此正确流程是procedure TdmMain.BorrowBike(const BikeID, Borrower: string); begin qBorrow.Close; qBorrow.SQL.Text : INSERT INTO BorrowRecord (BikeID, BorrowerName, Operator) VALUES (:BID, :BName, :OP); UPDATE BikeInfo SET Status 1 WHERE BikeID :BID AND Status IN (1,3); qBorrow.Parameters.ParamByName(BID).Value : BikeID; qBorrow.Parameters.ParamByName(BName).Value : Borrower; qBorrow.Parameters.ParamByName(OP).Value : GetCurrentUserName; // 自定义函数获取当前登录人 try qBorrow.ExecSQL; except on E: Exception do raise Exception.Create(借车失败 E.Message); end; end;注意ExecSQL用于无结果集的 INSERT/UPDATE/DELETEOpen用于 SELECT。混用会导致Cannot perform this operation...错误。4.2 还车操作更新归还时间 校验未借出状态还车不是简单 UPDATE必须确保该单车确实处于“已借出未归还”状态procedure TdmMain.ReturnBike(const BikeID: string); begin qReturn.Close; qReturn.SQL.Text : UPDATE BorrowRecord SET ReturnTime GETDATE() WHERE BikeID :BID AND ReturnTime IS NULL; IF ROWCOUNT 0 RAISERROR(该单车当前未被借出无法归还, 16, 1); qReturn.Parameters.ParamByName(BID).Value : BikeID; try qReturn.ExecSQL; except on E: Exception do raise Exception.Create(E.Message); end; end;SQL Server 的RAISERROR直接抛出异常Delphi 捕获后显示给用户比应用层判断更精准。4.3 查询“当前借出中”的单车LEFT JOIN 与 NULL 判断常用报表需求“列出所有正在被使用的单车及借用人”。这需要BikeInfoLEFT JOINBorrowRecord筛选ReturnTime IS NULLSELECT b.BikeID, b.Brand, b.Model, br.BorrowerName, br.BorrowTime FROM BikeInfo b LEFT JOIN BorrowRecord br ON b.BikeID br.BikeID AND br.ReturnTime IS NULL WHERE br.BorrowTime IS NOT NULL ORDER BY br.BorrowTime DESC在窗体中用TADOQuery执行此 SQL绑定到另一个TDBGrid即可实时展示“活跃借用车辆”。5. 解决 Delphi SQL Server 最常见的三类字符与连接问题Delphi 与 SQL Server 协作时90% 的线上故障集中在字符编码、连接超时、权限不足。这些问题不写代码只改配置就能解决。5.1 中文乱码不是 Delphi 问题是 SQL Server 排序规则与客户端声明不匹配delphi sqlite 亂碼热搜背后本质是字符集声明缺失。SQL Server 默认排序规则Chinese_PRC_CI_AS支持 UTF-16但 Delphi 的TADOConnection需显式声明在ConnectionString末尾追加;Charsetutf-8对较新驱动有效更可靠方案在 SQL Server 中为数据库设置默认排序规则ALTER DATABASE BikeSystem COLLATE Chinese_PRC_CI_AS;对于NVARCHAR字段Delphi 中必须用AsString而非AsString赋值且字符串字面量加前缀NINSERT INTO BikeInfo (Brand) VALUES (N永久);5.2 连接超时Login timeout expired的真实原因与修复默认连接超时仅15秒内网延迟或 SQL Server 启动慢时极易失败。解决方案分三层层级操作说明连接字符串追加;Connect Timeout30将超时延长至30秒SQL Server 配置SSMS → 服务器属性 → 连接 → “远程查询超时”设为 0不限制防止长查询中断Delphi 代码ADOConnection1.Connected : False; Sleep(1000); ADOConnection1.Connected : True;主动重试机制避免首次启动失败5.3 权限不足solidworks electrical 无法连接到 sql server类错误的复用排查法当Application cannot connect to SQL Server时按此顺序验证Windows 用户权限用当前 Windows 用户登录 SSMS能否访问BikeSystem数据库数据库用户映射在 SSMS 中展开Security → Logins右键你的登录名 → Properties → User Mapping → 勾选BikeSystem角色选db_datareader和db_datawriter防火墙放行Windows Defender Firewall with Advanced Security→ 入站规则 → 新建规则 → 端口 → TCP 1433 → 允许连接完成这三步95% 的连接拒绝问题消失。不要跳过第1步——很多问题根源是 Windows 用户本身没权限。6. 导出单车报表到 Excel不用第三方控件纯 ADO CSV 兼容方案Delphi 11/12 自带TExcelApplication需引用ComObj但依赖 Office 安装。更轻量、更可靠的方案是生成标准 CSV 文件用记事本或 Excel 直接打开且完全规避delphi 运行edgebrowser.navegate无反应等 COM 组件兼容问题。6.1 用 TStringList 生成 UTF-8 BOM CSV在dmMain中添加方法procedure TdmMain.ExportBikeToCSV(const FileName: string); var sl: TStringList; i: Integer; begin sl : TStringList.Create; try // 写入 BOM 头确保 Excel 正确识别 UTF-8 sl.Add(#$EF#$BB#$BF 编号,品牌,型号,购入日期,状态,位置,备注); qExport.Close; qExport.SQL.Text : SELECT BikeID, Brand, Model, CONVERT(VARCHAR, PurchaseDate, 120), CASE Status WHEN 1 THEN 可用 WHEN 2 THEN 维修中 ELSE 报废 END, Location, Remark FROM BikeInfo ORDER BY PurchaseDate DESC; qExport.Open; while not qExport.Eof do begin sl.Add(Format(%s,%s,%s,%s,%s,%s,%s, [qExport.FieldByName(BikeID).AsString, qExport.FieldByName(Brand).AsString, qExport.FieldByName(Model).AsString, qExport.FieldByName(PurchaseDate).AsString, qExport.FieldByName(Status).AsString, qExport.FieldByName(Location).AsString, qExport.FieldByName(Remark).AsString])); qExport.Next; end; sl.SaveToFile(FileName, TEncoding.UTF8); // 强制 UTF-8 编码 ShowMessage(导出完成 FileName); finally sl.Free; qExport.Close; end; end;6.2 在窗体中调用并触发系统默认程序打开procedure TfrmBikeList.btnExportClick(Sender: TObject); var SaveDialog: TSaveDialog; begin SaveDialog : TSaveDialog.Create(Self); try SaveDialog.Filter : CSV 文件 (*.csv)|*.csv; SaveDialog.DefaultExt : csv; if SaveDialog.Execute then dmMain.ExportBikeToCSV(SaveDialog.FileName); finally SaveDialog.Free; end; end;生成的 CSV 文件双击即用 Excel 打开中文不乱码列宽自适应无需安装额外组件。这才是生产环境真正“零依赖”的报表方案。本文还有配套的精品资源点击获取