
Access数据库教程:如何建立查询并筛选数据
在 Access 中,查询(Query)用于从一个或多个表中读取、汇总或修改数据。它可以把重复使用的筛选条件保存下来,也可以作为窗体、报表和其他查询的数据来源。与每次手动打开表格、再逐行筛选相比,查询更容易复用、核对和维护。
本文从最常见的选择查询开始,说明如何在设计视图中建立查询,怎样用 SQL 编写条件,如何处理参数、日期和空值,以及何时不该把查询当成安全边界。示例使用 Access 常见的 ACE/Jet SQL 与 DAO。不同版本的 Access 在界面名称上可能略有差异,但查询的基本做法相同。
一、先明确查询适合做什么
查询适合把“需要哪些数据、按什么条件筛选、如何排序或汇总”表达成可重复执行的规则。常见用途包括:
- 从订单表中查出指定日期范围内的记录。
- 筛选某个客户、地区或状态对应的数据。
- 把多个有关联的表连接起来,显示名称而非编号。
- 按部门或月份统计数量、金额等指标。
- 为窗体或报表提供经过筛选的数据源。
- 通过追加、更新或删除查询批量修改数据。
如果只是临时查看少量记录,直接使用表格筛选可能更快。若筛选规则需要长期复用、供他人使用,或要作为报表与自动化流程的基础,保存为查询通常更合适。查询本身一般不保存一份独立的数据副本;选择查询运行时读取其数据源,结果会随底层数据变化。
二、用设计视图建立第一个选择查询
下面以订单表 Orders 为例,假设表中有 OrderID(订单编号)、OrderDate(日期/时间)、CustomerID(客户编号)、Status(状态)和 Amount(金额)等字段。
- 在 Access 中打开数据库,在功能区选择“创建”>“查询设计”。
- 在“显示表”窗口中加入
Orders,然后关闭该窗口。 - 在下方设计网格中双击需要显示的字段,例如
OrderID、OrderDate和Amount。 - 在某个字段的“条件”行中输入筛选条件。比如只查看状态为“已完成”的记录,可在
Status列下输入"已完成"。 - 单击“运行”(红色感叹号)查看结果;确认后按 Ctrl+S 保存查询,并为它取一个有意义的名称,例如
qryCompletedOrders。
查询设计视图会把网格配置转换成 Access SQL。可切换到“视图”>“SQL 视图”查看或编辑 SQL。用名称区分不同类型的对象有助于维护,例如表名使用 tblOrders,选择查询使用 qryOrdersByDate;这是一种命名习惯,不是 Access 的强制要求。
三、选择查询的基本 SQL 结构
Access 的选择查询通常使用 SELECT 语句。下面的语句返回已完成订单,并按日期从新到旧排序:
SELECT [OrderID], [OrderDate], [CustomerID], [Amount]
FROM [Orders]
WHERE [Status] = "已完成"
ORDER BY [OrderDate] DESC;
其中,SELECT 指定要显示的字段,FROM 指定数据来源,WHERE 限制返回的记录,ORDER BY 排序。方括号可以用于包含空格、关键字或其他特殊字符的标识符;即使字段名看起来简单,始终加方括号也有助于避免名称冲突。尽量给字段取简洁、无空格且不与 SQL 关键字冲突的名称。
如果要返回所有字段,可以写 SELECT *,但在窗体、报表或长期使用的查询中,通常应明确列出需要的字段。明确字段能减少不必要的数据读取,也能降低表结构变化对下游对象造成的影响。
四、常见筛选条件
1. 比较、范围与逻辑组合
数字字段可以使用 =、<>、>、>= 等比较运算符。多个条件可以用 AND 或 OR 组合;需要同时满足的条件用 AND,满足其一即可用 OR。
SELECT [OrderID], [OrderDate], [Amount]
FROM [Orders]
WHERE [Amount] >= 100
AND [Status] = "已完成"
ORDER BY [Amount] DESC;
BETWEEN 可以表示包含两端的范围,例如:
SELECT [OrderID], [Amount]
FROM [Orders]
WHERE [Amount] BETWEEN 100 AND 500;
这里的金额边界 100 和 500 都包含在结果中。筛选条件较多时,最好用括号明确逻辑优先级,避免把 AND、OR 的组合读错:
SELECT [OrderID], [Status], [Amount]
FROM [Orders]
WHERE ([Status] = "已完成" OR [Status] = "处理中")
AND [Amount] > 100;
2. 文本匹配与通配符
Access SQL 可使用 LIKE 做模式匹配。常见 Access 数据库设置采用 ANSI-89 通配符:星号 * 表示任意长度的字符,问号 ? 表示一个字符。例如,查找以“陈”开头的姓名:
SELECT [CustomerID], [CustomerName]
FROM [Customers]
WHERE [CustomerName] LIKE "陈*";
若数据库启用了 ANSI-92 查询语法,通配符通常改为百分号 % 和下划线 _。数据库的 ANSI-92 设置会影响查询语法;不要想当然地把其他数据库(例如 SQL Server)的通配符写法直接搬进 Access。遇到匹配结果不符合预期时,先确认数据库的查询语法设置。
文本常量用引号括起。Access SQL 示例通常使用双引号表示文本;在 VBA 字符串中写 SQL 时,双引号本身需要再转义,后文会给出完整示例。不要把用户输入直接拼接进 SQL 条件。
3. 空值筛选
数据库中的空值(Null)表示值未知或缺失,不等于空字符串 "",也不等于数字 0。检查空值应使用 IS NULL 或 IS NOT NULL,不能写 = NULL:
SELECT [CustomerID], [Phone]
FROM [Customers]
WHERE [Phone] IS NULL;
如果字段可能同时出现 Null 和空字符串,而业务上两者都表示“未填写”,可分别判断:
SELECT [CustomerID], [Phone]
FROM [Customers]
WHERE [Phone] IS NULL OR [Phone] = "";
是否把空字符串视为缺失,应根据字段定义和数据录入规则决定。不要为了让筛选“看起来正确”就把 Null、空字符串和 0 混为一谈。
五、日期筛选:避免边界漏数
Access SQL 中的日期字面量通常用井号括起。若只筛选某一天,可能写成:
SELECT [OrderID], [OrderDate]
FROM [Orders]
WHERE [OrderDate] = #2025-03-01#;
但如果 OrderDate 实际存储了时间部分,例如上午 10 点,那么它并不等于当天零点,以上条件可能漏掉记录。筛选某个自然日或一段日期时,更稳妥的方式是使用左闭右开的范围:开始日期包含,结束日期不包含。
SELECT [OrderID], [OrderDate]
FROM [Orders]
WHERE [OrderDate] >= #2025-03-01#
AND [OrderDate] < #2025-03-02#;
这种写法能覆盖 3 月 1 日内的所有时间值。固定写在 SQL 中的日期字面量必须符合 Access 对日期的解释规则;不同区域设置可能影响某些日期格式的解析。实际程序中,建议用日期参数传入,不要依赖含糊的日/月顺序,也不要把格式化后的本地日期字符串直接拼进 SQL。
六、参数查询:把条件交给使用者或程序
如果每次查询都需要修改日期、金额或客户编号,可使用参数查询。参数既能让使用者运行查询时输入条件,也能由 VBA 程序设置。下面的查询以两个日期参数筛选订单:
PARAMETERS [pStart] DateTime, [pEndExclusive] DateTime;
SELECT [OrderID], [OrderDate], [Amount]
FROM [Orders]
WHERE [OrderDate] >= [pStart]
AND [OrderDate] < [pEndExclusive]
ORDER BY [OrderDate];
在 Access 中保存或运行包含未提供值的参数查询时,可能会出现输入参数的提示框。参数声明中的类型要与实际字段和值匹配;日期字段使用日期/时间参数,长整型编号使用适当的整数类型,文本参数则应声明为文本类型。文本参数可以按需要限制长度,例如 Text (255),但这不是所有参数都适用的统一上限。参数类型和长度应根据实际数据与 Access 支持的类型设置。
也可以让查询引用已打开的窗体控件,例如:
SELECT [OrderID], [OrderDate], [Amount]
FROM [Orders]
WHERE [CustomerID] = [Forms]![frmOrders]![cboCustomer];
此写法要求窗体 frmOrders 已打开,且控件 cboCustomer 存在并提供合适的值。如果窗体未打开、控件名称拼错,或控件返回的值与字段类型不匹配,Access 可能会把引用当作需要输入的参数,或者报错。需要在后台代码中稳定执行时,显式设置 DAO 参数通常更清晰。
七、用 DAO 执行参数查询
下面的 VBA 过程通过临时 DAO QueryDef 创建参数查询,设置日期范围并读取结果。假设 Orders 表的 OrderDate 是日期/时间字段。代码放在 Access 标准模块中,可通过 VBA 编辑器运行;当前 Access 通常包含 DAO 引用,但若项目引用被移除,需要在“工具”>“引用”中启用相应的 Access 数据库引擎 DAO 对象库。
Public Sub ShowOrdersForMarch()
Dim db As DAO.Database
Dim qdf As DAO.QueryDef
Dim rs As DAO.Recordset
Dim sqlText As String
On Error GoTo CleanFail
sqlText = "PARAMETERS [pStart] DateTime, " & _
"[pEndExclusive] DateTime; " & _
"SELECT [OrderID], [OrderDate], [Amount] " & _
"FROM [Orders] " & _
"WHERE [OrderDate] >= [pStart] " & _
"AND [OrderDate] < [pEndExclusive] " & _
"ORDER BY [OrderDate];"
Set db = CurrentDb
Set qdf = db.CreateQueryDef("", sqlText)
qdf.Parameters("pStart").Value = DateSerial(2025, 3, 1)
qdf.Parameters("pEndExclusive").Value = DateSerial(2025, 4, 1)
Set rs = qdf.OpenRecordset(dbOpenSnapshot)
Do While Not rs.EOF
Debug.Print rs!OrderID, rs!OrderDate, rs!Amount
rs.MoveNext
Loop
CleanExit:
On Error Resume Next
If Not rs Is Nothing Then rs.Close
Set rs = Nothing
Set qdf = Nothing
Set db = Nothing
Exit Sub
CleanFail:
MsgBox "查询失败:" & Err.Description, vbExclamation
Resume CleanExit
End Sub
db.CreateQueryDef("", sqlText) 创建的是未命名的临时 QueryDef,不会在数据库对象列表中保存查询。若要保存并重复使用,可在 Access 查询设计视图中保存,或用非空名称创建 QueryDef;若该名称已存在,需要先处理已有对象,不能假定创建操作会自动覆盖它。示例用 Debug.Print 将结果输出到 VBA 的“立即窗口”,它用于演示读取记录,不会自动把结果显示在表格或窗体中。
使用参数的关键是把 SQL 结构和数据值分开。不要为了传入日期而先用 Format 把日期变成字符串再拼接;直接给日期参数赋日期值更可靠,也避免区域格式造成的误读。
八、连接表、汇总与其他查询类型
当查询需要显示客户名称,而订单表只保存客户编号时,可以通过连接关联客户表。连接字段应具有匹配的数据类型;通常是主键与外键。若两张表没有正确的关系或连接条件,结果可能出现大量重复行,或遗漏本应保留的记录。
SELECT [Orders].[OrderID],
[Orders].[OrderDate],
[Customers].[CustomerName],
[Orders].[Amount]
FROM [Customers]
INNER JOIN [Orders]
ON [Customers].[CustomerID] = [Orders].[CustomerID]
WHERE [Orders].[Status] = "已完成";
INNER JOIN 只返回两侧都能匹配的记录。若需要保留所有客户,即使客户没有订单,也可考虑 LEFT JOIN,并把“保留哪一侧”的规则弄清楚后再调整查询。Access 对外连接的括号和嵌套写法有特定语法,复杂连接建议先在设计视图中建立,再切换到 SQL 视图核对。
要按客户汇总订单金额,可使用总计查询。例如:
SELECT [CustomerID],
Count([OrderID]) AS [OrderCount],
Sum([Amount]) AS [TotalAmount]
FROM [Orders]
WHERE [Status] = "已完成"
GROUP BY [CustomerID]
ORDER BY Sum([Amount]) DESC;
总计查询中,未参与聚合的输出字段通常必须出现在 GROUP BY 中;计数、求和等结果则用 Count、Sum 等聚合函数。需要按聚合结果筛选时使用 HAVING,而不是把聚合条件放进普通的 WHERE。Access 还提供交叉表查询,用于把一个字段的值展开为列;新增类别时,输出列可能变化,因此不适合要求固定字段结构的所有场景。
除选择查询外,Access 还有追加、更新、删除和建表等操作查询。这些查询会改变数据。运行前先确认筛选条件,并优先在备份或测试副本上验证。尤其是没有 WHERE 条件的更新或删除查询,可能影响整张表。不要仅凭结果预览判断修改范围;先用对应的选择查询核对命中记录,再执行操作查询。
九、参数与安全边界
参数查询有助于把值作为数据传入,而不是把用户输入当作 SQL 语句的一部分。这能减少因引号、日期格式或输入内容导致的语法问题,也是在编程中防止 SQL 注入的重要做法。但它并不意味着所有动态 SQL 都自动安全。
- 参数适用于数据值,例如客户编号、文本、日期和金额。
- 参数不能直接代替表名、字段名或排序方向等 SQL 结构。若需要动态选择字段或排序方式,应把允许值映射到预先定义的 SQL 片段,不要将任意用户输入拼接成标识符。
- 参数无法补救已经拼接进 SQL 的不可信文本。安全做法是为每个值定义参数,并通过参数集合赋值,而不是先连接成 SQL 字符串。
- Access 查询权限不等于可靠的行级安全机制。隐藏对象或限制界面操作不能替代数据库文件及操作系统层面的访问控制。
Access 文件适合桌面、小型部门应用和原型开发,但它不是带有集中式身份验证、细粒度权限和并发事务管理的服务器数据库。若应用需要按用户可靠隔离数据、供不可信客户端访问,或需要更强的审计与集中管理,应评估服务器数据库及相应的应用架构。把 Access 文件加密或设置密码,也不能简单等同于服务器端的用户授权体系。
十、常见错误与排查方法
条件正确,却没有任何结果
检查字段名、数据类型、引号和实际数据。文本值要与表中内容一致;数值字段不要误加文本引号。对于 Null,使用 IS NULL,而不是 = "" 或 = NULL。还要确认表单参数所引用的窗体已经打开。
日期查询漏掉当天记录
检查字段是否同时存储日期和时间。用 >= 起始日期 且 < 下一日 的左闭右开范围,通常比“等于某一天”或结束时间设为 23:59:59 更稳妥。
LIKE 查询匹配不到预期文本
确认数据库使用 ANSI-89 还是 ANSI-92 通配符。分别检查 * 与 % 等写法,并查看字段中是否有前后空格或不同的实际文本。
连接后记录数突然增加
检查连接两侧是否真的是唯一键与外键。如果连接字段在一侧存在重复值,一条记录可能匹配多条记录,结果便会扩展。必要时查看关系设计、主键设置和实际数据,而不是直接用 DISTINCT 掩盖连接错误。
运行时弹出意外的“输入参数值”窗口
这通常表示 Access 找不到某个字段、表单或控件名称,也可能是参数没有声明或没有赋值。逐一核对名称拼写、表单是否打开、查询是否包含拼错的字段名,并检查代码中参数名称是否与 SQL 中完全一致。
查询越来越慢
先确认是否只选择了需要的字段、连接条件是否完整、筛选是否作用于正确字段,并检查用于常见筛选和连接的字段是否适合建立索引。索引会增加写入和维护成本,也不能保证所有查询都变快。Access 查询性能还受数据量、文件位置、网络环境和表达式写法影响;不能仅凭“把条件写进查询”就推断一定会更快。
结语
建立 Access 查询的基本流程是:确定数据来源,选出需要的字段,写清筛选与排序规则,运行并核对结果,最后保存为可复用的对象。简单筛选可以从设计视图开始;需要可靠传值时使用参数;涉及日期时考虑时间部分与边界;涉及多表时仔细验证连接关系;执行会修改数据的查询前,先核对影响范围。
查询写法是否适合,最终要由字段类型、数据质量、数据库设置和实际使用方式共同决定。把这些边界纳入设计,比只记住几条 SQL 语句更能避免错误。








