内容简介

本书根据网络畅销书《“表”哥的Access入门》改编，该书以案例教学的方式，通过一个简单的小饭馆数据库管理程序的开发过程，介绍如何规范Access数据、如何设计查询和窗体，以及如何设计Access报表和宏。本书在进行案例讲解的过程中，详细介绍每一步操作背后隐含的Access数据库知识，让读者不但知其然，而且知其所以然，帮助读者轻松建立初步的数据库思维。

本书特别适合那些对Excel比较熟悉，想进一步利用Access知识提升个人和部门生产力的Office爱好者，以及那些想进一步利用Office技术优化本部门工作效率的职场工作人士阅读。除此之外，对于那些已经读过一些Access书籍，但仍然没有对Access建立一个整体概念的学习者，也非常适用！

未经许可，不得以任何方式复制或抄袭本书之部分或全部内容。

版权所有，侵权必究。

图书在版编目（CIP）数据

表哥的Access入门：以Excel视角快速学习数据库知识/林书明著.—北京：电子工业出版社，2016.2（CDA数据分析师系列丛书）

ISBN 978-7-121-28006-1

Ⅰ.①表…　Ⅱ.①林…　Ⅲ.①关系数据库系统　Ⅳ.①TP311.138

中国版本图书馆CIP数据核字(2015)第321315号

策划编辑：张慧敏

责任编辑：石　倩

印　　刷：三河市兴达印务有限公司

装　　订：三河市兴达印务有限公司

出版发行：电子工业出版社

北京市海淀区万寿路173信箱　邮编：100036

开　　本：787×980　1/16　印张：12　字数：192千字

版　　次：2016年2月第1版

印　　次：2018年2月第6次印刷

印　　数：10501～11500册　定价：49.00元

凡所购买电子工业出版社图书有缺损问题，请向购买书店调换。若书店售缺，请与本社发行部联系，联系及邮购电话：（010）88254888，88258888。

质量投诉请发邮件至zlts@phei.com.cn，盗版侵权举报请发邮件至dbqq@phei.com.cn。

本书咨询联系方式：010-51260888-819，faq@phei.com.cn。

前言

Access都可以做什么

当你在商场或超市结账时，是否注意过收银员面前那个小小显示器上的软件？收银员利用这个软件，结合扫描设备，就可以完成扫描商品、记录销售金额、扣减库存、查看历史记录甚至退换货等操作，我们可以把这个软件叫作“卖货的软件”，如果用专业一点儿的术语来说，这其实是一个典型的“商品进销存软件”。

你可能会想，进销存软件，如此高大上的名字，不是企业里面用的吗？与我们的生活有什么关系？是的，进销存软件确实是企业里最常用的软件之一，但是，如果我告诉你，我们的生活时时刻刻都离不开类似软件，你还觉得这与你的生活无关吗？

进销存软件在本质上是数据记录和处理软件，在智能设备越来越普及的今天，其实各种智能设备无时无刻不在记录和处理关于你的数据。有时你甚至意识不到，你在计算机上的每一次网购操作，就连你的手机，可能每天都在偷偷地监视着你的行踪，记录着你每一笔移动钱包上的消费，保存着你的每一句聊天记录，并有可能偷偷上传到云端服务器，记录在数据库里。记得有人说过：“互联时代无隐私”，一点也不夸张！

在数据库无处不在、数据库思维无处不在的今天，不主动了解一点数据库知识，真的有点OUT了。特别是对于已经熟悉Excel的我们，学习点数据库知识，不但可以帮助我们理解这个数字时代，而且还能提高我们的工作效率。

回到前面超市收银台的场景，如果我告诉你，利用Office中的Access软件也能制作出功能类似的软件，并且，更为神奇的是，仅靠Access软件，你几乎不用编写一句程序代码，让普通Office用户也能过上一把软件设计的瘾，会不会感到有些惊讶？

Access作为Office的组件之一，在众多领域中发挥着作用：利用Access构建中小型企业的生产、计划、库存、销售、人事管理、培训等数据库管理系统，以及用Access建立大公司的“部门级”应用等。不仅如此，Access还是一款极具效力的个人生产力提升工具，在很多Excel难以施展其能力的场所，也能轻松应对。

一提到数据库，有些人（特别是对数据库一知半解的人）动不动就拿Oracle、SQL Server等大型数据库系统说事儿，在这里我告诉大家，如果你想学习数据库知识，Access绝对是一款最好的入门工具，理由不用多，单指其易获得性、易安装性及普及性就足够了。Access作为一款易学易用，功能灵活的小型桌面数据库软件，其能力主要体现在以下两个方面。

（1）简单的操作，强大的功能

Access具有可视化的使用界面，这一点在所有的数据库软件里面是领先的。给人印象尤其深刻的是其可视化查询设计界面，至今无人能出其右，本书会重点介绍这方面知识。

由于本书是写给Excel用户作为Access的入门学习的，很多地方会以Excel作为Access的参照物进行对比。我们知道，同为Office组件之一的Excel具有灵活的数据处理和分析能力，然而，其能力是有局限的，比如，当涉及两个数据表之间的“关联”操作时，再比如两个表格之间的“一对多查找”、两个表格之间的“同步修改删除”操作，Excel几乎无能为力，而在Access中，这些都能以其内置功能轻松解决，非常容易。

利用Access中强大的查询功能，我们可以容易地进行各类统计分析操作，方便地组合多个相关的数据表格，实现灵活的表间操作。此外，在处理数据的数量方面，相对于Excel，Access在分析处理上万条记录、十几万条记录及以上的数据时，速度相当快捷且容易操作，极大地提升了部门的工作效率。

在数据处理自动化方面，Excel往往需要编写VBA程序代码来实现，而在Access中，利用其自带的“宏”功能，我们几乎可以抛弃VBA，以鼠标拖拽的方式，把Access中一个个基础的操作像搭积木一样按照自定义的顺序排列起来，形成连续、定制化地逻辑，轻松完成数据处理的自动化操作，大大降低了用户学习Access的难度。

（2）Access可以用来开发软件

开发软件向来被人们认为是一种高智商的活动，对于没有接受过编程训练的人来说，学习编写程序代码不亚于学习火星文，而Access对这一切有所改变。

Access可以用来开发各种数据库相关的软件，比如生产管理、销售管理、库存管理等各类企业管理软件。利用Access，我们可以轻松建立数据之间的关系，并且在不写程序代码的情况下，模拟真实商业活动的业务逻辑，设计出功能完善的软件界面。此外，我们还可以通过Access的“报表”功能，设计出符合用户需求的各种格式报告，满足软件用户的个性化需求。通过本书的学习，你会发现，Access作为一款数据库应用的“傻瓜”软件，即使是非计算机专业人员，也能学会。

Access软件满足了那些从事企业管理工作人员的需要，让他们不需要学习那些高大上的编程语言就能开发出实现自己管理思想的软件，让他们借助用软件来规范企业的业务规则，推行其管理理念，实现先进管理思想的真正“落地”。在这个意义上，Access帮助非计算机专业的管理人员实现了开发软件的“梦想”，能够把自己变成“懂管理会编程”的复合型人才。

很多管理人员都有自己的绝妙想法和创意，他们也能很清晰地把这些想法和创意用文字表达出来，但却因为这些想法和创意需要一些计算机知识去实现，因此经常感觉束手无策。或者，不得不等待“永远很忙”的信息技术（IT）部门的支援，以至于好的想法和创意长时间无法变为现实。因此，如果我们能够花一些时间，学习一些Access知识，依靠自己的力量编写一个体现自己独特管理思想的软件，把这些创想变成现实，岂不快哉！

Access和Excel的对比

（1）“自由”软件与“强规则”软件

与Access对用户的要求相比，Excel几乎是一种“自由”软件，对用户的操作行为几乎没有任何约束，用户可以在Excel单元格里输入任何内容，比如，文本、数字、日期等，甚至，如果你愿意，也可以在Excel单元格中输入一整篇文章！

Access却是一种“强规则”软件，用户在使用Access之前，必须了解一些要遵守的“规则”，否则你会处处碰壁。比如，在Access数据表中，如果某一列中规定了只能输入日期，那么其绝对不会接受日期以外的其他内容。否则，Access会以报错的方式表示抗议；再如，如果规定表格中的某一列不能有重复内容出现，你就必须遵守这个规则，除非你事先改变了规则。

这些严格的“规则”，似乎让Access变得“高傲”，不像Excel，几乎任何人都可以“调戏”它一下，在Excel工作表中随便输入点什么，然后再画个边框，就能作出一点儿看似有用的东西来。然而对于Access，如果你不懂它的规则，可能连一个字符都无法输入。但是，一旦你了解了Access中那些并不算多的几个“规则”，它就会变得服服贴贴、老老实实地听你指挥。在使用Access提升我们的工作效率之前，必须了解一些Access的规则。

民间有句俗话，叫作“没有规矩，不成方圆”，正是Access中的各种“规则”避免了将来数据处理活动中的各种“意外”的发生。君不见，在Excel中，由于用户一开始对数据组织的不规范，造成Excel报告难以理解，难以维护，难以扩展的例子比比皆是！

（2）当前的Access培训存在问题

如果你恰巧是在大一点的书店翻阅本书，那么，请你随便翻阅几本其他国内作者出版的关于Access的书籍，你会发现，这些书籍基本都遵循一个套路，那就是如何建立数据表、建立查询、设计窗体和制作报表。这个逻辑本身并说不上是什么错误，然而，作为一个Access培训资料，只让读者知其然，而不知其所以然。只介绍了Access的操作，却没有介绍这些操作背后隐藏的逻辑，这是不对的。按照这种教材学习，充其量也只能成为Access操作工，而不能成为潜在的数据库管理软件的设计者。

对于当前Access的培训机构，基本也存在着同样的问题，很多培训师只是按照培训材料上的操作步骤，告诉学员先点击什么，后点击什么，经常把Access的核心内容淹没在琐碎的操作细节之中，让学员感到茫然，难以抓住培训的重点。

这本书将竭力避免当前Access书籍和培训中普遍存在的问题，以案例教学的方式，通过一个简单的小饭馆数据库管理程序的开发过程，介绍如何规范化Access数据、如何设计和查询窗体，以及如何设计Access报表和宏。本书不仅介绍Access的操作，而且详细介绍这些操作背后隐含的Access数据库方面的知识，让大家知其然，而且知其所以然。

Access作为一款流行的小型数据库软件，相对于其他数据库软件产品，易学易用，在最大程度上避免了用户的编程需求，但是有得必有失，Access为了保持其开发的灵活性，在开发过程中会涉及各种各样的设置选项，介绍Access中每个选项的设置方法和设置效果是庞杂且无趣的，所以，在本书中我们将以一个小饭馆的管理软件为例，从头到尾地介绍整个软件的设计过程。在设计过程中，我们只对案例中用到的设置选项进行详细的介绍，这样可以避免分散我们的思路，减少我们的学习负担。

本书学习指导

本书不是一本大而全的Access书籍，而是一本以案例为导向，帮助读者快速了解Access的功能，初步理解数据库思维，并能在一步步引导下，开发出一个小型数据库管理软件的案例手册。

因为本书假设读者对Excel已经有了一定程度的了解或使用经验，所以没有像大多数Access书籍那样，开始就引入了大量的数据库专业术语，而是以读者的Excel知识为基础，逐渐过渡到Access的学习之中，让读者感觉到不是在学习一门全新的Access技术，而是在已掌握的Excel技术基础上的“自然扩展”。尽最大努力减轻读者的学习负担。

本书适合那些对Excel比较熟悉，想进一步利用Access知识提升个人和部门生产力的微软Office爱好者，以及那些想进一步利用微软Office技术优化本部门工作效率的职场工作人士阅读。除此之外，对于那些已经读过一些Access“操作手册”，但对Access还没有建立一个整体印象的Access初学者，也非常适用！

最后，我们打个比方，如果说学习Excel是学习一种“技能”，那么学习Access才是学习一种“技术”。如果你已经掌握了Excel，那么学习一些Access知识会让你的数据分析和处理能力有质的飞越——还在固步于Excel？我们都在用Access了！

案例下载地址

本书所使用的案例文件请到百度云盘http://pan.baidu.com/s/1o6r1LGm下载。关注“大数据玩家”公众号，回复“×××××”，也可以获得资源链接。

林书明

15.9.18

目　录

内容简介

前言

第1章　小饭馆也要信息化

1.1　饭馆里的数据

1.2　规范的数据才有价值

第2章　数据规范化

2.1　拆分数据表格

2.2　对小张的建议

第3章　Excel的故事

3.1　Excel最后的演出

3.2　Excel数据处理过程

3.3　Excel搞不定

第4章　Access登场

4.1　为什么是Access

4.2　导入数据到Access

4.3　重新“组装”数据

4.4　Access简单查询

4.5　Access高级查询

4.6　时间去哪了

4.7　根据时间做决策

4.8　人人都是程序员

4.9　让用户操作更方便

4.10　添加、修改和删除

第5章　关系型数据库

5.1　表格之间的原生关系

5.2　Access的核心

第6章　设计报表

6.1　创建报表

6.2　制作打印按钮

第7章　按钮背后的故事

7.1　不写代码也编程

7.2　用宏导出Excel报告

7.3　那些隐藏的宏操作

结束语

第1章　小饭馆也要信息化

本章内容提要 ：每天，我们用自己最熟悉的方式记录各种数据：日常花费、待办事项、物品的清单，等等。如果没有长远的规划，随着数据量的增加，现在可以灵活管理的数据也许在将来会变得不那么容易处理，甚至变得一塌糊涂！所以，从现在开始，你就需要对现有数据进行必要的规范化改造。

1.1　饭馆里的数据

小张在一个不大不小的公司每天过着朝九晚五的生活，工作单调乏味，毫无激情与乐趣。某年某月的某一天，小张终于决定创业，开了一家小小的以送餐业务为主的饭馆。

饭馆位于一个很大的居民区，该居民区有五个大院（1号院至5号院），每个大院有几十栋高层。饭馆的主要业务就是给住在这里的居民和商户提供送餐服务。目前来看，小张的生意不错，前景良好。

随着生意的转好，小张原来那种手写笔记的管理方式渐渐显得力不从心，决定用电脑来管理客户和订单信息，虽然市场上已经有现成的饭馆管理软件出售，但小张并没有购买那些现成软件的打算，这样做的理由是：

（1）刚开始创业，以节俭为原则，暂时不想花这份儿钱；

（2）商品化的软件程式化，难以应付自己将来可能的业务创新；

（3）小张对Excel比较熟悉，觉得Excel一定能胜任饭馆的数据管理工作。

下面的数据就是小张饭馆里的Excel格式的订餐数据。在这里，本书作者要提示大家，要学习数据库知识，最重要的一点就是对数据的了解，因此，为了让大家快速了解数据，不让我们有限的脑力被无关的业务细节所干扰，作者对小张的订餐记录做了一些非常必要的简化处理。目前，小张饭馆里的数据描述如下，具体如图1-1所示。

（1）Excel表格里当前只有三位客户：张3、李4、王5。

（2）张3、李4、王5各有3、4、5次订餐记录（为了好记）。

（3）每次客户订餐都有一个唯一的订单编号。

（4）送餐年月日的日期部分和订单编号的最后一位相同（为了好记）。

图1-1

我们之所以对订餐记录这样简化，其目的是能够让我们尽快地熟悉数据，让随后的Access的学习过程变得轻松。这里的数据虽然经过了简化处理，但可以确信的是，这样的处理并不影响将来我们把本书中的理念应用于各自的工作实践。要知道，大家在继续阅读下面的内容之前，熟悉客户订单表格中的数据是必须的。

作为一个饭馆，除了客户订单外，菜品清单（也就是饭馆的“菜单”）当然是必不可少的，如图1-2所示的表格就是小张饭馆的菜品价格表。

图1-2

一份客户订餐记录，一份菜品清单表，这就是目前小张饭馆的全部基础数据。饭馆在小张的悉心经营下，生意越来越好，很快就积累了几千条记录（我们并没有把几千条记录展示在这里，那样做只能扰乱我们的关注点，这里的十几行数据就足以说明问题了）。

1.2　规范的数据才有价值

有一天，小张想对饭馆的经营状况进行盘点（时髦点儿的说法叫作“数据分析”），他首先想要知道的就是：汇总当前“已完成”和“未完成”的订单总金额。也就是说，小张忙活了半天，要算算自己的营业收入如何。

小张认为，饭馆每天的经营数据已经用Excel保存成了电子格式，用计算机分析起来应该不成问题，然而，一旦真正做起来，事情似乎没有想象得那么简单。

作为对于Excel比较熟悉的我们，如果饭馆的订单数据存储成如图1-3所示的格式，那么，在Excel中，只需一个SUM（）函数就能汇总出一份订单的总金额。

图1-3

然而，不幸的是，小张为了当时记录数据的便捷，客户的订餐数据是如图1-4这样记录的（以订单记录中的DD-00012号订单为例）。

图1-4

在图1-4所示的客户的订餐记录中，客户所定的菜品“以文本格式”放在一个单元格中，几乎无法用Excel函数进行数据汇总。小张忽然意识到，自己的Excel的数据记录和管理方式也许存在严重的问题，为了避免走更多的弯路，“钱来”向我求助。

在了解了他的问题后，我半开玩笑地说：“混乱的数据不能产生价值，规范化的数据才有意义。如果你想一劳永逸地解决这个问题，必须进行‘业务流程再造’！”

我对他说，如果你想避免将来数据管理的麻烦，就必须从开始就要“规范化”你的数据。作为完全没有数据库基础知识的小张，我不能给他灌输太多数据库方面的术语，好在有实实在在的数据，再加上小张对 Excel也比较了解，我可以结合具体的数据把如何规范化数据一步一步地演示给他看。

我对小张说，为了能够灵活地对数据进行分析和处理，如果能把客户订餐记录的订单表格（图1-5中的表1）和菜品价格表（图1-5中的表2）结合起来，把一条记录拆成多条记录，变成图1-5中表3这样的数据格式就好了，这样，我们就可以轻松地汇总每种菜品的“销售数量”和“销售额”了。

图1-5

在图1-5中，“表3理想表格”中：

（1）“数量”列来自“表1 订单记录”中的“所定菜品”一列；

（2）“单价”列数据来自“表2菜品价格”；

（3）“数量×单价”是新生成的计算列。

在图1-5中，为了使数据适合 Excel 处理，我们仅以客户订餐记录中的DD-00012号订单为例，我们把“表1”中的“一条记录”（DD-00012号订单）经过处理并添加必要的信息后改造成了“表3”中的“三条记录”。

读到这里，你也许会产生这样的疑问，“表3”乍看起来并不十分理想！如果每次接到客户的订餐电话都这样记录在 Excel中，那么，本来一行数据就能解决的问题岂不需要输入多行才能完成？并且其中的“客户姓名”、“客户地址”、“联系电话”、“要求送餐时间”等信息还要重复输入很多次。

你的顾虑是符合逻辑的，可是，接着阅读本书随后的章节就会发现，“表3理想表格”并不是让我们手动输入客户订单，我们并不会直接在这个表格中输入客户订餐数据。这个表格是为了便于数据汇总，通过Excel或Access的表格关联技术“自动”生成的！

第2章　数据规范化

本章内容提要 ：大自然看起来纷繁复杂，但本质上只是百十个化学元素的不同组合；数据五花八门，我们也需要拆解出适于数据处理和分析的基本元素，然后再合理地组合起来！然而数据的拆分并非越细越好，适可而止才是正道。

2.1　拆分数据表格

图2-1是小张饭馆客户订餐记录中的一条，面对下面这样的客户订餐记录，观察“所定菜品”列中的内容。这里，表示菜品名称的“文本”和表示菜品价格的“数字”混合在同一个单元格内，根本无法进行自动数据汇总，这是很讨厌的事情，所以必须进行改造。我们必须把“能够参与计算的数字”和“不能参与计算的文本”分离开来（这是一条通用原则，在Excel中也适用哦）。

图2-1

作为第一步，我们把客户订单表格中的“所定菜品”列中的内容单独拆分出来形成图2-2中的表格。为了和被拆分表格建立“关联”，我们在拆分出来的表格中也包含了“订单编号”列。这样，我们就可以对于每一条客户订餐记录，都能够以“订单编号”作为关联，到所定菜品表格中查找该订单编号下的所有菜品，如图2-2所示。

图2-2

下一步，我们把“所定菜品”中的“数量”分离出来单独形成一列。同时，由于图2-2中“订单编号”合并单元格在Excel中不便于进行筛选、分组等数据分析操作，因此，我们的所定菜品表格最终改造为如图2-3所示的形式。

图2-3

有了改造后的每一个订单号的所定菜品及其数量的表格，再结合图2-4所示的饭馆菜品价目表（菜单），就可以在Excel中，利用VLOOKUP（）函数，把每种菜品的价目“抓取”到每一订单下所定菜品的表格中。

图2-4

进一步，在此基础上，我们还可以添加新的一列，计算出每一种菜品的“数量×单价”，如图2-5所示。

图2-5

就这样，最原始数据表格中的每个订单的“所定菜品”列，拆分出来并整理成上面的这个样子后，为了重新组合成理想中的、便于数据汇总的表格，可以通过“订单编号”的关联关系“重新组合回去”，形成如图2-6所示的表格。

图2-6

去除合并单元格后，整理形成如图2-7所示的表格。

图2-7

我们发现，曾被作为两个表格的“关联列”，第6列的“订单编号”和第1列内容重复，此时第6列的“订单编号”已经完成了它的历史使命，可以不要了。

最后，我们的整理出的“理想表格”如图2-8所示。

图2-8

为了方便大家理解，综上所述，单独一条客户订餐记录的整个拆分组合过程如图2-9所示。

图2-9

这里，我们只拿一个订单（订单号DD-00012）作为例子讲解如何把订单记录表格拆分组合成便于数据汇总的格式。如果对所有订单进行拆分，其拆分过程如图2-10所示。

在图2-10中，表1的客户订单拆分成了表2 和表3，然后，表3进一步整理成了表4的格式。

由于小张在记录饭馆业务的表格设计时没有想到将来的数据汇总需求，因此才造成了现在的麻烦，不得不对已有数据进行“规范化”整理。庆幸的是，这只是一次性的工作，规范化工作完成以后，新的数据就可以按照规范后的方案进行录入。

业务数据规范化的工作完成后，我们得到了表2和表4，接下来的问题（也是作为本书重点讨论的问题之一）是：如何利用计算机，以表2和表4为基础，整理出我们所期望的、便于进行各种数据汇总的“理想表格”？

图2-10

作为对比，为完成这个任务，我们介绍两种方法：一种是 Excel方式，一种是Access方式。通过这两种方案的讲解，我们会亲身感受Excel和Access的差异及Access在数据管理方面的优越性。

2.2　对小张的建议

在正式开始解决上一节的问题之前，本着帮人帮到底的原则，我在了解小张饭馆的基本情况后，又进一步了解了饭馆的原材料采购的管理。

关于饭馆的原材料采购，从小张处得到的反馈是：由于目前数据记录得不规范，在原材料采购方面基本无法进行准确的数据分析，食材的采购基本上是靠估计。所以，经常出现因为采购过量，过了保质期不得不扔掉，或者，因为食材准备不足而不得不推掉一些订单的情况。不过，由于目前饭馆规模还不大，一切还在可控范围之内。但在我的“开导”之下，小张确信，如果能把饭馆的数据有效地管理起来，用数据支持采购决策，恐怕效果会好很多。

根据小张的描述，为了优化饭馆的原材料采购流程，我建议小张准备一个“原材料清单”的表格。注意，这个清单不是饭馆的菜单，而是表示饭馆菜单中“各种菜品的原材料组成”的清单。

这个“原材料清单”表格列出了每一个菜品的原材料“组成成分”及其各自“用量”，有了这张表格，我们就可以结合“客户所定的具体菜品“与”菜品的原材料清单”，推算出（已经发生的和预测的）饭馆每个时间周期的原材料消耗量，实现饭馆食材采购的数据化管理，从而避免原材料采购方面的“拍脑门”决策。

在我的指导下，小张整理出下面这个饭馆当前经营的所有菜品的“原材料清单”。通过这个表格，我们可以知道每种菜品的原材料组成和用量。大家可千万别小看了这个表格，在制造业中，这个表格可以算得上企业的“核心数据”之一，在企业管理中发挥着重要作用。我们继续往下学习，你就会逐渐理解这个表格的重要。

好了，现在我们回到这个“原材料清单”的表格，如图2-11所示以夫妻肺片为例：我们观察到，每份夫妻肺片的原材料为牛肉150克、牛杂100克、调味包1袋。我们说过，在管理上，这个表格十分重要，它有一个专有名称，叫作“产品物料清单”，英文叫作BOM（Bill of Material）。

现在我们在小张的饭馆里引入了菜品的“原材料清单”的表格“物料清单”概念，接下来，我们正式对小张的饭馆实施“业务流程再造”！哈哈，话题似乎扯大了点，不过，意思差不多。

图2-11

第3章　Excel的故事

本章内容提要 ：经过对小张饭馆Excel数据的规范化处理，Excel的能力似乎也不差嘛！但是，即使Excel在某一方面的能力再强，也逃不脱它在另一方面能力的局限性。本章是Excel的告别演出，也很精彩。

3.1　Excel最后的演出

通常来说，人们一旦习惯了某种工具，他就会试图用这种工具解决尽可能多的问题，而不大愿意花一些时间去学习一种全新的、更适合的工具。我们看到一些Excel熟练用户经常用它做一些原本不应该它来做的事情。诸如，用Excel来绘制办公室甚至工厂布局图、用Excel制作手工编织图样……甚至，有人竟然用Excel绘画！

小张也不例外，他觉得，如果Excel 能够解决它目前关于饭馆数据分析的问题，何必花时间去学习一种全新的工具呢？我觉得也有道理，那么让我们先试试Excel的解决方案，用事实说服他吧。

我们用 Excel对饭馆的业务数据进行了基本的规范化整理后，最终形成了 5个不同内容的表格，每个表格保存在单独的 Excel工作表里。并且，为了将来使用方便，我们给每个表格取了一个规范的名字。当然，你完全可以随心所欲地给表格命名，但规范化的表格名称会让你的数据更易于管理，通常的做法是给表格名称加一个表格编号作为前缀，这里的“T”代表Table。这些表格的名字分别是：

（1）T0010_业务数据记录

记录“订单编号”、“客户姓名”、“客户地址”、“联系电话"、”要求送餐时间"，以及订单是否履行完毕的“备注”等信息。

（2）T0020_业务订单详情

记录每一个“订单编号”下，客户所定菜品的详情，包括“所定菜品”名称和“数量”，每一个菜品占据表格一行，在Excel中，我们可以用VLOOKUP（）函数，以该表中的“订单编号”为关键字与表T0010中的“订单编号”建立联系。

（3）T0030_菜品价目表

包括每个“菜品”的名称和对应的“价格（元）”。在Excel中，我们可以用该表的“菜品”与表T0020中的“所定菜品”用VLOOKUP（）函数建立联系。

（4）T0040_原材料清单

该表格包括每一“菜品”的“原料”名称、单位菜品原料消耗的“数量”和每一种原料的计量“单位”。这里需要我们注意的是，在Excel中，我们虽然可以用该表的“菜品”列内容与表T0020中的“所定菜品”列内容建立联系。但是，Excel中的VLOOKUP函数却难以解决这里的一个菜品对应多种原材料的“一对多”的问题！这也是我们最终不得不采用Access解决问题的原因之一。

我们把已经初步规范化的四个表格分别保存至 Excel的独立工作表中，工作表的名称就是表格的名称，四个表格的内容和最后的Excel工作簿如图3-1所示。

图3-1

下面，我们的任务是：基于这四个表格，用 Excel为工具，把它们重新“组装”成适合进行数据汇总和分析的“理想表格”。

在这里，请允许我先做一下“剧透”：基于这些表格，Excel方案只能完成小张所期望的任务的一半！而任务的另一半，实在不适合在 Excel里完成，因此也就引出了本书的主角：Access。

下面先看看 Excel是如何帮助我们完成这“一半”工作的。在介绍用 Excel处理数据的详细步骤之前，我们先看看用 Excel所能完成任务的最终形式。参照图3-2，基本逻辑如下：

从工作表“T0020_业务订单详情”出发，用Excel中的VLOOKUP（）函数，分别：

（1）以“订单编号”作为查找关键字，从工作表“T0010_业务数据记录”中提取出“客户姓名”、“客户地址”、“联系电话”、“要求送餐时间”、“备注”等信息。

（2）以“所定菜品”作为查找关键字，从工作表“T0030_菜品价目表”中提取出每个订单编号下所定的每个菜品的“价格（元）”。

图3-2

如果你对 Excel 中的 VLOOKUP（）函数比较熟悉，理解了上面的思路后，你就可以直接跳到下一章阅读了，因为 Excel也就只能帮到你这里了。但是，我还是建议你快速地扫描一下Excel方案的实现过程，万一里面有你不曾用过的Excel技巧呢？

如果你坚持用Excel处理数据的话，由于Excel功能上的局限性，这个Excel解决方案所能实现的最终表格，并不能达到我们的“理想表格”，它只能对饭馆的每一菜品的“销售数量”和“销售额”进行汇总分析，但是要对原材料的需求进行分析，它却无能为力。

3.2　Excel数据处理过程

下面我们详细介绍如何用Excel，基于工作表“T0020_业务订单详情”、“T0010_业务数据记录”和“T0030_菜品价目表”三个表格，生成上一节所示的Excel所能完成的最终表格。

首先，给每个工作表的数据区域指定一个名称：在“公式”选项卡的“定义的名称”组中，单击“名称管理器”，如图3-3所示。

图3-3

此时弹出“名称管理器”对话框，单击“新建”按钮，如图3-4所示。

图3-4

此时弹出“新建名称”对话框，单击该对话框下部“引用位置”文本框右侧的“单元格区域”选择按钮，选择工作表“T0010_业务数据记录”的A:F列。我们之所以选择A到F的整列，而不是精确地选择A1:F13数据所覆盖的区域，是因为考虑到将来在表格末尾追加数据的时候，不必再重新修改 Excel名称所指定的单元格区域范围。

在“新建名称”对话框的“名称”文本框处，给刚才所选择的单元格区域自定义一个单元格区域名称，这里我给的名称是“RN_业务数据记录”，这里的自定义前缀“RN”的意思是“Range Name”的缩写，如图3-5所示。然后单击“确定”按钮，此时Excel返回“名称管理器”对话框。

图3-5

在Excel“名称管理器”对话框下方的名称列表中，我们看到，列表中出现了刚才自定义的名称，如图3-6所示。单击“名称管理器”对话框右下角的“关闭”按钮，关闭“名称管理器”对话框。完成Excel单元格区域的自定义名称操作。

图3-6

现在，单击 Excel公式编辑栏左侧的“组合框”右侧的展开按钮（这个组合框在Excel中叫作“名称框”），你所自定义的Excel单元格区域的名称将会显示在这里。选择刚刚定义的名称选项，你会发现，该名称所代表的单元格区域被选中了，如图3-7所示。

图3-7

单元格区域的名称让我们有机会用自定义的、有意义的名称代替我们所指定的单元格区域的名称。我们在完成 Excel单元格区域自定义名称操作后，以后就可以在Excel公式和函数中，用容易理解的Excel自定义名称代替枯燥的、难以记忆的“行号列标式”的Excel单元格区域引用。

重复前面的步骤，分别定义其他几个工作表数据区的自定义名称。全部名称定义完成后，单击名称框右侧的展开按钮，我们会看到如图3-8所示的Excel自定义名称列表。分别选择每个名称，查看名称的定义是否正确。

图3-8

完成自定义名称的操作，我们就可以用Excel中著名的VLOOKUP（）函数把位于不同表格中的有用数据“抓”到一起来了。由于我们给每一个表格的数据都命名了一个容易理解的名称，因此，在 VLOOKUP（）函数中引用相应的单元格区域时，可以使用对应的自定义名称代替。参见图3-9，其中D:H列中的内容来自数据区域“RN_业务数据记录”；第I列中的内容来自数据区域“RN_菜品价目表”。

小张得到这个表格后，就可以通过 Excel公式函数或者数据透视表按照“订单号”、“所定菜品”、“客户姓名”、“备注”等内容对菜品的销售数量和销售金额进行汇总分析。

图3-9

3.3　Excel搞不定

到现在为止，我们通过对饭馆的原始数据进行拆分、整理，已经能够方便地对饭馆的销售数量和销售金额按照各种分组标准进行分类汇总了。以后，饭馆销售记录按照新的数据组织形式录入和管理，那么就可以随时进行销售数量和销售金额的汇总和分析了。

但是，在前面所提到的 Excel数据管理方案中，还有一个重要的问题没有解决，那就是，小张希望新的数据管理系统能够把食材（菜品原材料）的采购也有效地管理起来，能够根据客户“所订菜品的数量”和“要求送达时间”准确地推算出所需的：

① 原料的采购数量。

② 原料的运抵时间。

这样，就可以避免以前那种对食材采购的“拍脑门、估算式”决策，这将是饭馆经营管理上的一个“巨大提升”。

然而，不幸的是，即使你对Excel很熟悉，单纯用Excel解决这个问题也并非易事。所谓“并非易事”，意思是说：通过“所定菜品数量”和“每种菜品的原材料清单”推算各种原材料的需求量，虽然不能说不可以用 Excel完成，但非常麻烦！

一言以蔽之，Excel不是解决这类问题合适的工具，在微软Office组件中，解决这类问题最有效的工具是Access。

参照图3-10，通过“客户所定菜品数量”推算“原材料需求”问题的实质是：把客户订单中的每一种菜品对照“原材料清单”进行“分解”，从而得出各种原材料的需求。

我们拿编号为“DD-00001”的订单为例，该订单所定的菜品为“青椒鸡蛋”和“米饭”各1份。

对于每一份“青椒鸡蛋”，我们对照“T0040_原材料清单”可知，需要 200克青椒和150克鸡蛋；对于每一份“米饭”，对照“T0040_原材料清单”可知，需要150克米饭（假设米饭是外包的，已经做好了的成品）。

按照这个逻辑，我们就可以得到“表3　业务订单按原材料分解”虚线框中的表格。对于编号为“DD-00001”的特定订单，从图3-10中“表1 T0020_业务订单详情”虚线框A中的内容，结合“表2　T0040_原材料清单”生成“表3　业务订单按原材料分解”虚线框B中内容的过程叫作“物料清单分解”。

这个过程，Excel几乎无能为力。而在Access中，却可以自动完成！

图3-10

如果把“表1 T0020_业务订单详情”中的每一条记录都按照“表2　T0040_原材料清单”进行分解，我们就会得到“表3　业务订单按原材料分解”这个表格。而“表3　业务订单按原材料分解”则是适合进行原材料采购分析的理想格式表格。

在下一章里，我们将抛弃Excel解决方案，详细讲解Access处理饭馆经营数据的过程。在下一章，我们将把在前面已经做过部分整理的Excel数据搬到Access中，并根据饭馆的业务规则，建立表格之间的关系，设计一个可操作的程序界面，最终用Access建立一个小型的“饭馆业务管理”数据库软件。

第4章　Access登场

本章内容提要 ：本章将演示一个完整的Access小型数据库软件的开发案例，让你了解Access的能力和Access背后的数据之间的逻辑，让你具有开发小型数据库软件的初步能力。你会了解，Excel与Access，虽然都能处理数据，但Access还能通过建立原生的表间关系，自动“管理”数据，两者的逻辑完全不同。

4.1　为什么是Access

很多时候，我们也许过于迷信 Excel的能力（或者是潜意识里出于新事物的抗拒），很多问题都习惯性地试图在Excel里解决，因此出现了各种关于数据分析和数据处理的复杂，甚至古怪的Excel解决方案。

如果你在某些Excel报告里见到多重IF（）函数嵌套、复杂的Match（）、Index（）函数联合，以及令人费解的数组公式，甚至本无必要的VBA编程，等等，请不要感到奇怪。我们只能期望，你的前任不要给你留下一堆这样难以理解和维护的Excel报告。

事实上，对于很多数据分析和处理的问题，与其花大量时间寻求复杂古怪的Excel解决方案，不如换一个思路，学一些Access知识，利用Access的特有能力，化繁为简，化难为易，让我们在数据分析和处理方面的功力大增！

Access，作为微软Office家族中的一个重要组件。它与Excel、Word、PowerPoint共同称为微软Office软件中的“四大金刚”。

Access是世界上最流行的小型桌面数据库管理软件，在国外，特别是英语国家，在管理中已经得到了广泛的应用，它那“让人随手就能设计一个小软件”的特性让人印象深刻。

对于尚未熟悉Access的朋友，我们姑且认为它是一个超级Excel，它不但能存储多个数据表格，并且能够轻松实现不同数据表格之间的“联动”。此外，Access还能在不编写任何程序代码的情况下，设计出看起来还不错的软件界面，并且，Access还能实现自定义报表功能，可以说Access是一个理想的部门或个人提升生产效率的工具。

Access虽然号称“小型”数据库管理软件，但“麻雀虽小，五脏俱全”，学习它几乎能够体会到数据库软件的全部乐趣，在数据库无处不在的今天，学习Access软件是快速理解数据库思维的最好方式。

总之，Access能够解放部门和个人的生产力，让工作变得更轻松！现在，让我们带着上一章节提出的问题，看看Access是如何解决这些问题，并且做得更好的！

4.2　导入数据到Access

在本书中，作为示例，我们使用的是Access 2013。事实上，Access 2007、Access 2010、Access 2013的界面布局基本类似，只是功能上有少许差异（但在本书中几乎涉及不到这些差异）。如果你的计算机上安装有Access软件的上述任一版本，均可使用本书。

启动你的Access软件，我们可以看到Access新建文件的界面。在这个界面中有多个选项，其中包括一些可以直接使用的数据库模板。事实上，在你没有理解Access数据库的基本原理之前，使用这些模板并非易事！不过，如果你对这些模板感兴趣，学习这本书里的知识是非常有帮助的。

在这里，我们希望建立一个单机运行的Access数据库文件，所以，请单击“空白桌面数据库”图标，如图4-1所示。当然，如果小张的业务进展顺利，开了多家连锁店，则可以尝试建立一个“自定义Web应用程序”（只有Access 2013才有这个选项），但本书不涉及“自定义Web应用程序”的内容。

在弹出的“创建”对话框的“文件名”处给我们的数据库文件命名为“A010_My_Small_Shop.accdb”。为了便于将来的管理 ，我喜欢在文件名称前面加一个编号作为前缀（这里的前缀是：A010），如图4-2所示。

图4-1

图4-2

此时进入 Access默认的创建数据表格界面，并默认表格的名称为“表 1”。Access中的表格目前可以粗略地认为相当于 Excel中的工作表（其实更像 Excel中的“列表”或“表格”），如图4-3所示。

因为我们的数据已经保存在Excel中（上一章中介绍的所有数据），现在只需将这些数据导入Access，而不需要从Access创建表格做起，所以我们单击“表1”创建界面右上角的“×”图标关闭“表1”界面。

Access中的表格是Access存储数据的地方，这一点和Excel有些类似。与Excel不同的是，在Access中，不但能存储一个一个的表格，而且还能通过Access界面建立“表格与表格之间的关系”，正是这些很多“已经建立了关系”的数据表格，形成了“关系型”数据库。

图4-3

接下来的任务是把Excel中的数据表一个个地“搬”到Access中。首先，在Excel中选中工作表“T0010_业务数据记录”的数据区，然后按下快捷键［Ctrl+C］复制整个数据区域，如图4-4所示。

图4-4

然后，回到Access界面，在Access界面左侧的“所有Access对象”字样下的空白处单击鼠标右键，此时弹出Access鼠标右键快捷菜单。在Access鼠标右键快捷菜单里选择“粘贴”命令，如图4-5所示。

图4-5

这时弹出一个Access对话框，让你确认你所粘贴的数据是否包含列标题，因为Excel中的数据表中已经包含列标题，所以我们单击“是”按钮，如图4-6所示。

图4-6

在该对话框中单击“是”后，Access会提示数据已经成功导入，如图4-7所示。

图4-7

此时，在Access界面左侧的“所有Access对象”字样下，我们会看到刚刚导入的数据表格。通常情况下，从Excel中导入Access的数据，会以其工作表的名称作为Access数据表的名称，如果你想修改名称，可以通过鼠标右键单击表格名称重新命名。

双击导入的数据表格会打开刚刚导入的数据表，我们能够在Access界面右侧看到导入数据的详细内容，如图4-8所示。

图4-8

按照前面描述的同样步骤，我们导入Excel中的另外一个表格“T0020_业务订单详情”，导入后的效果如图4-9所示。

补充：如果从Excel中导入到Access中的数据量比较大，除了使用本节介绍的“粘贴法”导入外部数据，还可以选择Access中的“外部数据”选项卡，在“导入并链接”组中单击“Excel”选项，按照获取外部数据向导操作即可。

图4-9

4.3　重新“组装”数据

我们已经从Excel中的Access导入两个表格，分别是“T0010_业务数据记录”和“T0020_业务订单详情”（图4-10中由左边第1个和第2个表格）。我们在上一章中曾经介绍过如何基于第1个和第2个表格，利用Excel中的VLOOKUP（）函数生成第3个表格。在这一章，我们将完全抛弃Excel，学习如何使用Access中的“查询”功能，以简单“连线”的方式自动把两个表格“组装”成一个新表格。

查询功能是Access精华之一，它能够让我们以“可视化”的方式建立两个数据表之间的关联，从而轻松地从两个（或者多个）表格中提取所需要的数据。毕竟文字描述有些抽象，还是让我们接着以案例的方式来了解这个神奇的功能吧。

图4-10

目前，在我们现有的Access数据库中已经存在了两个数据表，一个是记录订单号码以及客户信息的“T0010_业务数据记录”；另一个是记录每一个订单下所定购的所有菜品的“T0020_业务订单详情”。我们通过观察发现，这两个表格通过各自的“订单编号”列中的内容相互关联（如果你已经忘记了两个表格中的内容，请再次熟悉一下）。

在Excel中，我们从“T0020_业务订单详情”数据表出发，以“订单编号”为关键字，用Excel中著名的VLOOKUP（）函数，到“T0010_业务数据记录”中抓取客户信息，把两个表格中的关键内容“组装”在一起。

而在Access中，我们将从“T0010_业务数据记录”数据表出发，结合数据表“T0020_业务订单详情”，把两个表的“订单编号作为关联”这个事实告诉Access，利用Access可以实现“一对多”的特殊能力，把两个数据表“组装”在一起。以下是具体的实现步骤：

在Access界面中，选择“创建”选项卡，单击“查询”组中的“查询设计”按钮，进入Access查询设计界面，如图4-11所示。

图4-11

在Access查询设计界面中，会出现一个“显示表”的对话框（如果没有出现，可以在鼠标右键菜单中单击“显示表”选项），在“显示表”对话框中会列出Access中已经存在的所有数据“表”和已经存在的“查询”（我们正在设计的这个查询完成后，会出现在这个对话框的“查询”下），如图4-12所示。

我们分别双击对话框中两个数据表的名称，把它们依次添加到Access查询设计界面上。然后单击“显示表”对话框右上角的“×”图标，关闭“显示表”对话框。

此时我们看到，Access查询设计界面上显示的不是所添加的数据表格中的具体数据，而是每一个数据表格的“结构”，即每一个数据表中包含哪些列，以及对应的列标题是什么（在数据库中，每一个数据表格的列标题对应的专业术语叫作“字段”）。我们透过这个表格结构，可以大致了解每一个表格中存储的内容。

下面，我们要做的是Access操作的关键步骤：用“连线”的方式建立两个表格之间的关联。

前面我们已经多次提到，表格T0010和T0020之间的联系是“订单编号”列，要让Access帮我们处理数据，必须要把这种关联关系明确地告诉Access。

图4-12

如何告诉Access，两个表格之间存在这种关联关系呢？你也许会觉得很有趣，那就是：Access软件的设计者允许我们通过一种“连线”的方式进行这种关联关系设置。

首先，单击表格T0010的“订单编号”列（或者叫作“字段”）名称，并保持鼠标按下状态的同时，将“订单编号”字段名称“拖拽”到表格T0020的“订单编号”字段名称上面，松开鼠标，此时，我们看到，两个数据表格的“订单编号”字段名称之间出现了一条“连接线”，如图4-13所示。

就这样，我们就以“连线”的方式把表格T0010和T0020之间的关联关系明确地告诉Access了。

在进行下一步操作之前，我们先来仔细观察一下Access的“查询设计”界面。在这里，为了让 Access查询设计界面的控件更大一点，我们单击“所有 Access对象”右侧的“<<”图标，折叠了“所有Access对象”列表栏（需要的时候可以单击“>>”图标恢复）。

图4-13

我们发现，在Access的“查询设计”界面的上部是我们选择的每一个表格的结构图和已经建立的不同表格之间的关联关系（也就是那根连接线）。在界面下部，是查询设计网格，在查询网格中我们可以对查询进行多种设置，查询设计网格有很多行，在这里，我们先来粗略地了解一下每一行的各自功能：

（1）第一行的名称叫作“字段”，表示哪些字段会显示在将来的查询运行结果中（查询的执行结果将是根据查询定义“按需”生成的一个数据表）。

（2）第二行的名称叫作“表”，表示所选择的字段来自于哪个具体的数据表。要知道，不同的表格里面可能含有相同名称的字段（比如，在这里，两个表格中都含有“订单编号”字段），只有知道每一个字段来自于哪个特定的表格，才不致造成混淆。

（3）第三行的名称叫作“顺序”，表示查询运行结果按照什么标准进行排序，排序与否？升序还是降序？

（4）第四行的名称叫作“显示”，该行决定某特定的字段是否显示在查询运行结果里，还是仅仅用来排序或者用来设置查询条件（即排序不显示在将来的查询执行结果中）。

（5）第五行的名字叫作“条件”，该行的作用有点像Excel中的筛选条件，我们可以在这里对查询结果进行筛选，决定哪些数据记录（行）显示在查询结果里。

（6）第六行的名字叫作“或”，这通常和第五行的“条件”配合使用。意思是，满足第五行“条件”的数据“或者”满足第六行“条件”的数据“都”需要显示在查询运行结果中。

关于Access查询设计网格中的这些项目的设置，我们会在将来的章节中逐步介绍（其实，如果你使用过 Excel中的“高级查询”功能，那么你对这个查询界面应该感到似曾相识了）。所以，现在不要有任何压力，在这本书中的很多知识，我们会在不断重复中学习，逐渐掌握，我们只要轻松向下阅读本书就可以了。

现在，我们把数据表T0010和T0020中的所有字段都拖拽到Access查询设计网格的“字段”行上。如图4-14所示。这样操作的结果是，当运行查询时，我们会在查询结果中看到两个表格中的所有列（字段）。

图4-14

选择“设计”选项卡，单击“结果→视图”图标下面的下拉箭头，在展开的选项中选择“数据表视图”（对应的，设计查询时的视图叫作“设计视图”），如图4-15所示，我们将会立即看到查询设计的运行结果。

图4-15

在Access查询运行结果中（如图4-16所示），我们看到两个表格中的所有字段（列）都包含在其中，并且发现，其中有一列（“订单编号”）的内容是重复的，但通过下一列表格的标题可知，它们分别来自不同的表格（数据表T0010和数据表T0020）。就这样，我们以建立表格间“连线”的方式得到了适合我们进行菜品的销售数量和销售额分析的“大表格”。

仅仅在两个表格之间建立一条连接线，好像玩着“连连看”游戏，就实现了Excel中需要写好几个VLOOKUP（）函数才能实现的效果。这是不是有点神奇？下面，我们必须介绍一下，当我们在Access里做“连连看”游戏时，Access背后发生的故事。

事情的发生是这样的（Access工作原理）：在Access中，当我们通过“连接线”建立了两个数据表之间的关联关系，并执行查询后，Access依据我们所关联的字段“组合”两个数据表中的数据，其规律是：

图4-16

读取左表（具体到这个例子是表格T0010）中的第一行数据（专业说法是“第一条记录”）中关联字段中的内容，然后到右表中（表格T0020）查看所有记录，如果发现右表中的某条记录的“关联字段”中的内容，与左表中关联字段中的内容相等（或者说“相同”），则将这两条记录首尾相连，形成一条新的记录，显示在查询结果中。重复上述过程，对左表中的每条记录都做相同的处理，最终生成查询结果。

下面我们结合案例中的数据表格验证一下以上表述：观察左边表格T0010 中第一条编号为 DD-00001的订单，该订单在左表中只有一条记录，但是，如果以两个表格中的“订单编号”字段内容作为关联的话，在右表中有两条记录与之匹配，因此，在最终的查询结果中生成了两行记录（理解这个非常重要）。

关于Access查询的执行过程，如图4-17所示，在这里，我们可以结合“李4先生”的编号为“DD-00006”的订单进行理解。

图4-17

下面我们来比较一下，同样是基于两个数据表，生成基本类似的“大表格”，Access方案和Excel方案的不同点。

在Excel方案中，我们是从表格“T0020_业务订单详情”出发，以订单编号作为查询关键字，到表格“T0010_业务数据记录”表格中“提取”客户信息。之所以要“从表格T0020出发到T0010中提取信息”，是因为：Excel中的Vlookup（）函数只能胜任一对一匹配，不能做到“一对多”匹配，如果 VLOOKUP（）函数发现在所查阅的第二个表格中有两条记录满足匹配条件，只能显示第一条。这也是VLOOKUP（）函数一个久为诟病的弱点。

在Access方案中，由于Access和Excel的运行机制完全不同（见前面的解释），Access中的查询功能可以完美地解决两个数据表格之间的“一对多”关系（比如这里的一个客户订单对应着该订单下的多个菜品），因此，我们可以从表 T0010出发，通过“连线”的方式建立表格T0010与表格T0020之间的关联，利用Access特有的“一对多”能力，达到相同的目的。

这里我们可以看到，相对于Excel方案，Access解决方案显得更简洁，而且更符合人们的思维逻辑顺序。

再一次观察查询结果，我们发现，在查询结果中有两列来自不同表格的“订单编号”字段（第1列和第7列），在这里没有必要，我们可以只保留第一个。

选择“开始”选项卡，单击“视图”下方的展开按钮，在下拉选项中选择“设计视图”，回到查询设计界面。

图4-18

对于“查询设计”视图，我们应该很熟悉了，这个正是我们在设计这个查询时使用的界面。现在，选中第2个“订单编号”字段，单击鼠标右键，在弹出的快捷菜单中选择“剪切”命令（或者选择“设计”选项卡，“查询设置”删除列”命令）删除该字段，如图4-19所示。

图4-19

此时，选择“开始”选项卡，单击“视图→数据表视图”命令，我们看到第二个“订单编号”列已经不存在了，如图4-20所示。

最后，保存查询。单击 Excel界面左上角的“保存”图标，或者单击查询运行结果表格右上角的“×”图标，Access会提示你是否保存该查询设计。我们选择“是”按钮，如图4-21所示。

图4-20

图4-21

此时，Access会提示你给查询取一个新的名称。在这里，我给这个查询取的名称是“Q0010_客户订单详情”。通常，为了便于管理，我喜欢给Access中各种名称前面加一个编号作为前缀，这里的前缀“Q”表示Query（查询），如图4-22所示。

图4-22

查询保存完毕后，你会发现：在Access界面左侧的“所有Access对象”下面多了一个叫作“查询”的分类。分类下面有我们刚刚保存的查询“Q0010_客户订单详情”。双击这个查询名称，会立即得到该查询的运行结果，如图4-23所示。

在Access中，“查询”和“表”是两种不同的Access“对象”。所谓“对象”，就是一个个我们正在“研究”、“设计”或者“操作”的东西（这里的“对象”和我们通常所指的男女朋友的概念完全不同哦）。

我们已经看到，在Access中的“数据表”以及刚刚设计的Access“查询”都显示在Access界面左侧的“所有Access对象”下面了。

虽然双击Access查询名称我们可以得到一个数据表格，但是，Access查询与Access表不同的是，Access查询并不是以“实体”数据表格的形式保存在Access里面，而只是保存了一个如何从不同表格提取数据的“定义”。当我们双击查询名称时，Access会按照查询“所定义的规则”，重新从相关表格中“组装”数据并显示出来，所以，当底层表格的数据改变后，重新执行查询的时候，查询的执行结果会实时地反映底层表格发生的变化。

图4-23

至此，我们已经把“T0010_业务数据记录”和“T0020_业务订单详情”通过Access查询“Q0010_客户订单详情”重新组装到一起。下面，我们继续在查询“Q0010_客户订单详情”基础上进一步对数据进行汇总分析，建立一个“基于查询的查询”，该查询的目的是按照“客户”汇总其订购的各种菜品的总数。

选择“创建→查询”选项卡，在“查询设计”按钮，进入Access查询设计界面。此时会出现“显示表”对话框。其实我对这个对话框的名称有些不满，因为在这里显示的不仅仅是表，不如直接取名为“显示”更好一些。不管它了，因为该对话框有三个页标签，分别是“表”、“查询”、“两者都有”。在“表”标签下显示的是Access中的实体表；“查询”标签下显示的是Access中所有的查询名称；而“两者都有”是将表格和查询显示在一起。

现在，在“显示表”对话框中选择“两者都有”标签。这里列出了Access中所有表和查询名称。双击刚刚设计的查询名称“Q0010_客户订单详情”把它加入到查询设计界面中，如图4-24所示。

图4-24

接下来，如图4-25所示，把“客户姓名”、“所定菜品”、“数量”从查询“Q0010_客户订单详情”中拖拽到Access查询设计界面下方的查询设计网格中，然后，在查询设计网格任意位置单击鼠标右键，并在弹出的鼠标右键菜单中选择“汇总”菜单选项。

当在鼠标右键菜单中选择“汇总”选项后，我们发现，在查询设计网格中自动增加了一行，行标题为“总计”。并且，在该行的每个对应的字段下面显示“Group By”字样，意思是按照该字段分类汇总。

图4-25

毕竟Access还是不具备人类的智能，我们想要的结果是按照“客户姓名”与“所定商品”分组，对“所定菜品”的数量进行汇总，可是，它却把“数量”字段下面也给设置上了“Group By”。我们可以单击“数量”字段下方的“Group By”，在展开的下拉选项中选择“合计”，这才是我们想要的，如图4-26所示。

图4-26

选择“设计”选项卡，在结果“运行”按钮，可以看到如图4-27所示的查询结果，我们观察到，李4先生在小张的饭馆一共定过14份夫妻肺片、22份饺子和22瓶啤酒。

图4-27

单击Access左上角的“保存”图标，给新设计的查询命名为“Q0020_客户菜品汇总”。此时我们会看到，在Access界面左侧的“所有Access对象”的查询类别下面增加了一个我们刚刚设计的新的查询名称，如图4-28所示。

到目前为止，我们的数据在 Access中是这样流动的：基于数据表 T0010和T0020，设计了查询Q0010，基于查询Q0010设计了查询Q0020，如图4-29所示。这里需要强调的是：Access中的查询只是对Access中数据表的操作规则的一个定义，查询所依赖的实体数据表格中的数据发生变化时，重新打开查询，查询的运行结果也会动态地反映这种变化。

图4-28

图4-29

为了验证这个结论，我们在表格“T0010_业务数据记录”中新增一条记录，该客户的名字叫“赵6先生”，这个名字以前可从来没有出现过，“赵6先生”在小张的饭馆里下了一个单，所对应的订单号码为 DD-00013。具体细节见图4-30最后一条记录。

图4-30

在表格“T0020_业务订单详情”中，我们输入赵6先生名下的号码为DD-00013订单的细节：13份饺子和13份啤酒，如图4-31所示。

单击“保存”图标，保存修改并关闭两个数据表格，然后双击最后一个查询“Q0020_客户菜品汇总”运行查询，这时，我们在查询结果上看到：赵 6先生的数据已经汇总到了最终的查询结果上了（由于赵6先生只下了一次单，因此汇总数据就是订单数量本身），如图4-32所示。

图4-31

图4-32

4.4　Access简单查询

我们已经知道，Access中的“数据表”是实实在在存在的“实体”表格；而Access中的“查询”仅仅是基于实体表格，对实体表格操作规则的一个“定义”。Access每次执行查询时，都会按照查询所定义的规则对实体表进行操作，实时反映其所依赖的底层数据表的最新变化。下面，我们将继续研究Access中的查询功能。

在上一节所设计的查询Q0010中，我们得到了如图4-33所示的查询结果。

图4-33

现在，我们将基于这个查询进一步引入新的数据源表格（T0030_菜品价目表），建立一个基于“已存在的查询”和新引入的“实体表格”的新查询，以便让小张能够对饭馆的经营状况进行更加深入细致的分析。

参照本书前面介绍的Excel数据导入Access的方法，将Excel工作表“T0030_菜品价目表”中的数据粘贴到现有的Access中。导入完成后，双击新导入的Access表格名称，可以看到“T0030_菜品价目表”的数据如图4-34所示。

图4-34

选择“创建”选项卡单击“查询”组中的“查询设计”按钮，进入Access查询设计界面。在“显示表”对话框中，单击“两者都有”页标签，在列表中分别双击“Q0010_客户订单详情”（查询）和“T0030_菜品价目表”（实体表），将其添加到查询设计界面上方，如图4-35所示。

现在，我们从查询“Q0010_客户订单详情”出发，以查询Q0010的“所定菜品”字段和数据表T0030的“菜品”字段作为关联字段，利用Access查询功能将二者中的数据整合到最终的查询结果中。

图4-35

选择Q0010中的“所定菜品”字段并将其拖动到T0030中的“菜品”字段上面，然后释放鼠标。此时，查询Q0010和表格T0030之间出现了一条“连接线”，这表示两个表格对应的字段间已经建立了关联关系，如图4-36所示。通过这样的操作，Access在执行该查询时，将遵循该关联关系所确定的运行规则，即：“查询结果中只包含两个表中联接字段相等的那些行”。

如果想进一步了解该Access查询的执行细节，请参阅本书前面讲述的关于查询“Q0010_客户订单详情”设计过程的讲解。但在这里值得一提的是，这根“连接线”中还隐藏着一个“秘密”，也就是：当双击这根连接线时，会弹出一个“联接属性”对话框。

在“联接属性”对话框里，我们可以对“连接线”的联接属性进行修改以适应对不同实战场景的需求。

图4-36

在“联接属性”对话框中，我们看到，当我们“用鼠标拖曳的方法”在左右两个表格特定字段间建立关联时，Access默认设置的连接线属性是第一个选项，即：“1：（查询结果中）只包含两个表中联接字段相等的行”。其实，我们还可以根据不同的实战需求，将连接线的“联接”属性设置为选项2或者选项3。

在这里，我们要做的是，把两个表格中的所有字段拖放到查询网格的字段行中。在设计Access查询时，如果希望在查询运行结果中显示表格中的“所有”字段，只需从相关表格结构图中把“*”星号拖放到查询网格中的“字段”行即可，星号代表表格中的所有字段，如图4-37所示。

图4-37

查询设计好后，选择“设计”选项卡，在“视图→数据表视图”中运行查询，查询的运行结果如图4-38所示，这里我们看到，菜品的“价格（元）”已经出现在了相应的“所定菜品”之后。

保存该查询之后，我们看到，在Access界面左侧“所有Access对象”列表中新增加了一个名称为“Q0030_客户产品价格”的查询对象。

双击查询名称观察运行结果，我们发现，在查询结果中已经有了每一个订单下的各个“所定菜品”的“数量”和相应的“价格（元）”。现在，我们还需要一列“数量×价格（元）”，计算出每一个订单号下各个菜品的金额小计。

在Excel中，我们可以用公式解决该问题；在Access中，我们也可以用类似的方法完成任务，只是该任务不能在数据表视图中直接完成，而需要在Access查询设计视图中设计“Access表达式”来完成。

图4-38

在Access界面左侧的“所有Access对象”列表中，选择查询“Q0030_客户产品价格”，单击鼠标右键，在鼠标右键菜单中选择“设计视图”，进入Access查询设计视图，准备对该查询进行修改。

在Access查询设计视图下方，查询网格最后一个字段后面的空白处，单击鼠标右键，在鼠标右键快捷菜单中选择“生成器”选项，如图4-39所示。我们将利用Access提供的“生成器”向导帮助我们完成这个任务。

Access中的“生成器”向导就像婴儿用的学步车，是我们快速熟悉Access的一个绝好的工具，利用它，我们可以快速熟悉Access中各种表达式（Access中的“表达式”类似于 Excel中的公式，但却有很多不同）的写法。当然，当我们对Access操作比较熟练后，就可以抛开“学步车”，直接在需要的地方写出表达式了。

图4-39

下面，我们在弹出的“表达式生成器”对话框中构建我们的“Access公式”。在此之前，让我们先观察一下“表达式生成器”对话框的结构。

在该对话框的上部是我们设计的“Access公式”的地方。在对话框的下部，从左到右，分为以下三个部分：

（1）最左边，是名称为“表达式元素”的可展开式列表。在这个列表里，包含了当前Access数据库中的几乎所有元素。双击每一个元素名称，会展开该元素下所包含的下一级内容。

（2）中间的部分是名称为“表达式元素”的列表框。双击列表框中的Access元素，则该元素会自动填写到“表达式生成器”对话框上部的文本框中。

（3）右侧部分是名称为“值”的列表框，用来显示所选择元素的细节，默认显示该元素的“值”。

我们要编写的Access的表达式为：所定菜品的“数量”乘以“价格（元）”。我们知道，所定菜品的“数量”和“价格（元）”都是查询“Q0010_客户菜品价格”中的字段名称，因此，我们可以按以下方法寻找这两个表达式元素。

在“表达式元素”列表中双击“查询”命令，在展开的查询列表中，双击查询名称“Q0010_客户菜品价格”，此时，表达式类别列表框中列出了该查询所包含的所有字段，双击“数量”时，我们发现，“数量”被添加到了对话框上部的文本框中了，其格式为“[数量]”，该字段名称被一对中括号包围（这是Access表达式认可的Access中字段的表示方式），如图4-40所示。

图4-40

接着，我们需要输入乘法符号，根据我们在 Excel中积累的经验推断，乘法符号应该是“*”，但我们还是用双击选择的方式输入。在“表达式元素”列表中双击“操作符”选项，然后在“表达式类别”栏中双击“全部”，最后在“表达式值”栏中双击“*”，输入乘法符号，如图4-41所示。

图4-41

依照上述步骤输入“价格（元）”，然后单击“表达式生成器”右上角的“确定”按钮关闭对话框，完成Access表达式的输入，如图4-42所示。

我们看到，在Access查询设计网格最后一个字段上出现了一个新的“计算字段”，该计算字段的内容是“表达式1:[数量]*[价格（元）]”。这里的“表达式1”是表达式生成器给新生成的计算字段所设置的默认名称，我们可以手动把这个默认名称修改为更有意义的名字；该计算字段冒号以后部分的“[数量]*[价格（元）]”是表达式的内容，如图4-43所示。

图4-42

图4-43

现在，选择“设计”选项卡，在“结果”组中单击“运行”按钮，查看查询的运行结果，我们看到最后一列的内容为“数量”乘以“价格（元）”的结果，其字段名称为“表达式1”，如图4-44所示。

保存该查询，并将其命名为“Q0030_客户菜品价格”。

图4-44

如果我们觉得计算字段名称“表达式 1”显得不够人性化，可以修改一下。选择“开始”选项卡，单击“视图→设计视图”命令，再次进入查询设计视图。在最后一个我们刚刚生成的字段上，把“表达式 1”修改为“小计”，如图4-45所示。

此时选择“开始“选项卡，单击“视图→数据表视图”命令，我们发现，刚才的字段标题“表达式1”已经修改为更有意义的“小计”，如图4-46所示。

图4-45

图4-46

4.5　Access高级查询

在这一节，我们将进入Access查询设计的核心内容，即“物料清单的分解”。乍听起来，“物料清单的分解”这个词对你来说可能有点儿陌生，不过不要紧，我们只需一个简单的案例，就可以让你清晰地理解这个生产管理中的重要概念。

我们知道，任何产品都离不开原材料，即使是饭馆经营的菜品也不例外。小张为了能够更加透彻地洞察自己饭馆的经营情况，进一步提升管理水平，在笔者的指导下，建立了如图4-47所示的每一种菜品的原材料消耗清单，这也就是生产管理上所谓的“物料清单（Bill of Material，简称BOM）”。

通过观察图4-47的饭馆的物料清单我们知道，每份夫妻肺片的原材料为：牛肉150克、牛杂100克、调味包1袋。

图4-47

有了这个清单，我们就可以利用Access查询特有的强大功能，轻松地将订单上所定菜品的数量转换为所需的原材料数量。

现在，我们按照Excel数据导入到Access中的方法，将Excel工作表“T0040_原材料清单”中的数据粘贴到Access。粘贴完成后的效果如图4-48所示。

图4-48

下面，我们设计一个查询，看看如何将订单上的菜品数量通过“原材料清单”分解为“原材料需求量”。

选择“创建”选项卡，单击“查询”组中的“查询设计”按钮，在“显示表”对话框中选择“两者都有”标签，然后分别双击查询“Q0010_客户订单详情”和“T0040_原材料清单”，将两者加入到查询设计界面上方，关闭“显示表”对话框。

选中查询“Q0010_客户订单详情”中的“所定菜品”字段，将其拖拽到表“T0040_原材料清单”上。此时我们看到两个字段之间出现了一条连接线。

最后，按照如图4-49所示，将两个表格中的相应字段拖拽到Access查询设计网格的“字段”行上面。

图4-49

选择“开始”选项卡，单击“视图→数据表视图”按钮，我们看到如图4-50所示的查询运行结果。观察该结果，以 DD-00008号订单为例，可以看到，该订单下的菜品已经被分解为原材料的需求量。其中，每份“夫妻肺片”需要三种原材料，分别是“调味包”1袋，“牛肉”150克，“牛杂”100克。这时，我们再结合该查询结果的第3列（表示菜品数量的列），可以轻松算出整个订单中的每一份菜品的原材料需求量。

通过上面的操作，我们看到，原材料分解的过程在Access里面实现起来很轻松，相反，这个产品物料清单的分解的“一对多”过程在Excel里面是很难实现的。

现在这个查询还不是我们要的最终结果。我们需要一个汇总数据，以订单完成与否、原材料名称、原材料单位作为分组，研究一下每种原材料的需求情况。这样，我们就能够以这些数据为参照，作为采购原材料时的参考。

在完成这个任务之前，需要先增加一个自定义的“计算字段”，该新增字段的目的是计算“所定菜品的数量”乘以“每份菜品所需各种原材料的数量”，以得到每行记录所需原材料总数量的小计。

图4-50

选择“开始”选项卡，单击“视图→设计视图”按钮，回到查询设计视图。在查询视图中最后一个字段后面的新字段位置输入下述表达式：原料小计:[Q0010_客户订单详情].[数量]*[T0040_原材料清单].[数量]，如图4-51所示。

在上面的表达式中，冒号前面的“原料小计”是我们给自定义计算字段取的字段名称，冒号后面是表达式的内容，表示两个字段内容相乘。我们之所以把各自数据表格的名称也写在了表达式中，是因为，两个表格中都有[数量]的字段名，在这种情况下，Access为了保证精确引用，要求每一个字段必须同时提供该字段所属的数据表的名称。

图4-51

下面，我们在查询网格中删除“所定菜品”和两个“数量”字段，然后从数据表格中拖入“备注”字段，调整后的字段如图4-52所示。

图4-52

在查询网格中单击鼠标右键，在鼠标右键菜单中选择“汇总”选项，如图4-53所示。此时，查询网格多出了一行“总计”，查询也就变成了汇总查询。

图4-53

我们在“原料小计”字段下选择“合计”，并在“备注”字段排序位置处，选择“升序”排列，如图4-54所示。

选择“开始”选项卡，单击“视图→数据表视图”运行查询，看到如图4-55所示的查询结果。分别按照订单完成与否分类，看到每一种原材料的总需求量。完成的订单代表历史数据，未完成的订单代表将来需要采购的原材料数量。保存该查询，并命名为“Q0050_菜品原料采购”。

图4-54

图4-55

我们知道，客户订单是有“要求送餐时间”的，对于已经完成的历史订单，我们没什么好说的；但对于将来的订单，我们将其分解为原材料的需求后，希望能据此指导我们的采购计划。在什么时间？需要采购什么？采购量是多少？

4.6　时间去哪了

如果你在 Excel中曾经处理过日期时间相关的问题，那么你一定知道，如果一列数据不是真正的日期时间格式，那么它是无法作为日期时间函数的参数的，如果非要这样做，函数也会报错。

不幸的是，我们导入到Access中的“T0010_业务数据记录”中的“要求送餐时间”也是这样一列不被Access承认的日期。我们必须把它修改成被Access承认的，真正的日期格式。

像 Excel一样，如果一列日期是真正的日期，那么，默认这列数据是靠右对齐的。而双击打开表格“T0010_业务数据记录”，很不幸，“要求送餐时间”是靠左对齐的，如图4-56所示。

当我们从Excel中把表格粘贴到Access中时，Access会自动分析每列数据的数据类型，并在后台为该表格生成一个表格结构模型。在这个表格结构模型中，严格规定了表格中每列数据所能容纳的数据类型。也就是说，一旦表格结构模型生成以后，表格中的每列数据只能容纳同一种类型的数据（可以是文本，数字、日期时间等之一），如果你在时间日期类型的列中键入文本字符，则Access会拒绝。Access在对每一列所能存储的数据类别这一点上，Access要比Excel严格得多。

Access对表格的每列所能容纳的数据类型的严格要求是十分必要的。俗话说“没有规矩，不成方圆”，Access的严格是为了将来在管理数据时少出错误，保证数据的准确、完整，运行结果的可靠。我们经常见到，在 Excel中，由于对数据要求的不严格、不规范，造成后期管理各种麻烦的例子，屡见不鲜。

图4-56

要查看Access中数据表格的结构图，请在Access界面左侧的Access对象列表中，选中表格“T0010_业务数据记录”，选择“开始”选项卡，单击“视图→设计视图”，这时，我们看到当导入数据时 Access为该表格自动生成的“表格结构图“。在这个结构图中，我们发现，“要求送菜时间”这个字段的数据类型是“短文本”而非时间和日期。这是因为，Access在导入数据时，对于"要求送餐时间"一列不认为是格式正确的时间和日期，所以用容错性更强的“短文本”类数据类型来容纳这列数据，如图4-57所示。

单击字段“要求送餐时间”右侧的“短文本”，我们看到这是一个下拉列表，在列表中我们可以看到Access所允许的各种数据类型。其中有一个选项就是“日期/时间”，如图4-58所示。

看到“日期/时间”数据类型选项，各位可能很自然地想到，在这里把“要求送餐时间”字段的数据类型改成“日期/时间”不就可以了吗？我建议你不要这样做，因为，如果你的Access数据库文件没有备份，那么你这么做，Access会把该列中所有不是真正日期时间格式的数据统统删掉！那时，你真的会惊叹：我的“时间”都去哪儿了？

图4-57

图4-58

作为一个比较可行的方法，在数据量比较少的情况下，我们可以这样把文本类型的日期转化为真正的日期时间格式：新增加一个“日期/时间”字段，然后手工把原来的日期时间字段的内容重新键入到新的字段中，再删除原来的字段。好，就这么办！

我们在表格“T0010_业务数据记录”设计视图中增加一个新字段，字段的名称为“送餐时间2”，在字段的数据类型列表中选择“日期/时间”，如图4-59所示。

图4-59

单击表格结构视图右上角的“×”图标，在弹出的是否保存提示对话框中单击“是”按钮，保存修改以后的表格结构，如图4-60所示。

图4-60

双击表格“T0010_业务数据记录”打开表格，我们看到该表格中新增加了一列名称为“送餐时间 2”的空白列，根据我们在表格设计视图中的设定，该列只接受“真正的”日期格式的数据。我们依照原来的“要求送餐时间”字段中的内容，手动将送餐时间转换成正确的日期时间格式输入到新的“送餐时间 2”中。输入完毕后如图4-61所示（对于较大的数据量，我们可以借助Excel完成数据的转换）。

值得注意的是，关于日期时间字段，Access为我们提供了一个有用的功能：当Access表格结构中的某列设置为日期时间格式后，那么，在表格的数据表视图中，单击该列中的单元格时，默认情况下，会自动弹出一个日期选择器控件，让我们的日期输入变得更容易。

现在，我们选中“送餐时间2”，复制“送餐时间2”中的内容到“要求送餐时间”字段，在弹出的确认是否粘贴对话框中单击“是”按钮，完成复制粘贴，操作如图4-62所示。

需要注意的是，“要求送餐时间”的数据类型目前还是“短文本格式”，它之所以能够接受“送餐时间 2”中的日期时间格式的数据，是因为日期时间格式的数据可以被Access作为文本对待，反之，则不一定能成功。

图4-61

图4-62

现在字段“送餐时间 2”已经完成了它的使命，我们选中该列，在鼠标右键菜单中选择“删除字段”选项。此时弹出是否确认删除对话框，单击“是”按钮，如图4-63所示。

图4-63

由于我们已经把原来的Access不能正确转换的日期时间变成了它能够正确转换的日期时间格式（在我的电脑里，Access不认可“2012年8月1日”，而认可“8/1/2012”，根据电脑的设置可能会有不同），现在，我们就可以放心地把“要求送餐时间”这列数据改成Access中的“日期/时间”数据类型而不用担心丢失数据了，如图4-64所示。操作完毕后，保存对表格结构的修改。

当然，在这个时间日期时间改造的过程中，我们也可以这样操作：在表格结构模型里，保留“送餐时间2”字段，删除原来的“要求送餐时间”字段，把“送餐时间2”改名为“要求送餐时间”，但在这里，我们没有那样做。

打开表格“T0010”，我们发现，改成“日期/时间”格式的“要求送餐时间”列变成右对齐了（默认情况下，无论在Access还是在Excel里，数字、日期、时间都是靠右对齐的，文本是靠左对齐的），如图4-65所示。说明原来文本格式的日期时间已经成功地转化为真正的日期时间。

图4-64

图4-65

4.7　根据时间做决策

在上一节中，我们讲解了文本类型的日期转换成正确的日期格式的问题，现在，我们就能够充分利用日期时间类型的数据的特性，修改现有查询，增加对日期时间的处理，让我们的数据分析更有时效性，指导小张在正确的时间作出正确的采购决策。

右键单击查询名称“Q0010_客户订单详情”，在鼠标右键菜单中选择“设计视图”进入查询设计界面，对该查询进行修改，如图4-66所示。

图4-66

我们将在该查询中新增加一列，按照“要求送餐时间”一列中的内容，利用Access中的时间日期函数，将每一个日期归属到某个月份，以便按照月份汇总数据。这次，我们还是使用“Access表达式生成器”这个工具完成这项工作。

在Access查询设计网格最后的空白字段中单击鼠标右键，选择“生成器”选项，如图4-67所示。

图4-67

在弹出的“生成器”对话框下部的“表达式元素”列表中，单击“函数→内置函数”，然后，在表达式类别列表中，选择“日期/时间”，在“表达式值”列表中双击“Year”和“Month”函数，并用“&”符号将“年”和“月”与函数连接起来，如图4-68所示。

图4-68

接着，用鼠标选中函数参数占位符“《date》”，然后在“表达式生成器”对话框中的“表达式元素”列表中双击查询“Q0010_客户订单详情”，将相应的参数填写到函数参数中。表达式构建完毕后如图4-69所示。这里需要注意，这里的引号一定要是英文半角的双引号！

关闭表达式生成器，我们看到一个新的计算字段出现了，该字段的默认名称为“表达式1”。我们将字段名称修改为更有意义的“年月”，如图4-70所示。

图4-69

图4-70

保存对查询所做的更改，然后选择“开始”选项卡，单击“视图→数据表视图”查看查询运行结果，我们看到“要求送餐时间”已经归类到具体的某个“年月”，如图4-71所示。

图4-71

我们把客户的“要求送餐时间”归类到某个月份后，接下来更改“Q0050_菜品原料采购”汇总查询，我们在这个查询上增加原材料需求的年月。这样，我们就可以知道“每个月”的原材料总需求量了，小张就可以按月准备菜品原料，而不必按“天”频繁地查看原材料需求了。

选中查询“Q0050_菜品原料采购”，在鼠标右键菜单中选择“设计视图”，进入该查询的设计视图。该查询是我们以前已经设计好的查询，它是基于查询“Q0010_客户订单详情”和表格“T0040_原材料清单”建立的。我们看到，我们刚刚对查询“Q0010_客户订单详情”所做的修改，新增加的计算字段“年月”已经反映到了该查询视图中了。

我们看到，查询“Q0010_客户订单详情”是一个汇总查询（特点是在查询网格中有一个“总计”行）。现在，我们把查询“Q0010_客户订单详情”中的计算字段“年月”拖拽到查询网格中，并按升序排列，如图4-72所示。

图4-72

选择“设计”选项卡，单击“视图→数据表视图”，我们看到如图4-73所示的查询运行结果。这里我们看到，菜品原材料的需求量已经按月汇总好了。我们看一下未完成订单里面牛肉的需求量：2016年8月，需要900克；2017年8月，需要1650克；2019年8月，需要1800克。现在，我们可以按照这个数据找供应商提前订货了。

图4-73

4.8　人人都是程序员

本书讲到这里，我想告诉大家的是，如果你对软件界面没有要求，那么，到现在为止，你已经用Access完成了一个最基础的饭馆经营数据库管理系统。如果你能接受（请对照具体的表格理解）：

（1）在表格“T0010_业务数据记录”中输入订单号码及客户信息。

（2）在表格“T0020_业务订单详情”中输入该订单号码下的菜品数据。

（3）在表格“T0030_菜品价目表”中维护菜品价格。

（4）在表格“T0040_原材料清单”中维护菜品的原材料组成信息。

那么，你就可以随时执行已设计好的各种查询了解饭馆当前的经营状况，并能够根据物料清单分解查询Q0050指导小张的采购行为。

但是，如果真是这样，这个管理系统的操作也太不人性化了，除非你是这个系统的开发者或者非常了解这个系统各个数据表格的用意，否则，你会觉得这个系统太不容易使用，为了让这个系统容易操作，最好给这个系统设计一个用户友好的界面。

让不懂一句程序代码的人也能设计出看起来还不错的程序界面，这是Access比较厉害的地方。当然，前提是用户必须非常熟悉数据库中各个表格中的数据，这个，我们现在已经做到了。接下来，我们演示一下Access是如何完成这个系统的界面设计的。

在Access中，设计软件程序界面的功能叫作“创建窗体”。在Access菜单中，选择“创建”选项卡，单击窗体组中的“窗体设计”按钮，进入窗体设计界面，如图4-74所示。

图4-74

这时，我们看到，Access界面上出现了一个背景，这个就是我们安放程序界面各个零件（按钮、下拉框、列表等）的“画板”。

因为我们设计程序界面的目的是要用这个界面和我们关心的数据打交道，目的是让这些“零件”和数据库中的底层数据“互动”起来，所以，我们需要给这个程序界面指定所“关联”的底层数据（底层数据可以是数据表，也可以是查询）。

在Access窗体设计界面上部的菜单栏里，选择“设计”选项卡，单击“工具”组中的“添加现有字段”，弹出“字段列表”对话框。如图4-75所示，在“字段列表”对话框中，列出了当前Access数据库中的所有数据表。双击表的名称可以看到该表中所包含的所有字段名称。

图4-75

下面，我们将设计一个用来记录饭馆送餐业务的界面。该界面的任务是让饭馆工作人员能够通过容易操作的程序界面，而不是直接在Access数据表格中记录饭馆日常业务信息。

记录饭馆日常经营业务数据所涉及的表格有两个，一个是记录客户订餐信息的表格“T0010_业务数据记录”，另一个是记录客户每个订单号码下的所定菜品详细信息的“T0020_业务订单详情”。

我们希望有这样一个界面：界面的上方显示可以让用户输入客户信息（与表格“T0010_业务数据记录”关联），界面下方显示可以让用户输入该客户所定菜品的详情（与表格“T0020_业务订单详情”关联）。

想象中完成后的界面大致如图4-76所示，界面当前显示的是张 3先生的第DD-00001号订单的情况，我们看到，张3先生在该订单里定了1份青椒鸡蛋和1份米饭。

设计这个界面的关键是如何把表格T0010和表格T0020同步起来，即当界面上方显示某一特定订单号码时，界面下部显示该订单号码下的所有菜品。

图4-76

下面，我们依次选中数据表T0010中的所有字段，将其拖拽到Access窗体设计界面中，并安排每一个字段到合适的位置，如图4-77所示。

这里必须要强调的是：当我们把Access表格中的字段拖拽到Access窗体设计界面时，Access上面就出现了一个显示底层数据表里面对应字段的“小窗口”。这个“小窗口”与它所依赖的数据表的“当前记录”的对应字段相关联，通过这个小窗口，我们可以对数据表里具体的某一行数据所对应的字段进行查看、修改等操作。

注意：从同一个数据表里面拖拽出来的不同字段在同一时刻显示的是该数据表里同一行的（当前记录）数据，也就是在数据表里当前获得焦点的那行数据。

图4-77

现在，我们已经把数据表“T0010_业务数据记录”以向窗体上面添加字段的方式与窗体关联起来了。在Access中，把数据表中的字段拖拽到窗体后，在窗体上形成的“小窗口”叫作“控件”，控件与数据表中的字段建立的关联关系叫作“绑定”。此时，当你在窗体中对控件中的内容进行修改时，实际上修改的是底层数据表中当前记录的对应字段中的内容。

我们知道，一个订单号码下可能包含多个菜品，紧接下来的问题是：我们已经在Access窗体上面显示了客户的某个特定订单的主体内容（订单号码、客户信息等）。接下来，怎么才能在窗体下部显示该订单号码下面所包含的所有菜品名称呢？这时，我们涉及了一个Access中新的概念，叫作“子窗体”。

所谓“子窗体”，就是在“大窗体”上面在建立一个“小窗体”，并让大窗体和小窗体中的数据关联起来，这时，相对于子窗体，“大窗体”的标准名称是“主窗体”。制作子窗体，我们需要完成如下两项工作。

（1）给“子窗体”指定底层数据表。

（2）建立“子窗体”的底层数据表和“主窗体”底层数据表之间的关系。

下面我们演示子窗体的制作过程：在Access窗体设计界面上选择“设计”选项卡，单击“控件”组中的“子窗体/子报表”按钮，如图4-78所示。在这里，我们将“使用控件向导”辅助完成子窗体的创建。

在Access中，如果你打算使用“控件向导”，那么，Access窗体设计界面上的“使用控件向导”的按钮必须处于按下的状态。

图4-78

接下来，我们在Access主窗体下部合适的位置画一个子窗体，由于我们按下了“使用窗体向导”按钮，因此，松开鼠标后，我们将得到如图4-79所示的“子窗体向导”对话框。窗体向导的作用是方便我们指定“子窗体所使用的底层数据表”及建立“子窗体和主窗体数据表之间的关系”。

在“请选择将用于子窗体和子报表的数据来源”下选择“使用现有的表和查询”，然后单击“下一步”按钮。

图4-79

此时弹出子窗体向导的第二个对话框，在这个对话框里，我们可以选择子窗体所用到的数据表格。在这里，子窗体所用到的表格是存储每一个订单号码下所有菜品的数据表“T0020_业务订单详情”，选择它，如图4-80所示。

到“选定字段”列表中，然后单击“下一步”按钮，如图4-81所示。

图4-80

图4-81

此时弹出建立子窗体的关键一步如图4-82所示，建立主窗体和子窗体之间的数据关系。在这一步中，Access试图自动为你建立主窗体和子窗体数据之间的关系，但是，毕竟计算机的判断应付不了千变万化的人为情况，它给出的答案不一定是对的。因此，我们选择“自行定义”选项，然后单击“下一步”按钮。

在“子窗体向导”的这一步中，Access允许我们自行定义“主窗体”和“子窗体”之间的数据关系。在对话框的左侧，显示的是主窗体背后的字段以及其数据类型的列表，在对话框的右侧，显示的是子窗体背后的字段及其数据类型的列表，如图4-83所示。

建立主窗体和子窗体之间的数据关系就类似于在Excel中用VLOOKUP（）函数建立两个表格之间的关系，但不同的是，Access能处理表格之间一对多的关系，而Excel中的VLOOKUP（）函数则无能为力。

图4-82

现在，我们在对话框左侧选择“主窗体”底层数据表的“订单编号字段”；在右侧选择“子窗体”底层的数据表的“订单编号”字段。然后单击“下一步”按钮。这样我们就建立了主窗体和子窗体数据表之间的关系。

其实，我们忙了半天，建立了主窗体和子窗体背后数据表之间的关系的目的只是为了在Access中实现这样一个特性：即，一旦建立了主窗体和子窗体底层数据表之间的关系，那么，Access主窗体和子窗体之间的数据便会“联动”起来！

图4-83

最后一步，我们需要给子窗体指定一个名称，在Access中，虽然子窗体建立在主窗体内部，看起来似乎是和主窗体密不可分是一个整体，但事实上，在Access中，子窗体会单独存储成一个Access窗体对象。我们给子窗体命名为“F0010_业务数据录入_子窗体”。最后单击“完成”按钮，如图4-84所示。

图4-84

此时，我们发现Access界面左侧的所有Access对象列表中多了一个窗体名称。这个窗体就是我们刚才保存的子窗体。由于主窗体我们还没有保存，因此目前还不在所有Access对象列表中，如图4-85所示。

图4-85

选择“开始”选项卡，单击“视图→窗体视图”，此时可以看到我们刚刚设计的饭馆管理程序界面的初步样子，如图4-86所示。

单击Access界面左上方的“保存”按钮，将主窗体保存为“F0010_业务数据录入”，我们看到，Access界面左侧的“所有Access对象”中出现了该窗体的名称，如图4-87所示。这里的F表示窗体（Form）。

图4-86

图4-87

在Access窗体界面，把鼠标光标置于主窗体上的任意字段内，连续按下键盘上的［PageDown］键，你会发现，子窗体中的数据会随着“主窗体”上的订单编号变化而同步变化，如图4-88所示。此时，如果对窗体上的数据进行修改，实际上修改的是窗体底层数据表中的数据。

图4-88

怎么通过窗体向数据库中增加新的销售数据呢，我们可以在主窗体的最下部单击“新（空白）记录”小按钮，如图4-89所示。在主窗体的最下面，找到了吗？

当单击“新（空白）记录”后，主窗体和子窗体都变成了空白（实际上，此时两个窗体底层的数据表的对应操作是：当前记录指针移动到了数据表的最后的空白行，即新增记录行）。

图4-89

我们按照图4-90增加一个新订单。注意，当我们填写“要求送餐时间”字段内容时，Access会给我们提供一个日期选择控件。那是因为，我们在窗体底层的数据表中，把“要求送餐时间”字段设置为日期时间数据类型，而当把这种类型的字段拖拽到窗体时，默认会出现一个日期选择框，这说明窗体控件继承了底层表格的一些特性。

接着，我们在子窗体中填写新订单下客户所订购的菜品名称，如图4-91所示，这里当然可以在该订单下填写多个菜品，但我只填写了一种菜品（6份饺子），然后单击Access左上角的磁盘图标保存更改。

图4-90

图4-91

我们现在已经知道，我们在窗体上对数据所做的更改其实是更改窗体底层的数据表。如果你不信，可以点开主窗体和子窗体所依赖的数据表，我们看到，两个相关表格中的数据已经被更新了，如图4-92所示。

图4-92

4.9　让用户操作更方便

在上一节中，我们已经完成了一个初级的Access程序界面，并演示了在Access程序界面上对底层数据表中的内容进行添加、修改的方法。这似乎比直接在Access中分别处理两个表中的内容方便一些，但还远远不够。

既然饭馆所经营的菜品种类通常是固定的，我们希望在输入客户所定菜品的时候，能够从列表中选择，而不是每次都要输入菜品名称这些文字，那该怎么办呢？别急，且听我慢慢道来。

选择窗体“F0010_业务数据录入”，选择“开始“选项卡，单击“视图→设计视图”进入窗体的设计界面，如图4-93所示。

图4-93

在窗体“F0010_业务数据录入”设计视图中，选择子窗体中的“所定菜品”控件。然后单击鼠标右键，在弹出的快捷菜单中，选择“更改为”菜单项，在弹出的子菜单中选择“组合框”。组合框实际上就是折叠起来的列表，而列表中的选项由我们自己指定，如图4-94所示。

图4-94

因为我们把“所定菜品”控件已经修改成了组合框，现在可以容纳很多供我们选择的选项，但是，这些选项从哪里来呢？需要我们告诉它。

保持“所定菜品”控件的选中状态，选择“窗体设计工具“设计”选项卡，单击“工具”组中的“属性表”，此时弹出控件的属性列表。所谓控件的“属性”，也就是属于控件的，我们可以设置的一些特性，其中包括控件的外观，控件对外部点击或者选择的响应动作，控件底层的数据来源，等等。

针对当前的控件，我们需要设置的是控件的数据来源。在属性表中，选择“数据”标签。在这个例子中，我们重点关注“数据”属性列表中的以下几项内容。

（1）控件来源：表示窗体上“所定菜品”控件与底层数据表的哪个字段关联。

（2）行来源：控件中的内容来自哪里（哪个查询，还是自定义列表）。

（3）行来源类型：控件中的内容是来自数据表/查询还是其他类型。

（4）绑定列：如果控件能够显示多个列，那么第几列内容将被存储在窗体底层的数据表中。

（5）限于列表：如果控件选项列表中没有所选的内容，是否允许用户自行添加。

这里属性列表的“控件来源”位置Access已经为你填写好了“所定菜品”字样，表示该控件与窗体背后数据表中的“所订菜品”字段相关联（或者叫作“绑定”）。

在属性列表的“行来源类型”处，Access默认为你填写了“表/查询”字样，表示控件中的数据来自你所指定的表或者查询，这正是我们需要的，如图4-95所示。

图4-95

由于在上一步中，属性列表的“行来源类型”处，我们认可了Access默认选择“表/查询”，因此，在单击下方的“行来源”时，Access为我们列出了所有可用的Access数据库中的表和查询名称。选择表格“T0030_菜品价目表”，如图4-96所示。因为在这个表的第1列中有所有的菜品名称，也正因为菜品名称在第一列，所以在“绑定列”处选择默认的1。在“限于列表”中，我们选择默认的“否”，意思是对于表格“T0030_菜品价目表”没有的菜品，我们可以直接在控件中输入。

图4-96

设定完成后，选择“开始”选项卡，单击“视图→窗体视图”按钮，我们看到，“所定菜品”一列变成可以从下拉列表中选择了，比手动输入方便多了，而且还不会引起手动输入引起的错误，要知道，数据的精确性对建立表格之间的匹配关系非常重要，如图4-97所示。

图4-97

4.10　添加、修改和删除

在上一节，通过使用组合框控件，提升了用户的菜品名称输入速度和精确性，但我们还无法满足于此，这样的界面和我们最常见的最基本的程序界面还有些差距。对于数据库程序的界面，最基本的功能就是添加、修改和删除，我们的程序界面上还没有这几个功能按钮，下面来添加一下。

Access作为简单易用的小型桌面数据库软件，只要我们理解了窗体和窗体背后的表格之间的依赖关系，那么在窗体上添加各种功能按钮是件非常容易的事情。通过下面的演示，你会逐渐体会到这一点。

首先，我们制作一个添加新记录的按钮。在窗体“F0010_业务数据录入”的设计视图界面，选择“设计”选项卡下的“按钮”控件图标。注意，在做这个操作之前，需要激活下面的“使用控件向导”按钮，如图4-98所示。

图4-98

接下来，在窗体的合适位置上画一个按钮，当松开鼠标时，会弹出一个如图4-99所示的“命令按钮向导”对话框。这个对话框将指导你完成“你想让按钮完成的任务”。

我们注意到，“命令按钮向导”对话框已经把Access中的命令按钮所能完成的任务分门别类地组织好，等待我们去选择。有时间时，你可以分别点开每一个类别查看一下。在这里我们是想制作一个向数据库中添加记录的操作，这属于“记录操作”类中的“添加新记录”，选择它，然后单击“下一步”按钮。

在这一步中，对话框向你询问需要在按钮上显示的内容，我们希望在制作的按钮上显示我们自定义的文本“添加记录”，所以接受默认设置，然后单击“下一步”按钮，如图4-100所示。

图4-99

图4-100

这一步中，命令按钮向导提示你给按钮取一个名字，注意，这个名字不是按钮上面显示的文字，而是将来万一需要编写程序代码时怎么指代或者引用这个按钮（不过放心，我们目前用不到编写代码）。我们给它取名“btnAdd”，然后单击“完成”按钮，如图4-101所示。

图4-101

就这样，我们就完成了在窗体上“添加记录”的按钮的制作，如图4-102所示。回顾按钮的制作过程，我们发现：制作一个Access界面上的功能按钮，只要在Access窗体界面上画按钮，然后告诉按钮要完成的工作就可以了。至于Access在背后的实现细节，目前根本不用我们关心。

图4-102

使用前面叙述的类似方法，分别制作一个“删除记录”按钮和“保存记录”按钮。制作过程基本一致，都是在Access窗体界面上画一个按钮，然后告诉按钮需要完成什么样的任务（如图4-103所示）。

图4-103

三个按钮添加完成后，窗体界面的样子大致如图4-104所示。

图4-104

单击Access界面左上角的磁盘图标保存我们所做的窗体设计，然后选择“开始”选项卡，单击“视图→窗体视图”按钮，可以看到窗体界面的最终设计结果如图4-105所示。我们可以将光标置于主窗体控件上，按下［Page Down］键浏览不同的订单内容，这里我们选择DD-00014号订单。

保持 DD-00014号订单的备选状态，按下“删除记录”按钮，我们看到一个Access警示框，表示你正准备删除一条Access记录，单击“是”按钮确认删除，如图4-106所示。

图4-105

图4-106

打开数据表“T0010_业务数据记录”。我们发现DD-00014订单记录已经被删除了，如图4-107所示。

图1-107

这里需要注意的是，由于我们的添加和删除按钮都画在主窗体上，因此，其功能只能影响到主窗体背后的数据表。这里，我们虽然在主窗体上删除了DD-0014号订单，但在子窗体背后的数据表“T0020_业务订单详情”上，该订单下的“所定菜品”依然存在，如图4-108所示。

如何解决两个数据表之间不能同步删除或者更新的问题呢？这可是Access数据库里面的一个“大问题”，也是关系型数据库的核心之一，需要我们在 Access数据库里面建立表与表之间的“原生关系”来解决。下面的章节我们就介绍一下如何在Access中建立表与表之间的“原生”关系。

图1-108

第5章　关系型数据库

本章内容提要 ：Access被定义为数据库“管理”系统软件，这里强调的是对数据的管理，Access不但具有数据存储、处理、分析功能，还提供对数据的“管理”功能，而其“管理”功能体现之一就是：Access能够对其存储的不同表格实现“联动”功能。

5.1　表格之间的原生关系

我们已经知道，在Excel中建立两个表格之间的关系并不是一件很容易的事。在Excel中，我们勉强可以使用Vlookup（）函数以两个表格中共同的关键字作为纽带，把另一个表格中的内容抓取到当前表格中来。但却不能实现两个表格之间的“一对多”查找和两个表格之间的“联动”。

在Access中，我们前面介绍的查询虽然可以很容易地实现两个表格之间的“一对多”查找，但却不能实现两个表格之间的“联动”。即，如果一个表格中的某条记录修改或删除，那么另一个表格中与其密切联系的相关记录也会“同步地”实现修改和删除。

具体地说，对于以下两个业务场景：

（1）比如我们在表格“T0010_业务数据记录”中删除了一条订单编号DD-00001的记录，那么表格“T00020_业务订单详情”中的该订单号下的所有订购菜品也一并同步自动删除。

（2）类似地，如果订单 DD-00001的客户希望把订单号 DD-00001改成DD-88888（也许客户需要一个吉祥号码），如果在数据表“T0010_业务数据记录”中修改了订单号码后，数据表“T00020_业务订单详情”中对应的订单号码也能同步自动修改。

这个功能是数据库软件的基本功能，在Access中，只需几个简单步骤，就可以达到两个数据表格之间的同步修改和同步删除的效果。下面我们看看该功能在Access的实现方法。

选择“数据库工具”选项卡，单击“关系”组中的“关系”按钮，如图5-1所示。

此时进入Access中建立表与表之间“关系”的空白界面。在空白处单击鼠标右键，从弹出的快捷菜单中选择“显示表”选项，此时弹出“显示表”对话框，双击要建立关系的两个数据表，将其加入到Access“关系”界面，如图5-2所示。

图5-1

图5-2

要建立两个数据表之间的关系，我们必须熟悉两个数据表中的内容。对于图中的这两个表格，我们再熟悉不过了。我们知道：表格T0010中保存的是客户订单编号和客户信息，而T0020中保存的是每一个订单号下的所有菜品。两个数据表之间可以通过两个表格中都存在的“订单编号”字段联系起来。

T0010和T0020两个数据表之间可以通过两个表格中都存在的“订单编号”字段联系起来，这是我们建立这两个数据表时有意这样做的。但是，Access并不了解我们的意图，我们必须用建立两个表格之间原生“关系”的方式明确告知它。

单击表格T0010中的“订单编号”字段，将其拖拽到表格T0020中的“订单编号”上面，松开鼠标。此时弹出一个“编辑关系”对话框，如图5-3所示。别急，对于这个对话框，我们似乎在哪里见过？

是的，我们在建立主窗体和子窗体底层数据之间的关系时也出现了类似的对话框。但是，与建立主窗体和子窗体之间的关系不同的是，在Access关系界面建立的关系是直接在数据表间建立的关系，是“原生”关系，其作用会影响到基于该表格所建立的查询和窗体。

图5-3

下面，我们在“编辑关系”对话框下部，勾选“实施参照完整性”复选框。勾选以后，该复选框下面的两个复选框也变成了可选择的状态。这两个选项分别是“级联更新相关字段”和“级联删除相关字段”。这两个功能实际上也是“同步更新这两个字段”、“同步删除这两个字段”的意思。全部勾选这两个字段，如图5-4所示。

到目前为止，事情似乎进展得很顺利。单击“创建”按钮。

图5-4

这时，不幸发生了，此时弹出了一个Access警告框，如图5-5所示，告诉你“主表的引用字段中，找不到唯一的索引”，这是什么意思呢？原来，在Access中，如果要创建两个表格之间的关系，主表（一般是指关系界面中左边的那个表格）的那个关联字段中的内容必须“不能重复”。虽然我们知道在表格T0010（主表）中的订单编号没有重复，但这没用，必须通过某种方式“让Access也知道”才行。

怎么才能让Access也知道T0010（主表）中的字段“订单编号”中没有重复内容呢。鼠标右键单击表格T0010，在右键菜单中选择“设计视图”，如图5-6所示。进入数据表的设计视图界面，在数据表T0010结构图中选中字段名称“订单编号”。

图5-5

图5-6

在数据表设计视图中选中字段名称“订单编号”后，单击鼠标右键，在鼠标右键菜单中，单击“主键”选项，如图5-7所示，将字段“订单编号”设置为“主键”，然后保存并退出数据表设计视图。这样，我们就告诉了Access，在表格T0010中，在“订单编号”字段上已经建立了“唯一索引”。

在数据库中，主键又称作关键字，作为关键字的字段的最低要求是这一字段中的内容不能有重复和空白。主键类似于汽车的牌照号或者人的身份证号，是数据表中每一行记录的唯一标记，如果我们要查找数据表中的某一行记录，只要说出这行记录中的主键的内容就行了，就像通过汽车牌照号可以找到具体的汽车一样。

图5-7

操作至此，似乎万事大吉，我们再次单击“数据库工具”选项卡中的“关系”按钮进入Access关系设计界面。按照前面描述的方法建立两个表格之间的关系，单击“创建”按钮后，一个新的警告信息出现了，如图5-8所示。这条警告信息的意思是，如果在T0010和T0020之间建立“参照完整性”，那么按照参照完整性规则，在T0020中不应该存在这样的记录：在关联字段中T0020中有，而T0010中没有，即T0010中没有这个客户订单编号，却在T0020中却存在该订单编号下的所定菜品信息（在另一个表中没有对应订单信息的“孤儿”记录）。

图5-8

检查两个表格发现，在子表（T0020）中存在一个在主表（T0020）中没有的订单编号。这是因为，我们在上一章节演示“删除”按钮的制作过程时，只删除了主表的记录，却没有删除子表的相关记录造成的。通过查看表格T0010和T0020中的内容，我们发现确实如此（数据表 T0020中存在一条孤立的记录：订单号DD00014，饺子，6份，如图5-9所示）。

我们在上一节中曾经在主表中删除了订单编号 DD-00014的记录，但在子表中却没有删除该订单编号下所定菜品的信息。我们在这里手动删掉它。

图5-9

此时，我们再次建立两个数据表之间的关系，单击“创建”按钮时，不再出现警告消息。并且，我们发现在Access关系界面的两个表格之间出现了一条连接线，连接线左边有个1的字样，连接线右侧有一个“无穷”的符号。这种样式的连接线表示两个表格之间是“一对多”关系。即主表T0010下的每一个订单编号记录行在子表T0020下面可能对应着多个订单编号记录行，如图5-10所示。

单击Access界面左上角的“保存”按钮，保存我们所建立的Access关系。双击打开表格 T0010，我们发现，表格中的每行记录前边都出现了一个可展开的“加号”。令人兴奋的是，双击加号我们看到了与其关联的子表（T0020）中的相关记录，如图5-11所示。

图5-10

图5-11

现在，既然两个表格之间的“级联更新”和“级联删除”的原生关系已经建立，下面我们做个试验，看看是否真的是这么回事。我再次单击折叠符号后，在主表T0010中删除DD-0001订单行，如图5-12所示。

图5-12

双击打开表格T0020，我们发现T0020中与表格T0010中订单编号DD-00001相关的订单也被同步删除了，如图5-13所示。看来Access两个表格中的“级联删除”功能实现了。

下面我们再试试两个表格之间的“级联更新”功能。打开表格 T0010，我们将订单编号DD-00002改成吉祥号码DD-88888，然后保存修改，如图5-14所示。

图5-13

图5-14

打开数据表T0020，我们看到数据表T0020下与原来订单编号DD-00002的相关记录行中的订单编号也已经被更新成了DD-88888，如图5-15所示。

图5-15

现在我们做好了所有设置，回到原来设计的Access程序界面，将光标放置于主窗体任意字段，按下键盘上的［PageDown］键选择订单编号 DD-00004，然后单击“删除”按钮，我们看到一个消息框，如图5-16所示。意思是不但主表中的数据被删除，相关表中的数据也将被删除，这意味着：我们成功了！

图5-16

5.2　Access的核心

在上一节里，我们用Access中“数据库工具”选项卡中的“关系”命令建立了两个数据表之间的“原生”关系，正是这种“原生”关系，使Access能够将两个表格自动同步起来。

Access作为一个关系型数据库管理系统软件，存在如下两大关系，理解了如下两大关系，基本上也就理解了Access数据库的精髓。

第一个关系是通过Access中的“数据库工具→关系”命令建立起来的Access中表与表之间的，能够将两个表格自动同步起来的“原生关系”。

第二个关系是通过Access的“查询”功能建立起来的表格与表格、表格与查询、查询与查询之间的“次生关系”，该关系可以将不同表格或者查询中的数据关联起来，重新组合成我们需要的数据。

Access之所以不同于Excel，就是因为在Access中，我们能够建立这些表格与表格、表格与查询、查询与查询之间“原生”和“次生”关系，从而让Access在某些方面超越Excel，能Excel所不能！

第6章　设计报表

本章内容提要 ：到目前为止，我们已经知道，Access靠数据表存储数据，靠查询处理数据，靠窗体与用户互动……而下面，我们将介绍Access靠什么展示数据，答案是：报表！Access作为一个小巧却功能强大的数据库软件，有了数据，打印高度定制的、格式化的报表对于它来说简直是“小菜一碟”！在本章里，我们将学习如何用Access打印类似图6-1所示的格式的送货（送餐）单。在Access中，生成各种格式化单据的能力叫作“报表”功能。

图6-1

6.1　创建报表

报表和Access窗体的制作过程类似，选择“创建”选项卡，单击“报表”组中的“报表设计”按钮，如图6-2所示，进入报表设计界面。

图6-2

在图6-3中，我们看到，报表设计界面和窗体设计界面类似。但是，报表的目的不是和用户互动而是用来呈现展示数据，所以报表上面一般不会安排按钮、下拉框等能与用户互动的控件元素。

报表设计界面分为两部分，上面的一小条是“报表页眉”，下面的一大部分是报表的“主体”。

我们可以这样理解报表设计界面这两部分的作用：报表页眉类似 Excel或者Word的页眉部分，也就是说，当Access报表超过一页，打印Access报表时，报表的页眉会在每页的顶部出现。在我们这个案例里，将不使用报表的页眉部分。报表“主体”是报表的主要部分，Access的数据将以我们设计好的格式呈现在报表主体位置。

图6-3

在本章开头我们已经看到了用Access制作的送货单的大致样子。现在我们将在报表设计界面“画”一个这样的报表。

首先，在报表设计窗口上画一个送货单标题。选择“报表设计工具”下的“设计”选项卡，单击“控件”组中的带有“Aa”字样的控件，然后将其拖拽到报表主体上合适的位置。这个控件叫作“标签”控件，是专门用来在报表上书写文字的，如图6-4所示。

图6-4

在标签控件上输入文字“小张饭馆送货单”，然后选中控件，单击鼠标右键，在鼠标右键菜单中选择“属性”，随后在弹出的属性对话框里设置标签控件中的字体名称和字号，如图6-5所示。

下面我们开始设置报表中所要显示的数据。像本书前面介绍的Access窗体设计过程一样，我们的报表也包含一个“子报表”，主报表显示的是客户信息，子报表显示客户所定菜品的清单。主报表和子报表中的数据分别来自Access中的两个不同的数据表，两个数据表靠“订单编号”联系并同步起来。

图6-5

首先，设置“主”报表背后的数据，选择“报表设计工具”下的“设计”选项卡，单击“工具”组中的“添加现有字段”按钮，在字段列表中单击表“T0010_业务数据记录”，展开的字段列表中按图6-6所示，将相应字段拖曳到主报表相应位置，并调整大小。

提示 ：报表的底层数据源可以是 Access数据表，也可以是 Access查询，如果需要更改报表底层的数据源，可以在报表“设计视图”状态下，单击报表左上角（两个标尺交叉点处）的矩形，选中报表，然后选择“报表设计工具”下的“设计”选项卡，单击“工具”组中的“属性表”按钮，在弹出的“属性表”对话框中的“数据”标签下，选择“记录源”处的不同的数据表或查询名称即可。

安排好主报表中显示的字段后，报表底层数据表中的内容就呈现在了主报表上。当我们预览或者打印报表时，数据表中的每一条记录都会生成这样格式的一份报表。

图6-6

设置好主报表上所要呈现的内容后，下面的任务是在报表的下方呈现主报表上每一条数据（客户订单）所包含的具体菜品的清单（子报表）。

我们知道，每一份订单所定菜品详情在Access数据表“T0020_业务订单详情”中，并且，我们知道，“T0020_业务订单详情”与主报表底层数据“T0010_业务数据记录”通过订单号码相关联，并且，关于这个事实，我们已经在Access数据库中通过“数据库工具”项目卡下的“关系”命令建立了两个表格的“原生”关系。

基于上面的事实，我们开始建立子报表，建立子报表有两种方法，一种是选择“报表设计工具”下的“设计”选项卡，单击“控件”组中的“子窗体/子报表”按钮来向主报表中添加子报表（像前面我们建立子窗体一样的过程）；另外一种方法是直接把子报表需要显示的数据表拖曳到主报表的方式添加子报表。我们这里演示第二种方法。

选中Access数据表T0010，将其拖拽到主报表，释放鼠标，此时弹出“子报表设计向导”对话框（如果没出现“子报表设计向导”对话框，请在“报表设计工具”下的“设计→控件”组下方启动向导）。

在“子报表设计向导”对话框中需要设置主报表所依赖的数据表与子报表所依赖的数据表之间的关系，以便使两个表格联动起来（进而，主报表和子报表上呈现的数据也联动起来）。

如图6-7所示，我们看到，“子报表向导”对话框已经为我们建立了主报表和子报表数据之间的关系。由于我们已经在Access数据库中通过“数据库工具→关系”命令建立了两个表格的“原生”关系，所以我们不用怀疑这个向导为我们自动建立的关系的正确性。但是，我们还是有点不放心，因为这个向导上面显示的说明文字实在太让人费解，为了讨个心安，我们还是自己建立主报表和子报表数据之间的关系吧。所以，选择“自行定义”选项，然后单击“下一步”按钮。

图6-7

在这一步中，我们选择两个数据表中的“订单编号”作为关联，这个设置将建立两个报表的关联和同步关系，如图6-8所示。完成后，单击“下一步”按钮。

图6-8

最后，给子报表取一个名字，然后单击“完成”按钮保存子窗体。我们发现，单击“子报表向导”对话框子报表设置“完成”后，往往需要调整报表中各个控件的大小以适应主报表的空间，如图6-9所示。

图6-9

所有设置完成后，选择“开始”选项卡，单击“视图→报表视图”命令查看报表的显示效果，如图6-10所示，看起来似乎还不错。

图6-10

需要注意的是，当我们向下拖曳报表显示窗口右侧的纵向滑动条时，我们看到，这里显示的不只是一份订货单，而是Access把每一个订单号码都生成了一张送货单，如图6-11所示。

图6-11

我们不需要打印所有的送货单，只需打印我们所指定的送货单。比如，我们只想打印本书前面设计的窗体上显示的订单，怎么办呢？

要想只打印一份送货单，那么报表底层只包含一份订单数据即可。进一步，如果要让报表只显示当前窗体上的订单，那么需要为报表所依赖的数据设计一个查询，把查询条件限制为窗体上的订单号码即可。既然逻辑方向有了，让我们看看怎么实现这个逻辑。

首先修改主报表底层的数据源，将鼠标置于报表左上角的横向标尺和纵向标尺的交叉点处，单击鼠标右键，在弹出的鼠标右键菜单中选择“属性”菜单选项（亦可直接点击Access菜单栏中的属性按钮），如图6-12所示。

图6-12

在Access界面右侧弹出的报表属性（也就是属于报表的各种设置）菜单中，选择“数据”标签，这里可以修改报表底层数据的来源，如图6-13所示。我们看到，在报表的“记录源”处，并非空白，那是因为，我们在设计主报表时已经为主报表选择了数据源。

图6-13

单击“记录源”右侧的带有三个小圆点的按钮，可以打开主报表所基于的数据源，它是一个基于数据表T0010的一个查询，这是我们在设置主报表数据来源的时候由Access自动生成的，包含数据表T0010中的所有数据，如图6-14所示。

图6-14

我们想让报表只显示窗体上的订单，那么需要修改这个查询的条件，把查询结果限定在窗体上显示的订单。我们用以前介绍过的“表达式生成器”工具来完成这个任务。

在查询设计界面的“订单编号”下方的“条件”单元格处，单击鼠标右键，在弹出的鼠标右键菜单中选择“生成器”选项，如图6-15所示。此时弹出表达式生成器界面。

图6-15

在表达式生成器界面中按图6-16所示选择“F0010_业务数据录入”窗体上的“订单编号”控件的“值”，双击“值”将其加入到对话框上部的文本框中，然后单击“确定”按钮关闭对话框。

我们看到，如图6-17所示，查询设计器“订单编号”下方的条件单元格处出现了我们用表达式生成器建立的限制条件，意思是该查询运行结果限定于“F0010_业务数据录入”窗体上的“订单编号”控件所决定的订单号码。

图6-16

图6-17

最后，单击查询设计界面右上角的“×”按钮关闭查询设计器，并单击“是”按钮保存更新，如图6-18所示。

图6-18

返回报表的设计界面，然后选择“开始”选项卡，单击“视图→报表视图”预览报表，如图6-19所示。

图6-19

你会发现，当你预览报表时，会跳出一个“输入参数值”的对话框，要求你输入订单号码，如图6-20所示。要知道，这个订单号码本打算由窗体F0010上的“订单编号”控件取得。但是，由于窗体F0010现在没有处于打开状态，无从取得订单号码，因此要求我们手动输入一个订单号码。这里输入一个已知存在的订单号码DD-88888，然后按下“确定”按钮。

图6-20

现在，由于我们输入了报表所需要的查询参数，对报表底层的数据做了限定，Access只显示了一份我们所指定的报表，如图6-21所示。

最后关闭报表，弹出“是否保存更改”的提示框，单击“是”按钮，如图6-22所示，保存更改。

图6-21

图6-22

6.2　制作打印按钮

下面我们在先前设计的窗体上添加一个按钮，用于打印窗体当前显示的订单的送货单。选择窗体 F0010，单击鼠标右键，在弹出的鼠标右键菜单上选择“设计视图”选项，如图6-23所示。

图6-23

选择“窗体设计工具”下的“设计”选项卡，单击“控件”组中的“按钮”，在窗体F0010的设计视图中合适的位置“画”一个按钮，在弹出的命令按钮向导中选择“报表操作→打开报表”，然后单击“下一步”按钮，如图6-24所示。

在“请确定需要命令按钮打开哪个报表”下面，选择刚刚设计的报表“R0010_小张饭馆送货单”，然后单击“下一步”按钮，如图6-25所示。

图6-24

图6-25

在这一步中，我们设置按钮上显示的文字为“打印送货单”，然后单击“下一步”按钮，如图6-26所示。

图6-26

我们给按钮取名为“btnPrint”，然后单击“完成”按钮，如图6-27所示。

图6-27

至此，打印按钮添加完毕，现在要试一下效果，选择“开始”选项卡，单击“视图→窗体视图”命令。我们看到窗体视图上多了一个“打印送货单”按钮，窗体上当前显示的订单号码是“DD-0003”，如图6-28所示。

图6-28

现在，我们单击“打印送货单”按钮试试效果。如图6-29所示，我们刚刚设计的报表“小张饭馆送货单”已经显示在屏幕上了，而且显示的内容正是窗体当前显示的那份订单。如果你现在正连接着打印机，可以单击“文件→打印”命令打印一份看看，然后根据纸张要求进一步调整报表的位置大小。

图6-29

最后，保存对窗体F0010设计的更改，如图6-30所示，单击“是”按钮。

图6-30

现在，我们已经完成了小张饭馆的数据库管理软件的设计。每天，小张就可以打开该数据库文件，双击窗体“F0010_业务数据录入”开始每天的经营了。虽然还有极大地改进余地，但由于小张参与了整个小软件的开发过程，理解了数据库思维的精髓，相信经过进一步的学习，小张有能力把这个软件优化得更好。

第7章　按钮背后的故事

本章内容提要 ：Access是一款神奇的软件，它的神奇之处就在于Access把大多数常用的“数据库操作”转化成了用户可轻松掌握Access的“界面设置”。在本书前面设计的小张饭馆数据库管理程序界面时，我们在窗体上添加了几个功能按钮，并依照Access控件向导的指引，给每个按钮添加了各自的功能（添加、删除、保存、打印送货单等）。在整个的按钮设计过程中，我们只需知道让按钮做什么（也许对于Access初学者来说，这已经够了），而无需知道Access是如何做的。在这一章里，我们将对按钮后面的故事，也就是所谓的 Access“宏（Macro）”，做一些探索。

7.1　不写代码也编程

你在学习Excel时，可能听说过Excel中的“录制宏”功能，Access中也有所谓的“宏（Macro）”功能，但其用法和Excel有很大差别。

在介绍Access中的“宏”功能之前，我们先回过头来看一看我们前面设计的按钮。选中“F0010_业务数据录入”窗体，然后选择“开始”选项卡，单击“视图→设计视图”命令进入该窗体的设计视图界面，如图7-1所示。

图7-1

现在我们看到了窗体上已经存在的4个按钮（添加记录、删除记录、保存记录、打印送货单），回想一下4个按钮的设计过程，你一定还记得如图7-2所示的这个“命令按钮向导”对话框，首先是在Access窗体界面上“画”一个按钮，然后按照“命令按钮向导”的引导，告诉它我们想让按钮所做的事情，最后单击“完成”按钮，做起来就这么简单！但是，你知道这样制作按钮时Access背后的故事吗？

图7-2

其实，当我们用控件向导制作一个按钮时，Access根据我们在控件向导对话框里的选择，自动生成一套Access能够理解的对Access操作的命令，正是这套Access背后偷偷生成的Access操作指令帮助我们实现了按钮的功能。

下面我们以“保存记录”按钮为例来证明这一点：选中窗体设计界面中的“保存记录”按钮，选择“窗体设计工具”下的“设计”选项卡，单击“工具”组中的“属性表”按钮，我们看到弹出的关于“保存记录”按钮的一些设置，在这个属性表中，选择“事件”标签，如图7-3所示。

在Access的控件属性对话框中的“事件”标签下，我们看到很多“事件”条目，所谓“事件”，是指控件（按钮、组合框、列表框等）能够“感知到的”用户对它的各种外界“刺激”。对于按钮控件，它能够感知到的事件包括最常见的对按钮的“单击”、“双击”，等等。

Access中的控件像一个个小动物，能够“感知到”用户对它的不同“刺激”，而且还能够对接收到的“刺激”产生我们希望它所做的“反应”（执行我们定义给它的操作），这个“我们希望它所做的反应”可以在控件属性表中的“事件”标签下定义。

观察控件属性表中“事件”标签下的内容，我们发现，在“单击”事件后面，我们看到“[嵌入的宏]”字样，这表明我们已经给该按钮的单击事件定义了某种操作（也就是当时设计这个按钮时通过控件向导对话框指定给该按钮的任务）。

图7-3

单击“单击”事件后面“[嵌入的宏]”字样右侧的带有三个小圆点儿的按钮，我们可以进入“[嵌入的宏]”的设计窗口。原来，我们在“按钮向导”对话框中所做的设置已经被Access翻译成了Access能够理解的所谓的“宏“语言了（注意，画线的字是作者为便于读者理解给宏添加的注释），如图7-4所示。

图7-4

在Access中的宏不像Excel中的宏，它不能自动“录制”Access的界面连续操作，而是把大多数常用的Access操作变成了一个个“宏”零件，像具有不同功能的一块块积木一样，让我们能够根据程序功能的需要，从Access中的“宏零件库”中挑选不同的零件，按一定的顺序重新组合出有特定功能的宏。

图7-4就是一个由Access自动组装生成的、完成保存记录操作的Access宏。正是Access的这种能力，让我们的Access初级用户，不用接触（有一些难度的）宏也能够完成简单的数据库管理工具的设计。下面，我们通过一个简单的案例介绍一下Access中的宏功能。

7.2　用宏导出Excel报告

Excel擅长数据分析，Access擅长数据处理，这是不争的事实。作为“表哥表姐”的我们，已经掌握了Excel，现在又学习了Access，发挥二者的优势将是我们的特长！

随着小张饭馆的兴隆，业务数据量越来越大，一些需要做详细分析和图表展示的报告，小张还是希望能够在Excel里完成，虽然Access也有制作图表的能力，但是和Excel相比，毕竟相差很多。

我们可以把Access表或查询中的数据直接复制粘贴到Excel中，但操作起来有些繁琐。小张希望，在程序界面上再添加一个按钮，能够把所需要数据库后台的数据一键导出到Excel。

这一次，我们要创建一个能够导出Access中表或查询的宏，然后再把这个宏指定给数据库管理程序界面上的新增加的按钮。

首先，选择“创建”选项卡，单击“宏与代码”组中的“宏”按钮，如图7-5所示，进入宏设计界面。

图7-5

在Access宏设计界面中的“设计”标签下，单击“操作目录”按钮，此时，我们在界面右侧看到了Access宏的操作目录，在这个操作目录窗口里，Access把完成不同功能的“宏零件”分门别类地整理好放在了不同的类别下（我们有时间时可以分别展开浏览一下），如图7-6所示。

图7-6

在Access宏设计界面的左上方，有一个组合框，点开组合框，我们看到，所有Access的基本操作都在这里了（没有经过分类），如图7-7所示。设计Access宏的任务就是如何选择、组合、设计这些基本操作，让Access按照我们的流程完成交代给它的任务。

这里需要提示的是，对于Access的入门读者，一开始可能看不懂这些展开项目中的大多数内容，不过不要紧，随着将来你对Access学习的深入，这些不懂的项目也会变得熟悉。

图7-7

好，现在开始宏的设计！我们要完成的任务是导出Access中的一些有用的表或者查询到Excel，因此，对于宏还不太熟悉的我们，猜想这个“宏零件”应该在“数据导入/导出”操作目录下，因此，按照图7-8所示，在宏“操作目录”中选择“ExportWithFormatting”，并双击将其添加到设计界面，我们看到，该宏动作需要多个参数，但大多数参数不需要我们手动输入，只需我们选择一下，设置起来很快捷。

“ExportWithFormatting”宏的作用是把Access中的各种可输出对象（数据表、查询、报表，等等）带格式输出。它的第一个参数让你选择要输出的对象，这里我们选择“查询”，因为查询是对Access数据表的预处理，能够利用Access的特别能力，完成Excel中不能完成的一些数据操作。

当你选择“查询”时，“对象名称”参数旁边的下拉框中便会列出当前Access数据库中所有查询的名称。我们选择查询“Q0010_客户订单详情”，当然你可以根据需要选择任意一个查询（如果Access中没有满足我们要求的查询，我们还可以随时在Access中专门设计一个查询）。

在“输出格式”参数处，我们选择“Excel工作簿”，在“输出文件”参数处，如果你填写了 Excel文件的路径，则每次执行宏时都会把数据输出到这个保存路径下，如果留空，则执行宏时会让你临时选择输出位置，我们这里留空。

在“自动启动”参数处，选择“是”，这样，数据输出完毕时，会自动打开响应的Excel文件，这往往是我们所需要的。

图7-8

设置好宏的参数后，保存宏，在弹出的“另存为”对话框中，我们给该宏取名为“M010_导出单个表格到Excel”，如图7-9所示，然后单击“确定”按钮退出设计宏窗口。

图7-9

保存新建的宏后，我们看到，在Access左侧的“所有Access对象”列表中的“宏”类别下面出现了我们刚刚设计的Access宏，现在我们测试一下宏的运行情况。

双击宏的名称（或者选中宏，单击鼠标右键选择“运行”命令）运行该宏，我们看到，如图7-10所示弹出了一个“输出到”的对话框，这是因为我们没有提前指定Access对象的输出位置，让我们临时选择导出文件的存储位置。指定输出位置后，单击“确定”按钮。

宏很快执行完毕，并且立即自动打开了输出的Excel文件，如图7-11所示。这样我们就可以利用Excel的强项进行各种数据分析了（数据透视、各种图表等）。

图7-10

图7-11

7.3　那些隐藏的宏操作

不知你注意到了没有，在Access宏设计界面上部菜单栏的右侧，紧挨着“操作目录”按钮的地方，还有一个叫作“显示所有操作”的按钮。这个按钮是做什么用的呢？原来，Access为了安全起见，默认情况下，隐藏了一些它认为有风险的宏，我们按下“显示所有操作”按钮，会发现，在我们前面研究的“数据导入/导出”操作目录列表里出现了一些前面带有感叹号（！）的宏操作（其他类别中也有这样的宏操作），如图7-12所示。

下面我们来研究一下“数据导入/导出”类别里的“ImportExportSpreadsheet”宏操作。这个宏操作与前面我们研究的宏操作“ExportWithFormatting”有些类似，但“ExportWithFormatting”的不足之处是不能把多个 Access表格导出到同一个Excel文件中，而“ImportExportSpreadsheet”的功能要强大许多。

图7-12

选中宏动作“ImportExportSpreadsheet”，连续拖拽两次到宏设计界面（连续执行两次），并按照图7-13所示设置参数。需要注意的是，两个宏操作虽然相同，但对其设置的参数略有差异，第一个宏操作导出的是查询“Q0010_客户订单详情”，第二个宏操作导出的是查询“Q0050_菜品原料采购”，该宏操作有一个小小的缺点，那就是“表名称”参数处必须手动输入Access中表或者查询的名称。

在“文件名”参数处，我们填写的是同一个 Excel文件名称，这样，在执行该宏时，就会导出两个不同的查询数据到同一个Excel文件。

图7-13

由于“ImportExportSpreadsheet”没有导出后自动打开选项，为了让用户知道数据已经导出完毕，我们需要设置一个对话框来向用户提供反馈。这里我来告诉大家，这个宏操作叫作“Messagebox”（随着你对Access的了解，也会了解越来越多的宏操作）。展开下方的添加新操作组合框，找到并选择“Messagebox”宏操作，如图7-14所示。

图7-16

在“消息”参数处，填写我们自定义的消息。其他参数根据需要自行设置，如图7-15所示。最后，保存该宏，并将该宏命名为“M020_多个表格到Excel”。

图7-15

现在，我们在Access用户界面窗体上，添加一个按钮用来触发我们所设计的宏。

选中窗体“F0010_ 业务数据录入”，单击鼠标右键菜单中的“设计视图”命令，进入窗体的设计视图。按照我们以前学过的添加按钮的方法，在窗体界面合适位置处“画”一个按钮。并按如图7-16所示设置“命令按钮向导”。

图7-16

选择“运行宏”并单击“下一步”按钮后，我们看到Access中已经存在的所有宏，我们选择刚刚设计的第二个宏。然后单击“下一步”按钮，继续设置“命令按钮向导”的其他内容，最后单击“完成”按钮，如图7-17所示。

完成“输出到Excel”按钮的设置后，我们单击该按钮看一下该按钮的执行效果。你会得到一个如图7-18所示的对话框，表明数据导出完毕。

图7-17

图7-18

按照宏设计时所指定的导出位置，我们打开相应的 Excel文件，我们发现，该Excel工作簿包括两个工作表，如图7-19所示。这意味着，我们成功了！

Access宏的学习难度介于Access界面操作和对Access编写程序之间，而且如果想熟练操作Access宏，前提是对Access中的各种功能的熟悉。

图7-19

Access中的宏功能很强大，它不仅能按顺序执行我们交给它的动作，还能按条件执行，循环执行，作为Access的入门读物，这里不再讲解。这本书的主要目的是让大家对Access有一个整体上的理解，并打下一个很好的Access进阶学习的基础。

虽然，作为Access初学者，我们在这里仅仅介绍了Access宏的一些简单内容。不过幸运的是，Access是如此灵巧，即使不了解宏，也能完成数据库操作中的大部分功能。

结束语

到目前，我们已经用Access开发了一个最基本的数据库管理程序，通过这个案例，我们讲解了Excel数据的迁移、数据表格的规范化、Access查询的设计、Access窗体的设计、Access底层表格关系的设计等过程，带领大家用最短的时间熟悉了Access数据管理程序的规划和设计。作为Access的入门书籍，我相信大家至此已经对 Access有了一个基本的理解，并且具备了进一步深入学习 Access知识的能力，从而为最终掌握Access这个个人生产力提升的工具打下了基础。

在本书开头曾经提到过，Excel是一门技能，Access是一门技术，既然是技术就有一定的理解上的难度。虽然通过本书，我们已经能够应用Access解决一些实战问题，但是，要想达到“自由鱼”的状态，单靠一本入门书籍是远远不够的。但值得高兴的是，到现在为止，以最经济的时间投入，我们已经具备了很好的Access进阶学习的基础。

最后，建议关注作者的新浪微博@MrAccess，上面会有作者关于Access知识的分享。