本节摘要:选择查询之外还有三类高频武器:参数查询让一个查询回答一族问题;交叉表把长流水转成宽矩阵,是报表的底座;动作查询负责批量增删改,威力大纪律也大。本节各给一个华彩项目里的真实用例。
前三板斧属于"看数据",这一节的三个兵器开始改变你与数据的协作方式:一次定义、反复提问;行转列、直接印报表;以及批量动手改库。每个都配完整操作过程。
老板娘的需求很快升级:"能不能我想看几个月就看几个月?"——把写死的 90 天改成运行时问一句即可。做法朴素到近乎简陋:在条件的日期表达式里塞进方括号包裹的一句话:
WHERE 订单.下单日期 >= Date() - [请输入回看天数] AND 订单.业务员 = [请输入业务员姓名 或留空看全部]
运行时弹出输入框填数即得结果,同一个查询模板服务无数临时口径。想再讲究一点,可以在查询属性里声明参数的数据类型(尤其日期型必须声明,否则文本比较会悄悄出错),还可以配合窗体上的下拉框引用控件值替代手工输入——[Forms]![录单台]![选业务员] 这种三段式地址能直接嵌进条件,第 4 章的交互查询全靠这招。
多值槽位还有一个常见痛点:某栏留空表示"不限",怎么让空与非空都工作?一行经典条件搞定:
Like IIf([地区留空则全部]="", "*", [地区留空则全部] & "*")
原理是当输入为空时退化为通配全体,否则按前缀匹配。它不优雅但极其耐用,记下来能用十年。
老板娘手机里存过一张同行发来的 Excel 矩阵:行为品类、列为月份、格子里是销量。这种透视形态在 Access 里有原生答案——交叉表查询向导或手写 TRANSFORM。以品类乘月份为例:
TRANSFORM Sum([成交单价]*[数量]) AS 月度销售额 SELECT 货品.品类, Sum([成交单价]*[数量]) AS 品类合计 FROM 订单 INNER JOIN (货品 INNER JOIN 订单明细 ON 货品.ID = 订单明细.货品ID) ON 订单.ID = 订单明细.订单ID WHERE 订单.下单日期 >= Date()-180 GROUP BY 货品.品类 PIVOT Format(订单.下单日期, "yyyy-mm");
读法要点:SELECT 行决定左侧钉住什么;PIVOT 行决定横向展开成几列;TRANSFORM 后面的聚合填进格子。Format 函数把日期捏成年月串,月份列自动生成,跨年也不怕。这个查询建好后,第 4 章把它挂进月结报表,一张现成的进货分析表就从机器里流出来了。
判断何时用交叉表有一条经验线:列数可预期且有限(十二个月、六种状态)适合交叉表;列数不定(每来一个新客户就多一列)会让矩阵疯长成灾难,那种需求该去找专门的数据透视组件。

前三类只读,第四类要动刀。动作查询四兄弟各有分工:更新(Update)、追加(Append)、删除(Delete)、生成表(Make-Table)。举华彩真实用过的一例——纸品涨价,把货品表现价整体上调:
UPDATE 货品 SET 单价 = 单价 * 1.05 WHERE 品类 = "纸品" AND ID NOT IN (SELECT 货品ID FROM 特价协议 WHERE 生效中 = True);
注意子查询排除已签特价的商品——商业规则常比教程复杂,而 SQL 的表达力恰好接得住。
但真正想传达的是动手之前的三个仪式,一笔都不能省:
SELECT TOP 10 或日期窄区间,确认行为符合预期再放开范围。删除查询另有一条铁律:只删自己的测试数据时才允许不带条件运行;生产环境上哪怕界面提示"将删除全部行"出现惊叹号,也必须立刻停下核对。另外记住参照完整性仍会在背后守门——你想删的客户若有订单挂着,引擎会拒绝执行,这是第 2 章投资的红利再次兑现。
参数与交叉表单独用都乖巧,合在一起就有一个经典报错:给交叉表查询挂上 [请输入年份] 参数后运行,弹出的却是"表达式键入不正确,或者太复杂"。原因是交叉表引擎需要预先固定行标题列的数据类型,而参数在声明之前是黑盒。解法藏在查询属性表里:打开参数表(查询设计菜单里的"参数"按钮),先把 请输入年份 声明为长整型(或对应类型),再回到网格里使用——顺序反了就是不通。
第二个坑出在动作查询与事务的交界。华彩做过一个"月末自动作废超期未付订单"的更新查询,界面一切正常,但偶尔只更新了一半就停——事后发现跑查询的机器恰在那个时段网络抖动。教训是:批量写操作要么放在前端本机对着本地链接稳定执行,要么包进 VBA 的 BeginTrans/CommitTrans 里追求原子性(第 8 章 8.1 会展开)。视徒手动作查询为绝对可靠的人,迟早被一次断网教育。
顺带补一条可复用的变式:把"留空即全部"的技巧升级成区间条件也很常用——付款日期列的条件写成 Between Nz([开始日期],#1/1/1990#) And Nz([结束日期],#12/31/2099#),管理员想查任意时段,全留空则是全历史。三个参数模板配齐后,日常报表九成的临时提问都不再需要你动手改 SQL。
四兄弟里最容易埋雷的是生成表(Make-Table)。它看似无害——不过是把查询结果落成一张新表——工程上却有两个讲究。
其一,命名必须挂专区前缀且纳入月度清理:生成表是快照不是资产,放任它以正式表的名字躺在导航窗格里,迟早有人把它当成原始数据引用,快照过期后所有下游跟着出错。华彩的约定是统一挂在 SNAP 字头之下并在说明里写明刷新时点。其二,重复运行会整体覆盖同名旧表且不恢复删除的操作没法撤销,因此正式流程里它只能活在两处:受控的每日夜间任务(如性能章的快照刷新)或者你亲手按下按钮的临时分析。任何要进入业务流程核心的环节都不该依赖生成表的产物长期供电。
查询写得快很重要,跑得慢更致命——下一节专治各种慢。