
简介这份资源是面向VB.Net初学者与桌面应用开发者的Access数据库操作示例工程围绕ADO.Net框架讲解如何连接Access数据库并完成增删改查。内容涵盖OleDbConnection连接字符串配置、OleDbCommand执行SQL、OleDbDataReader读取数据以及OleDbDataAdapter配合DataSet进行数据填充与绑定的完整思路适合需要快速上手数据库交互的开发者参考。压缩包共18个文件约47KB包含vbproj、sln、vb等工程与源码文件mdb数据库文件以及xml、resx、ico、exe等配置与资源文件构成一个可直接打开运行的Visual Studio解决方案。目前已有228人学习下载。通过该示例读者可掌握连接Access的两种提供程序写法、参数化SQL防注入、Using语句自动释放资源等实用技巧并借助完整工程结构理解数据驱动界面的搭建流程为后续业务开发打下基础。1. Access数据库操作示例从建表到增删改查一个桌面级方案的完整落地很多人第一次接触 Access 数据库是在一个很具体的场景里手头有一批 Excel 台账数据量不大但需要多表关联、需要做查询界面、需要给不写代码的同事用。这时候上 MySQL 或 SQLite 都显得重Access 反而是最顺手的选择——它把数据库引擎和可视化前端打包在一起.accdb文件双击就能打开表、查询、窗体、报表全在一个文件里。但顺手归顺手真要把增删改查写稳、把参数设对、把并发和权限的坑绕开还是得把底层那套东西理清楚。这篇笔记就按「建库建表 → 增删改查 → 参数与索引 → 避坑 → 进阶」的顺序把 Access 数据库操作示例讲透适合需要快速交付小型数据管理工具的开发者也适合想用 C#、Python 或 VBA 对接 Access 的工程师。Access 的核心是 Jet/ACE 数据库引擎.accdb是 ACE 格式.mdb是更老的 Jet 格式。它和 SQLite 一样是文件型数据库但多了一层「前台界面」能力。理解这一点很关键你写的 SQL 最终由 ACE 引擎执行而窗体、宏、VBA 只是调用这层引擎的壳。所以做操作示例时先把 SQL 层跑通再谈界面绑定顺序不能反。2. 建库建表与字段类型Access 数据库操作示例的第一步2.1 用 SQL 建表而不是手点设计视图很多人习惯在设计视图里拖字段但要做可复现的操作示例必须用 SQL。Access 的 DDL 和标准 SQL 有差异尤其是字段类型名。下面这段在 Access 查询设计器的 SQL 视图里可以直接跑-- 建一张员工表注意 Access 的类型名和标准 SQL 不同 CREATE TABLE Employees ( EmpID AUTOINCREMENT PRIMARY KEY, -- 自增主键等价于其他库的 IDENTITY EmpName TEXT(50) NOT NULL, -- 短文本必须给长度 DeptID LONG, -- 长整型用来关联部门表 HireDate DATETIME, -- 日期时间 Salary CURRENCY, -- 货币类型比 DOUBLE 更适合金额 IsActive YESNO DEFAULT TRUE -- 布尔Access 里叫 YESNO );逻辑说明AUTOINCREMENT是 Access 的自增写法不是AUTO_INCREMENTTEXT(50)必须带长度不带长度在某些版本会报错金额字段用CURRENCY而不是DOUBLE因为CURRENCY是定点数能避免浮点累加误差。参数上TEXT的最大长度是 255超过要用MEMO长文本但MEMO不能建索引、不能排序这是后面避坑章要展开的点。2.2 字段类型对照与选型理由Access 的类型名和 MySQL、SQLite 差别不小选错类型后期改表很痛苦。下面这张对照表是我实际项目里总结的Access 类型等价概念适用场景注意点TEXT(n)VARCHAR(n)姓名、编号、短备注n 最大 255MEMOTEXT/LONGTEXT长描述、日志不能索引、不能排序LONGINT外键、数量32 位够用DOUBLEFLOAT科学计算有精度误差CURRENCYDECIMAL金额定点推荐DATETIMEDATETIME时间精度到秒YESNOBOOLEAN开关状态存为 -1/0选型理由很直接凡是钱用CURRENCY凡是外键用LONG凡是可能超过 255 字的用MEMO并接受它不能索引的代价。我一般会在建表阶段就把这些定死避免后期ALTER TABLE——Access 的改表能力比 MySQL 弱改字段类型经常要求表里没数据。2.3 建关联表与设置外键Access 支持在 SQL 里建关系但更稳的做法是建完表后在「数据库工具 → 关系」里手动连。SQL 层可以这样写CREATE TABLE Departments ( DeptID LONG PRIMARY KEY, DeptName TEXT(50) NOT NULL ); -- 插入部门数据 INSERT INTO Departments (DeptID, DeptName) VALUES (1, 研发部); INSERT INTO Departments (DeptID, DeptName) VALUES (2, 市场部);逻辑说明Access 的INSERT一次只能插一行不支持VALUES (...), (...)多行语法这是和 MySQL 的一大差异。参数上DeptID手动指定而不是自增是为了让员工表的外键可控。建完两张表后在关系视图里把Employees.DeptID拖到Departments.DeptID勾选「实施参照完整性」这样删部门时如果有员工引用会被拦住避免脏数据。3. 增删改查四类操作Access 数据库操作示例的核心命令3.1 插入单行插入与批量插入的取舍Access 的INSERT语法前面提过一次一行。批量插入要靠循环或INSERT INTO ... SELECT-- 单行插入 INSERT INTO Employees (EmpName, DeptID, HireDate, Salary, IsActive) VALUES (张三, 1, #2024-03-01#, 12000, TRUE); -- 从临时表批量导入 INSERT INTO Employees (EmpName, DeptID, HireDate, Salary, IsActive) SELECT TempName, TempDept, TempDate, TempSalary, TRUE FROM TempImport;逻辑说明日期字面量在 Access 里用#包裹不是单引号这是最容易翻车的地方之一。INSERT INTO ... SELECT是批量导入的正路比逐行INSERT快一个数量级。参数上TRUE在 Access 里等价于-1FALSE等价于0如果你从其他系统导数据布尔值要转换否则会报类型不匹配。3.2 查询参数查询与通配符的坑查询是 Access 最常用的操作参数查询能让同一个查询复用-- 参数查询运行时弹出输入框 SELECT E.EmpName, D.DeptName, E.Salary FROM Employees AS E INNER JOIN Departments AS D ON E.DeptID D.DeptID WHERE E.Salary [请输入最低薪资] AND E.EmpName LIKE * [请输入姓名关键字] *;逻辑说明Access 的参数用[提示文字]表示运行时会弹框。通配符方面Access 在通过 DAO/ACE 访问时用*和?但在 ADO 或某些 ODBC 场景下要用%和_这个差异是血泪经验——同一个查询在查询设计器里跑得通用 C# 的 OleDb 跑就查不出数据八成是通配符用错了。参数上LIKE拼接时用而不是遇到 NULL 会让整个表达式变 NULL。3.3 更新与删除先 SELECT 再动手更新和删除在生产环境必须谨慎我的习惯是先写SELECT确认范围再改成UPDATE/DELETE-- 第一步先查要影响的行 SELECT EmpID, EmpName, Salary FROM Employees WHERE DeptID 2; -- 第二步确认无误后更新 UPDATE Employees SET Salary Salary * 1.1 WHERE DeptID 2 AND IsActive TRUE; -- 删除同样先查后删 DELETE FROM Employees WHERE IsActive FALSE AND HireDate #2020-01-01#;逻辑说明Access 的UPDATE不支持JOIN语法要关联更新得用子查询例如WHERE DeptID IN (SELECT DeptID FROM Departments WHERE ...)。参数上Salary * 1.1这种自引用更新在 Access 里是允许的但要注意CURRENCY类型乘以浮点数后可能丢精度涨薪这种场景建议先算好再赋值。3.4 用 C# 对接 Access 的最小示例桌面工具常用 C# 对接下面是一个能跑通的最小片段using System.Data.OleDb; string connStr ProviderMicrosoft.ACE.OLEDB.12.0;Data SourceD:\data\demo.accdb;; using (var conn new OleDbConnection(connStr)) { conn.Open(); // 参数化查询避免拼接 SQL 和注入 var cmd new OleDbCommand(SELECT EmpName, Salary FROM Employees WHERE DeptID ?, conn); cmd.Parameters.AddWithValue(?, 1); // OleDb 用位置参数顺序对应 using (var reader cmd.ExecuteReader()) { while (reader.Read()) { Console.WriteLine(${reader[EmpName]}: {reader[Salary]}); } } }逻辑说明连接字符串里的Provider要匹配你装的驱动版本32 位和 64 位不通用这是最常见的「未注册提供程序」报错来源。参数上OleDb 用?占位且按顺序绑定不像 SqlClient 支持命名参数多个参数时顺序错了不会报错但结果全错非常隐蔽。4. 索引、主键与查询性能Access 数据库操作示例里最容易被忽略的参数4.1 什么时候该建索引Access 的索引和别的库逻辑一致但有几个专属限制。下面这段在查询设计器里执行-- 建单字段索引 CREATE INDEX idx_empname ON Employees (EmpName); -- 建复合索引 CREATE INDEX idx_dept_active ON Employees (DeptID, IsActive); -- 唯一索引 CREATE UNIQUE INDEX idx_empname_unique ON Employees (EmpName);逻辑说明CREATE INDEX在 Access 里可用但MEMO和YESNO字段不能单独建索引YESNO因为只有两个值建了也没意义。复合索引的字段顺序很重要(DeptID, IsActive)能加速「按部门查在职」的查询但加速不了「只按 IsActive 查」。参数上索引不是越多越好每个索引都会拖慢插入和更新小表几千行以内甚至可以不建全表扫描更快。4.2 用 EXPLAIN 的思路看执行计划Access 没有 MySQL 那样的EXPLAIN但可以在查询设计器里看「显示表」和「查询属性」。更实用的办法是打开「显示执行计划」-- 在查询设计器里右键查询 → 属性 → 显示执行计划 -- 或者用 VBA 读取 QueryDef 的统计信息逻辑说明Access 的执行计划可视化比较弱我一般用两个土办法判断性能一是把查询拆成子查询分别计时二是用DFirst/DLookup这类域函数时特别小心它们在循环里调用会逐行查表性能极差。参数上如果查询超过 2 秒还没出结果先检查是不是在WHERE里对字段用了函数比如WHERE Format(HireDate,yyyy)2024这会让索引失效。4.3 参数查询对性能的影响参数查询方便但有个隐藏代价ACE 引擎在第一次执行参数查询时会生成执行计划如果参数选择性差异大计划可能不优。我的做法是对高频查询写成固定 SQL用代码拼接条件而不是全靠参数查询。这一点在数据量上万后差别明显。5. 避坑与常见问题Access 数据库操作示例里那些让人后悔的细节5.1 现象多用户同时写入报「文件已被锁定」原因Access 是文件型数据库默认以共享模式打开但写入时会锁文件或锁页。多人同时写同一张表冲突概率高。 解决把数据库拆成「前端界面 后端数据」两个文件后端放共享目录前端每人一份或者干脆控制并发写入操作排队。数据量再大就该考虑迁移到真正的 C/S 数据库。5.2 现象查询在设计器里正常代码里查不到数据原因通配符差异*vs%或日期格式差异#vs 引号前面提过。 解决统一用参数化查询日期用参数传DateTime对象而不是字符串通配符根据驱动选。写完后在代码里跑一遍别只信设计器。5.3 现象数据库文件越来越大删了数据也不变小原因Access 删除记录后不会自动回收空间文件里留着空洞。 解决定期「压缩和修复数据库」在「数据库工具」菜单里或者用命令行MSACCESS.EXE /compact。这个操作要独占打开别人在用的时候做不了。5.4 现象MEMO字段在查询里显示被截断原因MEMO字段在查询结果里默认只显示前 255 个字符这是显示限制不是存储限制。 解决在查询属性里把「文本格式」改一下或者用Left/Mid函数显式取。导出到 Excel 时也要注意超过 255 字会被截。5.5 现象C# 报「未注册提供程序 Microsoft.ACE.OLEDB.12.0」原因没装 Access 数据库引擎或者项目平台x86/x64和驱动位数不匹配。 解决装对应位数的引擎项目属性里把「目标平台」设成和驱动一致。这个坑几乎每个对接 Access 的人都踩过装完记得重启 IDE。6. 进阶技巧把 Access 数据库操作示例用到自动化与迁移场景6.1 用 VBA 做批量操作与定时任务Access 自带 VBA适合做批量清洗和定时导出。下面这段把员工表按部门导出成多个 ExcelSub ExportByDept() Dim db As DAO.Database Dim rs As DAO.Recordset Set db CurrentDb 取所有部门 Set rs db.OpenRecordset(SELECT DeptID, DeptName FROM Departments) Do While Not rs.EOF Dim qdf As DAO.QueryDef Set qdf db.CreateQueryDef(, _ SELECT * FROM Employees WHERE DeptID rs!DeptID) 用 TransferSpreadsheet 导出 DoCmd.TransferSpreadsheet acExport, acSpreadsheetTypeExcel12Xml, _ qdf.Name, D:\export\ rs!DeptName .xlsx, True rs.MoveNext Loop rs.Close End Sub逻辑说明TransferSpreadsheet是 Access 内置的导出方法比逐行写 Excel 快得多。参数上acSpreadsheetTypeExcel12Xml对应.xlsx老版本用acSpreadsheetTypeExcel9对应.xls。注意CreateQueryDef创建的临时查询在导出后要清理否则会堆积在数据库里。6.2 迁移到 SQLite 或 MySQL 的判断标准什么时候该离开 Access我的经验是三条线单表超过 50 万行、并发用户超过 5 个、需要 Web 访问。满足任意一条就该考虑迁移。迁移时表结构基本能平移但要注意AUTOINCREMENT改成AUTO_INCREMENTMySQL或INTEGER PRIMARY KEY AUTOINCREMENTSQLiteYESNO改成TINYINT(1)或BOOLEANCURRENCY改成DECIMAL(10,2)。数据导出用「外部数据 → 导出 → 文本文件」再导入目标库比直接连 ODBC 稳。6.3 一个验证操作是否正确的习惯我做完任何一批增删改查都会跑一遍「对账查询」统计总行数、关键字段的求和、外键是否有孤儿记录。下面这个查询模板我几乎每次都改改用-- 对账总数、金额合计、孤儿外键 SELECT COUNT(*) AS 总人数, SUM(Salary) AS 薪资合计 FROM Employees; SELECT COUNT(*) AS 孤儿数 FROM Employees WHERE DeptID NOT IN (SELECT DeptID FROM Departments);逻辑说明第一条确认数据量没丢第二条确认关联没断。参数上SUM遇到 NULL 会忽略如果Salary允许为空合计会偏小所以金额字段我一般设NOT NULL DEFAULT 0。这个习惯帮我拦下过好几次批量更新写错WHERE的事故——先查后改改完再对账比事后恢复强。说到底Access 数据库操作示例的价值不在于它多强大而在于它把「数据 界面 自动化」压进一个文件让小型项目能快速落地。把 SQL 层写规范、把参数和索引设对、把并发和迁移的边界想清楚它就能稳稳撑住很多场景。希望帮到你。本文还有配套的精品资源点击获取