资讯动态

停车场信息管理系统数据库设计:从需求分析到存储过程实现全流程

发布时间:2026/9/18 4:38:30 来源:尧图企业网站定制
简介恒达高停车场信息管理系统的数据库课程设计论文报告共55页、约18749字面向高校数据库课程设计或毕业设计场景。系统围绕停车场车位划分、车辆进出管理及收费、信息查询、系统管理四大需求展开并选用 SQL Server 2000 作为数据库平台重点介绍存储过程在计费计算、车位状态更新等业务逻辑中的应用。报告以完整论文结构呈现依次涵盖需求分析、系统功能模块设计、安全性与完整性要求、分流程图与总流程图、固定车位与自由车位业务流程图、数据字典、概念结构设计分/总 ER 图以及逻辑结构设计等核心环节可直接用于撰写课程设计报告、绘制数据库设计图表或梳理答辩思路。资源为单个 doc 文档大小仅 383KB便于下载和本地编辑目前已有 3467 人浏览学习适合需要快速搭建数据库课程设计文档框架的读者参考。1. 停车计费模型进入数据库恒达高系统的两类车位与全流程设计把两套计费规则塞进同一套数据库是停车场类管理系统中容易被低估的设计难点固定车位车辆包月进出、离场不收费却要维护车主档案和在位状态自由车位车辆按停留时长结算入口处还得随时响应还剩多少车位这种高并发查询。恒达高停车场信息管理系统的数据库课程设计论文恰恰把这两条业务线的完整建模过程整理成了一份55页的实操范本。从需求分析、数据字典、ER图到关系模式转换再到SQL Server下的建表、视图、索引和存储过程覆盖了关系型数据库课程设计中最常被追问的每一个环节。这份报告适合正在做数据库课程设计、需要对照完整设计流程的在校生也适合想看看业务规则如何映射为表结构和约束的开发者。2. 需求分析与数据字典从进出场业务流到四张核心表2.1 固定车位与自由车位的业务规则差异这份设计把需求分析的重点放在车位划分上而不是笼统地写系统需要管理车辆这一点值得先说清楚。固定车位也叫专用车位、内部车位对应包月或包年制使用者相对固定系统需要持久保存车辆和车主两方面的信息自由车位公用车位面向临时散客每辆车出场时按停车时长计费系统只需要保存当前谁停在这个车位上以及它什么时候进来的。两类车位的差异直接决定表设计的方向。如果一开始把两类车位合在一张表里用一个类型字段区分后面的查询和约束会变得很难写固定车位表必须携带车主信息、是否在位标记自由车位表更接近一张在场状态表车牌和进入时间填进去代表车位被占用出场后清掉。可以把两者的差异整理成一张对照表维度固定车位自由车位使用者固定车主绑定车牌临时散客不固定计费方式包月/包年出场不收费按时长计费出场结算核心数据车辆信息、车主信息、在位状态车牌号码、进入时间出场动作记录离场时间计算费用并写入记录空闲判断查是否在位标记在场记录行数反推这个划分不是论文作者的偏好而是停车场行业的通用做法商场、车站、机场类停车场自由车位比例高住宅小区和单位自用类停车场固定车位比例高。系统管理功能里允许设定和修改两类车位的数目说明这个比例应当作为可配置参数而不是写死在代码里。后面建表时会看到固定车位表和自由车位表各自独立正是为了支撑这种业务调整。2.2 进出场流程中的数据流数据流图是这套设计里连接业务和库表结构的桥梁。固定车进场路径是固定车作为数据源经过确认固定车位身份这个处理逻辑更新固定车位表的是否在位状态出场时不经过计费分支直接生成一条离场记录。自由车进场前要先查空闲车位数量没有空闲则提示等待或离开有空闲则占用一个自由车位出场时才进入计费流程按进入和离开时间差生成停车费用并写入进出记录表。这里有一个容易被忽略的设计点所有车辆不管是否收费进出场都写入同一张进出记录表用免费车辆这个逻辑字段区分。这样收费统计时直接对非免费记录做聚合而车辆轨迹查询不需要跨表拼接。数据字典中进出记录表包含车牌号码、进入时间、离开时间、停车费用、免费车辆五个字段就是这套思路的体现。如果按车辆类型拆成两张记录表后续指定车辆所有进出记录查询和按日收入统计都要做UNION得不偿失。2.3 数据字典与四张核心表资源里直接给出了四张核心表的字段、类型和宽度这部分完全可以当作建表前的地基。整理如下表名字段类型宽度固定车位表车位编号数值3固定车位表车牌号码字符12固定车位表车辆颜色字符6固定车位表车主姓名字符10固定车位表联系电话字符12固定车位表联系地址字符20固定车位表车辆照片通用4固定车位表是否在位逻辑1固定车位表进入时间日期时间8自由车位表车牌号码字符12自由车位表进入时间日期时间8进出记录表车牌号码字符12进出记录表进入时间日期时间8进出记录表离开时间日期时间8进出记录表停车费用数值6进出记录表免费车辆逻辑1工作人员表工作号字符12工作人员表姓名字符10工作人员表管理车号字符12几个值得注意的细节。车牌号码用CHAR(12)而不是VARCHAR这是早期SQL Server设计中的常见选择定长字符列在频繁更新时不会因长度变化触发页拆分比较开销更可控国内车牌加上汉字和特殊字符预留12个字符是够用的。车辆照片在SQL Server 2000中对应image类型文档标注4字节是因为该列存放的是OLE对象引用不是图片字节流本身实际项目里更推荐单独建照片附件表主表只存引用ID。自由车位表只有两个业务字段说明它被设计成在场车辆状态表没有为每个物理车位建字典行。这样设计的直接后果是空闲车位数量只能靠配置的总自由车位数减去当前在场记录数推算而不是对车位表做状态统计。数据量小时没问题如果停车场有几百个自由车位我一般会倾向为每个物理车位建一行数据加SpaceID和状态字段用PlateNo为NULL表达空闲这样入口显示和车位占用率统计都更直接。要确认实际建出来的库表和数据字典一致可以直接查系统视图SELECT TABLE_NAME, COLUMN_NAME, DATA_TYPE, CHARACTER_MAXIMUM_LENGTH FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME IN (FixedParkingSpace, FreeParkingSpace, AccessRecord, Staff) ORDER BY TABLE_NAME, ORDINAL_POSITION;这段SQL把已经建好的表结构从元数据里拉出来和设计文档逐字段比对。TABLE_NAME是表名COLUMN_NAME是列名CHARACTER_MAXIMUM_LENGTH对应字符类型的定义宽度数值型和日期时间型各自的位数从DATA_TYPE和NUMERIC_PRECISION里读取。答辩或自查时这是最快发现设计文档和实际库结构不一致的手段。INFORMATION_SCHEMA视图在SQL Server 2000到2019全系列通用比直接查sys.objects兼容性好得多。3. 关系模式转换与SQL Server建表主键、外键与车位状态约束3.1 ER图向关系模式的转换规则概念结构设计阶段产出的ER图不直接等于表结构它只刻画实体和联系。逻辑结构设计的任务是把ER图翻译成SQL Server能执行的关系模式核心规则就三条实体变成表实体的属性变成列一对一联系合并到任一侧表中用外键表达一对多联系在多的一侧加外键。本案例中车主和固定车位是一对一绑定所以不需要单独建车主表把车主姓名、联系电话、联系地址直接做进固定车位表固定车位和进出记录是一对多进出记录表里用PlateNo关联。判断一张表是否需要拆开标准是看它能否在自己的主键下自圆其说。进出记录表以记录ID为主键后车辆颜色、车主电话这类字段就不应该出现在里面因为它们依赖车牌号而不是记录ID。硬塞进去就破坏了第三范式一旦车辆被重新绑定到别的车位历史记录里的冗余字段就全部失真。答辩时被问为什么这张表只有这些字段回答到这个层面基本就是加分项。3.2 四张表的CREATE TABLE与约束关系模式确定后直接落到DDL。建表顺序有一个隐含要求先建被引用的父表再建引用方。这里四张表之间没有强外键依赖因为进出记录表关联的车牌既可能来自固定车位表也可能来自自由车位表无法用单一外键表达这是本系统的一个设计边界下文会展开。建表语句如下CREATE TABLE Staff ( StaffID CHAR(12) PRIMARY KEY, -- 工作号同时作为登录账号 StaffName VARCHAR(10) NOT NULL, -- 姓名 ManageSpace VARCHAR(12) -- 管理车位/通道编号 ); GO CREATE TABLE FixedParkingSpace ( SpaceID INT IDENTITY(1,1) PRIMARY KEY, -- 车位编号自增 PlateNo CHAR(12) NOT NULL UNIQUE, -- 车牌号码一个固定车位对应一辆车 CarColor VARCHAR(6), -- 车辆颜色 OwnerName VARCHAR(10) NOT NULL, -- 车主姓名 ContactPhone VARCHAR(12), -- 联系电话 ContactAddress VARCHAR(20), -- 联系地址 CarPhoto IMAGE NULL, -- 车辆照片OLE对象引用 IsOccupied BIT DEFAULT 0, -- 是否在位 EntryTime DATETIME NULL, -- 本次入场时间 ExitTime DATETIME NULL -- 上次离场时间 ); GO CREATE TABLE FreeParkingSpace ( SpaceID INT IDENTITY(1,1) PRIMARY KEY, -- 物理车位编号 PlateNo CHAR(12) NULL, -- NULL 表示该车位空闲 EntryTime DATETIME NULL -- 车辆入场时间 ); GO CREATE TABLE AccessRecord ( RecordID INT IDENTITY(1,1) PRIMARY KEY, -- 记录编号 PlateNo CHAR(12) NOT NULL, -- 车牌号码 EntryTime DATETIME NOT NULL, -- 进入时间 ExitTime DATETIME NULL, -- 离开时间未离场时为 NULL ParkingFee DECIMAL(6,2) DEFAULT 0, -- 停车费用 IsFree BIT DEFAULT 0 -- 是否免费车辆固定车为 1 ); GO几点参数说明SpaceID采用IDENTITY自增而不是应用层传入避免并发入场时两个收费口生成相同主键。课程设计里常见的错误是把车位编号设计成可手工输入的业务字段一旦调整车位布局历史记录里的编号就会错位。FixedParkingSpace.PlateNo加UNIQUE约束保证一个固定车位不会同时绑定两辆车这是实体完整性的直接体现。FreeParkingSpace.PlateNo允许NULL用NULL表达空闲。不建议用空字符串表达空闲CHAR类型里和NULL在查询条件和索引上的行为有差异统一用NULL更干净。AccessRecord的ParkingFee设置DEFAULT 0固定车辆出场时不需要显式传费用参数IsFree默认0出场存储过程会按车辆类型改写。3.3 视图与索引固定统计口径按查询模式建索引系统里最常见的两个查询场景入口显示屏实时显示自由车位空闲数管理端按日期统计收入。视图可以把这类统计口径固定下来让收费端程序不必理解车位表内部的NULL语义CREATE VIEW vw_FreeSpaceStats AS SELECT COUNT(*) AS TotalSpaces, SUM(CASE WHEN PlateNo IS NULL THEN 1 ELSE 0 END) AS AvailableCount, SUM(CASE WHEN PlateNo IS NOT NULL THEN 1 ELSE 0 END) AS OccupiedCount FROM FreeParkingSpace; GO CREATE VIEW vw_DailyIncome AS SELECT CONVERT(CHAR(10), ExitTime, 120) AS ExitDay, SUM(ParkingFee) AS DailyIncome, COUNT(*) AS TotalRecords FROM AccessRecord WHERE ExitTime IS NOT NULL AND IsFree 0 GROUP BY CONVERT(CHAR(10), ExitTime, 120); GOvw_FreeSpaceStats里COUNT(*)统计车位数CASE WHEN把空闲和占用的行分别计数入口程序直接SELECT这个视图就能拿到三个数字。vw_DailyIncome里CONVERT(CHAR(10), ExitTime, 120)把DATETIME截断到日期120是ODBC标准的yyyy-mm-dd格式码GROUP BY里必须写完整的CONVERT表达式不能引用SELECT别名SQL Server对别名的分组限制比较严格这是初学者容易踩的语法坑。索引设计围绕查询条件展开CREATE INDEX idx_AccessRecord_PlateNo ON AccessRecord(PlateNo); CREATE INDEX idx_AccessRecord_ExitTime ON AccessRecord(ExitTime);查询指定车辆所有进出记录时走PlateNo索引按日收入统计时ExitTime索引理论上可以辅助聚合。这里有一个容易被忽略的优化细节vw_DailyIncome里对ExitTime做了CONVERT函数包裹函数运算会让优化器放弃对这个索引的直接Seek数据量只有几万行时全表扫描并不慢一旦进出记录积累到百万级应该把按日汇总结果物化成日结表每天凌晨由作业计算一次而不是实时对明细表做GROUP BY。这是这类系统最常见的数据库优化切入点。3.4 规范化检查与冗余边界对上述四张表做规范化检查结果如下表名主键非主属性规范化结论FixedParkingSpaceSpaceID车牌、车主信息、在位状态等3NF车主信息属绑定冗余FreeParkingSpaceSpaceID车牌、进入时间3NFAccessRecordRecordID车牌、时间、费用、免费标记3NFStaffStaffID姓名、管理车号3NF所有列都是标量值满足1NF主键生成后所有非主属性完全依赖主键不存在对部分键的依赖满足2NF进出记录表里车牌、时间、费用都直接依赖RecordID没有传递依赖满足3NF。固定车位表把车主信息和车辆信息放在同一行看起来是冗余实际上符合业务语义一个固定车位对外出售后车主、车辆和车位三者的绑定关系是持久且唯一的强行拆分反而要在每次查询里多写JOIN。4. 存储过程封装车辆进出场计费计算与并发更新4.1 为什么把计费逻辑放进存储过程这份设计对存储过程的使用理由讲得很实在网络传输的代码量减少几百个客户端并发执行查询时不会把网络带宽打满计费逻辑修改时只更新服务器端脚本不需要重新编译和分发收费端程序。这个判断在本地单机演示环境里感觉不明显但在真实停车场场景中非常关键——每个收费口是一个客户端进程早晚高峰几十个收费口同时读写出场记录如果每笔业务都从客户端发三到四条SQL到服务器事务边界会变得不可控。存储过程的另一个价值是权限控制。给收费员账号只授予EXEC权限不授予对表的直接增删改权限外部调用者就无法绕过计费逻辑去手工修改停车费用。这一点和资源里安全性要求一节提到的访问级别、用户权限区分正好对应起来。4.2 入场存储过程占用车位与写入记录入场动作涉及两个数据变更进出记录表新增一行、车位状态被占用。两个变更必须在一个事务里完成否则会出现记录写入了但车位没占用或反过来车位被占了却查不到记录。写成存储过程CREATE PROCEDURE usp_CarEntry PlateNo CHAR(12), -- 车牌号码 IsFixed BIT -- 1 固定车位0 自由车位 AS BEGIN SET NOCOUNT ON; SET XACT_ABORT ON; BEGIN TRAN; INSERT INTO AccessRecord (PlateNo, EntryTime, IsFree) VALUES (PlateNo, GETDATE(), IsFixed); IF IsFixed 1 BEGIN UPDATE FixedParkingSpace SET IsOccupied 1, EntryTime GETDATE() WHERE PlateNo PlateNo; END ELSE BEGIN -- SQL Server 2000 风格只更新满足条件的第一行 SET ROWCOUNT 1; UPDATE FreeParkingSpace SET PlateNo PlateNo, EntryTime GETDATE() WHERE PlateNo IS NULL; SET ROWCOUNT 0; END COMMIT TRAN; END GO逻辑说明SET NOCOUNT ON关闭受影响行数的消息回传减少客户端网络包SET XACT_ABORT ON保证事务内任何语句出错时自动整体回滚不需要逐条判断返回值。IsFixed由收费端程序在识别车辆类型后传入。固定车更新FixedParkingSpace的在位状态和入场时间自由车从FreeParkingSpace中找一行PlateNo为空的车位填入车牌和当前时间。SET ROWCOUNT 1是SQL Server 2000下UPDATE只作用于一行的写法SQL Server 2005以上可以直接写UPDATE TOP(1)这个词面差异在面试中经常被问到。注意用完后要SET ROWCOUNT 0恢复否则同一连接后续所有UPDATE和DELETE都会受影响。这个版本没有返回是否成功占用车位。实际项目中我会增加OUTPUT参数或SELECT结果集让收费端在自由车位已满时立刻弹提示并在入口屏显示车位已满。4.3 出场计费存储过程费率计算与状态清理出场流程比入场复杂先判断车辆是不是固定车位车辆再决定走免费通道还是计费通道。资源里没有单独设计费率表把费率作为存储过程的显式参数正好符合系统管理功能可以设定和修改停车费率的需求CREATE PROCEDURE usp_CarExit PlateNo CHAR(12), -- 车牌号码 HourlyRate DECIMAL(6,2) 5.00 -- 每小时费率默认 5 元 AS BEGIN SET NOCOUNT ON; SET XACT_ABORT ON; DECLARE EntryTime DATETIME, ExitTime DATETIME, Minutes INT, Fee DECIMAL(6,2), IsFixed BIT; -- 先判断是否为固定车位车辆 SELECT IsFixed 1 FROM FixedParkingSpace WHERE PlateNo PlateNo AND IsOccupied 1; -- 取最近的未离场记录 SELECT EntryTime EntryTime FROM AccessRecord WHERE PlateNo PlateNo AND ExitTime IS NULL; IF EntryTime IS NULL BEGIN RAISERROR(未找到该车的入场记录或已经离场, 16, 1); RETURN; END SET ExitTime GETDATE(); BEGIN TRAN; IF IsFixed 1 BEGIN -- 固定车位车辆只记录离场时间不计算费用 UPDATE AccessRecord SET ExitTime ExitTime WHERE PlateNo PlateNo AND ExitTime IS NULL; UPDATE FixedParkingSpace SET IsOccupied 0, ExitTime ExitTime WHERE PlateNo PlateNo; END ELSE BEGIN -- 自由车位车辆按小时向上取整计费 SET Minutes DATEDIFF(MINUTE, EntryTime, ExitTime); SET Fee CAST(CEILING(Minutes / 60.0) * HourlyRate AS DECIMAL(6,2)); UPDATE AccessRecord SET ExitTime ExitTime, ParkingFee Fee WHERE PlateNo PlateNo AND ExitTime IS NULL; -- 释放自由车位 DELETE FROM FreeParkingSpace WHERE PlateNo PlateNo; END COMMIT TRAN; END GO计费逻辑拆解计费规则计算表达式改动点不足一小时按一小时CEILING(Minutes / 60.0) * HourlyRate除数保持60半小时计费CEILING(Minutes / 30.0) * HalfHourRate除数和费率都调整按分钟计费Minutes * HourlyRate / 60.0去掉CEILINGDATEDIFF(MINUTE, EntryTime, ExitTime)计算停车总分钟数CEILING(Minutes / 60.0)实现不足一小时按一小时计费。如果场地方要求半小时计费把60改成30要求按分钟精确计费就去掉CEILING乘HourlyRate / 60.0。固定车出场把FixedParkingSpace的IsOccupied置回0。注意这里有个业务语义固定车位是绑定的车辆不在场时车位也不能给别的车用IsOccupied只表示当前在不在场不代表可被其他车辆使用。把这个语义在文档里写清楚避免后续维护者误解。RAISERROR配合RETURN保证找不到入场记录时不会用NULL时间戳算出异常费用收费端捕获错误码后弹窗提示重新确认车牌这比静默失败更容易排查。4.4 常用查询空闲提示、收入统计与车辆轨迹存储过程和视图搭好之后业务层的增删改查变得很轻。入口空闲提示直接读视图SELECT AvailableCount, TotalSpaces FROM vw_FreeSpaceStats;AvailableCount等于0时收费端阻止自由车入场操作并在电子屏输出车位已满。管理端做收入对账时查询SELECT ExitDay, DailyIncome, TotalRecords FROM vw_DailyIncome WHERE ExitDay BETWEEN 2024-01-01 AND 2024-01-31 ORDER BY ExitDay;管理员查指定车辆无论固定还是自由的所有进出场记录SELECT EntryTime, ExitTime, ParkingFee, IsFree FROM AccessRecord WHERE PlateNo PlateNo ORDER BY EntryTime DESC;删除操作在这个系统里要格外谨慎。收费记录属于审计类数据业务上不能物理删除如果管理员发现一条错误记录常见做法是额外写入一条冲正记录或者加一个作废标记列把原记录置为无效原始行保留在表中。这个规则应当作为完整性约束的说明写进设计文档而不是依赖操作人员自觉。5. 完整性约束、索引与实施阶段的排错验证5.1 把静态约束写进模式把动态约束留给存储过程资源把数据库完整性分成列级静态、元组级静态、关系级静态以及对应的动态约束一共六类。落地时可以简化成一条判断标准不依赖业务状态迁移的规则直接放进表结构依赖时间顺序和状态流转的规则放进存储过程。前者例如车牌不为空、离场时间不早于进场时间、自由车位车牌要么为空要么满足车牌长度后者例如同一辆车必须先入场才能出场固定车出场不收费。触发器的性能开销较大且多级触发不易控制这类中小系统里能不用就不用。手工补两条约束让模式更完整ALTER TABLE FreeParkingSpace ADD CONSTRAINT CK_FreeParkingSpace_PlateNo CHECK (PlateNo IS NULL OR LEN(PlateNo) 7); ALTER TABLE AccessRecord ADD CONSTRAINT CK_AccessRecord_ExitTime CHECK (ExitTime IS NULL OR ExitTime EntryTime);约束命名采用约束类型_表名_列名的规则例如CK_AccessRecord_ExitTime在错误提示和后续维护中一眼能看出是哪张表的哪个列出了问题。CHECK (ExitTime IS NULL OR ExitTime EntryTime)这个约束在批量导入历史数据时能自动过滤逻辑上不可能的记录比在导入脚本里写IF判断更可靠。5.2 批量导入历史数据时临时禁用约束停车场更换管理系统时需要把原有车辆档案和历史进出记录导入新库。如果每条记录都经过CHECK和外键逐行校验导入速度会明显变慢。常见做法是导入前临时禁用约束导入完成后恢复并校验ALTER TABLE AccessRecord NOCHECK CONSTRAINT ALL; BULK INSERT AccessRecord FROM D:\data\access_history.csv WITH ( FIELDTERMINATOR ,, ROWTERMINATOR \n, FIRSTROW 1 ); ALTER TABLE AccessRecord CHECK CONSTRAINT ALL; DBCC CHECKCONSTRAINTS (AccessRecord) WITH ALL_CONSTRAINTS;三个注意点。NOCHECK只是让现有行不再触发约束校验新插入的行仍然受约束限制恢复CHECK后如果历史数据中有违反约束的行命令本身不会报错必须用DBCC CHECKCONSTRAINTS逐条验证导入完成后用SELECT COUNT(*)比对源文件行数和目标表记录数保证数据没丢行。这张表如果被其他表外键引用恢复约束时要先子表后父表否则会因依赖顺序失败。5.3 两个收费口同时结算时的死锁排查数据库死锁在这类系统里不是小概率事件。典型场景收费口A执行出场存储过程结算车辆X先更新AccessRecord再删除FreeParkingSpace收费口B同时结算车辆Y先删FreeParkingSpace再更新AccessRecord两个事务各自持有对方下一步要访问的资源死锁形成。SQL Server会选择其中一个事务作为牺牲者回滚用户侧看到的是操作超时请重试。排查时抓取当前活动事务和会话最后执行的语句DBCC OPENTRAN; -- 根据输出中的 SPID 值替换下面的 spid DBCC INPUTBUFFER(spid);解决思路通常有两个方向。一是统一所有存储过程对表的访问顺序约定先写AccessRecord再碰FreeParkingSpace和FixedParkingSpace避免环形等待二是在存储过程里收缩事务范围只把必要的UPDATE和DELETE放在BEGIN TRAN之后查询和变量赋值放到事务外。对表数量在五张以内的系统这两个方法比调整隔离级别更简单可控。5.4 实施验证清单数据库实施完成后按这个清单做一轮冒烟测试测试场景构造数据预期结果验证SQL固定车入场已绑定固定车位的车牌记录新增且IsFree1FixedParkingSpace.IsOccupied1SELECT * FROM AccessRecord WHERE PlateNo ...自由车入场正常车牌占用一个空闲自由车位AvailableCount减1SELECT * FROM vw_FreeSpaceStats自由车出场入场1.5小时后出场ParkingFee等于2小时费率FreeParkingSpace记录被清掉SELECT * FROM AccessRecord WHERE ExitTime IS NOT NULL非法时间手工UPDATE使ExitTime早于EntryTimeCHECK约束拒绝语句报错观察SQL Server返回的错误费率调整把HourlyRate改为8元后出场费用按8元/小时重新计算SELECT * FROM AccessRecord最后补一个容易被忽略的坑车牌大小写。SQL Server默认排序规则下京A12345和京a12345可能被当作相同字符串也可能不同这取决于库的Collation。测试数据里至少包含这两种写法确保查询要么统一转换大小写要么在车牌列上使用不区分大小写的排序规则否则固定车和自由车的计费分支可能走错。本文还有配套的精品资源点击获取

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价