本节摘要:查询是存储下来的"提问方式",运行时实时计算而非复制数据。选择查询是基本功中的基本功:单表筛选、多表联接、分组汇总是它的三板斧。本节以华彩的"近三月品类销量"需求为靶子,把三板斧完整演示一遍。
上一章末尾模型定稿,本章开篇老板娘要"按品类列出近三个月销量、库存和缺口"。别急着拖表——先看清查询这个对象的脾气。
初学者最顽固的误解:以为运行一个查询会"生成一份新数据"。恰恰相反,查询对象里保存的只是一段定义(对应一条 SQL),每次打开都基于最新表数据现场计算。这意味着三件重要的事:
理解了"查询即问题",你就明白为什么规范良好的库里查询往往比表还多——问题无限,事实有限。
任务拆解从最小处开始。第一步先不管品类汇总,只做"找出三个月内状态不是作废的所有订单行"。创建菜单点查询设计,加入订单明细和订单两张表(明细里的下单日期实际在订单头上,所以需要两表连线),双击需要的字段落进下方网格,条件行里写下判据。
设计视图网格布局示意 字段行 业务单号 | 下单日期 | 货品编码 | 数量 | 成交单价 表行 订单 | 订单 | 订单明细 | 订单明细| 订单明细 排序行 | —— 显示行 勾选 | 勾选 | 勾选 | 勾选 | 勾选 条件行 | 大于等于日期减90 | 或条件行 | 状态 不等于 作废 写在状态下 |
几个值得交代的细节:日期条件用 Date() 减天数得到动态起点,比写死某个日子聪明——这个查询明年也能用;文本匹配用 Like "*复印纸*" 实现包含查找;同一行的多个条件是"并且"关系,不同行的"或"行则是或者关系,这是设计视图里最容易搞反的一条规则。
老板娘的最终需求需要四张表的信息合流:订单(日期)+ 订单明细(数量单价)+ 货品(品名、品类、现价)+ 库存流水(现存量)。在设计视图里把四张表全部加进来,第 2 章建好的关系会自动带出联接线。这就是建模阶段的投资第一次兑现:联接不用手搭,因为键早已就位。
第 2.2 节我们说过明细行只存货品 ID 不存名称,此刻正是回收红利的时候:联接货品表之后,品名、品类这些字段随取随用,而价格快照原则又保证了历史金额不被现价污染。一个查询同时享受两种设计的便利,这正是好结构的复利。
联接类型默认是相等联接(两边都有才出现)。如果老板娘还想要"三个月没动销的货也列出来",就要把这根线的联接属性改成第二个选项,让货品表全量保留——界面上叫左联接,SQL 里写作 LEFT JOIN,下一节看它的原生模样。
最后一步把几百行流水压缩成十来个品类大数。网格工具栏点亮"汇总"按钮,每列会出现"总计"行,各字段选择角色:
汇总角色配置 品类字段 → Group By 分组依据 数量字段 → Sum 合计 销售额表达式 → Sum 对 成交单价乘数量求和 下单日期 → Where 只参与筛选不出现在输出
销售额那格值得展开说。直接对两个字段分别求和再相乘是错的——那是"总价合计乘总数量合计",结果毫无意义。正确做法是在字段格里直接写表达式 [成交单价]*[数量] 造出一个临时列,再对它聚合。Access 的表达式语法用方括号引用字段,加减乘除符号直书即可,这种"就地造列"的能力后面章节还会反复用到。

查询命名沿用 2.4 的约定(这个该叫"汇总品类三个月销售")。保存前养成两个自检动作:一是双击运行扫一眼结果总数是否符合常识——三个月的品类行数不该超过经营的真实品类数;二是抽一行手工核对:挑销售额最大的那个品类,拿原始订单过滤手算一次,两分钟能换来对整套口径的信心。
这三个问题华彩项目都真实发生过,写下来帮你省掉同款下午。
空值陷阱。 客户表补录时客服问:"为什么筛选'电话 <> 空'老是漏人?"因为空值不是零、不是空字符串,任何与 Null 的比较结果都是"未知",永远不会为真。判断缺失要写 Is Null 或 Nz(电话,"")=""。统计时同理:Count(电话) 数的是非空行数,跟总行数对不上多半是它在作怪。
去重的两种口径。 "看看到底服务过多少家客户"用 SELECT DISTINCT CustomerID ... 就够;但"数一数有多少家"必须写成子查询再计数——DISTINCT 去的是输出行的重,不等于计数值的去重。两种口径差一位小数点的例子,评审会上见得多了。
中文排序的直觉错位。 ORDER BY 对中文默认按拼音音序排,而业务同事想要的可能恰恰是入库顺序或自定义等级(比如 金银铜牌客户的层级)。前一种靠日期或自动编号字段恢复;后一种老老实实建一张带排序列的小字典表联进来。别试图用别名首字母硬凑——层次一多必然乱。
最后交付一门"听人话建查询"的手艺。业务方的提问长得千奇百怪,结构只有几副骨架,抓关键词即可对号:
| 问句里的信号词 | 该用的骨架 |
|---|---|
| "哪些、列出、有没有" | 选择加筛选,输出明细行 |
| "多少家、总共、平均" | 分组聚合,注意计数列是否含空值 |
| "分别按、每个月、各家" | 加分组维度,必要时升级成交叉表 |
| "低于、超过、最近" | 条件表达式,动态条件套日期函数 |
实战示范一句老板娘的原话:"帮我看看今年每个客户一共拿了多少货,欠款超五千的重点标出来。"拆解动作依次是:时间条件"今年"落在下单日期上(用 Year 函数生成动态起点)、"每个客户"提示按客户分组、"拿了多少货"选数量合计并联接明细、"欠款超五千"是分组后过滤所以走 HAVING 不走 WHERE。一句口语经过四次拆解就落成了标准的查询结构,这个翻译过程做熟了,需求会上你就能当场回答"这个数我下周给你"。
三板斧毕竟都在图形界面里施展,它们的背后是一整门语言。下一节掀开盖子学 SQL。