:從建表到增刪改查與連接方式詳解)
簡介這份資源是面向VB.Net初學者與桌面應用開發(fā)者的Access數據庫操作示例工程圍繞ADO.Net框架講解如何連接Access數據庫并完成增刪改查。內容涵蓋OleDbConnection連接字符串配置、OleDbCommand執(zhí)行SQL、OleDbDataReader讀取數據以及OleDbDataAdapter配合DataSet進行數據填充與綁定的完整思路適合需要快速上手數據庫交互的開發(fā)者參考。壓縮包共18個文件約47KB包含vb與vbproj項目源碼、sln解決方案、mdb數據庫文件、resx與resources資源文件、xml配置及exe可執(zhí)行程序等構成一個可直接運行的完整示例工程。目前已有228人學習下載。通過這份示例讀者可以對照源碼理解連接建立、參數化查詢、資源釋放等關鍵環(huán)節(jié)并借助VS調試工具排查問題為后續(xù)開發(fā)數據驅動的Windows應用打下基礎。1. Access 數據庫操作示例從單機文件到增刪改查的完整落地Access 數據庫操作示例本質上是在講一件事如何用最輕量的方式把一個.accdb或.mdb文件當成真正的數據庫來用而不是把它當成一個高級 Excel。很多人第一次接觸 Access 是在做課程設計或者小型管理系統表建好了、窗體拖出來了但一到寫查詢、做批量更新、處理并發(fā)就翻車。問題不在工具本身而在于沒有把 Access 當成一個有 SQL 方言、有事務邊界、有連接模型的數據庫來看待。這篇筆記面向三類人一是要用 Access 快速搭一個本地數據管理工具的后端開發(fā)者二是需要把 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做業(yè)務主鍵。如果后續(xù)要和其他系統同步建議用TEXT主鍵加唯一索引避免自增 ID 在合并數據時沖突。建表時最好顯式聲明NOT NULL和默認值Access 的默認值語法是DEFAULT但只對新增記錄生效歷史數據不會回填。2.2 用 SQL 建表的完整示例下面這段 SQL 可以在 Access 的查詢設計視圖里切換到 SQL 模式直接執(zhí)行也可以通過 ADO 或 ODBC 執(zhí)行。注意 Access 的CREATE TABLE不支持IF NOT EXISTS重復執(zhí)行會報錯所以腳本里要先判斷表是否存在。-- 先刪除舊表如果存在避免重復建表報錯 DROP TABLE 員工信息; -- 創(chuàng)建員工信息表 CREATE TABLE 員工信息 ( emp_id TEXT(20) NOT NULL, -- 工號業(yè)務主鍵用文本避免前導零丟失 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 是字符長度上限不是字節(jié)數CURRENCY在 Access 里實際是 8 字節(jié)定點數適合金額YESNO在 SQL 里可以用YES/NO或TRUE/FALSE但 Access 界面顯示為復選框。CONSTRAINT pk_emp PRIMARY KEY顯式命名主鍵方便后續(xù)用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 里最穩(wěn)的 Access 驅動。下面這段代碼演示如何用 ADO 執(zhí)行建表語句并檢查表是否已存在。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需要權限某些環(huán)境下會被拒絕備選方案是直接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)) { // 預先添加參數占位后續(xù)只改值 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一次循環(huán)里改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(*)確認影響行數再執(zhí)行。注意Access 的ORDER BY對中文默認按拼音排序如果按筆畫排序需要在界面里設置SQL 層面改不了。涉及中文排序的業(yè)務建議在應用層用StringComparer處理別依賴數據庫排序。4. 連接方式怎么選OleDb、ODBC 與 Python 的 pyodbc4.1 三種連接方式的適用場景Access 不是網絡數據庫它沒有服務端監(jiān)聽端口所有連接都是文件級的。常見的連接方式有三種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 支持有限更穩(wěn)的做法是確保系統區(qū)域設置和文件編碼一致。4.3 連接池與并發(fā)Access 的真實邊界Access 單文件同時只能有一個寫連接多個進程同時寫會鎖文件報數據庫已被其他用戶鎖定。讀操作可以并發(fā)但寫操作必須串行。如果你的場景是多用戶同時寫Access 不是正確選擇應該換 SQLiteWAL 模式或真正的服務端數據庫。單機工具、報表生成、數據導入導出這類場景Access 完全夠用。連接池在 Access 上意義不大因為文件級連接開銷本來就低。我一般建議每次操作開一個短連接用完就關避免長連接持有文件鎖。如果確實要復用用using或try/finally確保釋放。5. 避坑與排查Access 操作中最容易翻車的五個點5.1 報錯未注冊提供程序 Microsoft.ACE.OLEDB.12.0現象C# 程序在開發(fā)機跑得好好的部署到另一臺機器就報這個錯。原因是目標機器沒裝 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和系統區(qū)域一致。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 不支持多寫并發(fā)。解決寫操作加鎖串行化或者改用 SQLite。如果必須用 Access把寫操作集中到一個線程用隊列排隊。讀操作可以多線程但也要注意OleDbConnection不是線程安全的每個線程獨立連接。6. 進階技巧用 Access 做數據同步和自動化導出Access 最實用的進階場景是當數據中轉站從其他系統導出 CSV用 Access 做清洗和關聯再導出給報表工具。這里的關鍵技巧是用鏈接表Linked Table把外部數據源掛進 Access然后用本地查詢做關聯避免全量導入。鏈接表的 SQL 寫法是SELECT * FROM 表名 IN 路徑但更穩(wěn)的方式是在界面里建鏈接表再用 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 快得多。希望幫到你。本文還有配套的精品資源點擊獲取