
简介这份资源是面向VB.Net初学者与桌面应用开发者的Access数据库操作示例工程围绕ADO.Net框架讲解如何连接Access数据库并完成增删改查。内容涵盖OleDbConnection连接字符串配置、OleDbCommand执行SQL、OleDbDataReader读取数据以及OleDbDataAdapter配合DataSet进行数据填充与绑定的完整思路适合需要快速上手数据库交互的开发者参考。压缩包共18个文件约47KB包含vb与vbproj项目源码、sln解决方案、mdb数据库文件、resx与resources资源文件、xml配置及exe可执行程序等构成一个可直接运行的完整示例工程。目前已有228人学习下载。通过这份示例读者可以对照源码理解连接建立、参数化查询、资源释放等关键环节并借助VS调试工具排查问题为后续开发数据驱动的Windows应用打下基础。1. Access 数据库操作示例从单机文件到增删改查的完整落地Access 数据库操作示例本质上是在讲一件事如何用最轻量的方式把一个.accdb或.mdb文件当成真正的数据库来用而不是把它当成一个高级 Excel。很多人第一次接触 Access 是在做课程设计或者小型管理系统表建好了、窗体拖出来了但一到写查询、做批量更新、处理并发就翻车。问题不在工具本身而在于没有把 Access 当成一个有 SQL 方言、有事务边界、有连接模型的数据库来看待。这篇笔记面向三类人一是要用 Access 快速搭一个本地数据管理工具的后端开发者二是需要把 Access 里的数据接进 C#、Python 或报表工具的人三是被数据库增删改查这四个字困住、只会点鼠标不会写语句的新手。我会从表结构设计讲到 SQL 写法再讲到连接方式、参数化查询、批量操作和常见报错排查每一步都给可复制的代码和参数说明。Access 不是玩具它在单机和小团队场景下的性价比比很多人想象的高得多。2. 先把表结构和字段类型定下来Access 的数据类型与建表语句2.1 Access 的字段类型和常见误用Access 的字段类型和 MySQL、SQLite 不完全一样直接照搬会踩坑。最常见的几个类型是TEXT短文本最长 255 字符、MEMO长文本实际对应LONGTEXT、INTEGER长整型、DOUBLE双精度、CURRENCY货币精度高、DATETIME日期时间、YESNO布尔、COUNTER自增主键。很多人把身份证号、手机号存成INTEGER结果前导零丢失、超出范围把备注存成TEXT超过 255 字符直接被截断。正确做法是编号类字段一律用TEXT金额用CURRENCY长描述用MEMO。另一个高频问题是主键。Access 里可以用COUNTER做自增主键也可以用TEXT做业务主键。如果后续要和其他系统同步建议用TEXT主键加唯一索引避免自增 ID 在合并数据时冲突。建表时最好显式声明NOT NULL和默认值Access 的默认值语法是DEFAULT但只对新增记录生效历史数据不会回填。2.2 用 SQL 建表的完整示例下面这段 SQL 可以在 Access 的查询设计视图里切换到 SQL 模式直接执行也可以通过 ADO 或 ODBC 执行。注意 Access 的CREATE TABLE不支持IF NOT EXISTS重复执行会报错所以脚本里要先判断表是否存在。-- 先删除旧表如果存在避免重复建表报错 DROP TABLE 员工信息; -- 创建员工信息表 CREATE TABLE 员工信息 ( emp_id TEXT(20) NOT NULL, -- 工号业务主键用文本避免前导零丢失 emp_name TEXT(50) NOT NULL, -- 姓名 dept_code TEXT(10), -- 部门编码 salary CURRENCY, -- 薪资货币类型精度高 hire_date DATETIME, -- 入职日期 remark MEMO, -- 备注长文本 is_active YESNO DEFAULT YES, -- 是否在职默认是 CONSTRAINT pk_emp PRIMARY KEY (emp_id) ); -- 给部门编码建索引加速按部门查询 CREATE INDEX idx_dept ON 员工信息 (dept_code);这段代码里TEXT(20)的 20 是字符长度上限不是字节数CURRENCY在 Access 里实际是 8 字节定点数适合金额YESNO在 SQL 里可以用YES/NO或TRUE/FALSE但 Access 界面显示为复选框。CONSTRAINT pk_emp PRIMARY KEY显式命名主键方便后续用ALTER TABLE引用。索引单独用CREATE INDEX建不要写在CREATE TABLE里面Access 不支持内联索引定义。注意Access 的 SQL 方言对保留字很敏感字段名如果叫date、password、level必须用方括号包起来比如[date]。我一般建议字段名加前缀或改用hire_date这种明确写法省得后面到处加括号。2.3 用 ADO 在 C# 里建表和改结构如果是在 C# 项目里操作 Access推荐用System.Data.OleDb它是 .NET 里最稳的 Access 驱动。下面这段代码演示如何用 ADO 执行建表语句并检查表是否已存在。using System.Data.OleDb; string connStr ProviderMicrosoft.ACE.OLEDB.12.0;Data SourceD:\data\hr.accdb;; using (OleDbConnection conn new OleDbConnection(connStr)) { conn.Open(); // 先查系统表判断目标表是否存在 var checkCmd new OleDbCommand( SELECT COUNT(*) FROM MSysObjects WHERE Name员工信息 AND Type1, conn); int exists (int)checkCmd.ExecuteScalar(); if (exists 0) { string ddl CREATE TABLE 员工信息 ( emp_id TEXT(20) NOT NULL, emp_name TEXT(50) NOT NULL, dept_code TEXT(10), salary CURRENCY, hire_date DATETIME, remark MEMO, is_active YESNO, CONSTRAINT pk_emp PRIMARY KEY (emp_id) ); new OleDbCommand(ddl, conn).ExecuteNonQuery(); } conn.Close(); }连接字符串里的ProviderMicrosoft.ACE.OLEDB.12.0对应.accdb格式如果是老的.mdb要用Microsoft.Jet.OLEDB.4.0。MSysObjects是 Access 的系统表Type1表示本地表Type6表示链接表。查MSysObjects需要权限某些环境下会被拒绝备选方案是直接SELECT TOP 1 * FROM 表名然后捕获异常。ExecuteNonQuery返回受影响行数建表语句返回 0 是正常的。3. 增删改查四种操作参数化 SQL 与事务边界3.1 插入数据参数化避免注入和转义问题Access 的 SQL 里字符串用单引号包裹日期用#包裹比如#2024-01-15#。如果直接拼接字符串遇到姓名里有单引号比如 OBrien就会报错更严重的是被注入。参数化查询是唯一正确的做法。OleDb 的参数用?占位顺序必须和Parameters.Add的顺序一致这点和 SQL Server 的name不同容易翻车。string sql INSERT INTO 员工信息 (emp_id, emp_name, dept_code, salary, hire_date, remark, is_active) VALUES (?, ?, ?, ?, ?, ?, ?); using (OleDbCommand cmd new OleDbCommand(sql, conn)) { cmd.Parameters.AddWithValue(?, E1001); cmd.Parameters.AddWithValue(?, 张三); cmd.Parameters.AddWithValue(?, D01); cmd.Parameters.AddWithValue(?, 12500.00m); cmd.Parameters.AddWithValue(?, new DateTime(2024, 1, 15)); cmd.Parameters.AddWithValue(?, 试用期三个月); cmd.Parameters.AddWithValue(?, true); int rows cmd.ExecuteNonQuery(); }AddWithValue的顺序就是?的顺序写错一个位置数据就串列了。金额用decimal类型传入不要用double否则可能出现 12500.000000001 这种精度问题。日期直接传DateTime对象OleDb 会自动转成 Access 的日期字面量。布尔值传true/falseAccess 会存成-1/0查询时用YESNO字段直接比较TRUE即可。3.2 批量插入用事务把一千条压进一秒单条插入一千次每次开一个OleDbCommand在 Access 上大概要十几秒。正确做法是开事务复用同一个命令对象只改参数值。Access 对事务的支持是完整的OleDbTransaction可以显著提升批量写入性能。using (OleDbTransaction tx conn.BeginTransaction()) { string sql INSERT INTO 员工信息 (emp_id, emp_name, dept_code, salary, hire_date) VALUES (?, ?, ?, ?, ?); using (OleDbCommand cmd new OleDbCommand(sql, conn, tx)) { // 预先添加参数占位后续只改值 cmd.Parameters.Add(?, OleDbType.VarWChar); cmd.Parameters.Add(?, OleDbType.VarWChar); cmd.Parameters.Add(?, OleDbType.VarWChar); cmd.Parameters.Add(?, OleDbType.Currency); cmd.Parameters.Add(?, OleDbType.Date); for (int i 0; i 1000; i) { cmd.Parameters[0].Value E (2000 i); cmd.Parameters[1].Value 员工 i; cmd.Parameters[2].Value D0 (i % 5 1); cmd.Parameters[3].Value 8000 i * 10; cmd.Parameters[4].Value DateTime.Today.AddDays(-i); cmd.ExecuteNonQuery(); } } tx.Commit(); }关键点是OleDbCommand构造时传入tx否则命令不在事务里回滚无效。参数只Add一次循环里改Value避免反复解析 SQL。一千条数据用这种方式大概 0.5 到 1 秒比逐条提交快一个数量级。如果中途出错tx.Rollback()可以全部撤销这就是事务的后悔药。3.3 查询、更新和删除的写法差异查询用OleDbDataReader逐行读适合大数据量小数据量用OleDbDataAdapter填DataTable更方便。更新和删除的 SQL 语法和标准 SQL 基本一致但 Access 不支持UPDATE ... FROM和DELETE ... USING多表关联更新要写成子查询。-- 查询按部门筛选在职员工 SELECT emp_id, emp_name, salary FROM 员工信息 WHERE dept_code ? AND is_active TRUE ORDER BY salary DESC; -- 更新给指定部门全员涨薪 10% UPDATE 员工信息 SET salary salary * 1.1 WHERE dept_code ? AND is_active TRUE; -- 删除软删除把离职员工标记为非在职 UPDATE 员工信息 SET is_active FALSE WHERE emp_id ?; -- 物理删除慎用 DELETE FROM 员工信息 WHERE emp_id ? AND is_active FALSE;Access 的UPDATE支持表达式salary * 1.1会按行计算。DELETE不带WHERE会清空整表且 Access 没有TRUNCATE清空大表很慢。我一般建议用软删除加一个is_active字段查询时过滤既保留历史又避免误删。如果确实要物理删除先SELECT COUNT(*)确认影响行数再执行。注意Access 的ORDER BY对中文默认按拼音排序如果按笔画排序需要在界面里设置SQL 层面改不了。涉及中文排序的业务建议在应用层用StringComparer处理别依赖数据库排序。4. 连接方式怎么选OleDb、ODBC 与 Python 的 pyodbc4.1 三种连接方式的适用场景Access 不是网络数据库它没有服务端监听端口所有连接都是文件级的。常见的连接方式有三种OleDb.NET 原生性能最好、ODBC跨语言Python/Java 都能用、DAO老技术不推荐新项目用。OleDb 在 Windows 上依赖 ACE 驱动32 位和 64 位不通用这是最大的坑。如果你的 C# 项目是 64 位但装的 Office 是 32 位ACE 驱动可能只有 32 位版本运行时报未注册提供程序。ODBC 的好处是驱动独立可以单独装 64 位 Access ODBC 驱动不依赖 Office。Python 里用pyodbc连 Access连接字符串写DRIVER{Microsoft Access Driver (*.mdb, *.accdb)};DBQ路径。缺点是 ODBC 驱动版本更新慢某些新特性支持不如 OleDb。4.2 Python 操作 Access 的完整示例下面这段 Python 代码演示用pyodbc做增删改查包含连接、参数化查询和事务。import pyodbc from datetime import date # 连接字符串DBQ 后面是 accdb 文件的绝对路径 conn_str ( rDRIVER{Microsoft Access Driver (*.mdb, *.accdb)}; rDBQD:\data\hr.accdb; ) conn pyodbc.connect(conn_str, autocommitFalse) cursor conn.cursor() # 插入参数用 ? 占位顺序对应 cursor.execute( INSERT INTO 员工信息 (emp_id, emp_name, dept_code, salary, hire_date) VALUES (?, ?, ?, ?, ?), (E3001, 李四, D02, 9800.00, date(2024, 3, 1)) ) # 查询fetchall 返回列表每行是 pyodbc.Row cursor.execute(SELECT emp_id, emp_name, salary FROM 员工信息 WHERE dept_code ?, (D02,)) for row in cursor.fetchall(): print(row.emp_id, row.emp_name, row.salary) # 更新 cursor.execute(UPDATE 员工信息 SET salary ? WHERE emp_id ?, (10500.00, E3001)) # 提交事务 conn.commit() cursor.close() conn.close()autocommitFalse是默认值意味着必须显式commit()否则数据不落盘。pyodbc的参数占位符是?和 OleDb 一样按顺序匹配。日期传datetime.date对象pyodbc会自动转换。如果查询中文出现乱码检查连接字符串里是否加了CHARSETUTF8不过 Access ODBC 驱动对 UTF-8 支持有限更稳的做法是确保系统区域设置和文件编码一致。4.3 连接池与并发Access 的真实边界Access 单文件同时只能有一个写连接多个进程同时写会锁文件报数据库已被其他用户锁定。读操作可以并发但写操作必须串行。如果你的场景是多用户同时写Access 不是正确选择应该换 SQLiteWAL 模式或真正的服务端数据库。单机工具、报表生成、数据导入导出这类场景Access 完全够用。连接池在 Access 上意义不大因为文件级连接开销本来就低。我一般建议每次操作开一个短连接用完就关避免长连接持有文件锁。如果确实要复用用using或try/finally确保释放。5. 避坑与排查Access 操作中最容易翻车的五个点5.1 报错未注册提供程序 Microsoft.ACE.OLEDB.12.0现象C# 程序在开发机跑得好好的部署到另一台机器就报这个错。原因是目标机器没装 ACE 驱动或者装的位数和程序不匹配。解决装对应位数的 Access Database Engine32 位程序装 32 位驱动64 位程序装 64 位驱动。如果机器上已有 Office注意 Office 位数会决定默认驱动位数必要时用/quiet参数单独装驱动。5.2 中文乱码或问号现象插入的中文变成???或乱码。原因通常是连接字符串没指定编码或者字段类型用了TEXT但长度不够导致截断。解决OleDb 连接字符串加Jet OLEDB:Global Partial Bulk Ops2意义不大关键是字段用TEXT且长度给够Python 端确保字符串是str不是bytes。如果从 CSV 导入CSV 要存成 UTF-8 带 BOM 或 GBK和系统区域一致。5.3 日期格式报错标准表达式中数据类型不匹配现象WHERE hire_date 2024-01-01报错。原因是 Access 的日期字面量必须用#包裹写成#2024-01-01#。解决参数化查询传DateTime对象不要拼字符串。如果非要拼用#yyyy-MM-dd#格式且月份日期补零。5.4 批量插入后数据库体积暴涨现象插入十万条数据后.accdb文件从几 MB 涨到几百 MB删除数据后文件不缩小。原因是 Access 不会自动回收空间删除只是标记。解决用压缩和修复数据库功能或者在代码里调用DBEngine.CompactDatabase。命令行可以用msaccess.exe /compact。定期压缩是维护 Access 的必备习惯。5.5 多线程写入导致文件锁死现象两个线程同时写报无法更新数据库或对象为只读或文件已被锁定。原因是 Access 不支持多写并发。解决写操作加锁串行化或者改用 SQLite。如果必须用 Access把写操作集中到一个线程用队列排队。读操作可以多线程但也要注意OleDbConnection不是线程安全的每个线程独立连接。6. 进阶技巧用 Access 做数据同步和自动化导出Access 最实用的进阶场景是当数据中转站从其他系统导出 CSV用 Access 做清洗和关联再导出给报表工具。这里的关键技巧是用链接表Linked Table把外部数据源挂进 Access然后用本地查询做关联避免全量导入。链接表的 SQL 写法是SELECT * FROM 表名 IN 路径但更稳的方式是在界面里建链接表再用 SQL 操作。另一个技巧是用 Access 的宏或 VBA 做定时导出。比如每天凌晨把查询结果导出成 Excel用DoCmd.TransferSpreadsheet一行代码搞定。如果不想用 VBA可以用 Python 的pyodbc读数据再用openpyxl写 Excel灵活性更高。import pyodbc from openpyxl import Workbook conn pyodbc.connect(rDRIVER{Microsoft Access Driver (*.mdb, *.accdb)};DBQD:\data\hr.accdb;) cursor conn.cursor() cursor.execute(SELECT emp_id, emp_name, dept_code, salary FROM 员工信息 WHERE is_active TRUE) wb Workbook() ws wb.active ws.append([工号, 姓名, 部门, 薪资]) for row in cursor.fetchall(): ws.append([row.emp_id, row.emp_name, row.dept_code, float(row.salary)]) wb.save(rD:\data\员工报表.xlsx) conn.close()这段代码把 Access 查询结果直接写成 Excelfloat(row.salary)是因为CURRENCY类型在 pyodbc 里返回Decimalopenpyxl 不认要转成 float。如果数据量大用write_onlyTrue模式写 Excel内存占用更低。验证同步是否成功我一般会做三件事一是对比源表和目标表的行数二是抽样比对关键字段的哈希值三是跑一遍全量查询看有没有报错。Access 没有内置的校验和函数可以用SELECT COUNT(*), SUM(salary)做粗略校验精确校验要在应用层做。最后说个血泪经验Access 的.accdb文件不要放在网络共享盘上直接操作延迟高且容易锁死。正确做法是复制到本地操作处理完再传回去。如果多人协作用 OneDrive 或共享盘同步文件但同一时间只能一个人写。这个边界认清之后Access 在单机数据管理上的效率比搭一套 MySQL 再写 ORM 快得多。希望帮到你。本文还有配套的精品资源点击获取