0% found this document useful (0 votes)
39 views1,831 pages

Access Hacks - by Ken Bluttman

Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
39 views1,831 pages

Access Hacks - by Ken Bluttman

Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 1831

Access Hacks

By Ken Bluttman
...............................................
Publisher: O'Reilly
Pub Date: April 2005
ISBN: 0-596-00924-0
Pages: 352

Table of C ontents | I ndex | E xamples | E rrata

As part of the Microsoft Office suite, Access has become the


industry's leading desktop database management program for
organizing, accessing, and sharing information. But taking
advantage of this product to build increasingly complex Access
applications requires something more than your typical how -to
book. What it calls for is Access Hacks from O'Reilly. This valuable
guide provides direct, hands-on solutions that can help relieve the
frustrations felt by users struggling to master the program's
various complexities. For experienced users, Access Hacks
offers a unique collection of proven techniques and tools that
enable them to take their database skills and productivity to the
next level. For Access beginners, it helps them acquire a firm
grasp of the program's most productive features. A smart
collection of insider tips and tricks, Access Hacks covers all of
the program's finer points. Among the multitude of topics
addressed, it show s users how to: w ork w ith Access in multi-
user environments utilize SQL queries w ork w ith external data
and programs integrate Access w ith third-party products Just
imagine: a learning process w ithout the angst. Well, Access
Hacks delivers it w ith ease, thanks to these dow n-and-dirty
techniques not collected together anyw here else. Part of O'Reilly's
best-selling Hacks series, Access Hacks is based on author Ken
Bluttman's tw o decades of real-w orld experience in database
programming and business application building. It's because of his
vast experiences that the book is able to offer such a deep
understanding of the program's expanding possibilities.
Access Hacks
By Ken Bluttman
...............................................
Publisher: O'Reilly
Pub Date: April 2005
ISBN: 0-596-00924-0
Pages: 352

Table of C ontents | I ndex | E xamples | E rrata

Credits
About the Author
Contributors
Acknow ledgments
Preface
Why Access Hacks?
How to Use This Book
How This Book Is Organized
Conventions Used in This Book
Using Code Examples
Safari Enabled
How to Contact Us
Got a Hack?
Chapter 1. Core Access
Section 1.1. Hacks 112
Hack 1. Help Users Find the Objects They Need
Hack 2. Personalize Your Access Application
Hack 3. Work Fast and Avoid Typos
Hack 4. Optimize Data Changes
Hack 5. Transfer Data Betw een Versions of Access
Hack 6. Organize and Enhance Your Macros
Hack 7. Rid Your Database of Clutter
Hack 8. Protect Valuable Information
Hack 9. Work w ith Any Amount of Data
Hack 10. Find Database Objects in a Snap
Hack 11. Use a Junction Table
Hack 12. Stop the Database from Grow ing
Chapter 2. Tables
Section 2.1. Hacks 1318
Hack 13. Create an AutoNumber Field w ith a Custom
Value
Hack 14. Copy Data Betw een Tables Without an
Append Query
Hack 15. Steer Clear of System Tables
Section 16. Hide Sensitive Information
Hack 17. Simulate Table Triggers
Section 18. Create Tables Faster
Chapter 3. Entry and Navigation
Section 3.1. Hacks 1927
Hack 19. Help Users Navigate Through Long Forms
Hack 20. Help Users Enter Additional Text
Hack 21. Let Users Add Custom Items to
Predesigned Lists
Hack 22. Populate and Sort Lists w ith Flair
Hack 23. Use Custom Controls on Your Forms
Hack 24. Confirm Record Updates Before Saving
Hack 25. Put a Clock on a Form
Hack 26. Be Tab-Smart
Hack 27. Highlight the Active Control
Chapter 4. Presentation
Section 4.1. Hacks 2839
Hack 28. Separate Alphabetically Sorted Records
into Letter Groups
Hack 29. Create Conditional Subtotals
Hack 30. Use Conditional Formatting to Point Out
Important Results
Hack 31. Provide a Direct Link to a Report
Hack 32. Protect Intellectual Property
Hack 33. Create a Slideshow in Access
Hack 34. Play Videos in Access Forms
Hack 35. View Reports Embedded in Forms
Hack 36. Put Line Numbers on a Report
Hack 37. Shade Alternating Lines on a Report
Hack 38. Save Paper by Reducing Whitespace
Hack 39. Include the Date, Time, and Page Count
Chapter 5. Queries and SQL
Section 5.1. Hacks 4054
Hack 40. Return a Sample of Records
Hack 41. Create Bulletproof Insert Operations
Hack 42. Find Unmatched Records on Multiple Field
Keys
Hack 43. Place a Grand Total in a Query
Hack 44. Sort Any Arbitrary String of Characters
Hack 45. Summarize Complex Data
Hack 46. Get All Combinations of Data

Hack 47. Don't Let Nulls Ruin Data Summaries


Hack 48. Use a Custom Function in a Query
Hack 49. Create Access Tables w ith SQL Server
Scripts
Hack 50. Use Wildcards in Queries
Hack 51. Get Cleaner Or-Based Criteria
Hack 52. Get Cleaner And-Based Criteria
Hack 53. Create an Outer Join
Hack 54. Use Regular Expressions in Access
Queries
Chapter 6. Multiuser Issues
Section 6.1. Hacks 5558
Hack 55. Test for Duplication
Hack 56. Distribute a Split Database w ith Predefined
Table Links
Hack 57. Build a Time-Out Feature
Hack 58. Implement Unique Usernames
Chapter 7. External Programs and Data
Section 7.1. Hacks 5971
Hack 59. Import Noncontiguous Ranges of Data from
Excel
Hack 60. Use Excel to Reorient Access Data
Hack 61. Use Excel Functions Inside Access
Hack 62. Use Word to Compare Data in Tw o Access
Tables
Hack 63. Import Varied XML Data into Access
Hack 64. Export XML Data Sanely
Hack 65. Break Through VBA's Transformation
Barrier
Hack 66. Leverage SQL Server Pow er by Calling
Stored Procedures
Hack 67. Manage Word Documents from Access
Hack 68. Use Access as a Front End to MySQL
Hack 69. Send Access Data Through Outlook
Automatically
Hack 70. Create Access Tables from Outside
Access
Hack 71. Write VBA w ith the Macro Recorder in
Word and Excel
Chapter 8. Programming
Section 8.1. Hacks 7291
Hack 72. Store Initial Control Selections for Later
Recall
Hack 73. Write Code Faster by Turning Off Syntax-
Checking

Hack 74. Substitute Domain Aggregate Functions for


SQL Aggregate Functions
Hack 75. Shrink Your Code w ith Subroutines
Hack 76. Shrink Your Code w ith Optional Arguments
Hack 77. Protect Programming Code from Curious
Users
Hack 78. Build a Secret Developer Backdoor into
Your Applications
Hack 79. Help Users Drill Dow n to a Record
Hack 80. Prevent Users from Disabling Your Startup
Options
Hack 81. Inform Users of a Long Process
Hack 82. Allow Users to Choose a Back-End
Database
Hack 83. Override the Timeout Interval
Hack 84. Save Values from Unbound Controls for
Later Recall
Hack 85. Sort Records Randomly
Hack 86. Bulk-Update Controls on a Form
Hack 87. Provide Complete XML Control to Any
Version of Access
Hack 88. Use Custom Enumerations
Hack 89. Convert Text to the Desired Case
Hack 90. Create a Code Library
Hack 91. Automatically Check for Database Table
Updates
Chapter 9. Third-Party Applications
Section 9.1. Hacks 9295
Hack 92. Document Your Database w ith Total
Access Analyzer
Hack 93. Build an Application Shell w ith EZ
Application Generator
Hack 94. Load Your Database w ith Test Data
Hack 95. Use Access as an XML Database
Chapter 10. The Internet
Section 10.1. Hacks 96100
Hack 96. Export a Report as HTML
Hack 97. Use a Brow ser Inside Access
Hack 98. Pull the HTML Source Code from a Web
Site
Hack 99. Dow nload Files Using the Web Brow ser
Control
Hack 100. Use a Smart Tag to Open a Web Page
Colophon

Index
Credits

A bout the A uthor

C ontributors

A c knowledgments
About the Author
Ken Bluttman has been hac king around with A c c es s for years .
H aving honed his programming s kills bac k when the P C was jus t
bec oming a hous ehold item, Ken found A c c es s a joy to us e, even
bac k in earlier vers ions .

Bes ides A c c es s , Ken exc els at E xc el and the other O ffic e


produc ts , as well as SQ L Server, web development, and
V B/V B.N E T development. N o wonder he rarely s leeps .

Ken is als o the author of Developing Micros oft Office Solutions


(A ddis on Wes ley) and Excel Formulas and Functions for Dummies
(Wiley), as well as numerous print and web- bas ed artic les .

O n the pers onal s ide, Ken is a mus ic ian and a nature lover. H ave
guitar, will travel to the woods . Ken lives in N ew York with his
wife, s on, dog, and s everal amphibians .

V is it Ken at his web s ite: http://www.bluttman.c om.


Contributors
T he following people c ontributed their hac ks , writing, and
ins piration to this book:

Steve C onklin is an independent s oftware developer and


the owner of U ltra D .N .T. (D evelopment, N etworks , and
Training) Tec hnology C ons ulting, loc ated in Q ueens ,
N ew York. H e s pec ializes in A c c es s , V is ual
Bas ic /V B.N E T, and M S- SQ L Server development, and is
releas ing a line of P oc ketP C applic ations for the mobile
profes s ional. Steve has written s everal artic les for
Acces s /VB/SQL Advis or magazine and he teac hes
M ic ros oft Windows and O ffic e c ours es at a N ew York
C ity c ommunity c ollege. H e is available for development
work and c an be reac hed at U ltraD N T @ H otmail.c om.

Steve H uff has been developing A c c es s databas e


applic ations for more than nine years . H e has a
c omputer s c ienc e degree from N orthern Kentuc ky
U nivers ity, where he is taking night c ours es toward a
mas ter's degree in information s ys tems . H e has been
developing M ic ros oft O ffic e s olutions as a c ons ultant
working for SA RC O M for more than s even years . Steve
lives in Kentuc ky with his wife, M elis s a. You c an reac h
him through his web s ite: http://www.huffs .us .

Kirk L amb has been dabbling with A c c es s for many


years . A lthough his expertis e is in boating, he knows a
good databas e when he s ees one. Kirk lives with his wife,
D ill, in Was hington s tate.

A ndrea M os s firs t got involved with A c c es s when s he


des igned a s ys tem to trac k ins uranc e c laims . Sinc e
then, s he has applied her artis tic s kills to des igning
layout and c olor s c hemes for various G U I s , inc luding
A c c es s forms and web s ites . A long the way, s he has
pic ked up a few A c c es s tric ks of her own.

M ic hael Sc hmalz works in banking and performs


bus ines s and tec hnology c ons ulting in a variety of
indus tries . H e has been a tec hnic al editor for O 'Reilly on
M ic ros oft O ffic e books . M ic hael has a degree in financ e
from P enn State. H e lives with his wife and daughter in
P enns ylvania.

Simon St.L aurent is a web developer, network


adminis trator, c omputer- book author, and XM L
troublemaker living in I thac a, N ew York. H is books
inc lude XML: A Primer, XML Elements of Style, Building XML
Applications , Cookies , and Sharing Bandwidth. H e is a
c ontributing editor to XM L hac k.c om and an oc c as ional
c ontributor to XM L .c om.

M argaret L evine Young has us ed s mall c omputers s inc e


the 1 9 7 0 s . She graduated from U nix on a P D P - 1 1 to
A pple D O S on an A pple I I to D O S, Windows , and U nix
on a variety of mac hines . She has done all kinds of jobs
that involve explaining to people that c omputers aren't
as mys terious as they might think, inc luding managing
the us e of P C s at C olumbia P ic tures , teac hing s c ientis ts
and engineers what c omputers are good for, and writing
and c owriting c omputer manuals and books , inc luding
Unders tanding Javelin PLUS, The Complete Guide to PC-File,
UNI X for Dummies , and The I nternet for Dummies . M argy
has a degree in c omputer s c ienc e from Yale U nivers ity
and lives with her hus band and two c hildren in Vermont.
Acknowledgments
T his book is a c ollaborative effort. M y thanks go to the
c ontributors for providing great hac ks that I am s ure you will
enjoy as muc h as I have.

Spec ial thanks and apprec iation go to my editor, M itc h Tulloc h.


M itc h has s tuc k with me through the thic k and thin of getting
this projec t c ompleted. H is patienc e and pers everanc e have
been awes ome. M itc h would like to thank M T S C ommunic ations
I nc . (http://www.mts .c a) for providing I nternet s ervic es for
hos ting his web s ite (http://www.mtit.c om).

T hanks to M ic hael Sc hmalz for tec h- reviewing the material and


keeping on my bac k about early binding vers us late binding and
other pertinent topic s dear to our profes s ion.

T hanks to Brian Sawyer and the great O 'Reilly team. T hanks to


all of you.

T hanks to my agent, N eil Salkind, and the Studio B team. N eil


c alled me one day las t year to s ee if I would be interes ted in
writing Acces s Hacks . O f c ours e! A nd that's how I met M itc h
Tulloc h.

T hanks to the s taff at D atabas e C reations , I nc .


(http://www.databas ec reations .c om) and FM S, I nc .
(http://www.fms inc .c om) for providing c opies of their outs tanding
produc ts .

L as t but not leas t, thanks to my wife G ayla and s on M atthew.


Working on a book is always s tres s ful, and they have been real
troopers in giving me s pac e and time to c omplete the book. I t's
c ute to s ee a s even- year- old bec oming s uc h a c omputer pro.
O ften, M atthew will s it on my lap and watc h what I am typing.
N ow he is an A c c es s expert in his own right.
Preface
A c c es s really is an amazing produc t. I ts power is vas t, and yet
its maintenanc e is low. I n fac t, in mos t ins tallations it s its on
the des ktop and is maintenanc e- free. I t's flexible enough to be
us ed by one pers on or to run an entire c ompany. I t's a rapid
applic ation development (RA D ) tool that outs hines other s uc h
tools (s uc h as V is ual Bas ic ) in time to development and eas e of
us e.

A c c es s is als o a c omplete databas e applic ation s ys tem. I t


inc orporates both the bac k- end and front- end elements of a
databas e, thereby eliminating the need to us e two produc ts to
get your work done. E ven s o, its flexibility allows an A c c es s
databas e file to be jus t a bac k end or jus t a front end. A c c es s
c an c ontrol data in external databas e s ys tems s uc h as SQ L
Server and O rac le.

N eed I s ay more? I don't think you need any c onvinc ing to know
what a great produc t A c c es s is . E ither you are us ing it already,
or you are about to s tart. Well, here is s ome great news : this
book is going to s how you even more ways to us e A c c es s .
Whether it's how to run Union queries , play video files in A c c es s ,
view web s ites within A c c es s , or even c ontrol A c c es s from
another produc t, there are hac ks here to tic kle every fanc y.

Acces s Hacks lets you move beyond the familiar tables , forms ,
and reports paradigm and get new ins ights into making your
databas e applic ations more valuable and exc iting. I t's my
pleas ure to s how you new ways to work with your favorite
databas e produc t. So, fire up your c omputer, and let's get
s tarted!
Why Access Hacks?
T he term hacking has a bad reputation in the pres s . T hey us e it
to refer to s omeone who breaks into s ys tems or wreaks havoc
with c omputers as their weapon. A mong people who write c ode,
though, the term hack refers to a "quic k- and- dirty" s olution to a
problem, or a c lever way to get s omething done. A nd the term
hacker is taken very muc h as a c ompliment, referring to s omeone
as being creative, having the tec hnic al c hops to get things done.
T he H ac ks s eries is an attempt to rec laim the word, doc ument
the good ways people are hac king, and pas s the hac ker ethic of
c reative partic ipation on to the uninitiated. Seeing how others
approac h s ys tems and problems is often the quic kes t way to
learn about a new tec hnology.
How to Use This Book
Acces s Hacks is not meant to be a s equential read, although I
won't c omplain if you read it s traight through, from c over to
c over! T he book c ontains 1 0 0 hac ks , and eac h s tands on its
own merit. You c an read them in any order. Some hac ks have a
c ommon theme with other hac ks , in whic h c as e the flow is duly
noted. O ther than that, jus t dig in, and s ee what interes ts you.
O ne group of hac ks might be what you need for today's projec t,
and another group might be what you need tomorrow.
How This Book Is Organized
E ac h c hapter in Acces s Hacks c enters on a fac et of A c c es s . I n
this way, you c an foc us on areas in whic h you need a little
ins piration. I f you need help with queries and SQ L , go to C hapter
5 . I f you want to learn s ome programming tric ks , go to C hapter
8 . I n partic ular, here is what you'll find in eac h c hapter:

C hapter 1 , Core Acces s

T he firs t c hapter c overs the bas ic s , from organizing


databas e objec ts to working with data. I n this c hapter,
you'll find nuggets about helping us ers , overc oming
vers ion inc ompatibility, and even how to work with any
amount of data.

C hapter 2 , Tables

Tables are the c ore objec t of any databas e. I n this


c hapter, you'll find hac ks that s how you how to move
data between tables and how to res et A utoN umbering to
begin with a number of your c hoic e. A ls o, you will learn
what s ys tem tables are and how to have them s tay out of
your way.

C hapter 3 , Entry and Navigation

T his c hapter foc us es on us ers ' needs . Bes ides s toring


data, a databas e s ys tem needs to make it eas y for us ers
to manage the data. C hapter 3 is c hoc k- full of hac ks
that improve how us ers work with forms , whic h of c ours e
are the mos t c ommon databas e objec ts us ers interac t
with.

C hapter 4 , Pres entation

O nc e data is entered and s tored, the res t of the equation


involves reporting. T his c hapter s hows you new ways to
work with reports . L earn how to us e a watermark, provide
s ophis tic ated s orting, and provide c onditional totals .
D on't forget to c hec k the hac ks on c reating a s lide s how
and playing videos !

C hapter 5 , Queries and SQL

Running queries is a big part of databas e work. M any of


the hac ks in this c hapter take you beyond the bas ic s of
the query des ign grid. I mmers e yours elf in the SQ L
language its elf as you dis c over Union queries , us ing the
In and Not operators , and how to us e c us tom func tions in
queries . T here is even a hac k that enc ourages you to
query unrelated tables to return all c ombinations of data
in two fields .

C hapter 6 , Multius er I s s ues

C ertain is s ues exis t only in a s hared environment. I n


this c hapter, you'll find hac ks that provide workarounds
for c ommon problems . L earn how to end an unattended
edit and how to dis tribute a databas e with no has s les .
C hapter 7 , External Programs and Data

A c c es s is eas y to integrate with other programs and


protoc ols . T his c hapter s hows you many ways to us e
A c c es s with other produc ts , inc luding E xc el, Word,
M ySQ L , and SQ L Server. I f you have an inkling of how to
work with XM L data, this c hapter inc ludes hac ks for that.
T here is even a hac k that s hows you how to c reate
A c c es s tables without running A c c es s .

C hapter 8 ,Programming

T his c hapter provides a number of programming


tec hniques . I t inc ludes hac ks for optimizing c ode,
writing fas ter c ode, and protec ting c ode. O ther hac ks
provide minis olutions , s uc h as a way to drill down to a
s pec ific rec ord and to provide feedbac k during a long
proc es s .

C hapter 9 , Third-Party Applications

T his c hapter previews a few third- party produc ts that


make your databas e work a breeze. L earn about
produc ts that c reate a databas e framework, doc ument
your databas e, and even provide data. L as t but not
leas t, this c hapter provides an overview of a c omplete
XM L- bas ed applic ation s olution.

C hapter 1 0 , The I nternet


T he hac ks in this c hapter s how you how to c reate H T M L
files from A c c es s . With jus t a little editing with an H T M L
tool or in a text editor you c an turn an A c c es s report
into the format you need. You'll find hac ks in this c hapter
for putting a web brows er direc tly on an A c c es s form.
N eed to c hec k your online inves tments ? You c an do s o
without leaving the databas e.
Conventions Used in This Book
T he following is a lis t of the typographic al c onventions us ed in
this book:

Plain text

I ndic ates options , queries , and options entered us ing


A c c es s 's graphic al us er interfac e (G U I ), inc luding table
titles , c ell identifiers , named ranges , menu titles , menu
options , menu buttons , and keyboard ac c elerators (s uc h
as A lt and C trl).

I talics

I ndic ates U RL s , filenames , filename extens ions , and


direc tory/folder names . For example, a path in the
files ys tem appears as /Developer/ Applications .

Constant width

Shows c ode examples , the c ontents of files , c ons ole


output, as well as the names of variables , c ommands ,
func tions , mac ros , s tatements , c ommand- line queries ,
and other c ode exc erpts .

Constant width bold


H ighlights portions of c ode, typic ally new additions to
old c ode.

Cons tant width italic

U s ed in c ode examples and tables to s how s ample text


to be replac ed with your own values .

Color

T he s ec ond c olor indic ates a c ros s referenc e within the


text.

You s hould pay s pec ial attention to notes s et apart from the text
with the following ic ons :

T his is a tip, s ugges tion, or general note.


I t c ontains us eful s upplementary
information about the topic at hand.

T his is a warning or note of c aution, often


indic ating that your money or your privac y
might be at ris k.
T he thermometer ic ons , found next to eac h hac k, indic ate the
relative c omplexity of the hac k:

beginner moderate expert


Using Code Examples
T his book is here to help you get your job done. I n general, you
c an us e the c ode in this book in your programs and
doc umentation. You do not need to c ontac t us for permis s ion
unles s you're reproduc ing a s ignific ant portion of the c ode. For
example, writing a program that us es s everal c hunks of c ode
from this book does not require permis s ion. Selling or dis tributing
a C D - RO M of examples from O 'Reilly books does require
permis s ion. A ns wering a ques tion by c iting this book and
quoting example c ode does not require permis s ion. I nc orporating
a s ignific ant amount of example c ode from this book into your
produc t's doc umentation does require permis s ion.

We apprec iate, but do not require, attribution. A n attribution


us ually inc ludes the title, author, publis her, and I SBN . For
example: "Acces s Hacks by Ken Bluttman. C opyright 2 0 0 5
O 'Reilly M edia, I nc ., 0 - 5 9 6 - 0 0 9 2 4 - 0 ."

I f you feel your us e of c ode examples falls outs ide fair us e or the
permis s ion given here, feel free to c ontac t us at
permis s ions @oreilly.com.
Safari Enabled

When you s ee a Safari® E nabled ic on on


the c over of your favorite tec hnology book, that means the book
is available online through the O 'Reilly N etwork Safari Books helf.

Safari offers a s olution that's better than e- books . I t's a virtual


library that lets you eas ily s earc h thous ands of top tec h books ,
c ut and pas te c ode s amples , download c hapters , and find quic k
ans wers when you need the mos t ac c urate, c urrent information.
Try it for free at http://s afari.oreilly.c om.
How to Contact Us
We have tes ted and verified the information in this book to the
bes t of our ability, but you might find that features have c hanged
(or even that we have made mis takes ! ). A s a reader of this book,
you c an help us improve future editions by s ending us your
feedbac k. P leas e let us know about any errors , inac c urac ies ,
bugs , mis leading or c onfus ing s tatements , and typos that you
find anywhere in this book.

P leas e als o let us know what we c an do to make this book more


us eful to you. We take your c omments s erious ly and will try to
inc orporate reas onable s ugges tions into future editions . You c an
write to us at:

O 'Reilly M edia, I nc .
1 0 0 5 G ravens tein H wy N .
Sebas topol, C A 9 5 4 7 2
(8 0 0 ) 9 9 8 - 9 9 3 8 (in the U .S. or C anada)
(7 0 7 ) 8 2 9 - 0 5 1 5 (international/loc al)
(7 0 7 ) 8 2 9 - 0 1 0 4 (fax)

To as k tec hnic al ques tions or to c omment on the book, s end


email to:

bookques tions @ oreilly.c om

T he web s ite for Acces s Hacks lis ts examples , errata, and plans
for future editions . You c an find this page at:

http://www.oreilly.c om/c atalog/ac c es s hks

For more information about this book and others , s ee the


O 'Reilly web s ite:
http://www.oreilly.c om
Got a Hack?
To explore H ac ks books online or to c ontribute a hac k for future
titles , vis it:

http://hac ks .oreilly.c om
1. Core Access
Sec tion 1 .1 . H ac ks 1 1 2

H ac k 1 . H elp U s ers Find the O bjec ts T hey N eed

H ac k 2 . P ers onalize Your A c c es s A pplic ation

H ac k 3 . Work Fas t and A void Typos

H ac k 4 . O ptimize D ata C hanges

H ac k 5 . Trans fer D ata Between Vers ions of A c c es s

H ac k 6 . O rganize and E nhanc e Your M ac ros

H ac k 7 . Rid Your D atabas e of C lutter

H ac k 8 . P rotec t Valuable I nformation

H ac k 9 . Work with A ny A mount of D ata

H ac k 1 0 . Find D atabas e O bjec ts in a Snap

H ac k 1 1 . U s e a J unc tion Table

H ac k 1 2 . Stop the D atabas e from G rowing


1.1. Hacks 112
A c c es s is us ed in many different s ituations , in many different
ways , by a divers e group of people. Some are novic es , while
others have been us ing a s ingle c us tom A c c es s s olution for
years . Still others are s ophis tic ated us ers who want to take
advantage of the applic ation's bells and whis tles , or they are
A c c es s developers who make thos e bells and whis tles ring and
s ing.

O ne thing all us ers and developers want is for A c c es s to s upport


their needs as effic iently as pos s ible. T his c an require a little
c us tomization or a down- right minis olution that's implemented
ins ide A c c es s and helps to s upport the purpos e of the overall
s olution.

T his c hapter inc ludes a c ollec tion of hac ks you c an implement


direc tly within A c c es s . You'll find hac ks des igned to help general
us ers by making their experienc e more rewarding and more
effic ient. You'll als o find hac ks a power us er c an implement to
expand the value of his c us tom A c c es s applic ation. You'll even
find hac ks that only a developer c an implement. A little V BA
goes a long way here.
Hack 1. Help Users Find the Objects
They Need

Place shortcuts to pertinent objects in custom groups so that


users don't have to wade through all the database objects.

T he A c c es s databas e window c an be overwhelming to s ome


us ers . Tables , queries , forms , reports ; determining where to find
objec ts you need within thes e objec t c ollec tions is n't exac tly a
us er- friendly proc es s . Bes ides , s ometimes a us er needs jus t a
handful of objec ts to c omplete his work. A nd yet he might be
c onfronted with c ons iderably more objec ts than he needs .

L uc kily, the A c c es s databas e window allows you to c reate


c us tom groupings in whic h you c an plac e s hortc uts to only the
des ired objec ts . J us t as the Windows des ktop has s hortc uts to
folders , files , and applic ations , A c c es s lets you make s hortc uts
to your databas e objec ts . A nd it's a c akewalk to do s o!

1.2.1. The Plain Database Window

Your A c c es s applic ation might open to a navigation, or main,


form. From there, us ers c lic k their way through the applic ation.
But not all applic ations are made in this way. Figure 1 - 1 s hows
the plain databas e window in all its unimpres s ive glory. Some
applic ations open to this func tional but ineffic ient window.
Figure 1-1. The standard Access database
window

O f c ours e, you c an get to all the objec ts you need from here:
c lic k the Q ueries tab to find the queries you want to run, or c lic k
the Reports tab to find the reports you want to run. But you c an
avoid this drudgery. O ne great thing about the databas e window
is the ability to make your own groups . I n fac t, it is c lear that the
databas e window does s eparate obj ects from groups . I n Figure 1 -
1 , on the left s ide of the databas e window, you c an s ee a c lear
dis tinc tion of groups in the bottom half of the window.

1.2.2. Using Groups

By default, there is one Favorites group, in whic h you c an plac e


s hortc uts to objec ts . I t's eas y to do; jus t find the objec t
wherever it exis ts within the various tabs , and then c lic k and
drag it to the Favorites group. Figure 1 - 2 s hows the res ult of
doing jus t that. T he Favorites group has been filled with
s hortc uts to s ome of the databas e objec ts . N ote that thes e are
s hortcuts . T he original objec ts are s till where they belong within
the objec t c ollec tions . You c an delete a s hortc ut in the Favorites
group, and the original objec t remains .

Figure 1-2. Placing shortcuts in the Favorites


group
C learly, us ing the Favorites group lets you foc us us er ac tivity!
H owever, you c an als o go a s tep further by adding additional
groups for even better organization. H ow about a group for eac h
us er or type of us er? For example, data entry operators and
s upervis ors might us e the s ame databas e applic ation, but with
different objec ts ; the data entry operators might us e c ertain
forms , and s upervis ors or managers might us e queries and
reports to s ee overall ac tivity.

I t's eas y to add a new group. J us t right- c lic k in the G roups area,
s elec t N ew G roup from the lis t of options , and give the group a
name. A t this point you c an drag objec ts to the new group.
Figure 1 - 3 s hows how two new groups have been added to the
applic ation. E ac h has its own lis t of s hortc uts .

A nother good point about groups is that the s ame objec ts c an


res ide in more than one group. I f you have a reas on to plac e a
s hortc ut to a partic ular report in three different groups , A c c es s
won't hold you bac k. I n fac t, you c an even c opy s hortc uts from
one group to another.

Figure 1-3. Creating and using a custom group


Hack 2. Personalize Your Access
Application

Build personalization f unctionality so that users can set up the


application in ways that work best f or them.

T here is no reas on to limit all us ers to us ing an A c c es s


applic ation in the s ame way. I t's eas y to overlook this c apability
bec aus e A c c es s allows you to des ignate only one opening form
in its s tartup options that is , unles s you tap into its databas e-
opening events . T hen, you c an c hoos e whic h form will open, what
its properties are, and more. You c an effec tively make all fac ets
of the applic ation unique to a partic ular individual or profile. H ere
are a few items you c an tailor this way:

Forms

Spec ify the opening form, how it's dis played, and what
func tionality it inc ludes

D ata s ourc es

Spec ify whic h pers onalization tables , internal or


external, are needed for eac h us er's tas ks
Reports

Show or hide details

T his hac k s hows you how to us e the AutoExec mac ro to run an


opening func tion that delivers a pers onalized interfac e to the
us er. For this to work, you mus t firs t c reate a databas e table to
s tore us er preferenc es , and then, when the databas e s tarts up,
you mus t be able to identify the us er to the databas e. You c an do
this in a number of ways : for ins tanc e, a pop- up input box c an
as k for a name or initials (pos s ibly with a pas s word), a
c ommand- line s witc h c an provide the us er identity, or, if the
A c c es s s ec urity model is in us e, the us er I D c an be made
available through the CurrentUser property.

1.3.1. Storing Preferences

U s er preferenc es are s tored in a table that has a field for eac h


pers onalization fac et. You determine whic h features to
pers onalize. For example, a L ong datatype field c an s tore the
preferred bac kground c olor, a text field c an s tore the name of the
preferred opening form, and s o on. Figure 1 - 4 s hows s uc h a
table, aptly named C us tomized, with a few preferenc es filled in.
T he field name indic ates the preferenc e, and the ac tual value in
the field is the s etting.

Figure 1-4. A table to hold single user


preferences
T his table is perfec t for databas es that are dis tributed to loc al
c lient mac hines . I n this c onfiguration, only one us er us es an
ins tanc e of the databas e. T herefore, the table is s truc tured to
s tore the preferenc es of jus t a s ingle us er. A key point about the
table is that it always has jus t a s ingle rec ord. T hat s ingle
rec ord c ontains a field for eac h pers onalized item.

I n a s hared databas e c onfiguration (s uc h as when all us ers are


us ing a network c opy), the table needs to have an additional field
to identify eac h us er. T he number of rec ords this table ends up
c ontaining matc hes the number of us ers , plus onethat is , one
rec ord per us er, plus a default rec ord for the A dmin us er. Figure
1 - 5 s hows this s truc ture.

I t's a good idea to leave a rec ord for the


A dmin us er. T his is the default A c c es s
us er ac c ount and is pres ent even when the
s ec urity model is n't us ed. When no
s ec urity login is us ed, the CurrentUser
property defaults to Admin.

Figure 1-5. A table to hold multiple user


preferences

A ll that's left to c omplete this hac k is to give us ers a way to


s elec t their preferenc es . N o, us ers aren't expec ted to enter s uc h
a c ryptic thing as the numeric al repres entation of a c olor! So,
we'll us e a form (what els e! ) to c apture preferenc es . T his unique
form s erves to jus t manage preferenc es ; it has no other
interac tion with the databas e. Figure 1 - 6 s hows the s truc ture of
s uc h a form.

Figure 1-6. A form in which users select their


preferences
O nc e the s elec tions are made on the form, the Save P referenc es
button writes the preferenc es to the table. For a s ingle- us er
table, a s imple SQ L ins ert does the tric k, like this :

Update Customized Set FormBackGroundColor=8454143, FontSiz


OpeningForm='Receivables', ShowReportDetails='No'

For the multius er c onfiguration, the extra field is in the SQ L


s tatement:

Update Customized Set FormBackGroundColor=8454143, FontSiz


OpeningForm='Main Form', ShowReportDetails='Yes' Where Use

T hes e SQ L s tatements are as s embled us ing the values of the


form c ontrols . A c tiveX D ata O bjec ts (A D O ) is us ed to update
the values in the table. A fter the SQ L s tatement is as s embled,
the Execute method of the Connection objec t runs the update:

Private Sub cmdSave( )


On Error GoTo err_end
Dim conn As ADODB.Connection
Set conn = CurrentProject.Connection
Dim ssql As String
ssql = "Update Customized Set " & _
"FormBackGroundColor=" & _
Me.groupFormColor & ", " & _
"FontSize='" & _
Choose(Me.groupFontSize, "Small", "Large") & "', "
"OpeningForm='" & Me.lstForms & "', " & _
"ShowReportDetails='" & _
Choose(Me.groupReportDetail, "Yes", "No") & "'"
conn.Execute ssql
conn.Close
Set conn = Nothing
MsgBox "Preferences updated!"
Exit Sub
err_end:
conn.Close
Set conn = Nothing
MsgBox Err.Description
End Sub

1.3.2. Applying the Preferences

J us t s toring the preferenc es does nothing, s o let's c rac k this


applic ation open a little wider. O ne of the preferenc es s elec ts
whic h form to dis play at s tartup. T he AutoExec mac ro is us ed here
to run a func tion that us es the las t s aved preferenc e s etting. A s
before, if this is a s ingle- us er ins tallation, one type of table is
us ed, but in a multius er c onfiguration, the us ername plays a role.

H ere are two func tions that c an be c alled by the AutoExec mac ro.
T he AutoExec mac ro's RunCode ac tion is us ed with the func tion
name as the parameter. I n either c as e, the DLookup func tion grabs
the opening form preferenc e and opens that form. T he differenc e
is in whether the DLookup func tion filters to a us ername. I n the
firs t func tion, it does n't, but in the s ec ond func tion, it does :

Function open_up_single( )
On Error GoTo err_end
Dim myform As String
myform = DLookup("OpeningForm", "Customized")
If Not IsNull(myform) Then
DoCmd.OpenForm myform
Else
DoCmd.OpenForm "Switchboard"
End If
Exit Function
err_end:
MsgBox Err.Description
End Function

Function open_up_multi_user( )
'On Error GoTo err_end
Dim myform As String
Dim username As String
myform = _
DLookup("OpeningForm", "Customized", "UserName ='" & _
CurrentUser & "'")
If Not IsNull(myform) Then
DoCmd.OpenForm myform
Else
DoCmd.OpenForm "Switchboard"
End If
Exit Function
err_end:
MsgBox Err.Description
End Function

N ote that an If…Else bloc k handles opening the default


Switchboard form in c as e a null value is returned.

You need to implement how to us e other types of preferenc es ,


s uc h as inc luding report details or us ing a different font s ize,
when and where it makes s ens e for the given preferenc e. For
example, here's how you c an c hange the bac kground c olor of a
form in the open event of the form:

Private Sub Form_Open(Cancel As Integer)


Me.Detail.BackColor = DLookup("FormBackGroundColor", "Cust
End Sub

1.3.3. Using the Hack

A ll that's left now is to dec ide how to handle opening the


c us tomization form. You c an make this ac tion available on a
toolbar, via a menu, or via a mac ro. A great idea is to put it into a
c us tom group of c ommonly us ed objec ts . See "H elp U s ers Find
the O bjec ts T hey N eed" [Hack #1] to learn about making
c us tom groups .
Hack 3. Work Fast and Avoid Typos

Save time and avoid mistakes by using simple keystrokes f or


entering the date, time, or other commonly used entries.

T he mous e is nic e, but nothing beats getting around an


applic ation fas ter than keyboard s hortc uts . C trl- C for c opy, C trl-
V for pas te, and s o on, are pretty familiar. H ow about keyboard
s hortc uts for entering the date, time, and other data? U s ing
thes e s hortc uts will s ave valuable time when you are in a rus h to
finis h a projec t. A nd how often are you not in a rus h?

1.4.1. Know Thy Shortcuts

Table 1 - 1 s ummarizes us eful keyboard s hortc uts to us e within


your A c c es s applic ations . T his is n't an exhaus tive lis t of
keyboard s hortc uts not by a long s hot! You c an us e the A c c es s
H elp s ys tem to find all the s hortc uts . T he ones pres ented in
Table 1 - 1 are s pec ific s hortc uts for entering data.

Table 1-1. Keyboard shortcuts for entering data


Keyboard
A ction
shortcut
E nter the c urrent time. C trl- :

E nter the c urrent date. C trl- ;

I ns ert data from the s ame field in the previous


C trl- '
rec ord.

C trl- A lt-
I ns ert the default value for the field.
s pac ebar

I ns ert a new line in a text or memo field. C trl- E nter

E nter a new rec ord. C trl- +

P as te the c ontents of the Windows c lipboard. C trl- V

T hes e s hortc uts are quite handy. H ave you ever forgotten the
c urrent date when you had to enter it in a field? Well, all you need
to remember now is the keyboard s hortc ut to enter the c urrent
date. Whic h brings us to the next point…

1.4.2. Remember Where to Reference


Shortcuts
I t takes time to memorize a group of s hortc uts , s o the next bes t
thing is to boil it down to memorizing jus t one. T he twis t here is
to have the lis t of keyboard s hortc uts available on a form that
you c an eas ily dis play us ingyou gues s ed ita keyboard s hortc ut.
H owever, you need to c reate this s hortc ut.
Using the AutoKeys Macro

T he AutoKeys mac ro lets you as s ign databas e ac tions to


c us tom keyboard s hortc uts . You c an as s ign ac tions to
the func tion keys , to key c ombinations s uc h as C trl- A ,
and to the I ns ert and D elete keys . You mus t follow a
s tric t s yntax, however: a c arat (^) repres ents the C trl
key, and a plus s ign (+) repres ents the Shift key. You
enter regular keys verbatim, and you enc los e func tion
keys and s pec ial keys (I ns ert and D elete) in brac es
({}). H ere are a few examples :

^A s ets an ac tion to C trl- A .

{F9} s ets an ac tion to the F9 func tion key.

+{F9} s ets an ac tion to Shift- F9 .

{INSERT} s ets an ac tion to the I ns ert key.

When you s et a c us tom s hortc ut to an exis ting default


s hortc ut, the c us tom s hortc ut overrides the default one.
T herefore, you c an override c ommon keyboard
s hortc uts , s uc h as C trl- V (pas te), and ins tead provide
your own.

T he s yntax s tatements are plac ed in the M ac ro N ame


c olumn, and the appropriate ac tions are s et in the
A c tion c olumn. T he only other requirement is that the
mac ro is ac tually named AutoKeys.

Figure 1 - 7 s hows a form that lis ts the keyboard s hortc uts . T he


form is bas ed on a table that holds the s hortc uts and their
des c riptions in two res pec tive fields . A n alternative is to jus t
us e label c ontrols in whic h the s hortc uts and des c riptions have
been entered.

Figure 1-7. A quick-reference form for keyboard


shortcuts
A func tion key is eas y enough to remember. F9 is a good one to
target bec aus e it is n't c ommonly us ed. By c ontras t, F1 is n't a
great c hoic e bec aus e it's the s tandard for entering the H elp
s ys tem. To es tablis h a c us tom keyboard s hortc ut, us e the
s pec ial AutoKeys mac ro. T he AutoKeys mac ro is ac tivated at
s tartup in the s ame fas hion as the AutoExec mac ro.

Figure 1 - 8 s hows the AutoKeys mac ro s et up with a few c us tom


keyboard s hortc uts . P res s ing F9 opens the frmKeyboardShortcuts
form s hown in Figure 1 - 7 .

Figure 1-8. Using the AutoKeys macro to set up


custom keyboard shortcuts
Hack 4. Optimize Data Changes

A void having to propagate data changes manually throughout


related tables by establishing cascading updates and deletes.

T he one c ons tant you c an c ount on is c hange. Why not plan for
this eventuality in your applic ations ? Take a real example: a
c us tomer c hanges her name. I f you are in bus ines s long enough,
this is s omething you will need to ac c ommodate in your
databas e.

D ata c hanges need to be propagated in two ways . I f the data


is n't us ed as a table key or a foreign key, you need to c hange the
data in all the plac es it res ides in your tables . H opefully, your
data res ides in only one plac e! A c orrec tly modeled databas e
holds a piec e of data, s uc h as a c us tomer name, in jus t one
plac e. I f you do have s uc h a piec e of data in a few plac es ,
however, you pres umably have a reas on for doing this .
A pplic ations grow over time and often are handled by a
s uc c es s ion of developers . I t happens .

I f you have databas e applic ations in whic h the s ame data is


found all over the plac e, a brus h up on data modeling is in order.

What about data that exis ts in table keys ? T his c an be a


frus trating c hange to propagate if many c hild tables us e the data
as the foreign key. T hat is , it will be frus trating unles s you plan
your relations hips with c as c ading updates .

When c reating relations hips between tables , one option is to


es tablis h c as c ading updates . Figure 1 - 9 s hows two tables of
data. T he tblC us tomers table on top has c us tomer information.
T he values in the key field, C us tomerI D , are the initials of the
ac tual c ompany names . I n the lower tblI nvoic es table, the
C us tomerI D s erves as the foreign key.

Figure 1-9. Related tables


Figure 1 - 1 0 c onfirms the relations hip between the tables . A line
leads from the C us tomerI D field in tblC us tomers to the
C us tomerI D field in tblI nvoic es . T he number 1 on the
tblC us tomers table s ide of the line indic ates that tblC us tomers
is the parent table. T he infinity s ymbol ( ) above where the line
meets the tblI nvoic es table indic ates that tblI nvoic es is the
c hild table. T his is a one-to-many relations hip. T he tblI nvoic es
table has other relations hips as well.

Figure 1-10. The Relationships window


T he E dit Relations hips dialog box, s hown in Figure 1 - 1 1 , is
where you s et relations hips . To open the dialog in the
Relations hips window, double- c lic k on a line that c onnec ts two
tables . N ote the C as c ade U pdate Related Fields c hec kbox.
When this box is c hec ked, c hanging the value in the key field of
the parent table automatic ally c hanges the values in the related
field in the c hild table. T his is a good thing! When a c us tomer
c hanges her name, all you have to do is c hange the value in the
key. A ll the oc c urrenc es of the value in the c hild table
automatic ally c hange to the new value.
I n the example s hown in Figure 1 - 9 , if Bes t E quipment c hanges
its name to Bes t Tools , the C us tomerI D value s hould be
c hanged to BT. M aking this c hange onc e in the tblC us tomers
table automatic ally updates all related rec ords in the
tblI nvoic es table.

A nother option in the E dit Relations hips


dialog box (Figure 1 - 1 1 ) is to es tablis h
c as c ading deletes . T he C as c ade D elete
Related Rec ords s etting ens ures that when
you delete a rec ord in the parent table, all
related rec ords in c hild tables are als o
deleted. When this option is n't s et, you
have to delete the rec ords in the c hild
table firs t, and then you c an delete the
rec ords in the parent table.

Figure 1-11. Selecting to use cascading updates


and deletes
I f the option to have c as c ading updates is n't s et, you have to
c hange eac h table's rec ords s eparately. You c an do that only if
you remove the relations hip firs t bec aus e A c c es s does n't let
you c hange the values in the key field in either table if there are
any related rec ords . Trying to make an update in that way is
quite mes s y.
Hack 5. Transfer Data Between Versions
of Access

Say goodbye to version incompatibility issues.

M ic ros oft has releas ed more than half a dozen vers ions of
A c c es s over the years . Some people and organizations buy into
eac h upgrade, s ome s kip around, and s ome hold on for dear life
to the one they have been us ing s inc e the previous c entury! T he
vers ion does n't matter when you or your organization work in a
vac uum, but when you exc hange data with external c ompanies ,
vers ion inc ompatibility c an rear its ugly head.

L et's s ay you have A c c es s 2 0 0 3 and you s end a databas e filled


with your orders to a vendor. T he vendor has A c c es s 9 5 . U h- oh!
T he vendor c an't open your databas e.

O ne of the rec ent data tec hnologies initiated throughout the


c omputing world is the us e of XM L and other platform- neutral
protoc ols . T his purportedly removes data inc ompatibility. XM L is
nic e, but only the mos t rec ent vers ions of A c c es s c an read XM L .

T he way to s hare data is via a tried- and- true, low- key, low- tec h
method: export and s ave your data as text. A lthough they vary
in terms of how text c an be s aveddelimited, type of delimiter
c harac ter, text qualifier, fixed- width, and s o onall vers ions of
A c c es s c an read and write text files . Figure 1 - 1 2 s hows the
E xport Text Wizard, in whic h you s et your text export options .
When you initiate to export an A c c es s table or query and s elec t
text as the type, the wizard s tarts up.

Figure 1-12. Using the Export Text Wizard


A dmittedly, exporting and importing text is n't an ideal approac h,
es pec ially when you have to export or import many tables of
data. But it s ure beats los ing bus ines s bec aus e your c lient c an't
open your databas e.

XM L has paved the way for eas y data exc hange among vers ions
and s ys tems . XM L s upport is dec ent enough in A c c es s 2 0 0 3 ,
les s s o in A c c es s 2 0 0 2 and A c c es s 2 0 0 0 . I f working with text
files jus t does n't s eem right for your needs , you c an always us e
XM L . A s eparate external XM L pars er does the tric k.

1.6.1. See Also

"P rovide C omplete XM L C ontrol to A ny Vers ion of


A c c es s " [Hack #87]

"U s e A c c es s as an XM L D atabas e" [Hack #95]


Hack 6. Organize and Enhance Your
Macros

Optimize and reduce the number of macros using the optional


name and condition columns.

M ac ros are often us ed for s mall automations us ually for tas ks


that aren't too c omplex or s ophis tic ated bec aus e V BA is
available to handle the heavy proc es s ing. L et's think that way no
more. A c tually, mac ros c an handle a dec ent amount of intelligent
proc es s ing and, in fac t, have a c ondition- tes ting ability s imilar
to the If…Then s truc ture in V BA . T his hac k s hows you how to
trans form a s ingle mac ro into a multipurpos e workhors e.

1.7.1. Conditional Macro Actions

M ac ros have but a s ingle mandatory c olumn: the A c tion c olumn.


A mac ro c an have one or more ac tions . H owever, mac ros als o
have an optional C ondition c olumn, in whic h a little entry c an go
a long way toward adding s ome punc h to the proc es s . When
you're des igning a mac ro, us e the V iew menu to dis play the
C ondition c olumn.

A c ondition c an tes t a field value, evaluate the res ult returned


by a func tion, and even us e the returned value from a mes s age
box. C onditions als o c an us e Boolean logic , inc orporating
and/or- type logic in the c ondition tes ting.

Figure 1 - 1 3 s hows a mac ro in whic h a s eries of ac tions oc c ur


when the mac ro is run. A few of the ac tions run only when their
c ondition is met. For ins tanc e, the End of Month func tion and the
E nd of M onth report are inc luded in the proc es s ing only when it
is the firs t day of the month (pres umably tallying up figures
about the month that jus t ended). U s ing the D ay and Now
func tions takes c are of tes ting for the firs t day of the month.

T he E mployee Bonus report runs only when a c ondition tes ted


with a DLookup func tion is TRue.

T he unc onditional ac tions in the mac ro always run. E ven when


the ac tions with unmet c onditions are pas s ed over, the mac ro
c ontinues to run and does n't s top prematurely.

1.7.2. Creating Macro Groups

M ac ros c an als o be organized into groups , known as macro


groups . By c reating mac ro groups , you c an reduc e the number of
overall mac ros and keep s imilar mac ro ac tions together in one
plac e. T he key differenc e between a mac ro and a mac ro group is
the us e of the optional M ac ro N ame c olumn.

When you're des igning mac ros , us e the V iew menu to dis play the
M ac ro N ame c olumn. Figure 1 - 1 4 s hows a mac ro group named
RunReport. T he mac ro group handles the tas k of opening a
number of individual reports . A n important point, though, is that
thes e reports won't open at the s ame time. E ac h mac ro name
exis ts as a s eparate mac ro within the larger group.

Figure 1-13. Using conditions in a macro


Figure 1-14. Using the Macro Name column
When a partic ular ac tion needs to be initiated, you us e the name
of the mac ro group, a dot qualifier, and the name in the M ac ro
N ame c olumn, like this :
DoCmd.RunMacro "RunReport.Inventory Status"

T he point where the ac tion s tarts is the row with the mac ro
name. Suc c es s ive ac tions will run until another mac ro name is
enc ountered. N ot all rows require a value in the M ac ro N ame
c olumn. T his is the beauty of mac ro groups . O ne c ohes ive
des ign hous es any number of s maller ac tion s ets . T he benefit is
a c leaner and eas ier- to- manage mac ro implementation.
Hack 7. Rid Your Database of Clutter

Implement an object-use log to clean up an overloaded


database by analyzing user actions and then deleting never-used
objects.

Some A c c es s databas e applic ations jus t get plain ugly. I f you


have ever brows ed through a databas e with dozens and dozens
of forms and reports , you know what I am referring to. T his is
often the res ult of a us er c ommunity turned loos e: forms for
every point and purpos e; a report for eac h day of the week; and
then s ome.

A dding ins ult to injury, you c an't eas ily tell whic h objec ts the
us ers are ac tually us ing. L uc kily, there is a way to reign in the
applic ation and reduc e the c lutter.

T he goal is to find out whic h objec ts are no longer being us ed.


O ften, us ers c reate forms or reports that they us e onc e and
never look at again. O nc e you've identified whic h objec ts are no
longer being us ed, you c an delete them from the databas e. T his
will likely improve the performanc e of the databas e and c ertainly
reduc e its memory footprint after you c ompac t it. T he tric k to
deleting unus ed objec ts is to c reate a lis t of objec ts that are
being us ed and then to delete the objec ts that didn't make it on
the lis t.

1.8.1. Tracking Object Use


A ll forms and reports c ontain an open event. By putting a s imple
c ode routine into all open events , you c an populate a log with the
names of the objec ts being opened. Before you do this , you need
to c reate a log table to s tore the objec t names . T his does n't
need to be fanc y; indeed, the log table c an have jus t a s ingle
field to s tore the names . O ptional fields c an s tore a times tamp,
the type of objec t, and s o forth.

Figure 1 - 1 5 s hows the des ign of s uc h a table. I t c ompris es two


fields : one c aptures the objec t name, and the other c aptures the
objec t type. T he table rec eives a rec ord eac h time an objec t is
opened.

To append a rec ord to the log table, an objec t mus t have a little
bit of c ode in its open event. H ere is a s nippet that would go into
the open event of a form named Customers:

Private Sub Form_Open(Cancel As Integer)


Dim conn As ADODB.Connection
Set conn = CurrentProject.Connection
Dim ssql As String
ssql = "Insert Into tblObjectLog Values ('Customers', 'Form'
conn.Execute ssql
conn.Close
Set conn = Nothing
End Sub

Figure 1-15. A table for logging objects as they


are opened
When the form is opened, a rec ord is written into the log with the
form's name and objec t type. You s hould put s imilar c ode into
the open event of all forms and reports . T hen let your us ers us e
the databas e again, and watc h the log table begin to fill up. A fter
a reas onable amount of timea week, a month, whatever makes
s ens eexamine the log table. You will s ee numerous entries . I f a
times tamp field was not us ed, you will s ee quite a number of
duplic ate rec ords . U s e a Select query with a Group By aggregate
c laus e to view the res ults without s eeing duplic ates .

1.8.2. Identifying Unused Objects

Figure 1 - 1 6 dis plays a query of the O bjec t log table. T he lis ted
objec ts repres ent the definitive lis t of objec ts us ers are opening.
You c an c ompare this lis t to the full lis t of forms and reports in
the databas e, and you c an s afely delete the forms and reports
that aren't on the lis t as long as you're c omfortable that enough
time has pas s ed. D on't forget to c ompac t the databas e after
deleting the objec ts !

Figure 1-16. Reviewing used database objects


1.8.3. Hacking the Hack

P art of this hac k c onc erns the nec es s ity to add c ode to the
opening routine of all the forms and reports . What a manual
has s le! H owever, you c an automate this tas k. H ere is an
example of c ode that updates the open events of all the reports in
the databas e:

Public Sub insert_open_report_event()


' !! Make sure all reports are closed before running !!
Dim rpt As AccessObject
For Each rpt In CurrentProject.AllReports
DoCmd.OpenReport rpt.Name, acViewDesign
With Reports(0).Module
On Error Resume Next
open_proc_start = .ProcBodyLine("Report_Open", vbext_pk
If Error <> 0 Then
'has no open event, so create one
Err.Clear
open_proc_start = .CreateEventProc("Open", "Report")
End If
.InsertLines open_proc_start + 1, _
"Dim conn as ADODB.Connection"
.InsertLines open_proc_start + 2, _
"Set conn =CurrentProject.Connection"
.InsertLines open_proc_start + 3, _
"Dim ssql as String"
.InsertLines open_proc_start + 4, _
"ssql = ""Insert Into tblObjectLog Values('" & _
Reports(0).Name & "', 'Report')"""
.InsertLines open_proc_start + 5, _
"conn.Execute ssql"
.InsertLines open_proc_start + 6, _
"conn.Close"
.InsertLines open_proc_start + 7, _
"Set conn = Nothing"
End With
DoCmd.Close acReport, Reports(0).Name, acSaveYes
Next
MsgBox "All Reports Updated"
End Sub

T his c ode routine works with the module behind the report. T his
is ac tual V BA that writes V BA kinda neat! Bas ic ally, eac h report
is opened in D es ign mode; c ode is then ins erted into the report's
c ode module. You c an develop a s imilar routine to work with
forms , too; you'll need to addres s the AllForms c ollec tion ins tead
of the AllReports c ollec tion.
Hack 8. Protect Valuable Information

Protect your data using the read-only command-line switch so


that users can't edit the data.

C reating a des ktop s hortc ut to a databas e provides a behind-


the- s c enes benefit. Spec ific ally, you c an us e c ommand- line
s witc hes that are uns een by all but the tec hnic ally c urious .

I n this manner, it is eas y to s et up a s hortc ut to open a databas e


in read- only mode and, thus protec t your data and des ign
elements . To do this , add the /ro s witc h at the end of the target
s tring in the des ktop s hortc ut. N ote that the full target s tring
is n't jus t the path to the databas e; it needs to s tart with the path
to the A c c es s exec utable, followed by the databas e path,
followed by the s witc h.

U s ing A c c es s 2 0 0 3 , whic h by default is in the s tandard Program


Files /Micros oft Office/Office 11direc tory, the full target s tring
looks like this :

"C:\Program Files\Microsoft Office\OFFICE11\MSACCESS.EXE"


"C:\Sales Summaries\Sales2005.mdb" /ro

When the des ktop s hortc ut is c lic ked, the databas e opens in
read- only mode. A c onfirmation mes s age is pres ented at
s tartup, s hown in Figure 1 - 1 7 . D ata c an't be added, deleted, or
edited.

T his is a great way to dis s eminate information without c onc ern


for data integrity is s ues . D is tributing the databas e applic ation in
s uc h a way that a des ktop s hortc ut is c reated or updated
guarantees that the databas e opens in jus t the way you
intended.

Figure 1-17. A reminder about the read-only


status

1.9.1. But Just in Case

O f c ours e, a half- s avvy us er c an jus t s tart up A c c es s and open


the databas e via the O pen dialog, thus bypas s ing the des ktop
s hortc ut. T he databas e is then opened in full read/write mode,
unles s a gotc ha is in plac e to prevent this .
To handle this , you c an plac e a s imple SQ L Insert operation in
the databas e's opening routine, and you c an inc lude an extra
table in the databas e for jus t this purpos e. I f the operation
s uc c eeds , the us er is warned to us e the des ktop s hortc ut (as
s hown in Figure 1 - 1 8 ), and the databas e c los es .

Figure 1-18. Catching users who try to skip


using the desktop shortcut

1.9.2. The Code


H ere's the routine that tes ts a SQ L Insert:

Public Function test_mode()


On Error GoTo err_end
Dim conn As ADODB.Connection
Set conn = CurrentProject.Connection
Dim ssql As String
ssql = "Insert Into tblModeTest Values ('test')"
conn.Execute ssql
conn.Close
Set conn = Nothing
'if got this far then database is not in read only mode
'tell user, then quit
MsgBox "Database must be opened through desktop shortcut
DoCmd.Quit
Exit Function
err_end:
'all is well, an error was expected
End Function

T here is a twis t to this : the databas e is s uppos ed to be in read-


only mode, s o the optimal outc ome is that the operation will fail
ins tead of s uc c eed. A n error trap is implemented in the routine,
s o if the error is triggered, all is well, and no ac tion is taken. I f
the ins ert s uc c eeds , the warning is dis played, and the Quit
method c los es the databas e. T his routine s hould be c alled by
the AutoExec mac ro s o that it runs immediately when the
databas e opens .
Hack 9. Work with Any Amount of Data

Plan a multiple-database architecture to house any amount of


datagigabytes, even terabytes!

T he only s ize limit in A c c es s is that a table c an't c ontain more


than 1 G B of data. Well, if that's it, there is a lot of opportunity
here. A c c es s is n't c ut out for inc redibly large s tores of data,
granted, but that's not the point. I f SQ L Server or O rac le is n't
going to be ins talled at your plac e of bus ines s for another year,
take advantage of A c c es s 's flexible arc hitec ture to work with
any amount of data.

T he tec hnique is s imply to make a plan for how to s truc ture the
data among multiple databas e files and tables . T here is no rule
that s ays an A c c es s applic ation mus t res ide c ompletely in a
s ingle A c c es s file. A n A c c es s applic ation c an be s plit into a
front end and a bac k end. T hat is , the forms , reports , and queries
s tay in the front end, and the data its elf is put into a s eparate
file. T he data tables are then linked to the front end. T his is
s tandard fare, the quintes s ential c lient/s erver in its s imples t
exec ution, s hown here in Figure 1 - 1 9 .

Figure 1-19. A simple front-end/back-end


configuration
1.10.1. Splitting Up Data

T here is no reas on to be limited to a s ingle file on the bac k end.


T he organization of and fac ts about the data will drive the
dec is ions c onc erning how it c an be pars ed into s maller data
s tores . For example, if you are working with a large c us tomer
bas e, you c an s plit the data from one data table into 2 6
tables one for eac h letter of the alphabet. Figure 1 - 2 0 s hows
s uc h a c onfiguration.

Figure 1-20. Using multiple databases on the


back end
A n alternative is to s plit a c us tomer lis t by c ity, s tate, provinc e,
or other geographic delimiter. A gain, this allows you to take an
overwhelmingly large s et of data and turn it into manageably
s maller (albeit s till large) s tores of data.

Splitting the data is the key to this hac k. A nalyze the data, and
c ome up with a game plan. P erhaps the data is date- bas ed. You
c an s plit it up by month, day, or whatever makes s ens e.

1.10.2. Working with Split Data

T here is an unwelc ome s ide effec t to s plitting data. I n a


relational s ys tem, you los e the s implic ity of relying on the
es tablis hed relations hips when data is s plit out to additional
tables . P ic ture this : you have a mas ter table of c us tomers and a
related table of c us tomer purc has es . You s plit the c us tomers
into 1 0 s maller tables . What happens to the relations hip? You
c an work around this problem in two ways .

O ne way is to relate the purc has es table to all 1 0 mas ter tables .
T he other is to leave out the relations hips altogether and
ins tead inc orporate behind- the s c enes proc es s ing to wed data
bac k together as needed by front- end ac tivity.

T his is n't as c omplic ated as it might s ound. I n a nuts hell, V BA


and A D O work together to find c us tomers and purc has es that
matc h bas ed on whatever c riteria are being s elec ted in the front
end. A workable approac h to find purc has es that matc h a
c riterion is to c reate a rec ords et or array of rec ords from the
purc has es table, and then run thes e rec ords agains t the 1 0
c us tomer tables while looking for a matc h on the key fields . N o,
this is n't an eloquent or partic ularly effic ient way of proc es s ing
data. H owever, it enables A c c es s to work with gigabytes or more
of data, and that is the meas ure of s uc c es s in this c as e.
Hack 10. Find Database Objects in a
Snap

Use the description property to prevent users f rom being


overwhelmed by sif ting through cryptic-sounding f orms, queries,
and reports.

M any of us follow naming c onventions when c reating databas e


objec ts . A mong the developer c ommunity, we have c ome to
rec ognize and take for granted that tbl, frm, rpt, and other
prefixes are part and parc el of our work. For example, tblStaff is
a table, frmA dmin is a form, and rptC ontac ts is a report.

H owever, when you c omplete a databas e with s everal objec ts


that are named in this way, it's a c hallenge to the average
databas e us er to unders tand the names . Figure 1 - 2 1 s hows a
perfec t example of a databas e with s everal forms .

T here is a way to res olve this dilemma, and it does n't mean
developers have to c hange their naming habits . A ll databas e
objec ts c an be given a des cription. T he bes t thing is that you c an
enter des c riptions for objec ts direc tly in the databas e window
without having to open an objec t in D es ign mode.

I n the databas e window, jus t right- c lic k an objec t, and from the
menu that appears , c lic k P roperties . A s mall dialog box opens
for you to enter a natural- s ounding des c ription, as s hown in
Figure 1 - 2 2 .
A fter you enter des c riptions for all the objec ts , jus t be s ure to
lis t the databas e objec ts in L is t view ins tead of I c ons view. T his
makes the des c riptions vis ible. Figure 1 - 2 3 s hows how the
group of forms in Figure 1 - 2 1 is now unders tandable.

Figure 1-21. Cryptic form names that can stump


a user
Figure 1-22. Entering a description
A neat thing about this approac h is that you c an even us e a
warning mes s age s o that us ers know not to open an objec t. T his
is partic ularly helpful in the c as e of s ubforms . U s ers s houldn't
open s ubforms direc tly bec aus e they appear ins ide other forms .
T he des c ription tells us ers not to open them.

Figure 1-23. Selecting a form by its description


Kirk Lamb
Hack 11. Use a Junction Table

Correctly model a many-to-many relationship.

I t's eas y to fall into the trap of as s uming all relations hips are of
the one-to-many type. I t's true that many data relations hips do
follow the one- to- many paradigm. For example, one pers on has
zero or more telephone numbers . H owever, not all data is meant
to be modeled in this way.

A perfec t example of data that appears to fit the one- to- many
model, but does n't, is the relations hip between ins truc tors and
s tudents . O n the one hand, one ins truc tor does have many
s tudents , thereby proving a one- to- many relations hip exis ts . O n
the other hand, one s tudent has many ins truc tors whic h is als o a
one- to- many relations hip. So, what is the problem?

Figure 1 - 2 4 s hows one way to model ins truc tors and s tudents .
T he ins truc tor table oc c upies the one s pot and the s tudent table
oc c upies the many s pot. I ns truc tors and s tudents get together
for appointments . T his model works but emphas izes that
ins truc tors are of a different level than s tudents , whic h might not
be true.

Figure 1-24. An inefficient one-to-many


relationship
I n Figure 1 - 2 4 , the s tudent table is als o required to have the
ins truc tor I D as the foreign key. T his is ac c eptable, but now look
at the appointments table; it c ons iders appointments as
belonging to s tudents , but appointments belong to both
ins truc tors and s tudents .

Figure 1 - 2 5 s hows how to res olve the dilemma in the data


model. Bec aus e appointments belong to both ins truc tors and
s tudents , that is how the model s hould look. T he appointments
table s erves as a j unction table between ins truc tors and
s tudents .

A junc tion table bec omes the many table for two or more other
tables . A ll the key fields of the one tables bec ome foreign keys in
the junc tion table. A ny other pertinent fields are inc luded in the
junc tion table. I n this example, the junc tion table has fields for
the date and time the ins truc tor and s tudent will meet. A ls o, the
s tudent table no longer has the ins truc tor I D as a foreign key. I n
this example, ins truc tors and s tudents have no hierarc hy;
therefore, it makes s ens e that one does n't s erve as a many to
the other.

Figure 1-25. A better model


Hack 12. Stop the Database from
Growing

Use the Compact on Close option to keep a database f rom


getting too big.

A c c es s databas es are notorious for their ability to grow in s ize.


T his is es pec ially true as data is moved in and out. For example,
when a databas e applic ation regularly imports data, proc es s es
the data, and then exports it bac k out, the databas e c an bec ome
huge, on the order of s everal megabytes in s ize. T his c an be the
c as e even when the data moving in and out is of a reas onable
s ize.

To s hrink the databas e bac k to the s ize it s hould be, you need to
compact the databas e. H owever, expec ting us ers to c ompac t
their databas es is n't a great idea, es pec ially if your us ers aren't
tec hnic ally s avvy. L uc kily, A c c es s inc ludes an option to
c ompac t a databas e when it is c los ed. T his option was not
available in older vers ions of A c c es s , but it is available in
A c c es s 2 0 0 2 and A c c es s 2 0 0 3 .

Figure 1 - 2 6 s hows the O ptions dialog box (Tools O ptions )


with the G eneral tab on top. N ote the C ompac t on C los e
c hec kbox.

A c c es s is unlike other produc ts , s uc h as SQ L Server, in that it


does n't allow you to c ontrol the s ize of the databas e. Setting the
databas e to c ompac t eac h time it c los es removes what has
traditionally been a rec urring problem with A c c es s .

Figure 1-26. Selecting to compact on close


2. Tables
Sec tion 2 .1 . H ac ks 1 3 1 8

H ac k 1 3 . C reate an A utoN umber Field with a C us tom


Value

H ac k 1 4 . C opy D ata Between Tables Without an A ppend


Q uery

H ac k 1 5 . Steer C lear of Sys tem Tables

Sec tion 1 6 . H ide Sens itive I nformation

H ac k 1 7 . Simulate Table Triggers

Sec tion 1 8 . C reate Tables Fas ter


2.1. Hacks 1318
Were it not for tables , we would have no plac e to s tore data!
Tables are s traightforward; they c ompris e rows and c olumns , or
rec ords and fields . So, what is there to hac k?

Table des ign is one area in whic h a little c us tomization goes a


long way. C hanging the default datatype and other properties
s peeds up development time. Without this intervention, text
fields default to 5 0 c harac ters . I s this a good s ize? T hat
depends on your projec t.

H ave you ever wis hed you c ould implement triggers the way SQ L
Server does ? You c an! U s e the inherent events available to
forms to get the s ame res ults .

N eed to hide data? T here's a hac k for that, too!


Hack 13. Create an AutoNumber Field
with a Custom Value

The A utoNumber f ield doesn't need to begin with a value of 1.


You can override A ccess's def ault autonumbering scheme to
better suit your requirements.

A great feature that A c c es s brings to the table- c reation proc es s


is the A utoN umber field. T his field type plac es a value of 1 in the
firs t rec ord and automatic ally inc reas es the value by 1 as
rec ords are added. I t does n't c ontain any s ignific ant or
meaningful data. I ts bas ic purpos e is to bec ome the key field
and thereby provide uniquenes s to the data rec ords .

J us t plop a field into the table des ign, and des ignate it as an
A utoN umber field. Typic ally s uc h a field has a name with I D or
N um in it, s uc h as C us tomerI D or Rec ordN um. N ote that a table
c an have only one A utoN umber field.

A ll in all, A utoN umber is a great feature, but there is one gotc ha:
the value always s tarts at 1 . O ften, this is n't an is s ue bec aus e
the field value really is unimportant. T he fac t that the values are
unique is what matters more. But what if you need to us e a s elf-
inc rementing number that s tarts at a different value? C an you do
this ? O f c ours e!

2.2.1. Seeding AutoNumber with a Number


of Your Choice

T he A utoN umber field type does n't have a property to s pec ify
the s tarting value. Figure 2 - 1 s hows a table des ign. A s you c an
s ee, the firs t field is an A utoN umber field, and its addres s able
properties fill the lower- left area of the table des ign window. N ote
that you have nowhere to input a default s tart value.

Figure 2-1. AutoNumber, an incrementing field


type
To be c lear, the table does c ontain a N ew Values property, but all
it tells you is whether new values are inc remented or are
random. I t tells you nothing about s tarting the inc rement at a
value of your c hoic e. So, the firs t rec ord will have a value of 1 in
the field, the s ec ond rec ord will have a value of 2 in the field, and
s o on.

To override the default s tarting value of 1 , you c an us e an Append


query to ins ert a different s tarting value. A fter you have
des igned the table and are ready to us e it, you mus t get the
initial value in plac e. Figure 2 - 2 s hows an Append query that
s pec ific ally plac es a value in the A utoN umber field. T hat is , one
rec ord gets added to the table, with the A utoN umber field
rec eiving the des ignated value.

Figure 2-2. Using a query to set the beginning


AutoNumber value
N ote that you enter this query in the SQ L view. T hat's bec aus e
it's not obvious how to enter this query in the des ign view (the
query grid), in whic h Append queries are typic ally us ed to append
one table to another. T his operation works by appending a value
to a field in a table with no other table involved (in a pinc h, you
c an des ign another table jus t to hold the value, but you don't
have to do s o).

Figure 2 - 3 s hows the res ults of running the Append query. T he


E mployees table was empty but now it c ontains its firs t rec ord,
and the A utoN umber value for that rec ord is 1 0 0 .

Figure 2-3. The first record with the designated


starting AutoNumber value

N ote that no other fields have been populated yet. I f any other
fields mus t have a value, you mus t populate them with
appropriate values in the query. For example, you c an modify the
query to populate additional fields , s uc h as E mployee and T itle,
like this :
INSERT INTO Employees (EmployeeID, Employee, Title)
VALUES (100, 'John Smith', 'Supervisor');

Figure 2 - 4 s hows the res ult of applying this new query. O n the
s urfac e, this s eems to take c are of two birds with one
s tones tarting the A utoN umber with a value of your c hoic e, yet
without us ing a dummy rec ord (as in Figure 2 - 3 ) to do s o.
H owever, this approac h c an be problematic . I t's a little odd to
populate the firs t rec ord in this manner and then to populate all
s ubs equent rec ords via forms , proc es s ing, or other methods .
T he point is that it probably is n't prac tic al to populate the firs t
rec ord in any method that differs from how other rec ords are
ins erted.

Figure 2-4. Using a query to fill the AutoNumber


field, along with other fields

But how c an you get the firs t rec ord to have the des ired s tarting
A utoN umber value without having a dummy rec ord as the firs t
rec ord in your table? T he twis t is to s till populate the firs t rec ord
us ing a query, but to populate the A utoN umber field with a value
of one les s than the real s tarting value. You c an then delete this
rec ord, and the A utoN umber will inc rement as s ub- s equent
rec ords are added. T his means the firs t real data rec ord, entered
in whatever way your s ys tem handles it, will have the firs t value
of c hoic e. T he A utoN umber will jus t inc rement from there, as
expec ted.

I n the example s hown in this hac k, the query needs to populate


the table with a s ingle rec ord, in whic h a value of 9 9 is given to
the A utoN umber. T hat rec ord is then deleted (manually or
otherwis e; it does n't matter how). When the firs t real data rec ord
is added, it will have an A utoN umber value of 1 0 0 .

2.2.2. Hacking the Hack

You c an res et the value of the A utoN umber field whenever you
want. T his does n't c hange the exis ting rec ords . I t lets new
rec ords be numbered s tarting from a new initial value. You jus t
run the Append query, as s hown in Figure 2 - 2 (adjus ted as
dis c us s ed to handle any other required fields ), but res et the
value to one that is higher than the highes t value already in the
table. For example, if the las t rec ord entered in the table has a
value of 2 2 0 , res et the c ount to s omething higher. O bvious ly,
you would s kip the next inc remental number; otherwis e, there
would be no reas on to rees tablis h where the inc rement begins .

T his offers an interes ting option for managing your data. What if
a s eries of rec ords in a table has s ome unobvious but related
attribute? For example, you c an res et the A utoN umber field to a
new s tarting value at the s tart of eac h year. T hen, eac h year's
data will be eas y to dis tinguis h. For example, all rec ords in the
year 2 0 0 5 might lie within the 5 ,0 0 0 5 ,9 9 9 range, all rec ords for
2 0 0 6 within the 6 ,0 0 0 6 ,9 9 9 range, and s o on.
Hack 14. Copy Data Between Tables
Without an Append Query

Use Paste A ppend to easily copy data across tables.

A c c es s us ers often us e an Append query to append rec ords from


one table to another. I n a produc tion environment in whic h data
is always being s huffled around, us ing Append queries c an
bec ome tedious . E ac h time you des ign one, you have to matc h
the fields of the des tination table with the fields of the s ourc e
table. T his is eas y when the fields have the s ame name, but it
takes manual intervention when the field names differ.

I f you have des igned and s aved an Append query definition, and
the s ourc e and des tination tables never c hange in name or
s truc ture, all is well for you. H owever, if even a s ingle extra
c harac ter is mis plac ed or is mis s ing in the field names , the
query either bombs or as ks you to fill in the value for the
unidentifiable field. N either is an option you c an live with.

H ow c an you deal with thes e ac c idents waiting to happen?


Fortunately, you c an c opy data between tables in another way:
us e P as te A ppend.

2.3.1. Appending Across Tables

A pas te method unique to A c c es s , P as te A ppend appends the


c ontents of the c lipboard to a databas e table. T he data has to
matc h the table in s truc ture, but it does not need to have
matc hing field names . T hat right there improves on the tedious
data entry involved when us ing the query grid. To be fair to
Append queries , they do have an advantage of their own: an Append
query c an us e c riteria to append filtered s ets of rec ords . P as te
A ppend, on the other hand, jus t appends everything. H owever, if
the need to apply c riteria is n't an is s ue, P as te A ppend has the
advantage.

Figure 2 - 5 s hows two tables : one c ontains exis ting c us tomers ,


and the other c ontains a lis t of leads that have to be added to
the lis t of exis ting c us tomers . T he rec ords in the tblL eads table
need to be added to the tblC us tomers table. T he field names
aren't the s ame, although the field types and purpos es matc h.

T he s imples t thing to do is to s elec t all the rec ords in tblL eads


(C trl- A ). C opy the rec ords , go to the tblC us tomers table, and
us e the E dit P as te A ppend menu to enable P as te A ppend,
as s hown in Figure 2 - 6 .

Figure 2-5. Appending similar data from one


table to another
Figure 2-6. Using Paste Append
N ote that the rec ords are appended without c onc ern for the field
names . H owever, an alternative method is available that is
eas ier s till: the table with the rec ords to be appended (tblL eads
in this example) does n't even have to be open! J us t s elec t and
c opy the table while it is c los ed. D o this direc tly in the databas e
window. T hen, open the table that rec eives the rec ords
(tblC us tomers in this example), and us e the P as te A ppend menu
item as before.

T his method has an is s ue, though. When the field names are the
s ame, the method works like a c harm. H owever, if at leas t one
field name is different, the method s till works , but the field
names of the table being c opied from might be ins erted as a
rec ord!

O f c ours e, by nature, field names are text- bas ed, s o if the table
rec eiving the append c ontains fields that aren't text- bas ed,
P as te A ppend won't pas te the field names . You might get an
error about the datatype being wrong, but this is O K. Strange but
true!

Finally, even when you know a rec ord will appear that c ontains
field names ins tead of data, the P as te A ppend method s till might
be preferable to c reating an Append query bec aus e it is us ually
muc h eas ier to delete a s ingle rec ord from a table than it is to
des ign a new query from s c ratc h.

2.3.2. Appending Across Databases

T he tec hniques in this hac k work not jus t within a s ingle


databas e applic ation, but als o ac ros s databas es . I n other words ,
you c an s elec t and c opy rec ords from a table in one databas e
and then append thes e rec ords to a table in a different databas e.
H owever, both databas es mus t be open to make this pos s ible.
Figure 2 - 7 s hows two databas es , s ide by s ide on the des ktop.

Figure 2-7. Appending data across databases

T he tblL eads table, in the databas e on the left, is s imply being


dragged over to the open tblC us tomers table in the databas e on
the right. T he tblL eads table is effec tively being c opied, not
moved; the original s tays in the firs t databas e. L etting go of the
mous e button c ompletes the append operation.
Hack 15. Steer Clear of System Tables

A void incorrect results by leaving system tables out of your


table count and def inition routines.

H ow many tables are in your databas e? You might think finding


this out is as eas y as c ounting how many tables are lis ted on the
Tables tab of your databas e window. To that I res pond, "Try
again! "

A c c es s us es a number of s ys tem tables to c ontrol its own


internal workings . U s ually, thes e additional tables are hidden,
but they are there nonetheles s . Figure 2 - 8 s hows a databas e
with s ome tables .

Figure 2-8. Tallying the tables


I t looks like this databas e c ontains eight tables , does n't it?
L et's try getting a c ount in a different way. I n the V B E ditor,
ac tivate the I mmediate window (C trl- G ). T hen, enter the
following c ode s nippet and pres s the E nter key:

?Application.CurrentData.AllTables.Count

Figure 2 - 9 s hows the c ode and its res ults in the I mmediate
window. For the databas e in Figure 2 - 8 , the res ult is 1 5 , s o
A c c es s is telling us the databas e ac tually c ontains 1 5 tables ,
although only eight are vis ible on the Tables tab.

Figure 2-9. Counting all the tables


T he c ode s nippet tells the truth, however: this databas e does
indeed c ontain 1 5 tables . T he ones you c ouldn't s ee before are
the s ys tem tables . L et's dis play them!

Bac k in the databas e proper (not the V B E ditor), us e the Tools


O ptions menu to dis play the O ptions dialog box. Selec t the
V iew tab. A s s hown in Figure 2 - 1 0 , one of the options in the
Show area is to dis play s ys tem objec ts . Selec t this c hec kbox to
make the s ys tem tables vis ible.

Figure 2-10. Selecting to show system objects


N ow, looking at the Tables tab in Figure 2 - 1 1 , you c an s ee the
s ys tem tables .

N ote that all the s ys tem table names s tart with M Sys . T his is
ac tually a us eful attribute about thes e tables bec aus e it makes
it eas y to remove them from a table c ount.

Figure 2-11. Displaying all tables, including


system tables
2.4.1. The Code

But why does any of this matter? O ne reas on is that an


applic ation might need to iterate through all the tables in a
databas eperhaps to add a property, to look for a field or data, to
alter the table s truc ture in s ome way, and s o on. I n s uc h
c irc ums tanc es , the s ys tem tables mus t be avoided. Fortunately,
a s imple c ode routine eas ily handles this by purpos ely avoiding
all tables that have names beginning with M Sys , as follows :

Sub count_tables()
'list tables in database
Dim table_num As Integer
Dim tbl_count As Integer
With Application.CurrentData
For tbl_count = 1 To .AllTables.Count
If Left(.AllTables(tbl_count - 1).Name, 4) <> "MSys" Then
Debug.Print .AllTables(tbl_count - 1).Name
End If
Next tbl_count
End With
End Sub

T his c ode routine c yc les through all the tables in the databas e
and writes the name of eac h table to the debug (I mmediate)
window, as long as the table's name does n't s tart with M Sys . To
us e this routine, replac e the table names with any partic ular per-
table proc es s ing you need.

2.4.2. Running the Code

Figure 2 - 1 2 s hows the output of this routine. T he I mmediate


window is filled with jus t the pertinent applic ation data tables ,
and that's exac tly what we need.

Figure 2-12. Listing just the data tables


By is olating the data tables from the s ys tem tables in this way,
you c an work with the data tables how ever you want, without
worrying about c ras hing your applic ation.
16. Hide Sensitive Information

Name tables with the USys pref ix to prevent them f rom being
visible.

H ere's a quic k and eas y hac k to hide data from prying eyes . O f
c ours e, any A c c es s guru with enough notc hes in his belt will
figure this one out. But ordinary us ers ? N ot likely. You c an hide
your tables us ing this approac h and s till retain full func tionality
in your applic ation. Q ueries , forms , reports , mac ros , and c ode
will s till work, but anyone viewing the Tables tab won't find the
tables you des ignate as hidden.

To do s o, prefix your table names with U Sys . T his ac ts as a flag


to A c c es s to treat the tables as a quas i- mix of s ys tem and us er
tables , and the ability is built in to hide or dis play them. Figure
2 - 1 3 demons trates this proc edure: a form is open and is c learly
dis playing data, but the Tables tab in the databas e window has
no tables !

Figure 2-13. A form based on a hidden table


T he form in Figure 2 - 1 3 has the rec ord s ourc e property s et to
the U Sys C lients table. I n the Tools O ptions V iew
menu, you'll find a s etting for dis playing s ys tem objec ts , as
s hown in Figure 2 - 1 4 . N ote that c hec king to dis play s ys tem
objec ts makes U Sys tables vis ible.

Figure 2 - 1 5 s hows all the s ys tem objec ts in their glory. T he


U Sys tables are there, as well as the M Sys tables [Hack #15].

T he prefix is n't c as e- s ens itive. You c an


us e U SY S, U Sys , us ys , and s o on; they all
work to differentiate a table.

2.5.1. An Alternative

A nother way to hide objec ts in your databas e is to right- c lic k a


databas e objec t, whic h then dis plays a menu that inc ludes a
P roperties option. Selec ting this dis plays a P roperties dialog, as
s hown in Figure 2 - 1 6 . C hec king the H idden c hec kbox hides the
objec t.

Figure 2-14. Selecting to display USys-prefixed


tables
Figure 2-15. Displaying all USys and MSys
tables
To dis play hidden objec ts , s imply c hec k "H idden objec ts " in the
Show s ec tion of the O ptions dialog box, as s hown previous ly in
Figure 2 - 1 4 . But note that between prefixing objec t names with
U Sys and s etting the hidden attribute, you've got enough
c apability to be a little s mart and a little dangerous . J us t
bec aus e you c an't s ee objec ts does n't mean they aren't there!

Figure 2-16. Setting the Hidden attribute


2.5.2. Hacking the Hack

A lthough this hac k s howed you how to hide tables and, therefore,
avoid giving us ers ac c es s to raw data, you c an hide other
databas e objec ts as well. J us t prefix the names of queries ,
forms , reports , and s o on, with U Sys , and they magic ally
dis appear. O r, s et the hidden attribute in the P roperties dialog. I t
helps to write down the names firs t!

A really c ool tric k is to us e the U Sys prefix, or to s et the hidden


attribute, for all databas e objec ts . A s a res ult, anyone viewing
the tabs in the databas e window will s ee abs olutely nothing. By
s etting the Startup form to a form prefixed with U Sys , you c an
get the entire applic ation running. A s long as you are fully aware
of how all the objec ts are named, you c an c reate a c omplete
applic ation without a s ingle vis ible objec t in the databas e
window tabs . O f c ours e, the objec ts bec ome vis ible when they
are opened, but by taking the c orrec t meas ures to keep us ers
out of your des ign elements , you c an dis tribute an invis ible
databas e.
Hack 17. Simulate Table Triggers

Incorporate the same f unctionality as SQL Server or Oracle in


your A ccess application.

A c c es s 2 0 0 3 and earlier vers ions don't s upport table events . A


trigger is a table event that you c an fire on an ins ert, an edit, or a
delete ac tiona valuable func tion. A us eful example is to c atc h an
edit before it c ompletes and to s tore the original datathat is ,
s tore the original rec ord s omewhere els e, s uc h as in a bac kup
table. T his leaves you with a data audit trail. I f for s ome reas on
the edited data is problematic , you c an rec all the original data.

T his logic applies to deletes as well. U s ing triggers , you c an


hook into a delete and arc hive the data ins tead of jus t dis c arding
it. I n the c as e of ins erts (s uc h as new rec ords being added to a
table), data c an be validated before being allowed into the table.

U nfortunately, A c c es s does n't let you do any of this direc tly


from the point of view of the table its elf. But you can do all of this
when working through forms . Forms have plenty of events to
hook into, and you c an handle s imilar func tionality as traditional
triggers by working through forms ins tead of tables .

2.6.1. Setting Up an Audit Log

To demons trate how all this works , let's add a new table to a
databas e to mirror an exis ting data table and c reate an audit log
of c hanges to the data table. We'll do this by us ing two additional
fields : one to s tore the type of operation and one to s tore a
times tamp. Figure 2 - 1 7 dis plays two tables : the data table
(tblC lients ) and a table to s tore rec ords from the firs t table jus t
prior to them being edited or deleted (tblC lients A uditL og).

H ere are a c ouple of points to c ons ider:

T he log table c ontains two additional fields : A c tion and


T imes tamp.

T he C lientI D field is the primary key in the data table,


but it is purpos ely not s et as a primary key in the log
table. T his is bec aus e the log table might hold multiple
rec ords that pertain to the s ame c lient (and therefore
the s ame C lientI D ).

2.6.2. Checking Out the Form Events

N ow you c an us e a s tandard form to view, add, edit, and delete


rec ords from the data table. Figure 2 - 1 8 s hows a typic al form
bas ed on the tblC lients table.

Figure 2-17. Using an audit log table to store


records
Figure 2-18. Inserts, updates, and deletes, done
with a form
O f c ours e, there is s ome c ode behind this form. Two events are
tapped: the Before Update event and the Delete event. Before
Update handles both ins erts and updates , and Delete handles
deletes . I n partic ular, when an ins ert is made, the Before Update
event validates the data (i.e., it c hec ks to s ee if there is a las t
name). I f the validation fails , the Cancel property is s et to true,
whic h c aus es the event to abort.

When an update (an edit) is made, the rec ord rec eiving the
c hange is written to the log table, prior to the c hange. T his
means the original data is kept intac t. When a delete is made,
the rec ord that is to be deleted is als o written to the log table,
prior to the c hange.

2.6.3. The Code

H ere is the c ode behind the form. T he Action field in the log table
rec eives one of two values : Update or Delete. T he two event
routines us e a c ommon func tion (build_sql):

Private Sub Form_BeforeUpdate(Cancel As Integer)


On Error GoTo err_end
Dim ssql As String
Dim conn As ADODB.Connection
Set conn = CurrentProject.Connection
If NewRecord = False Then
ssql = build_sql(ClientID, "Update")
conn.Execute ssql
conn.Close
Set conn = Nothing
Else
If IsNull(ClientLastName) Or ClientLastName = "" Then
MsgBox "Must provide name"
Cancel = True
End If
End If
Exit Sub
err_end:
MsgBox Err.Description
End Sub

Private Sub Form_Delete(Cancel As Integer)


On Error GoTo err_end
Dim ssql As String
Dim conn As ADODB.Connection
Set conn = CurrentProject.Connection
ssql = build_sql(ClientID, "Delete")
conn.Execute ssql
Exit Sub
err_end:
MsgBox Err.Description
End Sub

Function build_sql(client_id As Long, operation As String) As S


build_sql = "Insert Into tblClientsAuditLog Values ("
build_sql = build_sql & ClientID & ", "
build_sql = build_sql & "'" & _
DLookup("ClientFirstName", "tblClients", "ClientID=" & _
client_id) & "', "
build_sql = build_sql & "'" & _
DLookup("ClientLastName", "tblClients", "ClientID=" & _
client_id) & "', "
build_sql = build_sql & "'" & _
DLookup("ClientAddress1", "tblClients", "ClientID=" & _
client_id) & "', "
build_sql = build_sql & "'" & _
DLookup("ClientState", "tblClients", "ClientID=" & _
client_id) & "', "
build_sql = build_sql & "'" & _
DLookup("ClientCity", "tblClients", "ClientID=" & _
client_id) & "', "
build_sql = build_sql & "'" & _
DLookup("ClientZip", "tblClients", "ClientID=" & _
client_id) & "', "
build_sql = build_sql & "'" & _
DLookup("ClientPhone", "tblClients", "ClientID=" & _
client_id) & "', "
build_sql = build_sql & "'" & operation & "', "
build_sql = build_sql & "#" & Now() & "#)"
End Function

2.6.4. Running the Code

T he c ode runs when ins erts , updates , and deletes are made
us ing the form. N o partic ular additional ac tion, s uc h as c lic king
a button, is required. T he log table fills up with rec ords as us ers
do their thing. T he log table keeps trac k of all the c hanges and
even s tores multiple c hanges per c lient. T he build_sql func tion
c reates an Insert SQL s tatement. T he s tatement will inc lude
either Update or Delete as one of the values being written, the
differenc e being whic h routine c alled the func tion (and pas s ed
the word Update or Delete as an argument). T he SQ L s tring is
handed bac k to the c alling routine, from where the ins ert is run.

T his c ertainly c an be us eful in the real world. For example, s ay a


c lient moves (an addres s edit), gets married (a name edit),
s tarts buying from your c ompetitor (a delete! ), and s o on. Figure
2 - 1 9 s hows the res ulting log table with s ome rec ords . E ac h
rec ord dis plays the ac tion and the times tamp.

T he methods us ed in this hac k s imulate what SQ L Server,


O rac le, and other databas e produc ts provide in the way of
triggers . L et's not allow the elite of the databas e world to believe
A c c es s is n't up to s nuff!

Between the c lient table and the log table are multiple rec ords ,
and there is a s equenc e to the rec ords , thanks to the wonderful
times tamp.

Figure 2-19. Records copied to the audit log


table before being updated or deleted

2.6.5. Hacking the Hack


T his hac k was written with the advantage of knowing the table
s truc ture and field types . T herefore, I knew ahead of time to
plac e s ingle quotes around text values in the build_sql func tion.
When adapting this hac k, you will probably know ahead of time
what type of data to expec t, but if you don't, you c an tap into the
A D O X library to determine datatypes .

A D O X provides a way to read through eac h field in a table and


determine its type (as well as other properties ). T he following is
a bas ic routine to read through a s ingle table and have the name
and type returned for eac h field:

Sub get_fields()
Dim cat As ADOX.Catalog
Set cat = New ADOX.Catalog
Dim fld As ADOX.Column
cat.ActiveConnection = CurrentProject.Connection
For Each fld In cat.Tables("tblClients").Columns
Debug.Print fld.Name & " " & fld.Type
Next
Set cat = Nothing
End Sub

N ote that to us e the A D O X library, you mus t s et a referenc e. A s


with other referenc es , go to the V B E ditor and us e the Tools
Referenc es menu to dis play the Referenc es dialog box,
s hown in Figure 2 - 2 0 . T he library is named Micros oft ADO Ext. 2.7
for DDL and Security. C hec k the appropriate c hec kbox, and c lic k
O K to c los e the dialog.

T he routine returns a numeric al c ons tant for eac h field type. For
example, a value of 2 0 2 indic ates a Text field type (although
properly noted as a variable- width c harac ter field in A D O X
lingo). U s e the O bjec t Brows er to view the DataTypeEnum
c ons tants to s ee what the numbers repres ent. T his c ollec tion of
datatype c ons tants is available onc e the referenc e to A D O X is
s et. Figure 2 - 2 1 s hows the O bjec t Brows er zeroed in on the lis t
of datatype c ons tants . For any c ons tant, you c an s ee the
numeric al repres entation at the bottom of the O bjec t Brows er.

Figure 2-20. Setting a reference to ADOX


Figure 2-21. Reviewing datatype constants
U s ing Select Case or a s et of If s tatements inters pers ed with the
A D O X c ode in this s ec tion, you c an write a routine that does n't
rely on knowing the field types ahead of time.
18. Create Tables Faster

Optimize table design by changing the design def aults to match


your needs.

A Text field is 5 0 c harac ters . A N umber field is a long integer,


and the default value is 0 . Sound all too familiar? H ow often have
you gone out of your way to alter thes e defaults ? Well, with this
hac k, you no longer need to.

I n the O ptions dialog box (Tools O ptions ), on the


Tables /Q ueries tab, you'll find s ettings for s elec ting the default
s ize for text fields , the default number type (integer, long, s ingle,
etc .) for number fields , and even the overall default type. Figure
2 - 2 2 s hows this dialog box and the s ettings .

Figure 2-22. Changing field defaults


I n Figure 2 - 2 2 , the default Text field s ize has been c hanged to
1 0 0 . T his means that as new text fields are added to the des ign
of a table, they will default to a s ize of 1 0 0 . A ls o, the default
N umber type has been s et to Single. A s new number fields are
added, they default to the Single datatype. T he overall default
field type is s et to N umber; therefore, as new fields are entered
into a table des ign, they default to a N umber field typeand that
type will be of the Single number type.

A ltering thes e des ign defaults c an be quite us eful. I f, for


example, you are des igning a table that predominantly c ontains
dates , s et the default field type to D ate/T ime, and s ave yours elf
a lot of field- type s elec tion. A s you enter new fields , they will
default to D ate/T ime. You will need to adjus t only the minority of
fields that aren't of this type.

2.7.1. Setting Default Values

T he s ettings in the O ptions dialog box c ontrol field type s ettings


but offer nothing to indic ate default values . I n other words , you
c an s elec t Single as the default number type, but you c an't
s pec ify that the field defaults to a value of 1 .2 5 (for example) as
new rec ords are added to the table.

H owever, a s etting is available in whic h you c an indic ate a


default value. T he field in the third row of the table being
des igned in Figure 2 - 2 3 has been manually s et to 1 .2 5 , and this
bec omes the default value for the field.
Figure 2-23. Setting a default field value
2.7.2. The Code

What if 1 0 0 other fields need to be s et to s uc h a default value?


M y fingers hurt jus t thinking about the manual entry that would
be involved! A utomating this tas k will be a lifes averwell, at leas t
a finger s aver. A little c ode to the res c ue!

Sub change_field_defaults()
Dim cat As ADOX.Catalog
Set cat = New ADOX.Catalog
Dim fld As ADOX.Column
cat.ActiveConnection = CurrentProject.Connection
For Each fld In cat.Tables("myNewTable").Columns
If fld.Type = adSingle Then
fld.Properties("Default").Value = 1.25
End If
Next
Set cat = Nothing
End Sub

T his c ode us es the A D O X library [Hack #17] to work with the


fields in the des ignated table. I n this example, the table name is
hardc oded, but it c ertainly c an be pas s ed as an argument. T his
c ode example c yc les through the fields , and when a field type is
des ignated as Single (indic ated by the adSingle c ons tant), its
default value is s et to 1 .2 5 .
You c an expand this c ode routine to s et default values for all
pos s ible field types . E ven more, you c an s et default values for
c ombinations of field types and field names . For example, a field
named loc al_rate c an be a s ingle field type, whic h you c an s et it
to a default value of .2 5 ; likewis e, you c ab s et a field named
national_rate, als o a Single datatype, to have a default value of
.5 .
3. Entry and Navigation
3.1. Hacks 1927
A n applic ation's s uc c es s often res ts on us er ac c eptanc e. With
this in mind, it makes s ens e to plan how to make your
applic ation's front- end experienc e as vis ually pleas ing and
us er- friendly as pos s ible.

Sometimes us er experienc e is overlooked. D evelopers c an


s pend oodles of time des igning fields and tables , s etting up
relations hips , writing tric ky SQ L , and s o on. D oes this mean
anything to the typic al us er? N ot one whit!

L et's fac e it. A c c es s is more than jus t a databas e. I t is a


databas e with built- in front- end tools . Tables are the c ore of a
databas e, but the ability to c reate forms and reports is the main
job of a development platform. A c c es s has both, s o let's make
the bes t of both.

T he hac ks in this c hapter have been drummed up with the


ordinary us er in mind. E ntering data is a major us er ac tivity, and
thes e hac ks make this often mind- numbing ac tivity a little more
pleas ant.
Hack 19. Help Users Navigate Through
Long Forms

Use Page Break controls and command buttons so that users


won't have to scroll through long data-entry f orms.

I nformation is wonderful. T he more you know, the more you c an


do and plan forunles s you are the one s tuc k entering the data.
T hen, all you c an plan on is a lot of typing and mous ing around.

Figure 3 - 2 s hows an entry form, and a rather long one at that.


T his entry form c ontains more entry fields than c an reas onably
fit ons c reen, as evidenc ed by the s c rollbar on the right of the
form. A nyone us ing this form will need to s c roll or tab through to
the fields on the bottom.

Figure 3-1. A form that takes a lot of entries


A tab c ontrol is great for managing a lot of
c ontrols on a form. H owever, this hac k is
bas ed on a real- life s ituation. I onc e
worked on a projec t in whic h data- entry
operators were entering information from
legal- s ize forms . T he entry s c reen had to
matc h the layout of the form.

T he P age U p and P age D own keys on the keyboard make it eas y


to s c roll up and down through the form, but you c an't c ontrol how
muc h s c rolling will oc c ur. T he odds that pres s ing P age U p or
P age D own will leave the form right where you need it are indeed
s mall.

L uc kily, you c an get this to work properly. A ll you need to do is


add Page Break controls to the form. What are P age Break
c ontrols , you as k? Figure 3 - 2 s hows the Toolbox with the P age
Break c ontrol pointed out. By plac ing page breaks s trategic ally
in the form des ign, you c an get the P age U p and P age D own keys
to s c roll the form right to where you need it.

Figure 3-2. The Page Break control on the


Toolbox
Figure 3 - 3 s hows the form in D es ign mode. A P age Break c ontrol
has been plac ed direc tly above the P ers onal I nformation
s ec tion. T his is a rather unobtrus ive c ontrol. I t s imply appears
as a s tring of dots in D es ign mode, and in V iew mode, you don't
even s ee the c ontrol.

Figure 3-3. Adding Page Break controls


N ow, when you us e the P age D own key you will s c roll the form
direc tly to where the P age Break c ontrol res ides . Figure 3 - 4
s hows how the form eas ily s c rolls to the P ers onal I nformation
s ec tion.

Figure 3-4. Smart scrolling


3.2.1. Smart Navigation

U s ing the P age U p and P age D own keys is an adequate way to


s c roll a form, but we c an do better. I magine this : a form has
s everal s egregated data areas , and you need to ac c es s them in
random order. H aving to pres s P age U p or P age D own s everal
times to get the form to where you need it is a lot of work.

To avoid this problem, you c an plac e a s eries of c ommand


buttons in the form header (or form footer). T he header and
footer are always vis ible bec aus e thes e s ec tions are exempt
from s c rolling. Figure 3 - 5 s hows the form in D es ign mode, with
c ommand buttons in the header that will let us ers s c roll to where
they want.

Figure 3-5. Command buttons to facilitate form


navigation
3.2.2. The Code

T he tric k is for eac h button to s imulate P age U p and P age D own


keys trokes . T he following c ode us es the SendKeys s tatement to
ac c omplis h this . T he four new buttons on the form eac h initiate a
s eries of SendKeys s tatements :

Private Sub cmdWorkInfo_Click()


'
' 4 SendKeys up, 1 SendKeys down
'
navigate_form 4, 1
End Sub

Private Sub cmdPersonalInfo_Click()


'
' 4 SendKeys up, 2 SendKeys down
'
navigate_form 4, 2
End Sub
Private Sub cmdContactDetails_Click( )
'
' 4 SendKeys up, 3 SendKeys down
'
navigate_form 4, 3
End Sub

Private Sub cmdOrders_Click( )


'
' 4 SendKeys up, 4 SendKeys down
'
navigate_form 4, 4
End Sub

Sub navigate_form(u As Integer, d As Integer)


For form_up = 1 To u
SendKeys "{PGUP}"
Next form_up
For form_down = 1 To d
SendKeys "{PGDN}"
Next form_down
End Sub

E ac h Click event s ends the number of P age U p and P age D own


keys trokes that are required to have the form s c roll to the
des ired area. T hes e s imulated keys trokes ac c es s the P age
Break c ontrols plac ed earlier on the form. When a button is
c lic ked, the routine immediately s ends four P age U p keys trokes .
T he number of keys trokes is bas ed on this partic ular example.
T he point is to get to the top of the form. Four P age U p
keys trokes are needed only if the form is c urrently at the bottom
page break, but us ing four regardles s of the form's loc ation
does n't pres ent a problem.

T he c ode then s imulates the c orrec t number of P age D own


keys trokes . T he number differs for eac h buttonfor eac h area
being s c rolled to. I n other words , the Work I nfo button has one
P age D own, the P ers onal I nfo button has two P age D owns , and
s o forth.

With this approac h, any area of a long form is jus t a s ingle c lic k
away, and it is n't nec es s ary for us ers to go through the entire
form.
Hack 20. Help Users Enter Additional
Text

Place the insertion point at the end of the text in a text box so
that additional entries land just where they should.

T his tec hnique makes s o muc h s ens e, and yet it is often


overlooked. H ave you ever notic ed that when you're editing data
in a form, and you tab into a text box, the entire text is s elec ted?
U nfortunately, this default behavior makes the data vulnerable to
ac c idental overwriting. Figure 3 - 6 s hows the addres s text box
fully s elec ted. A s s uming an edit is needed to add additional text
(not to replace the text), the us er mus t move his mous e to the
end of the text and then c lic k to des elec t it.

Figure 3-6. Automatically selected data,


vulnerable to an accidental delete or overwrite
Wouldn't it be nic e if the us er didn't have to c lic k firs t to
des elec t the text? O f c ours e, there is a way to do this . I t takes
jus t a s mattering of c ode.

M any c ontrols , inc luding text boxes , have an Enter event, whic h
is triggered when the c ontrol is c lic ked or tabbed into. T his is
the event in whic h you c an plac e c ode to move the c urs or to the
end of the text, before the us er has a c hanc e to enter any
keys trokes . T he following c ode s nippet is bas ed on the c ontrol
being named CompanyAddress1:

Private Sub CompanyAddress1_Enter()


Dim text_length As Integer
text_length = Len(Me.CompanyAddress1)
Me.CompanyAddress1.SelStart = text_length
End Sub

T he length of the text is determined with the Len func tion, and
then the SelStart property is s et to the length. I t's that s imple.

You c an add a routine s uc h as this to all the text boxes in a form,


if it makes s ens e to do s o. I s data us ually overwritten, or does it
rec eive additional c harac ters ? O nly you know your applic ation,
s o only you c an dec ide where to inc orporate this c ode. I n this
example, s ome additional information has been added to the
addres s , as s hown in Figure 3 - 7 .

Figure 3-7. Information added, but not used to


replace the existing text
T he SelStart property has two related members : SelLength and
SelText. Singly or in c ombination, three text s elec tion properties
give you fine c ontrol over handling text in a text box or c ombo
box.

So far, this hac k has s hown you how to us e SelStart to s et where


the entry will begin. But onc e I had to provide a us er with an
eas y way to revers e las t name/firs t name to firs t name/las t
name for a s et of rec ords . N ames often inc lude initials , middle
names , and s o forth. I f you've ever written a name- pars ing
routine, you know how diffic ult it is to get the routine to handle
all the variations found in names .

T his was a one- s hot deal, s o it didn't make s ens e for me to


c reate a long, drawn- out routine. I ns tead, I us ed the s elec tion
properties , and I left s ome of the work up to the us er. H ere's how
it worked.

I pres ented the c ontac t names that had to be revers ed in a form,


s uc h as that s hown in Figure 3 - 8 .

Figure 3-8. The names that needed to be


reversed
T he us er s imply s elec ted the firs t nameor firs t name and initial,
or firs t name and middle name, and s o onand then pres s ed either
the Tabor the E nter key, as s hown in Figure 3 - 9 .

T hat's all it took! I t worked by implementing the s elec tion


properties on the text box's Exit event. I n this example, the text
box is named Contact. T he routine explained earlier, whic h us es
the Enter event, is als o us ed in this example:

Private Sub Contact_Enter()


'
'remove selection effect
'and place insertion point at end
'
Dim text_length As Integer
text_length = Len(Contact)
Contact.SelStart = text_length
End Sub

Private Sub Contact_Exit(Cancel As Integer)


'
'if there is selected text and the selection
'is less than the full text size then
'if the selection starts past the first position then
'move the selected text to the front
'
Dim new_text As String
If Contact.SelLength > 0 And _
Contact.SelLength < Len(Contact) Then
If Contact.SelStart > 1 Then
new_text = Contact.SelText & " " & _
Left(Contact, Len(Contact) - Contact.S
Contact.Text = Trim(new_text)
End If
End If
End Sub

Figure 3-9. A first name selected


For c onvenienc e, the Enter event makes s ure the text is n't
s elec ted at the beginning. T he us er s elec ts the firs t name, and
middle initial or middle name, if pres ent, and then either tabs out
of the text box or jus t pres s es the E nter key. T his fires the Exit
event, whic h tes ts the text to make s ure s omething has been
s elec ted, it is n't the s ame length as the entire text, and it
does n't s tart at the beginning.

T he routine then revers es the s elec ted and uns elec ted portions
(the firs t name and the las t name) and returns them to the text
box. Figure 3 - 1 0 s hows the res ult.

Figure 3-10. All the names reversed


T he routine us es all three s elec tion properties : SelStart,
SelLength, and SelText. T hes e properties give you all you need to
work with s elec ted portions of text. When you c ompare them to
the equivalent text properties and methods Text, Len, Left,
Right, Mid, and s o onyou c an s ee that they give you quite a bit
of c ontrol when manipulating text.
Hack 21. Let Users Add Custom Items to
Predesigned Lists

A void f orcing choices to existing list items only by adding a


procedure to handle new values.

U s ers often c hoos e items from an exis ting lis t via a c ombo box
on a form. Sometimes , however, a us er might need to enter into
the c ombo box a value that is n't on the lis t. T his c an happen, for
example, when the us er is working with a new c us tomer that he
has not yet appended to a c us tomer table, or when the us er is
c orrec ting an exis ting lis t item that is mis s pelled.

T he Limit To List property c ontrols whether a new value is


allowed entry in a c ombo box. I f this property is s et to No, us ers
c an enter new values into the c ombo box. T his is fine, but with
one c aveat: if the new value is meant to bec ome a permanent
member of the lis t, jus t typing it into the c ombo box does n't add
it to the lis t.

I f it makes s ens e for your applic ation to let us ers permanently


add values to a c ombo box's s ourc e lis t, you need to us e a
different tec hnique. Firs t, s et the Limit To List property to Yes.
(Yes , this means the new item won't be allowed, but read on! )
T he tric k to this hac k is to implement inc lus ion of the new item
by tapping the On Not In List event, whic h works only when the
Limit To List property is s et to Yes.

Figure 3 - 1 1 s hows a form in D es ign mode. T he form has a


c ombo box on it, and the property s heet s hows the properties for
the c ombo box, with the Limit To List property s et to Yes .

When a us er attempts to add a new value to the c ombo box, the


On Not In List event fires . Within this event, a c ode routine
handles adding the new value to the lis t.

3.4.1. The Code

T he c ode is s imple and s traightforward. Two arguments are


provided to the routine: NewData and Response. T he event s tub
c omes predes igned with thes e arguments , s o you don't have to
c reate them:

Private Sub cmbCustomers_NotInList(NewData As String, _


Response As Integer)
Dim ctl As Control
Set ctl = Me.cmbCustomers
Response = acDataErrAdded
ctl.RowSource = ctl.RowSource & ";" & NewData
End Sub

Figure 3-11. The Limit To List property set to


Yes
T he Response argument tells A c c es s to override the behavior of
not allowing a value to be added. T he developer does this by
s etting the Response to the adDataErrAdded c ons tant. T he new data
(s upplied by the NewData argument) is then added to the Row
Source.
3.4.2. Hacking the Hack

So far, this hac k works on the premis e that the Row Source Type is
s et to a Value List. I f the Row Source Type is Table/Query, you
need an append routine to plac e the new value in the underlying
data s tore. I n this c as e, the Not In List event appends the new
value to the s ourc e table.

H ere is an example of how to c ode the routine. T his example


as s umes a s ourc e table named tblShippingM ethods with a field
named Shipping- M ethod:

Private Sub cmbShippingMethods_NotInList(NewData As String


Response As Integer)
Dim new_data As String
Dim conn As ADODB.Connection
Set conn = CurrentProject.Connection
'double up any apostrophes before Insert
new_data = Replace(NewData, "'", "''")
Response = acDataErrAdded
conn.Execute "Insert Into " & _
"tblShippingMethods(ShippingMethod) Values('" & _
new_data & "')"
End Sub
Hack 22. Populate and Sort Lists with
Flair

Use these three clever techniques to populate and sort listbox


controls.

L is ts are integral to form des ign. True, not all forms need a lis t,
but when they're applic able, s elec ting an item from a lis t is muc h
eas ier than typing in the value. T his als o makes it eas ier to
avoid typos .

T his hac k pres ents three ways to populate and s ort lis tbox
c ontrols . I n eac h example, the underlying tables and s truc ture
are key elements . T he examples s how how to s ort alphabetic ally,
but from two s ourc es ; how to s ort bas ed on a key value; how to
s ort on plac ement in the SQ L s tatement; and even how to s ort
by trac king the popularity of the lis t items thems elves ! T he SQ L
Union c laus e is a key fac tor to getting muc h of this to happen.

3.5.1. The Form

Figure 3 - 1 2 s hows a form with three lis ts , aptly named L is t 1 ,


L is t 2 , and L is t 3 .

Figure 3-12. Three list controls on a form


Behind the s c enes , two tables populate the lis t c ontrols :
tblFruits and tblVegetables , s hown in Figure 3 - 1 3 . N ote that
they s hare two c ommon fields : SortN umber and L is tI tem. T his
c ommon s truc ture is put to good us e, as you will s ee s oon.

Figure 3-13. Two tables used to populate the list


controls

3.5.2. Populating a Listbox Alphabetically


from Two Sources
L is t 1 dis plays the values from the two tables , s orted
alphabetic ally as one larger lis t. T he tric k is to have the List
c ontrol us e the rec ords of both tables in its Row Source property.
You do this by c ombining the rec ords of both tables in a Union
query. Figure 3 - 1 4 s hows the form in D es ign mode with the
property s heet s et to L is t 1 .

Figure 3-14. The Row Source property for List 1


T he SQ L s tatement in the Row Source property reads like this :

Select ListItem from tblFruits UNION Select ListItem from


T he Union c laus e allows the values from the two tables to be
c ombined, given that the s truc ture and datatype are the s ame.
I n other words , the L is tI tem field from eac h table is addres s ed
with the SQ L s tatement. Q uerying the s ame number of fields in
eac h Select s tatement with the Union query is a requirement. T he
query c an't run if the number of fields being ac c es s ed from eac h
table differs .

A s a res ult the c ombined rec ords are s orted as if they really
c ome from one s ourc e (whic h tec hnic ally is true via the Union
query). T herefore, the dis tinc tion of fruits and vegetables is
purpos ely los t, and ins tead, as paragus follows apple, broc c oli
follows banana, and s o on.

T his tec hnique is us eful when you need to pres ent items in a lis t
that c ome from more than one s ourc e. A s dis c us s ed in the
following s ec tion, you c an bring together as many s ourc es as
you need with multiple Union c laus es .

3.5.3. Controlling the Sort in a Listbox


Populated from Multiple Sources

I n Figure 3 - 1 2 , L is t 2 s hows the res ult of s orting fruits in a


c ertain order and vegetables in a c ertain order. A dditionally, the
fruits and vegetables aren't s orted with eac h other. T he lis t als o
inc ludes s eparators and values not found in the s ourc e tables :
All, All Fruits, and All Vegetables. H ow did all thes e items get
into the lis t?

A Union query populates the lis tbox. T he two s ourc es tblFruits


and tblVegetables are us ed, but ins tead of letting the lis t mix and
s ort the items alphabetic ally, the SortN umber field c ontrols the
s ort.

A key point here is that the range of values for SortN umber in
the tblFruits table is different from the range of values for
SortN umber in the tblVegetables table. T he Union operation
ac tually does c ombine both s ourc es into one s ort, but the
SortN umber field ranges keep the two lis ts apart in the lis tbox.

Figure 3 - 1 5 s hows the form in D es ign mode with the property


s heet s et to L is t 2 . T he SQ L s tatement that s erves as the Row
Source property is dis played in the Zoom box.

H ere is the SQ L s tatement:

Select "All" as a, -2 as SortNumber from tblFruits


Union Select "---" as a, -1 as SortNumber from tblFruits
Union Select "All Fruits" as a, 0 as SortNumber from tblFruits
Union Select ListItem, SortNumber From tblFruits
Union Select "---" as a, 99 as SortNumber from tblVegetables
Union Select "All Vegetables" as a,
100 as SortNumber from tblVegetables
Union Select ListItem, SortNumber From tblVegetables
Order By SortNumber

Figure 3-15. The Row Source property for List 2


Q uite a bit is going on here. O verall, the SQ L c ombines items
from the s ourc e tables with items provided right within the SQ L .
A ll thes e tie together via the SortN umber field.

T his SQ L s tatement us es the Union c laus e s everal times , to


make s ure that all Select s tatements point to the s ame number
of fields . I n this example, that number is 2 .

T he SQ L s tarts by getting the word All to the top of the lis t. T his
s nippet forc es the word All into the lis t:

Select "All" as a, -2 as SortNumber

T he c ode s nippet does this by giving the word All the lowes t
value of SortNumberin this c as e, - 2 . To be c lear, neither the word
All nor the value - 2 ac tually c omes from an underlying table.
H owever, their plac ement in the SQ L follows the s truc ture of all
the other Select s tatements in the SQ L , whic h allows them to be
c ombined with the other values being ac c es s ed by the SQ L .

T he SQ L us es Union to c ombine values from the tables with


thes e on- the- fly values . A number of thes e values are in the
SQ L :

Select "All" as a, -2 as SortNumber from tblFruits


Select "---" as a, -1 as SortNumber from tblFruits
Select "All Fruits" as a, 0 as SortNumber from tblFruits
Select "---" as a, 99 as SortNumber from tblVegetables
Select "All Vegetables" as a, 100 as SortNumber from tblVegeta

A ll thes e parts of the SQ L forc e the lis t to pres ent a value: All,
All Fruits, All Vegetables, or - - - . N one of thes e values c omes
from the tables . H owever, all of them are paired with a s ort
number, and this is what plac es them in their s equential plac e in
the lis tbox.

C ons ider the s ort numbers as s oc iated with thes e on- the- fly
items , while c ons idering the s ort numbers of the items in the
tables (s ee Figure 3 - 1 3 ). Sort numbers for the vegetables s tart
at 1 0 1 . T herefore, the All Vegetables item has been as s oc iated
with the number 1 0 0 . T his forc es it to appear in the lis t direc tly
above the ac tual vegetables .

Keep in mind that a lis tbox s uc h as this , with s everal pos s ible
items a us er c an s elec t, als o requires a related level of
func tionality to handle the us er's s elec tion. I f a us er s elec ts a
s ingle fruit or vegetable, c hanc es are the applic ation will
c ontinue proc es s ing. H owever, what if a us er s elec ts All Fruits?
Your proc es s ing will need to handle all the values in the tblFruits
table.

A ls o note that you enter the s eparator c harac ters (- - - ) into the
lis t for the s ake of s egregating parts of the lengthy lis t of items .
T his is rather pleas ing for s omeone s c rolling through a long lis t;
however, a us er c an s elec t the s eparators ! T herefore, you need
to ens ure that us er validation and feedbac k are in plac e in c as e
this happens . Typic ally, if a us er s elec ts the s eparator
c harac ters , a mes s age s hould appear alerting him to make
another s elec tion.

3.5.4. Sorting List Items by Popularity

I t's not always eas y to know ahead of time whic h items us ers
will s elec t mos t often from a lis t. You c an us e a Sort N umber
field to arrange lis t items in a way that s eems bes t, but there is
an even better way to do this .
Why not let us er ac tions drive the way the lis t is s orted?
Keeping in mind that it is eas y to s ort a lis t by a numeric al field,
logic dic tates that the values in the numeric al field s hould
reflec t the popularity of the lis t items .

T his is eas y to do by updating a lis t's Sort field eac h time it is


s elec ted. Figure 3 - 1 6 s hows the form in D es ign mode with the
property s heet s et to L is t 3 .

H ere's the Row Source SQ L s tatement for L is t 3 :

SELECT Occurrence, ListItem FROM tblFruits ORDER BY Occurrenc

T his lis tbox us es the tblFruits table exc lus ively. T his table has
the additional O c c urrenc e field, whic h drives the way items are
s orted in the lis tbox. N ote from the Row Source property that
items are lis ted bas ed on the O c c urrenc e field values being in
des c ending order.

Figure 3-16. The Row Source property for List 3


To make s ens e of this , it is nec es s ary to s omehow update the
values in the O c c urrenc e field. T his update oc c urs when you
proc es s the s elec ted lis t valuein whatever way your proc es s ing
works . For the purpos e of this demons tration, a button has been
plac ed on the form. H ere's the Click event for the button:

Private Sub cmdUpdateCount_Click()


'get the current count for this item
Dim selected_item_count As Integer
If Not IsNull(Me.List3) Then
selected_item_count = _
DLookup("Occurrence", "tblFruits", "ListItem='" & Me.Li
'increase the count and update the table
selected_item_count = selected_item_count + 1
DoCmd.SetWarnings False
DoCmd.RunSQL ("Update tblFruits Set Occurrence=" &
selected_item_count & " Where ListItem='" & Me.
Me.List3.Requery
End If
End Sub

I n a nuts hell, the DLookup func tion finds the c urrent value of the
O c c urrenc e field for the s elec ted item and s tores it in the
selected_item_count variable. T he value is inc remented by 1 , and
a SQ L Update s tatement writes the value bac k into the table, for
the given item. Finally, the lis t is refres hed s o that on the form
the lis t will res ort.

A s a res ult, when items in the lis t are s elec ted and proc es s ed,
they float to the top of the lis t. You c an s ee this by c omparing
the plac ement of items in L is t 3 in Figure 3 - 1 2 with the values of
the O c c urrenc e field in the tblFruits table in Figure 3 - 1 3 . For
example, ras pberry is the firs t item in L is t 3 bec aus e it has the
highes t value in the O c c urrenc e field.
Hack 23. Use Custom Controls on Your
Forms

Move past standard A ccess controls, and discover new design


possibilities.

I f you develop enough A c c es s applic ations , you begin to take


the Toolbox for granted. You know all the c ontrols and when to
us e them. But did you ever notic e the Toolbox has a button that
leads to more c ontrols ? Figure 3 - 1 7 s hows where this button is
loc ated.

Figure 3-17. Finding additional controls


C lic king the M ore C ontrols button opens a lis t of new c ontrols
from whic h to s elec t. I don't s ugges t tes ting all of them bec aus e
I think that s ome aren't us eful in A c c es s . H owever, if you s c roll
to the M ic ros oft c ontrols , you might find s ome interes ting ones
to try on a form. L et's s ee how we c an us e a few of thes e c us tom
c ontrols .

3.6.1. Adding a Custom Control to a Form

T he lis t of c ontrols will probably differ between c omputer


s ys tems , but it's a s afe bet that you have the M ic ros oft Forms
c ontrols loaded on your c omputer bec aus e they are ins talled
with M ic ros oft O ffic e.

A s an example, I plac ed a M ic ros oft Forms 2 .0 s pinbutton


c ontrol on a form, as s hown in Figure 3 - 1 8 . I did this by s imply
s elec ting it from the long c ontrol lis t and then drawing on the
form us ing the mous e.

Figure 3-18. A spinbutton control on a form


T he s pinbutton c ontrol has minimum and maximum value
properties that you c an s et. T herefore, you c an us e the s pinner
to c yc le through from 1 1 0 0 , 1 2 8 1 3 3 , or whatever makes s ens e
for your applic ation. You c an ac c es s the s pinner's value from
c ode in the s ame way you do for other c ontrols .

N ext, I put the M ic ros oft D ate and T ime P ic ker C ontrol 6 .0 on
the form, as s hown in Figure 3 - 1 9 . T his nifty c ontrol s tays in a
c ollaps ed s tate until you c lic k it. I t then opens to a s c rollable
c alendar. T his is a great us er enhanc ement; us ers don't have to
enter dates manually when you put s uc h a c ontrol as this on a
form. A nd the good thing about this c ontrol is that it remains
s mall when it's not in us e.

Figure 3-19. Making it easy to select a date


I t's us eful to bec ome familiar with a few of the new c ontrols and
to think of them as part of your c ontrol toolbox. T his opens the
door to new ways to des ign forms , and it provides us ers with an
enhanc ed experienc e.
3.6.2. See Also

"P lay V ideos in A c c es s Forms " [Hack #34]

"U s e a Brows er I ns ide A c c es s " [Hack #97]


Hack 24. Confirm Record Updates Before
Saving

Give users a chance to review their edits bef ore they save a
record.

When you're working on a bound form, as you s c roll through


rec ords , data c hanges are s aved automatic ally. T his behavior is
normal and is often apprec iated rather than ques tioned. H owever,
s ometimes it is prudent to interrupt this proc es s and let a us er
review her work. O nc e the update happens , the original data is
gone, unles s other meas ures , s uc h as bac kups , are in plac e.

O ne thing that works in our favor to c ontrol this is the Before


Update event. By hooking into this event, you c an as k the us er
whether s he wants to c omplete the update. I f the ans wer is no,
you undo the c hanges .

U s ers s hould c ontrol whether they want to be prompted to


c onfirm c hanges bec aus e the prompts c an bec ome annoying. A
us er might want this feature s ometimes but not other times .
Figure 3 - 2 0 s hows a form with a c hec kbox in the upper- right
s ec tion that ac ts as a flag indic ating whether to c onfirm updates .

Figure 3-20. A checkbox to indicate whether to


confirm updates
T he Before Update event fires only when the data c hanges . I n the
event, the c hec kbox value is tes ted, and if the value is true, the
us er is prompted. Figure 3 - 2 1 s hows the prompt.

I f the us er c lic ks Yes , the update proc eeds . I f s he c lic ks N o, an


undo c ommand runs , thereby dropping the c hanges to the data.
H ere is the event and the c ode:

Private Sub Form_BeforeUpdate(Cancel As Integer)


If Me.chkConfirm = True Then
proceed = MsgBox("Do you want to save the changes?", vbYes
"Save Changes")
If proceed = vbNo Then
DoCmd.RunCommand acCmdUndo
End If
End If
End Sub

A key point to this hac k is letting the us er dec ide whether to be


prompted. Being as ked to c onfirm endles s c hanges will quic kly
bec ome a s ourc e of frus tration. T he nic e thing is that us ers c an
dec ide to turn on the feature when updating c ritic al information,
s uc h as names and addres s es , but turn off the feature when
making c hanges to les s important data.

Figure 3-21. Confirming an update


Hack 25. Put a Clock on a Form

Give users the time and date, even f or more than one time zone.

By c ombining a form's On Timer event, its timer interval, and the


s ys tem c loc k, you c an plac e a func tional c loc k on a form.

Figure 3 - 2 2 s hows a form with a c loc k in the header. I ts


plac ement in the header, and not in the details s ec tion, makes
s ens e bec aus e it is unbound and is n't s pec ific to any rec ord in
the detail.

You c an go about your bus ines s moving through rec ords , editing
data, and s o on; the time will jus t keep tic king away undis turbed.

3.8.1. Creating the Clock

T he c loc k is eas y to c reate. Firs t, plac e a label c ontrol on the


form. T hen, s et the form's timer interval to 1 ,0 0 0 (whic h equals
one s ec ond). Finally, plac e a s ingle line of c ode in the form's On
Timer event:

Me.lblClock.Caption = Format(Now( ), "hh:mm:ss AMPM")


Figure 3-22. A form that tells the time

T his as s umes the label c ontrol is named lblClock. T he Now


func tion returns the s ys tem time, and the Format func tion gives
the time a des irable look. T he format is n't nec es s ary, though. I f
you don't us e it, the full date and time is returned. T he format,
as applied here, dis plays jus t the time; hh:mm:ss is a format for
hours (hh), minutes (mm), and s ec onds (ss).
3.8.2. Hacking the Hack

You c an do a lot to boos t the c loc k's appeal and func tionality.
O ne idea is to dis play the time for c ities in different time zones .
Figure 3 - 2 3 s hows a form with two c loc ks . O ne dis plays the time
in N ew York, and the other dis plays the time in C hic ago.

Figure 3-23. Two clocks on a form


C hic ago is one hour behind N ew York. You ac c ount for the one-
hour differenc e by applying the DateAdd func tion. H ere is the
updated On Timer event:

Me.lblClockNewYork.Caption = Format(Now( ), "hh:mm:ss AMPM


Me.lblClockChicago.Caption = Format(DateAdd("h", -1, Now( )),
"hh:mm:ss AMPM")

DateAdd c an add or s ubtrac t time. I n this c as e, a value of - 1


s ubtrac ts one hour.

H ere's another idea: give the us er a way to c hange the format.


You ac c omplis h this by us ing a public variable and the label
c ontrol's DblClick event. When the form is opened, a public
variable, named format_type in this example, is given a value of
1 . E ac h time a us er double- c lic ks the c loc k, the format_type
variable inc rements . When it hits 4 , it is s et bac k to 1 . T he On
Timer event tes ts the format_type variable and applies the
partic ular format. H ere is the full c ode behind the form that takes
c are of this :

Option Compare Database


Public format_type As String

Private Sub Form_Open(Cancel As Integer)


format_type = 1
End Sub

Private Sub Form_Timer( )


Select Case format_type
Case 1
Me.lblClock.Caption = Format(Now( ), "hh:mm:ss AMPM")
Case 2
Me.lblClock.Caption = Format(Now( ), "hh:mm AMPM")
Case Else
Me.lblClock.Caption = Format(Now( ), "mm/dd hh:mm AMPM")
End Select
End Sub

Private Sub lblClock_DblClick(Cancel As Integer)


format_type = format_type + 1
If format_type = 4 Then format_type = 1
End Sub

N ow, a us er c an double- c lic k the c loc k until it dis plays the date
and time in a way that s uits him. O f c ours e, you c an eas ily
inc reas e the number of available formats .
Hack 26. Be Tab-Smart

Override a f orm's tab order so that users get to the entry boxes
they need.

Setting the tab order is probably not the bigges t thing on your
mind when you des ign a form. I t does n't really require muc h
planning or analys is .

Typic ally, you do it at the end, after all the c ontrols are in plac e.
To be hones t, at times I 've s imply forgotten to s et a tab order on
my forms . But, of c ours e, the us er c ommunity lets me know when
that happens .

So, I s tarted thinking that if tab s tops on a form are really


important to us ers , why not go one s tep better and make the tab
order work more intelligently. O n s ome forms only s ome boxes
rec eive entries . A s mart way to tab through a form in this
s ituation is to s ens e whic h entry boxes c an be s kipped.

T his makes s ens e when the c ondition or entry into a text box is
bas ed on s ome unders tandable logic flow. For example, if you're
entering information about a new c us tomer, us ually you fill in
every field. H owever, when you're entering a piec e of information
on an exis ting c us tomer, mos t likely you tab over s everal text
boxes .

Figure 3 - 2 4 s hows a form in whic h a s elec tion has been made


from a c ombo box. T he c ombo box, named Status , has four
c hoic es : E nrolled, Walk- I n, P hone, and Renewal.
I n the Exit event of the c ombo box, a Select Case s tatement s ets
the foc us to different form c ontrols , depending on the s elec tion
made in the c ombo box. For example, a s tatus of Walk- I n is us ed
for unregis tered people who c ome in without an appointment.
T hey need to be s et up in the s ys tem, s o the next entry box to
go to is the Referral Sourc e. By c ontras t, when an E nrolled
pers on c omes in, the next box to enter is C omments . H ere is the
c ode in the Exit event:

Private Sub status_Exit(Cancel As Integer)


Select Case status
Case "Enrolled"
Me.txtComments.SetFocus
Case "Walk-In"
Me.txtReferralSource.SetFocus
Case "Renewal"
Me.txtStudentFirstName.SetFocus
End Select
End Sub

T he gis t of this approac h is that you c an apply rules to the tab


order bas ed on what makes s ens e to the bus ines s . A s another
example, a databas e s ys tem at a c ar dealers hip s hould s kip
right over a s ec tion about what type of new c ar a c us tomer wants
to purc has e if s he is in the market for a us ed c ar. I f the s ec tion
about new c ars c ontains 1 2 fields , the s ales pers on needs to tab
through 1 2 unneeded boxes , unles s a better method is in plac e.
I ns tead, s ome other c ontrol on the form c an ac t as a flag s o that
you c an implement s mart tabbing. U s ing the Exit events of
c ontrols is a great way to s upport this bus ines s workflow.

Figure 3-24. A combo box selection to drive the


tab order
Hack 27. Highlight the Active Control

Provide a visual aid on a busy f orm by emphasizing the control


that has the f ocus.

A s you tab through a form and enter data, it is pos s ible to get
los t. T he more c ontrols on a form, the more likely this is to
oc c ur. T his hac k s hows you how to keep the c urrent entry box
highlighted.

"Be Tab- Smart" [Hack #26] s hows a way in whic h us ers c an tab
around the form in a s ens ible manner. T hat hac k works with a
little c ode put into a c ontrol's Exit event. T his hac k us es the
Exit event, as well as the Enter event. I n this hac k, all c ontrols
that ac c ept entry have c ode in both events .

Figure 3 - 2 5 s hows the entry form with a border around the Firs t
N ame entry box. A s a us er tabs through the form or c lic ks
s pec ific c ontrols , the border will always appear around the ac tive
c ontrol. T he border is dis played on only one c ontrol at a time.

Figure 3-25. A border appearing around the


active control
To get this to work, the c ontrols on the form mus t have their
Special Effect property s et to Flat. You c an us e other s ettings
that allow borders to be dis played, s uc h as Shadow, but Flat
s eems to work bes t. When Shadow is us ed, and when the c ontrol
los es the border, the c ontrol s till retains the s hadow part of the
border, and that does n't look right.

Figure 3 - 2 6 s hows the c ode module behind the form. Two


s ubroutines are at the top: active_control_enterand
active_control_exit. T hes e s ubroutines run when they're c alled
by eac h c ontrol's Enter and Exit events . T he active_
control_enter s ubroutine s ets the ac tive c ontrol, whatever it is at
the time, to have a thic k, red border. T he border s tyle value of 1
indic ates a s olid border. T he border width value of 2 indic ates a
thic k border. Red is then s et as the border c olor.

T he active_control_exit s ubroutine has jus t a s ingle line of c ode;


it s ets the border to trans parent.

Figure 3-26. The code for turning a border on


and off
E ac h time a c ontrol is entered, it means a different c ontrol was
jus t exited. T herefore, thes e two s ubroutines fire in a pair. T he
Exit routine turns off the border for one c ontrol, and the Enter
routine turns it on for another.

U s ing the tec hnique in this hac k ens ures that us ers c learly s ee
whic h c ontrol is ac tive.
4. Presentation

Sec tion 4 .1 . H ac ks 2 8 3 9

H ac k 2 8 . Separate A lphabetic ally Sorted Rec ords into


L etter G roups

H ac k 2 9 . C reate C onditional Subtotals

H ac k 3 0 . U s e C onditional Formatting to P oint O ut


I mportant Res ults

H ac k 3 1 . P rovide a D irec t L ink to a Report

H ac k 3 2 . P rotec t I ntellec tual P roperty

H ac k 3 3 . C reate a Slides how in A c c es s

H ac k 3 4 . P lay V ideos in A c c es s Forms

H ac k 3 5 . V iew Reports E mbedded in Forms

H ac k 3 6 . P ut L ine N umbers on a Report

H ac k 3 7 . Shade A lternating L ines on a Report

H ac k 3 8 . Save P aper by Reduc ing Whites pac e

H ac k 3 9 . I nc lude the D ate, T ime, and P age C ount


4.1. Hacks 2839
A s c omprehens ive as a databas e c an bewith programmatic
func tionality, s ophis tic ated queries , and other bells and
whis tles you s till need to be able to communicate fac ts about the
data it c ontains . T his is where forms and reports c ome into play.
A dec ent databas e front end is c ritic al for interac tion between
the databas e and us mere mortals . A c c es s , of c ours e, s hines in
this area.

T he report des igner in A c c es s is a feature- ric h development


platform. I t inc ludes formatting tools , grouping and s orting
options , a palette full of c ontrols , and the ability to hook into
events and mus ter up s ome c oolnes s with V BA . Forms als o have
events , formatting options , and properties galore. P utting them
all to us e is beyond the s c ope of a s ingle c hapter. So ins tead,
this c hapter highlights s ome exc iting ways to work with forms
and reports .

You've already s een how to us e forms to dis play databas e


rec ords . H ow about us ing a form to play a s lides how or movies ?
"C reate a Slides how in A c c es s " [Hack #33] and "P lay V ideos in
A c c es s Forms " [Hack #34] s how you how.

Firs t impres s ions are us ually the bes t. M ake s ure your reports
are eye- poppers . A number of hac ks in this c hapter explain how
to provide s ophis tic ated grouping and formatting in reports .
"P rovide a D irec t L ink to a Report" [Hack #31] s hows how a
bus y us er c an c lic k a s hortc ut to print a report without fus s ing
around with the databas e. "V iew Reports E mbedded in Forms "
[Hack #35] explains how to inc orporate reports into forms .
Hack 28. Separate Alphabetically Sorted
Records into Letter Groups

Tap the Pref ix Characters property to gain new layout


possibilities.

Sorting alphabetic ally is nothing new; in fac t, it's rather oldone


of the s tandard prac tic es we take for granted. When you've got
dozens or hundreds of printed rec ords , though, it c an be tedious
to flip through report pages looking for a partic ular line item,
even though they're in alphabetic al order.

A neat thing to do is to s egregate the rec ords on a report


alphabetic ally. Figure 4 - 1 s hows a page from a report in whic h
s orted rec ords lis t repeatedly with no s uc h s egregation or break.
T he rec ords are s ortedno ques tion on that s c orebut the layout
makes it c hallenging to flip to the approximate area you need to
find.

Figure 4-1. A report with a repetitive layout


T he report's des ign is s traightforward. T he details s ec tion
c ontains the fields that bec ome the line items . T he report in this
format does n't us e groups , and that is why it is monotonous to
look at. Figure 4 - 2 s hows the des ign of the report.

4.2.1. Segregating by Letter

A way to break up the endles s line- item lis ting is to add a group
to the report. Figure 4 - 3 s hows how the report's des ign has been
altered to inc lude a group.

T he group is bas ed on the C lientL as tN ame field, whic h, of


c ours e, is the field being s orted on. H ere are a few key points
about how this group is being us ed:

Figure 4-2. A report that doesn't use grouping


and sorting
Figure 4-3. A report that uses grouping and
sorting
T he group has a header. A footer is n't required. I n the
Sorting and G rouping dialog box, G roup H eader and
G roup Footer are s et to Yes and N o, res pec tively.

I n the Sorting and G rouping dialog box, the G roup On


property is s et to Prefix Characters, and the Group
Interval property is s et to 1.

I n the group header its elf, an unbound text box has been
ins erted, and its Control Source property is s et to an
expres s ion.

When the report runs , the expres s ion in the unbound text box
forc es the group break to oc c ur on the letters of the alphabet,
ins tead of on eac h oc c urrenc e of a las t name. A s a res ult, all the
A s are together, all the Bs are together, and s o on. You
ac c omplis h this by us ing the Left func tion to return the firs t
letter:

=Left([ClientLastName],1)

Figure 4 - 4 s hows how the report s egregates by letter.

Figure 4-4. Clients broken out by first letter


T he larger font, bold, and underline s ettings make the
dis tinc tions vis ually c lear when thumbing through a report.

4.2.2. Hacking the Hack

N ote that on the report page s hown in Figure 4 - 4 , none of the


c lients ' las t names s tart with the letter J . T he fac t that s ome
rec ords don't exis t c ould be vital news to s omeone. I c an jus t
hear the bos s yelling, "What happened to the J ohns on ac c ount? "
Suc h a reac tion is bas ed on expec ting to s ee s omething that
is n't there. T he flip s ide to this is that mis s ing rec ords might be
identified only by pointing out that no rec ords have met a
c ondition.

I n partic ular, it would be us eful if the report s tated that no


rec ords were found for the letter J . We need a way to s till dis play
the alphabetic letter on the report, but in the c urrent des ign, this
won't ever happen. A ny alphabetic letters that c urrently appear
on the report are there bec aus e rec ords in whic h the las t name
s tarts with the letter J do exis t.

To get all letters to appear on the report, regardles s of whether


rec ords beginning with thos e letters exis t, inc lude s omewhere in
the des ign a lis t of all the letters to be c ompared agains t. T he
approac h us ed here is to relate the c lient table with a table of
the letters , ins tead of bas ing the report on jus t the c lient table.

A table is added to the databas e with jus t one field: L etter. T he


table c ontains 2 6 rec ords , for the letters A through Z. Figure 4 -
5 s hows the table, named tblL etters .
Figure 4-5. A table filled with letters of the
alphabet
I t's not a bad idea to inc lude the digits 0 9 in the table as well,
es pec ially if you're working with the names of c ompanies .

T he report's Record Source property was previous ly s et to the


c lient table (tblC lients ). N ow, though, the report's rec ord s ourc e
will be bas ed on a query. H ere is the SQ L s tatement:

SELECT tblClients.ClientFirstName, tblClients.ClientLastName,


tblClients.ClientAddress1, tblClients.ClientCity, tblLetters.Lette
FROM tblClients RIGHT JOIN tblLetters ON
left(tblClients.ClientLastName,1) = tblLetters.Letter;

A key point about this s tatement is that a RIGHT JOIN is us ed to


relate the tables . T his ens ures that all rec ords from the letters
table (tblL etters ) will be pres ent. I n other words , every letter will
be available to the report, even when no las t names s tart with
that letter.

T he report's des ign als o needs a s light c hange. T he group is no


longer bas ed on the las t name; ins tead, it's bas ed on the L etter
field. A ls o, a new expres s ion is us ed in the unbound text box.
Figure 4 - 6 s hows thes e c hanges .

Figure 4-6. Grouping on the alphabet


T he expres s ion in the text box returns one of two pos s ible
s tatements . When at leas t one rec ord c ontains a las t name
s tarting with a given letter, the letter is dis played. When no
rec ords c ontain a las t name s tarting with the given letter, a
mes s age is dis played that no rec ords were found for that letter.
You ac c omplis h this us ing the IIF and Count func tions :

=IIf(Count([ClientLastName])>0,[Letter],"No records for " & [Lette

A s a res ult, this report has all the alphabetic al letters as group
headers , regardles s of whether any rec ords matc h, as s hown in
Figure 4 - 7 .

Figure 4-7. Reporting that no records exist


You c an adapt this hac k in a number of ways . For example, you
c an hide the details s ec tion, and you c an alter the expres s ion in
the header to print a line only when no rec ords exis t. T his alters
the report to lis t exc eptions only.
Hack 29. Create Conditional Subtotals

Split a grand total into pertinent business summaries using


running sums and expressions.

A c ommon reques t is to c reate two s ets of totals for c omparis on.


T his , by its elf, is reas onable in a report des ign; you c an s et a
group that is bas ed on a field that breaks on different values . A
perfec t example is data bas ed on a year. I f the report inc ludes a
Year field, you c an inc lude s ubtotals in the group footer. T hat is ,
you c an get a s ummary (of whatever other fields ) for eac h year.

But when you throw in the need to report totals on more than one
c ondition, things s tart to get a bit mes s y. You c an c reate two
groups , but you mus t dec ide whic h group nes ts ins ide the other.
T hat dec is ion is n't always c lear- c ut. A dded to this are various
layout options . I f you want to arrange thes e s ubtotals in any
fas hion other than underneath eac h other, you are out of luc kthat
is , unles s you us e running s ums and c alc ulated c ontrols .

Figure 4 - 8 s hows a report that dis plays grand totals for eac h
year and, underneath them, the yearly grand totals s eparated by
eac h s tate's c ontribution.

Figure 4-8. Grand totals and subtotals


O f c ours e, you c an c reate a report s uc h as
the one in Figure 4 - 8 us ing other methods ;
for ins tanc e, you c ould us e a s ubreport
ins tead. T he running s ums method
outlined in this hac k is only one method
available for reporting totals on more than
one c ondition.

T his hac k us es an example of a veterinary prac tic e, whic h has


data about vis its to the prac tic e over two years and c lients who
c ome from five different s tates . T he report's rec ord s ourc e is
bas ed on a Union query that c ombines two identic al Select
queries identic al, that is , exc ept that one us es rec ords for 2 0 0 3
and the other us es rec ords for 2 0 0 4 . T he report's rec ord s ourc e,
therefore, is the following s tatement:

SELECT * FROM qryServiceDates_2003


Union SELECT * FROM qryServiceDates_2004

Figure 4 - 9 s hows the qryServiceDates_2003 query. E ac h c us tomer


has zero or more pets , and eac h pet has zero or more vis its .
Bear in mind that the report reports on vis its only. T he type of
pet is n't relevant, but the data model c alls for the pets table
(tblPets) to be inc luded.

Figure 4-9. Querying information about visits


4.3.1. Using Running Sums

T his report proc es s es hundreds of rec ords , but only the totals
appear bec aus e the detail s ec tion's Visible property has been
s et to false. E ven s o, the details s ec tion plays a vital role in
hous ing a s et of text boxes that are us ed for running s ums .
Figure 4 - 1 0 s hows the report des ign.c reate two s ets of totals for
c omparis on. T his , by

I n addition to ac tual data fields , the detail s ec tion c ontains 1 0


unbound text boxes , all of whic h have the Running Sum property
s et to Over All, as s hown in the property s heet in Figure 4 - 1 0 .

T he 1 0 text boxes handle the 1 0 pos s ible c onditions . T he data


c ompris es two years and five s tates , for a total of 1 0 pos s ible
s ubtotals . E ac h unbound text box has a c alc ulation for its
c ontrol s ourc e. For example, the txtC T 2 0 0 4 text box c ontains
this expres s ion:

=IIf([ClientState]="CT" And Year([DateOfService])=2004,1,0)

T his s tatement gets the running s um to inc rement only when the
s tate is C T and the year is 2 0 0 4 . E ac h text box works in this
way, with eac h inc rementing on s ome variation of the two
c onditions , s tate and year.

Figure 4-10. The Running Sum property set to


Over All
T he names of thes e text boxes are vital bec aus e they are
referenc ed in other c ontrols in the report footer. T he names are
txtC T 2 0 0 3 , txtC T 2 0 0 4 , txtM A 2 0 0 3 , txtM A 2 0 0 4 , and s o on.
A ll in all, five s tates are us ed: C T, M A , N Y, N J , and P A .
T he report footer c ontains two areas , one for the s ummary of
eac h year. T he areas are s eparated vis ually with s ome line
c ontrols . T here is no real s etting to s plit the report footer.

A ll the text boxes in the report footer are unbound, and they
referenc e the text boxes in the detail s ec tion. For example, the
report footer text box that dis plays the total for C T for 2 0 0 3
s imply referenc es the txtC T 2 0 0 3 running s um text box, with
this s tatement:

=[txtCT2003]

T he 1 0 s ummaries in the report footer that dis play a s um bas ed


on year and s tate all work in the s ame way. E ac h referenc es a
s ingle text box from the detail s ec tion. T he two grand totals in
the footer, the ones bas ed on total year, s imply s um the
as s oc iated five text boxes from the detail s ec tion. For example,
the text box that dis plays the grand total for 2 0 0 4 has this
s tatement for its c ontrol s ourc e:

=[txtCT2004]+[txtMA2004]+[txtNY2004]+[txtNJ2004]+[txtPA2004]

By c alc ulating totals in the detail s ec tion and then referenc ing
thos e running s um text boxes , you c an arrange the report's
layout any way you wis h.

4.3.2. Hacking the Hack

T he data model s hown in this hac k (s ee Figure 4 - 9 ) inc ludes a


table with pets . What if the us er wanted to report by year, s tate,
and pet? A s s uming the data inc ludes 1 0 types of pets (c at, dog,
bird, and s o on), you would have 1 0 0 variations of c onditions :
that is , 2 years times 5 s tates times 1 0 pet types . You c ould
c reate s uc h a report us ing the s teps des c ribed in this hac k, but
this would be tedious . A better approac h with s uc h a large
number of c onditions is to bas e the report on a Crosstab query.
T he example in "Summarize C omplex D ata" [Hack #45] us es
the data model from this hac k to s how how s uc h a query works .

4.3.3. See Also

"Summarize C omplex D ata" [Hack #45]

"U s e C onditional Formatting to P oint O ut I mportant


Res ults " [Hack #30]
Hack 30. Use Conditional Formatting to
Point Out Important Results

Not only can you use the built-in conditional f ormatting f eature,
but you also can roll your own with a little VBA !

Why not add a little impac t to important res ults or fac ts about
your data? I ns tead of having a report dis play res ults textually,
us e a bit of formatting bas ed on c onditions in the data to draw
readers ' eyes direc tly to the important news . I f the news is good,
you c an take all the c redit and maybe get a promotion. I f the
news is bad, you c an always us e the "D on't s hoot the
mes s enger" line.

"C reate C onditional Subtotals " [Hack #29] demons trates how to
c reate a report bas ed on data that c overs two years , with eac h
year broken out as its own total. T his is great for c ommon
analys es in whic h you're c omparing res ults from one year to the
next to s ee how muc h the data has c hanged (inc luding whether
the c hange was pos itive or negative).

Some reports , however, als o print a third c olumn indic ating the
perc ent c hange when the two values are c ompared. A lthough
this is n't c overed here, you c an apply the c onditional formatting
explained in this hac k to the perc ent c hange text boxes if you
c hoos e to inc lude them.
4.4.1. Standard Conditional Formatting

A c c es s provides a nic e c onditional formatting utility. With it you


c an eas ily c hange font attributes , foreground c olor, and
bac kground c olor properties when a s pec ified c ondition is met.
Figure 4 - 1 1 s hows the C onditional Formatting dialog box. I n this
example, expres s ions have been entered for the c onditions .
A lternatively, you c an bas e the c onditions on ac tual data values .

Figure 4-11. Font colors that change based on


the condition
U s e the Format C onditional Formatting… menu to dis play
the C onditional Formatting dialog box. T he C onditional
Formatting dialog box manages formatting for one c ontrol at a
time. T herefore, you mus t s elec t a c ontrol before you c an
ac c es s the menu. A ls o, the menu item is dis abled unles s
c onditional formatting c an be applied to the s elec ted c ontrol.

Figure 4 - 1 1 s hows the c onditional formatting that has been s et


up for the txtC T 2 0 0 4 Total text box. I n partic ular, for this c ontrol
the following three formatting options have been s et, to tes t for
the differenc e in perc entage between the 2 0 0 3 and 2 0 0 4
amounts :

Greater than 20%

([txtCT2004]-[txtCT2003])/[txtCT2003]>0.2

Greater than 15% and equal to or les s than 20%

([txtCT2004]-[txtCT2003])/[txtCT2003]<=0.2 And
([txtCT2004]-[txtCT2003])/[txtCT2003]>0.15

Greater than 10% and equal to or les s than 15%

([txtCT2004]-[txtCT2003])/[txtCT2003]<=0.15 And _
([txtCT2004]-[txtCT2003])/[txtCT2003]>0.1

E ac h c ondition provides different formatting bas ed on s elec tions


made in the C onditional Formatting dialog box. T his works fine,
but the three- c ondition limit might require another approac h.
4.4.2. Conditional Formatting the VBA Way

By plac ing c ode into the report's event s tubs , you c an provide
robus t formattingbeyond what the s tandard c onditional
formatting feature allows . T he s tandard formatting has two major
limitations : you c an tes t for three c onditions only, and s ome of
the formatting options aren't available.

T he workaround is to jus t c ode up your own us ing V BA . Figure 4 -


1 2 s hows the report; note that Total V is its for 2 0 0 4 is s et in
italic and has a border around it.

Figure 4-12. Conditional formatting applied


through VBA code
T his formatting was applied bec aus e a c ondition was met, bas ed
on what was tes ted in the c ode. T his c ode has been plac ed in the
report's ReportFooter_Print event:

Private Sub ReportFooter_Print(Cancel As Integer, PrintCount As In


Dim visits_change As Single
visits_change = ([txt2004Total] - [txt2003Total]) / [txt2003Tota
Select Case visits_change
Case Is >0.25
Me.txt2004Total.Properties("ForeColor") = vbBlue
Me.txt2004Total.Properties("Borderstyle") = 1
Me.txt2004Total.Properties("BorderColor") = vbBlack
Me.txt2004Total.Properties("FontItalic") = 1
Case Is <= 0.25, Is > 0.2
Me.txt2004Total.Properties("ForeColor") = vbBlue
Case Is <= 0.2, Is > 0.15
Me.txt2004Total.Properties("ForeColor") = vbGreen
Case Is <= 0.15, Is > 0.1
Me.txt2004Total.Properties("ForeColor") = vbMagenta
Case Is <= 0.1, Is > 0
Me.txt2004Total.Properties("ForeColor") = vbBlack
Case Is <= 0
Me.txt2004Total.Properties("Borderstyle") = 1
Me.txt2004Total.Properties("BorderColor") = vbRed
End Select
End Sub

T he c ode tes ts the perc entage c hange and then us es a Select


Case s tatement to apply different formatting bas ed on the
perc entage c hange. Six c onditions are provided, but you aren't
limited in terms of number of c onditions ; us e whatever number
makes s ens e for your applic ation. A ls o, the type of formatting is
open to whatever you c an c ontrol through V BA , whic h is jus t
about everything.
Hack 31. Provide a Direct Link to a
Report

Provide a desktop shortcut to a report so that users can


completely skip the process of starting up the database.

What c an be eas ier for a manager or a databas e- c hallenged


individual than jus t s kipping the proc es s of opening the
databas e? You c an eas ily provide this func tionality by inc luding
a s hortc ut direc tly to the report. T he s hortc ut goes on the us er's
des ktop.

4.5.1. Creating a Shortcut

To c reate s uc h a s hortc ut, firs t open the databas e, and then


right- c lic k the des ired report. T he c ontext menu inc ludes a
C reate Shortc ut… menu item, as s hown in Figure 4 - 1 3 .

When the us er c lic ks the C reate Shortc ut… menu item, a dialog
box pops up for her to s elec t where to plac e the s hortc ut. T he
us er's P C des ktop will probably be filled in as the default, as
s hown in Figure 4 - 1 4 .

A fter the us er c lic ks the O K button, the des ired s hortc ut is


c reated. C lic king the s hortc ut s tarts up the databas e and opens
the report, but unfortunately, the databas e s tays open.
Figure 4-13. Creating a shortcut to a report
Figure 4-14. Selecting the location for the
shortcut
4.5.2. Printing a Report and Closing the
Database in One Click

A better approac h is to enable the us er to c lic k the s hortc ut,


print the report, and c los e the databas e afterward, all
automatic ally via a s ingle c lic k. Sounds like a mac ro to me!

Figure 4 - 1 5 s hows a s imple mac ro that prints a report and then


c los es the databas e.

Figure 4-15. Using a macro to run the report and


close the database
A ll that is nec es s ary is the ac tion to open the report. I t's
important that View is s et to Print, not to Print Preview or Design.
T his s ends the report direc tly to the printer ins tead of dis playing
it. T he follow- up Quit ac tion c los es the databas e.

N ote that if the report is us ually generated with s elec tions made
on a form, you s hould c reate a s hortc ut to the form ins tead. You
c an c reate s uc h s hortc uts for any databas e objec t.
Hack 32. Protect Intellectual Property

Prevent misuse of conf idential and copyrighted material by


printing watermarks on reports.

I f s omeone is bent on taking your intellec tual property, you have


no fool- proof way to prevent it. H owever, c ommon s ens e dic tates
that we do our bes t to protec t our as s ets . O ften, putting s ome
wording on the page or in the report header or footer s aying the
material is c onfidential s erves this need. H owever, this method
does n't nec es s arily make the mes s age s tic k out like a s ore
thumb.

A n additional meas ure is to put watermarks on reports . A


watermark s its right within the body of a report, page after page.
I t ends up underneath the ac tual text and is s omewhat
trans parent; that way, it does n't obs c ure the text, but it is
evident enough to get the mes s age ac ros s in a big way.

Figure 4 - 1 6 s hows a report in whic h a watermark s its mixed in


with the data. T he word "C onfidential" s tretc hes diagonally from
the lower left to the upper right. T he text appears to s it on top of
the watermark.

Figure 4-16. Using a watermark to get the


message across
4.6.1. Making the Watermark

To c reate a watermark, you need to c reate a graphic that you will


s et to the P ic ture property on a report. You will need a graphic s
program to c reate a dec ent watermark. Several good graphic s
programs are available. I us e an exc ellent, affordable program
c alled P aint Shop P ro by C orel C orp. (http:// www.jas c .c om.)
Whic hever graphic s program you us e, it mus t be able to do the
following:

Work with text as a graphic

You want to be able to s tretc h and orient the data.

Apply trans parency s ettings

T he final graphic s hould be in the ballpark of 7 5 %


trans parent. I t's bes t to determine the ac tual s etting via
trial and error.

Specify the s ize of the graphic

When c reating the graphic , it's important to make it


almos t as large as the paper you are us ing. Whatever
s ize you make the graphic is the s ize it appears on the
report (bas ed on a property s etting des c ribed later in
this hac k).

Save the graphic as a .j pg, .bmp, and s o on

You c an us e any file format that c an be us ed when


tapping the Picture property on the report.

C reating s uc h a graphic is beyond the s c ope of this hac k, but for


your information, the graphic in Figure 4 - 1 6 is 7 0 % trans parent
and was s aved as a .j pg file. T he graphic is about 4 x 7 inc hes .
Figure 4 - 1 7 s hows how the graphic s file appears on its own.

Figure 4-17. The watermark as a graphics file


4.6.2. Using the Watermark

O nc e you s ave the watermark as a file, go into the report's


D es ign mode. I n the property s heet, c lic k the Picture property,
and brows e to s elec t the graphic s file, as s hown in Figure 4 - 1 8 .

Figure 4-18. Setting the report's Picture


property
A few other relevant s ettings work with the Picture property:

Picture Type

You have a c hoic e of E mbedded or L inked. E mbedded is


the c orrec t c hoic e here. L inked will attempt to open the
graphic s eparately, whic h is n't the point of us ing a
watermark.

Picture Size Mode

T he c hoic es are C lip, Stretc h, and Zoom. E ac h treats


how the graphic is plac ed on the report in a different
manner. E xperimenting with thes e s ettings is the bes t
way to unders tand how they work. H owever, as
mentioned earlier, if you s ized the graphic c orrec tly when
you c reated it, us e the C lip s etting here. Following this
approac h helps to avoid having to gues s your way
through the pic ture's plac ement on the report.

P ic ture A lignment

You have five c hoic es : Top L eft, Top Right, C enter,


Bottom L eft, and Bottom Right. M os t people c hoos e
C enter, but you might find a different s etting s erves your
needs .
P ic ture T iling

T he s ettings are Yes and N o. T he N o s etting plac es the


graphic onc e on the page. T he Yes s etting tiles the
graphic , whic h c reates a repeating pattern. Try both of
them to s ee whic h is better for you. T he Yes s etting
makes a bus ier- looking watermark, but perhaps that is
what you want.

P ic ture P ages

T his lets you des ignate on whic h pages the watermark


will appear. T he c hoic es are A ll P ages , Firs t P age, and
N o P ages . I hope you don't c hoos e N o P ages , or you
won't s ee your watermark!

You als o might have to c hange the Back Style property on the
report. You might have to do this bec aus e the watermark
appears under the text, and text boxes c an take up more room
than the ac tual text they dis play. Figure 4 - 1 9 demons trates this
dilemma. I n the report, the rec tangular s hape of the text box
c overs up part of the watermark. You don't ac tually s ee the text
box, but the rec tangular s hape bec omes apparent when it's
c ontras ted with the watermark underneath.

Figure 4-19. Text boxes covering up the


watermark
To avoid this behavior, go into the des ign of the report. For any
text boxes that s it over the watermark, c hange the Back Style
property from Normal to transparent. T his forc es the text boxes to
dis play jus t the text, whic h is exac tly the effec t you want. Figure
4 - 2 0 s hows how the report appears when the text boxes are
trans parent.

Figure 4-20. The watermark, appearing through


the text boxes
Hack 33. Create a Slideshow in Access

Use images and the OnTimer event to create a controllable


visual show.

I nc orporating pic tures into A c c es s is a great thing. I t provides a


way to depic t produc ts , pers onnel, or any other items you want
to s how via pic tures ins tead of via a textual des c ription.
Typic ally you do this by s toring the paths to graphic s files in a
table field. I n this manner, the graphic s and the table data are
c onnec ted. O n a form, when a rec ord is dis played, an image
c ontrol c an be updated with the graphic found at the related
path.

T his hac k does n't imitate s uc h a data- bas ed s c enario. I ns tead,


it works with an unbound form to dis play unbound graphic s . You
c an mix the func tionality des c ribed here with a data- bound
s olution. For example, while a form dis plays databas e rec ords ,
the unbound graphic s func tionality c ould be inc orporated into
the form header or footer.

T his hac k us es the image c ontrol to dis play the graphic s and
s hows how to update the graphic s being dis played on a periodic
bas is , typic ally a few s ec onds . T he hac k als o s hows you how to
let us ers drive the graphic dis play update on demand.

4.7.1. The Graphics


For the example in this hac k, all the graphic s are in the s ame
direc tory. T he direc tory is hardc oded in the c ode, but the c ode
c an eas ily be updated to handle multiple direc tories , pas s ed
paths from a dialog, and s o on. For now, let's s ay that a group of
.j pg files s it in a direc tory, as s hown in Figure 4 - 2 1 .

Figure 4-21. Picture files in a directory


N ote that all the files don't have to be in the .j pg format. You
als o c an us e other graphic s types , s uc h as bitmaps (.bmp) and
T I FF (.tif) files (this is als o s pec ified in the c ode).

4.7.2. The Form Design

Figure 4 - 2 2 s hows the form in D es ign mode. I t c ontains jus t a


few c ontrols : s ome buttons , a c hec kbox, and the image c ontrol.
T he image c ontrol has an initial pic ture, but this is overridden as
the s lides how runs .

Figure 4-22. The design of the slideshow form


A s you c an s ee, the us er c an either run an automated s lides how
or navigate through the graphic s manually. T he N ext and
P revious buttons allow the us er to move forward and bac kward
through the graphic s while not in an automated mode. T hes e
buttons jus t c yc le through a V BA c ollec tion of paths to the
graphic s , and the us er c an us e them to update the image
c ontrol's Picture property.

T he c hec kbox is us ed to s et the mode. I f it's c hec ked, the


s lides how runs . Regardles s of whether the us er lets the graphic s
c hange automatic ally, s he mus t pres s the Start button. T his is a
nic e feature bec aus e it gives the us er c ontrol over when to s tart
the s how. A fter all, s omeone might s till be getting popc orn!

H owever, bec aus e there is a Start button, it makes s ens e to als o


have a Stop button. C lic king the Stop button merely c hanges the
value of a Boolean- type public variable, named stop_show, from
false to true.

4.7.3. The Code

A vital piec e of this applic ation is the us e of the form's OnTimer


event. I n the following c ode, note that the Timer Interval has a
s etting of 2000. T his tells the form to fire its OnTimer event every
two s ec onds . You c an c hange this value to ac c ommodate other
intervals :

Option Compare Database


Public stop_show As Boolean
Public pixpaths As Collection
Public pixnum As Integer
'
Private Sub Form_Open(Cancel As Integer)
'
'set initial properties
'
Me.chkRunContinuous = False
Me.cmdNext.Enabled = False
Me.cmdPrevious.Enabled = False
End Sub
'
Private Sub cmdStart_Click()
'
'read paths of graphics into a collection
'displays the first graphic
'

stop_show = False
If Me.chkRunContinuous = False Then
Me.cmdNext.Enabled = True
Me.cmdPrevious.Enabled = True

End If
'replace with your path!!
pix_path = "C:\Product Photos"
Set pixpaths = New Collection
Set fs = Application.FileSearch
With fs
.LookIn = pix_path
.FileName = "*.jpg"
If .Execute() > 0 Then
For i = 1 To .foundfiles.Count
pixpaths.Add Item:=.foundfiles(i)
Next i
Else
MsgBox "No files found!"
End If
End With
'load first pix
Me.imgPixHolder.Picture = pixpaths(1)
pixnum = 1
End Sub
'
Private Sub cmdNext_Click()
'

'advances to the next graphic


'cycles forward through collection
'

If pixnum = pixpaths.Count Then


pixnum = 1
Else
pixnum = pixnum + 1
End If
Me.imgPixHolder.Picture = pixpaths(pixnum)
End Sub
'
Private Sub cmdPrevious_Click()
'
'displays the previous graphic
'cycles backward through collection
'

If pixnum = 1 Then
pixnum = pixpaths.Count
Else
pixnum = pixnum - 1
End If
Me.imgPixHolder.Picture = pixpaths(pixnum)
End Sub
'
Private Sub cmdStop_Click()
'
'sets global variable to false
'disables Previous and Next buttons
'

stop_show = True
Me.cmdNext.Enabled = False
Me.cmdPrevious.Enabled = False
End Sub
'
Private Sub Form_Timer()
'
'if the mode is to run continuously and the
'stop button has not been clicked, then keep cycling gra
If Me.chkRunContinuous = True _
And stop_show = False Then cmdNext_Click
End Sub

T his c ode module c ontains a handful of routines . T he form's Open


event builds a c ollec tion of paths to the graphic s , found within a
s pec ified direc tory. T he s pec ific direc tory and graphic s file type
are hardc oded. T he FileSearch objec t us es thes e values to find
the graphic s files . You c an expand this to look in more than one
direc tory and/or for more than one file type. Read up on the
FileSearch objec t in the H elp s ys tem or on the I nternet for more
information.

T he c hkRunC ontinuous c hec kbox determines how the


applic ation will run. I f it is unc hec ked, the N ext and P revious
buttons are us ed to navigate through the graphic s . When the
us er c lic ks either button, the index to the c ollec tion is inc reas ed
or dec reas ed, and the Picture property of the image c ontrol is
updated to the partic ular c ollec tion item.

I f the us er c hec ks the c hkRunC ontinuous c hec kbox, the N ext


and P revious buttons are dis abled, and the s lides how runs on its
own. T he time that elaps es between eac h graphic is bas ed on
the Timer Interval. T he s lides how will run c ontinuous ly until the
us er c lic ks the Stop button. When the Stop button is c lic ked, it
s ets the stop_show variable to true. T his c aus es the Timer event
to s kip updating the graphic bec aus e the update oc c urs only
when stop_show is false.

4.7.4. Hacking the Hack

You c an enhanc e this bas ic s lides how in s everal ways . O ne thing


to c ons ider is that the graphic s have no s upporting text. Keeping
an unbound approac h, one way around this problem is to provide
a way to dis play details about a graphic when it is c lic ked. T he
image c ontrol has a Click event, whic h you c an us e to return the
value of the Picture property, as s hown in Figure 4 - 2 3 .

Figure 4-23. Taking advantage of the image


control's Click event
Bec aus e the pic ture being dis played c an provide its name, you
c an us e the image c ontrol's Click event to find and dis play
anything pertaining to the partic ular graphic . For example,
bec aus e the graphic 's filename c an be is olated, you c an gather
additional information about the graphic from a text file or other
s ourc e.

O f c ours e, another approac h is to s imply bind the image c ontrol


to paths s tored in a table. A nother field c an then s upply textual
information about the pic ture in a text box. I n fac t, this is the
more s tandard approac h. I didn't point this out earlier bec aus e it
requires more work to update the paths in table rec ords than jus t
onc e in the c ode. A ls o, if you adapt the c ode to work with a us er-
s upplied path, of c ours e the unbound approac h makes more
s ens e bec aus e there would be no preentered paths .
Hack 34. Play Videos in Access Forms

Deliver your message the multimedia way with the Windows


Media Player.

H ere's a really neat way to s pic e up your A c c es s applic ations :


play movies ! A lthough this might s eem a little too entertaining
for "real" bus ines s us e, c ons ider that movies are one of the bes t
vehic les for delivering information. You c an inc orporate movies
into your databas e des ign in s everal ways . You c an relate movie
c lips to data rec ords , in whic h c as e the as s oc iated movie runs
when a rec ord is dis played. You c an als o have an unrelated
movie play on demand (requiring a button c lic k or s ome other
way to initiate the movie to play).

To play movies you need to inc orporate a c ontrol that c an handle


movie files . A number of thes e are available, mos t notably
Windows M edia P layer, whic h is what this hac k us es .

4.8.1. Putting the Player on the Form

Firs t, you mus t add Windows M edia P layer to the form. Bec aus e
this is n't a s tandard c ontrol, you mus t ac c es s it us ing the M ore
C ontrols button on the toolbox, as s hown in Figure 4 - 2 4 .

C lic king the M ore C ontrols button dis plays a lengthy lis t of
c ontrols and libraries . Sc roll down to find Windows M edia P layer,
as s hown in Figure 4 - 2 5 .

A fter you c lic k the c ontrol in the lis t, draw it on the form. Figure
4 - 2 6 s hows a form in whic h Windows M edia P layer, a lis tbox, and
a c ommand button have been ins erted. I n this c onfiguration, the
lis tbox dis plays a lis t of movies from whic h to s elec t; c lic king
the button plays the s elec ted movie.

I n Figure 4 - 2 6 , the lis tbox is populated with paths to .mpg


movie files . T he lis tbox has two c olumns . T he firs t c olumn is the
bound c olumn, whic h holds the paths to the movie files . I ts width
is s et to zero, s o it is n't dis played to the us er. I ns tead, the
s ec ond c olumn, whic h c ontains friendly names for the movies , is
dis played. When the us er has s elec ted a movie, s he s imply
pres s es the c ommand button to s tart the movie. T his effec tively
is a s imple playlis t. Figure 4 - 2 7 s hows the form in V iew mode
before playing a movie.

Figure 4-24. Looking for more controls


Figure 4-25. Selecting Windows Media Player
4.8.2. Playing a Movie

So, jus t how does a movie play? A c tually, it's quite s imple: the
path to a movie file is handed to Windows M edia P layer's URL
property and the movie s tarts playing automatic ally. T his
example s hows the button's c ode; it takes the path from the
lis tbox and hands it to the player:

Private Sub cmdPlayMovie_Click()


If Not IsNull(Me.listMovies) Then
Me.WMPlayer.URL = Me.listMovies

Figure 4-26. Form design with Windows Media


Player
Figure 4-27. Selecting a movie
Else
MsgBox "First select a movie"
End If
End Sub

Starting, s topping, fas t- forwarding, rewinding, and paus ing are


func tions built into the player. T hes e func tions are available
through the buttons on the player its elf. T his allows the us er to
work the movie in any needed fas hion.

Windows M edia P layer has many events you c an hook into. A


little thought and c reativity will go a long way toward integrating
movies into your applic ations . T his hac k s hows the bas ic way to
implement a movie, but you c an c ode around and work with the
player in myriad ways .

4.8.3. See Also

Windows M edia Support C enter


(http://s upport.mic ros oft.c om/default. as px? s c id=fh;en-
us ;wmp)
Hack 35. View Reports Embedded in
Forms

Preview reports, whether current or historical, directly on the


f orm you are working on.

A c c es s is one of the mos t powerful reporting tools on the


market. Beginning with A c c es s 9 7 , M ic ros oft introduc ed the
ability to c reate s naps hot reports that you c an view with the free
Snaps hot V iewer, available for download from M ic ros oft (s earc h
for A c tiveX Snaps hot V iewer at http://www.
mic ros oft.c om/downloads ).

A c c es s databas e applic ation developers c an us e the A c tiveX


Snaps hot V iewer to c us tomize the look and feel of their
applic ations by dis playing reports embedded in forms .

4.9.1. Creating the Form

T he form is c ompos ed of a c ombo box and the A c tiveX Snaps hot


V iewer. T he c ombo box c ontains a lis t of all the reports in the
databas e. When the form opens , the Load event exec utes the
following c ode to fill the c ombo box with a lis ting of all available
reports :

Private Sub Form_Load( )


Dim obj As AccessObject, dbs As Object
Dim strList As String
Set dbs = Application.CurrentProject
For Each obj In dbs.AllReports
strList = strList & obj.Name & ";"
Next obj
cboReports.RowSourceType = "Value List"
cboReports.RowSource = strList
End Sub

To add the A c tiveX Snaps hot V iewer, s elec t M ore C ontrols from
the toolbox, as s hown in Figure 4 - 2 8 , s c roll down, and s elec t
Snaps hot V iewer C ontrol 1 1 .0 . N ote that depending on your
vers ion of A c c es s , your c ontrol might be earlier than the 1 1 .0
vers ion.

Figure 4-28. Adding the ActiveX Snapshot


Viewer
Size the Snaps hot V iewer C ontrol to an approximate width that
matc hes the s ize of the paper on whic h you will print the report.
T his helps avoid having to s c roll left and right to s ee a report
onc e it is dis played. Figure 4 - 2 9 s hows the form des ign with the
Snaps hot V iewer C ontrol in plac e.

Figure 4-29. Sizing the snapshot viewer


A fter adding the Snaps hot V iewer C ontrol, plac e the following
c ode in the On Change event of the c ombo box. M ake s ure the
name of the Snaps hot V iewer on your form matc hes the name in
the c ode:

Private Sub cboReports_Change()


DoCmd.OutputTo acOutputReport, cboReports,
Application.Curren
SnapshotViewer1.SnapshotPath = _

Application.CurrentProjec
End Sub

I n this example, a temporary s naps hot report is c reated, c alled


temp.s np, and it is plac ed in the direc tory in whic h the databas e
is running. T he temp.s np s naps hot report is then loaded into the
Snaps hot V iewer. T his temp.s np file is replac ed eac h time a new
s elec tion is made. I f you are running from a s hared loc ation with
a multius er databas e, make s ure you s tore the temporary
s naps hot file on the loc al mac hine, not on the network; this
avoids any multius er is s ues .

A s s hown in Figure 4 - 3 0 , the final form dis plays an invoic e


report that was s elec ted from a c ombo box.

Figure 4-30. Displaying a report


T his hac k gives the applic ation one plac e for us ers to s elec t
reports and view them before printing. A ls o note that the
A c tiveX Snaps hot V iewer inc ludes a P rint button next to the
navigation buttons .
4.9.2. Hacking the Hack

A n advantage to s aving reports as s naps hots is they are s tatic ,


as oppos ed to the dynamic reports you get with report objec ts in
A c c es s . You c an s ave thos e reports in a folder and view them as
his toric al reports with the Snaps hot V iewer. You als o c an c hange
the c ode to have a c ombo box that dis plays the report names
from a given direc tory, whic h allows the us er to s elec t his toric al
reports to view with the Snaps hot V iewer.

Steve Huff
Hack 36. Put Line Numbers on a Report

Use the Running Sum property to include an incremental counter


in your report.

Sometimes you might want to inc lude line numbers on a report.


T his is fairly eas y to do. T he tric k is to inc lude an unbound text
box, s et its Running Sum property to Over Group or Over All, and
s et its Control Source to the s tarting value.

Figure 4 - 3 1 s hows a report in D es ign mode. T he text box on the


left is unbound and will dis play an inc remental value when the
report runs . T he property s hows how its c ontrol s ourc e is s et to
=1, the beginning value.

Figure 4-31. A text box to display line numbers


Figure 4 - 3 2 s hows how the report looks when run.

T he value plac ed in the Control Source property not only provides


the s eed value, but als o s erves as the inc rement value. When a
value of 1 is us ed, the c ounting s tarts at 1 and inc rements by 1 .
You c an us e other values as well. For example, a value of 10
might be des irable, in whic h c as e the c ounting s tarts at 1 0 and
inc rements by 1 0 , as s hown in Figure 4 - 3 3 .

Figure 4-32. Displaying a report with line


numbers
Figure 4-33. Lines counting by tens
Hack 37. Shade Alternating Lines on a
Report

Go f or the readability f actor. Use alternating shaded lines to


make a more pleasing presentation.

A quic k way to make reports eas ier to read is to s hade every


other line. A lthough no direc t property or method provides this
feature, you c an ac hieve the look with a little planning. To
ac c omplis h this , us e an unbound text box to keep an
inc remental value. A s line items are proc es s ed, the
inc rementing value toggles between even and odd. You c an then
us e this toggle's values to your advantage.

T he bac kground c olor property of the report's details s ec tion is


c hanged, depending on the value of the inc remental running s um.
When the value is odd, one c olor is applied. When the value is
even, another c olor is applied.

You have to s et a few properties for this to work:

I n the report's details s ec tion, an unbound text box is


inc luded. I ts Control Source property is s et to =1.I ts
Visible property is s et to No. Set its name to
txTRunningSum.
Set the Back Style property of the text boxes and labels
to transparent. T his applies to c ontrols in the details
s ec tion only.

4.11.1. The Code

I n the details s ec tion's Format event, plac e this c ode:

Dim even_odd As Integer


Me.Detail.BackColor = vbWhite
even_odd = Me.txtRunningSum Mod 2
If even_odd = 0 Then
Me.Detail.BackColor = vbYellow
End If

You us e the Mod operator to determine whether the c urrent


running s um value is even or odd. Mod returns the remainder of a
divis ion operation. When an even number is divided by 2 , the
remainder is 0 . T he even_odd variable holds the res ult of the Mod
operation.

4.11.2. The Results

T he routine s tarts out by defaulting the bac kground c olor to


white. I f the even_odd variable is n't 0, the bac kground c olor is
c hanged to yellow.

Figure 4 - 3 4 s hows how the report looks when run.


4.11.3. Hacking the Hack

A c ouple of alternatives are available. I f, for example, you have


to s hade every third line, you c an tes t whether the running s um
is a multiple of 3 . A ny multiple of 3 divided by 3 has no
remainder.

A lternatively, you c an us e the RGB func tion to c ontrol the c olor.


RG B is an ac ronym for red, green, blue. T he func tion works by
blending the three c olors , eac h as a number between 0 and 2 5 5 .
L ook up the RGB func tion in the A c c es s H elp s ys tem; it's a great
func tion to get familiar with. To us e it in this hac k, jus t c hange
the BackColor property, like this :

Figure 4-34. A report with alternate row shading


Me.Detail.BackColor = RGB(200, 200, 200)

You will have to experiment with different s ettings , but here's a


guide you c an follow:

Setting all three RGB argument func tions to 0 c reates


blac k.

Setting all three RGB argument func tions to 255 c reates


white.

A ll other c olors are available by applying varying values to the


arguments .
Hack 38. Save Paper by Reducing
Whitespace

Use the Can Shrink property to condense your reports.

E mpty data fields in a long report c an pos e a problem when it


c omes time to print the report. I magine a lis t of 1 ,0 0 0 c ontac ts ,
and only half have a phone number entered into a phone number
field. When you des igned the report, you inc luded the phone
number field for c ontac ts that have a phone number. H owever,
when you print the report, you s ee 5 0 0 empty s pac es
repres enting the phone number fields of c us tomers without
phone numbers . When other data fields are empty as well, the
s ituation jus t gets wors e.

A ll told, this whites pac e c an ac c ount for 5 0 or more extra pages


in the report, depending on how the report is laid out. Figure 4 -
3 5 s hows a report that s uffers from this problem. Some c ontac t
information is mis s ing, yet room is s till s et as ide for it.

Figure 4-35. A waste of paper


Figure 4 - 3 6 s hows the des ign of the report. T he detail s ec tion
c ontains a group of fields . A s s hown in Figure 4 - 3 5 , s ome of the
fields are empty.

Figure 4-36. Setting the Can Shrink property


O n the property s heet, s et the Can Shrink property to Yes. A pply
this to the fields in the detail s ec tion and to the detail s ec tion
its elf.

When you us e the Can Grow or Can Shrink


properties , you mus t apply the s ettings to
both the bound c ontrols and the detail
s ec tion.

With Can Shrink s et to Yes, any empty data fields on this report
won't take up s pac e. Figure 4 - 3 7 s hows the improved report.

Figure 4-37. A more compact report


I n that figure, you c an s ee s ome c ontac ts are mis s ing a phone
number, and others are mis s ing all the data. I n both c as es , the
amount of empty s pac e s hrinks . A s you c an s ee when c omparing
the report in Figure 4 - 3 7 with the one in Figure 4 - 3 5 , even the
firs t page dis plays more c ontac ts . A s was teful whites pac e is
dropped, the number of pages on the report is reduc ed.

T he Can Grow property provides the oppos ite func tionality as well.
When des igning a report, plac e c ontrols where they make the
mos t s ens e. O c c as ionally, you might have more data than you
c an dis play in the field, given the s ize of the bound c ontrol.
Setting Can Grow to Yes lets the field expand as needed to print all
the data.
Hack 39. Include the Date, Time, and
Page Count

Use common expressions to quickly insert necessary header and


f ooter inf ormation.

I t's always helpful to inc lude a times tamp on a report, indic ating
when it was printed. T his might be the only c lue as to whether
the information is up to date. I nc luding page c ounts is als o
important. H aving dozens of printed pages and not knowing the
order in whic h they go c an be quite frus trating.

A c c es s provides an eas y way to inc lude thes e nec es s ary and


s ometimes overlooked items . T he E xpres s ion Builder c ontains a
lis t of c ommon expres s ions . While des igning a report, the bes t
way to dis play the E xpres s ion Builder is to firs t plac e an
unbound text box in the report header or footer (or wherever
makes s ens e) and then c lic k the ellips es (…) next to the c ontrol
s ourc e for the unbound text box. T his opens up the E xpres s ion
Builder, s hown in Figure 4 - 3 8 .

Figure 4-38. Using the Expression Builder to


insert common expressions
T he available c ommon expres s ions inc lude page numbers , date
and time, and c urrent us er. T he P age N of M s etting is
partic ularly us eful bec aus e it not only s tates the page number
but als o provides a mes s age s uc h as "P age 1 5 of 4 0 ." O nc e
you've s elec ted the expres s ion you want, c lic k the O K button to
ins ert the expres s ion into the unbound text box. When the report
runs , the page numbering (or other c ommon expres s ion) is
inc luded on the report.
5. Queries and SQL
Sec tion 5 .1 . H ac ks 4 0 5 4

H ac k 4 0 . Return a Sample of Rec ords

H ac k 4 1 . C reate Bulletproof I ns ert O perations

H ac k 4 2 . Find U nmatc hed Rec ords on M ultiple Field


Keys

H ac k 4 3 . P lac e a G rand Total in a Q uery

H ac k 4 4 . Sort A ny A rbitrary String of C harac ters

H ac k 4 5 . Summarize C omplex D ata

H ac k 4 6 . G et A ll C ombinations of D ata

H ac k 4 7 . D on't L et N ulls Ruin D ata Summaries

H ac k 4 8 . U s e a C us tom Func tion in a Q uery

H ac k 4 9 . C reate A c c es s Tables with SQ L Server


Sc ripts

H ac k 5 0 . U s e Wildc ards in Q ueries

H ac k 5 1 . G et C leaner O r- Bas ed C riteria

H ac k 5 2 . G et C leaner A nd- Bas ed C riteria

H ac k 5 3 . C reate an O uter J oin

H ac k 5 4 . U s e Regular E xpres s ions in A c c es s Q ueries


5.1. Hacks 4054
You c an ac c omplis h a lot with the A c c es s query grid. You c an
ac hieve even more by working direc tly in the SQ L pane. Q ueries
are c ategorized into two types : Select (pas s ive) queries and
Action queries . You us e Select queries to pull data rec ords out of
tables . You us e Action queries to do s omething with or to the
data. You c an run both types of queries in the query grid, but
only up to a point. For example, to us e a Union query you mus t
us e s traight SQ L s tatements . You als o mus t us e the SQ L
language for c ertain s ophis tic ated Action queries .

I n this c hapter, you'll find all s orts of SQ L ac tivities going on,


both in and out of the grid. For ins tanc e, "Return a Sample of
Rec ords " [Hack #40] s hows how to return a s mall s et of rec ords
us ing s ettings in the grid. M eanwhile, "P lac e a G rand Total in a
Q uery" [Hack #43] is ac hieved with SQ L .

A ll in all, thes e hac ks s how how to ac c omplis h s ome c ool and/or


nec es s ary tas ks you might be hard- pres s ed to figure out how to
do on your own. D ec ent knowledge of the SQ L language pays off
in s pades as you work on your projec ts . T he query grid s hields
you from learning SQ L , but after a while, you probably will need
to do things you c an't do us ing the grid. H opefully, the hac ks in
this c hapter will get the brain gears turning and s erve as a
s pringboard to even more s ophis tic ated databas e work.
Hack 40. Return a Sample of Records

Use the Top predicate to return a portion of your records


without bias.

M os t often, you us e a Select query to return all the rec ords that
matc h c ertain c riteria. U s ually, this query returns a data s et that
is s maller than the table or tables upon whic h the query is built.
T hat is , not all rec ords matc h the c riteria, and the number of
rec ords that do matc h is s maller than the underlying s et of table
data.

Sometimes , you might need only a s ample of rec ords that aren't
bas ed on the c riteria or in whic h the c riteria are irrelevant. T his
is n't the s ame as fine- tuning the c riteria to limit the number of
returned rec ords . For example, s tatis tic al work might require a
s ample from whic h to infer fac ts about the whole data s et.
Regardles s of whether the population data is a table or a filtered
data s et already bas ed on s ome c riteria, the point is that the
next s tep of getting a s ample is c ompleted without any
prec onc eived notion. T his is where the SQ L Top predic ate c omes
in handy.

T he Top predic ate works in two ways :

Returns a number of rec ords


Returns a perc entage of rec ords

5.2.1. Using the Top Predicate

T he Top predic ate allows you to is olate rec ords from the top of a
data s et. I f you want to get rec ords from the bottom, firs t apply a
revers e s ort (i.e., des c ending ins tead of as c ending). E ither way,
you will c ontinuous ly get the s ame s et of rec ords eac h time you
run the query. L ater in this hac k, we'll dis c us s a method for
getting a true random s ample.

Figure 5 - 1 s hows a query in D es ign mode in whic h the query


property s heet is us ed to indic ate the number of rec ords to
return. T he Top Values property has a few values from whic h to
s elec t. You c an us e one of thes e, or you c an enter your own.

Figure 5-1. Selecting a value for the Top


predicate
With 25 as the s elec ted c ount to return, the query returns , no
s urpris e, 2 5 rec ords from the top of the data s et, as s hown in
Figure 5 - 2 .

Figure 5-2. Returning the designated number of


records
I t's interes ting to s ee the SQ L the A c c es s query grid generates .
Switc hing to SQ L view, here is what you s ee:

SELECT TOP 25 Occurrences.Reading


FROM Occurrences;

T he Top predic ate s its jus t after the Select s tatement and
s pec ifies the number of rec ords to return.

To return a perc entage of rec ords , s imply add the word Percent to
the SQ L s tatement, after the number:

SELECT TOP 25 PERCENT Occurrences.Reading


FROM Occurrences;

To indic ate perc ent when us ing the query des igner, add the
perc ent s ign (% ) to the Top Values property, as s hown in Figure
5 -3 .

Figure 5-3. Indicating to return a percentage of


records
5.2.2. Hacking the Hack

T he Top predic ate is great for grabbing a handful of rec ords , but it
will always grab the s ame rec ords . E ven when no s ort is plac ed
on the s ourc e data, the rec ords s till s it in the order in whic h they
were plac ed in the table.

Returning a random s et of rec ords requires us ing the Rnd


func tion. You apply this as a s ort. N ormally, a s ort is n't what you
want to us e to return an unbias ed data s et, but s orting on a
random value makes this a moot point. To make this work, alter
the SQ L s tatement to look like this :

SELECT TOP 25 Occurrences.Reading


FROM Occurrences
ORDER BY RND([Reading]);

E nter the name of the field as the argument for the Rnd func tion.
E ac h time the query runs , a random s elec tion of rec ords is
returned.
Hack 41. Create Bulletproof Insert
Operations

Prevent f ailed append operations so that all the records make it


into the table.

You us e the SQ L Insert s tatement to append rec ords to a table.


A lthough this us ually works great, it is prone to is s ues that c an
make it bomb. T his hac k s hows two things you c an do to validate
data before handing it off to an Insert operation. Before we
dis c us s thes e validation methods , let's c reate a s imple table, as
s hown in Figure 5 - 4 .

Figure 5-4. A table that accepts names and ages


T he table has two fields :

Patient

M eant to take jus t the firs t name; the length is s et to


10.

Age

T he age of the patient.

5.3.1. Handling Excessive Text Length

O ne thing that c an trip up an Insert is trying to s tic k data into a


field when the data is longer than the field length. T his is an
is s ue only for text fields . H ere is an Insert s tatement that works
fine:

"Insert Into Patients (Patient, Age) Values ('Gary', 22)"

T he name Gary fits in the P atient text field. N ow, look at this
s tatement:

"Insert Into Patients (Patient, Age) Values ('Bartholemew'


U h- oh. T he name Bartholemew is 1 1 c harac ters long, but the
P atient field c an ac c ept a maximum of only 1 0 c harac ters . T he
eas ies t way to fix s tatements s uc h as thes e is to trunc ate the
text to 1 0 c harac ters by us ing the Left func tion.

H ere is a s ample c ode routine that appends rec ords from the
N ewP atients table to the P atients table. T he Left func tion s its in
the middle of the Insert s tatement and ens ures that no name is
longer than 1 0 c harac ters :

Dim myDB As ADODB.Connection


Set myDB = CurrentProject.Connection
Dim rsNewPatients As ADODB.Recordset
Set rsNewPatients = New ADODB.Recordset
rsNewPatients.Open ("Select * from NewPatients"), myDB
Do Until rsNewPatients.EOF
myDB.Execute ("Insert Into Patients Values ('" & _
Left(rsNewPatients.Fields("Patient"), 10) & _
"', " & rsNewPatients.Fields("Age") & ")")
rsNewPatients.MoveNext
Loop
rsNewPatients.Close
myDB.Close
Set myDB = Nothing

T he Left func tion c uts the s ize of the name to 1 0 c harac ters .
A nother option, of c ours e, is to inc reas e the s ize of the table
field.
5.3.2. Watching Out for Apostrophes

N othing dis rupts an Insert fas ter than the odd apos trophe or
s ingle quotation mark. I t's reas onable to have thes e in your
data; after all, the name O 'Reilly has one. But in a SQ L Insert,
the s ingle quote qualifies text. T herefore, without a little help,
this Insert operation will fail:

"Insert Into Patients (Patient, Age) Values (Left('O'Reill

T he problem is that as the s tatement is exec uted, the s ingle


quote before the letter O s tarts the text and the s ingle quote
after the letter O ends the text. T his leaves the Reilly part of the
name as unidentifiable.

D oubling up s ingle quotes removes the problem, and the way to


do this is to us e the Replace func tion. Replace replac es eac h
ins tanc e of a s ingle quote with two s ingle quotes . H ere is the
previous c ode routine modified to handle s ingle quotes :

Dim myDB As ADODB.Connection


Set myDB = CurrentProject.Connection
Dim rsNewPatients As ADODB.Recordset
Set rsNewPatients = New ADODB.Recordset
rsNewPatients.Open ("Select * from NewPatients"), myDB
Do Until rsNewPatients.EOF
myDB.Execute ("Insert Into Patients Values ('" & _
Replace(rsNewPatients.Fields("Patient"), "'", "''") & _
"', " & rsNewPatients.Fields("Age") & ")")
rsNewPatients.MoveNext
Loop
rsNewPatients.Close
myDB.Close
Set myDB = Nothing

H ere is how to us e the Replace func tion:

Replace(rsNewPatients.Fields("Patient"), "'", "''")

Replace works by tes ting a s tring of text for one or more


c harac ters . I f the s tring is found, it is replac ed with another
s tring of one or more c harac ters . T he three func tion arguments
are:

T he s tring being s earc hed

T he c harac ters being s earc hed for

T he replac ement s tring

A ll data c oming from the N ewP atients P atient field is tes ted for
the s ingle quote, and if it's found, the quote is replac ed with two
s ingle quotes . T his c reates an ac c eptable SQ L s tatement, and
the ins ert c an proc eed.

5.3.3. Combining the Two Validations


I left the bes t for las t. You need to tes t for both exc es s ive length
and apos trophes . C an you tes t for both s imultaneous ly? You
bet! J us t nes t one func tion ins ide the other with the following
c ode:

myDB.Execute ("Insert Into Patients Values ('" & _


Left(Replace(rsNewPatients.Fields("Patient"), "'", "''"), 1
"', " & rsNewPatients.Fields("Age") & ")")
Hack 42. Find Unmatched Records on
Multiple Field Keys

The Find Unmatched Query Wizard looks f or unmatched records


based on a single related f ield. You can adapt this query to work
on more than one related f ield.

T he eas ies t way to find rec ords in one table that has no related
rec ords in another table is to us e A c c es s 's built- in Find
U nmatc hed Q uery Wizard. Figure 5 - 5 s hows the N ew Q uery
dialog box, whic h ac c es s es the wizard.

Figure 5-5. Starting up the Find Unmatched


Query Wizard
A handful of wizard s c reens walk you through s etting up the
query. You s elec t the two tables and even whic h way the query
s hould work. For example, do you need to know whic h rec ords in
Table A have no related rec ords in Table B? O r do you need to
know whic h rec ords in Table B have no related rec ords in Table
A ? E ither way, the key to making this pos s ible is that the tables
are related in the firs t plac e.

Stric tly s peaking, the tables s elec ted to


be in an unmatc hed query don't have to be
formally related, at leas t in regard to
s etting up a relations hip in the
Relations hips window. I t's jus t that the
fields being matc hed s hould be hous ing
the s ame data; otherwis e, all rec ords are
returned as unmatc hed.

Tables c an be related on s ingle field keys or on multiple field


keys . U nfortunately, the wizard lets you s pec ify only a s ingle
field to relate the tables , as s hown in Figure 5 - 6 .

Figure 5-6. Specifying a single field to be


included for the match
Selec t a s ingle field from eac h table, on the left and right, and
then c lic k the button between the two tables to s et the matc h
the query will us e. T he wizard generates a query that is s aved in
the databas e. T his is c onvenient bec aus e it allows you to reus e
the query without having to rec reate it. You als o c an c hange the
query, whic h I 'll des c ribe next.

5.4.1. Reviewing the Query

T he example us ed here finds whic h c us tomers have no matc hing


rec ords in a s ales table. U s ing the Find U nmatc hed Q uery
Wizard, I c an look for c us tomers bas ed on their las t name alone.
Figure 5 - 7 s hows the query des ign the wizard generated.

T he query us es a LEFT JOIN to return all rec ords of c us tomers


whos e las t name field is Null in the s ales table. T he SQ L looks
like this :

SELECT tblCustomers3.FirstName, tblCustomers3.LastName


FROM tblCustomers3 LEFT JOIN tblSales3 ON
tblCustomers3.LastName = tblSales3.LastName
WHERE (((tblSales3.LastName) Is Null));

Figure 5-7. The unmatched query design


T here is a problem here, though. Two c us tomers might have the
s ame las t name. I n that c as e, as long as one of the c us tomers
has a rec ord in the s ales table, any other c us tomers with the
s ame las t name don't appear in the query's res ults , even if they
s hould.

Figure 5 - 8 illus trates this point. L eft to right ac ros s the s c reen
are the c us tomer table, the s ales table, and the query that looks
for c us tomers that have no s ales . Starting on the left, there are
two c us tomers with the s ame las t name: Kam Winter and M uriel
Winter. I n the s ales table, in the middle, M uriel Winter has a
s ales rec ord. I n the query res ult on the right, Kam Winter is not
lis ted as a c us tomer with no s ales , even though Kam s hould be
there.

Bec aus e the las t name is all that is tes ted, all c us tomers with
the s ame name are s kipped in the query res ults , as long as one
of them has a s ales rec ord. T his is n't ac c eptable.

5.4.2. Changing the Query

A ll you need to do is alter the query s o that both the las t name
and the firs t name are tes ted. We do this in the query des ign, in
either the grid or the SQ L pane. Figure 5 - 9 s hows how the query
is des igned now.

I t's important to make s ure a few things are c hanged c orrec tly:

You need to add a c riterion that looks for Null in First


Name.

You need to add a s ec ond relations hip between the


tables , on the new inc luded field. L ook c los ely at the
differenc es in how the tables in the query are related,
c omparing the des ign in Figure 5 - 7 with the des ign in
Figure 5 - 9 .
Figure 5-8. Reviewing tables and the
unmatched query
Figure 5-9. The unmatched query, now
testing on two fields
You s hould unc hec k the fields that c ome from the
s ec ondary table (the s ales table in this example); that
is , they s hould not appear in the output.

Figure 5 - 1 0 s hows how the query returns Kam Winter as being a


c us tomer with no s ales rec ords . Some other c us tomers appear
in the res ult as well.

Figure 5-10. The correct unmatched records


Hack 43. Place a Grand Total in a Query

Use a Union query to combine raw data records with the data
total.

H ere's a neat way to lis t the rec ords in a table and have the total
appear at the bottom. Firs t, c reate a Select query to return the
table rec ords ; then us e the Union s tatement to c ombine them
with the data total. T he Sum aggregate func tion handles returning
the total. T he as s umption, of c ours e, is that the data is numeric .

You need to enter this type of query in the SQ L pane bec aus e
the query grid does n't s upport c reating or dis playing Union
queries . H ere is a s ample SQ L s tatement that c ombines s ales
rec ords with the s um of the s ales :

SELECT tblSales.Amount
FROM tblSales
UNION ALL SELECT Sum(tblSales.Amount) AS SumOfAmount
FROM tblSales;

Figure 5 - 1 1 s hows the bottom of the returned query rec ords .


Sure enough, a grand total is in the las t rec ord.

Figure 5-11. Including the total with the data


5.5.1. Hacking the Hack

You c an eas ily modify this query to return other aggregate


values , s uc h as a c ount or an average. For example, here is the
SQ L from before, but modified to return the average:

SELECT tblSales.Amount
FROM tblSales
UNION ALL SELECT Avg(tblSales.Amount) AS AvgOfAmount
FROM tblSales;
Hack 44. Sort Any Arbitrary String of
Characters

The A ccess query grid is great f or sorting your data, but you
need to help it sort on characters in the middle of a f ield.

I love the query grid. I t's very helpful for doing all s orts of s orts
(pun intended). But did you ever notic e that s orting on text data
always oc c urs on the whole field, going left to right? T his makes
s ens e bec aus e this is the mos t c ommon s orting requirement.
I magine, though, the problem of having to s ort on, s ay, jus t the
fifth c harac ter, or the las t three c harac ters , or in any other way
that is n't the norm.

T his is an is s ue es pec ially when A c c es s is us ed with data that


c omes from other s ys tems . A c c ounting s ys tems are notorious
for this . T hey often lump together a bunc h of dis parate data into
a fixed- width field. H ere's another c las s ic problem: you are
given a lis t of people's names in one field, s truc tured as firs t
name/las t name, but you need to s ort on jus t the las t name.

5.6.1. Sorting in the Middle

Figure 5 - 1 2 s hows a table filled with s ales rec ords . T he rec ords
follow a s tric t format c ompris ing a two- c harac ter vendor c ode
and a s ix- c harac ter date; the remaining digits are the s ales
amount, and the las t two of thos e digits are the dec imal part of
the amount. T herefore, the firs t Sales D ata rec ord
(C T 1 0 2 3 0 4 4 5 9 5 ) breaks down like this :

Vendor c ode is C T.

T he date is O c tober 2 3 , 2 0 0 4 .

T he amount is $ 4 5 .9 5 .

L et's s ay you need to s ort the rec ords by date. A s s hown in


Figure 5 - 1 2 , in eac h rec ord, the date s tarts in pos ition 3 and
takes up s ix plac es .

H ave you ever worked with data s uc h as this ? You need a rec ord
layout to go with the data; otherwis e, you c an't tell what kind of
data it is . What if you had to gues s whic h c harac ters make up
the date? G arbage in, garbage out, as the s aying goes .

T he bes t way to tac kle a problem s uc h as this is to us e the Mid


func tion. Mid is one of the func tions that let you manipulate
textual data. I t works by is olating a part of a larger text s tring.
You have to tell Mid tH Ree things : the s tring of data, the pos ition
you want to s tart from, and how many c harac ters to inc lude. T he
s yntax looks like this :

Mid(string, starting position, length)


E ven though we are c onc eptually working
with dates in this example, the s tored
information is in text format. T herefore, it's
eas y to manipulate the date with s tandard
s tring func tions .

Figure 5 - 1 3 s hows a query des ign in whic h the Mid func tion is
us ed. T he firs t c olumn is the Sales D ata field its elf, and the
s ec ond c olumn is a c alc ulated field us ing the Mid func tion. Within
the func tion, SalesData is enc los ed in brac kets . T his is the
s tandard way to put a field name in a func tion. Mid's parameters
are s et to is olate s ix c harac ters s tarting at pos ition 3 (the date,
in other words ).

Figure 5-12. A vendor code, date, and amount,


combined in one field
When the query runs , the s ec ond c olumn has jus t the date in it
bec aus e Mid does the job of grabbing the c harac ters from
pos itions 3 through 8 . T he s ec ond c olumn rec eives the s ort
bec aus e, after all, the date is what we need to s ort on. So, where
the Sort row and the s ec ond c olumn meet, s et the c hoic e to s ort
in as c ending order by s elec ting A s c ending from the drop- down
menu.

N ote in Figure 5 - 1 3 that the Show c hec kbox for the c alc ulated
field is unc hec ked. You don't have to ac tually dis play the c olumn
when the query is run. I t is us ed jus t to make the s ort happen,
but it does n't nec es s arily have to appear in the res ults .

Figure 5-13. Using the Mid function to isolate the


date for sorting
Figure 5 - 1 4 s hows the res ult of running the query. N ow the s ales
rec ords are s orted by date. T he firs t returned rec ord
(M R0 1 0 4 0 4 7 0 1 1 ) c ontains 0 1 0 4 0 4 , the equivalent of J anuary
4, 2004.

Figure 5-14. Records sorted by date


5.6.2. Sorting on Mixed Fixed Positions

What if you have to s ort on both the date and the amount? What
if the s ort has to s how the date in as c ending order and the
amount in des c ending order? T his is a c ommon requirement: to
s ee money amounts s orted from high to low. C an you do this ?

But of c ours e! I n this c as e, the tec hnique is to have two


c olumns with expres s ions , one eac h for the date and the amount.
Figure 5 - 1 5 s hows how you do this , with the amount s tarting in
the ninth pos ition. T he length parameter for the Mid func tion that
proc es s es the amount is s et to 5 . U s ually, a length is known, but
not always . I n this example, the amounts among the rec ords
might be four or five digits long, s o s etting the length to 5 works
for all rec ords .

Figure 5-15. A query design for sorting on two


subsections of the field
I n this example, as before, only the ac tual Sales D ata field is
s hown when the query runs . T herefore, the s ec ond and third
c olumns both have unc hec ked Show boxes . T he s ec ond and
third c olumns both us e Mid to work on different s ubs trings within
the s ame full SalesData s tring.

N ow the res ult is s lightly different. Figure 5 - 1 6 dis plays the


returned data. C omparing this res ult to the res ult s hown in
Figure 5 - 1 4 , you c an s ee that rec ords 6 through 8 have been
reordered. T hes e rec ords s hare the s ame date of J anuary 1 6 ,
2 0 0 4 (0 1 1 6 0 4 ), but now the amounts are reordered bas ed on
the query s pec ific ation.

Figure 5-16. Sorting on date and amount, which


returns a different order
5.6.3. Sorting on Characters When Their
Position Is Unknown

O ften, you need to manipulate data imported from external


s ys tems before you c an us e it in your applic ation. T his is a
c ommon is s ue with names . Your databas e table might have
s eparate fields for firs t and las t names . T his of c ours e makes it
a no- brainer to s ort on las t name. But imagine the diffic ulty when
you are given full names in one field. What if the names are in the
order of firs t and then las t name, with a s pac e in the middle, and
you need to s ort on the las t name? T he differenc e here,
c ompared to the previous s ales information example, is that you
c an't know, rec ord by rec ord, in whic h pos ition the las t name
s tarts .

T he tric k to s orting by las t name is to firs t determine the


pos ition of the s pac e. I n this c as e, you us e the InStr func tion
with the Mid func tion. I ns tead of hard- c oding the pos ition of the
s pac e, InStr returns the pos ition of the s pac e.

T he InStr func tion tells you the s tarting pos ition of the firs t
oc c urrenc e of a s ubs tring ins ide a larger s tring. I n this example,
the s tring being s earc hed is the C lient field, and the s ubs tring is
a s pac e. By its elf, InStr looks like this :

InStr([Client]," ")

H ere we us e the InStr func tion to tell the Mid func tion the
pos ition from whic h it s hould s tart c ounting. InStr is embedded
ins ide the Mid func tion. Together, they look like this :

Mid([Client],InStr([Client]," ")+1,10)

N ote that although the InStr func tion returns the pos ition of the
s pac e, we are interes ted in the s tarting pos ition of the las t
name. T his is one pos ition to the right of the s pac e, and for this
reas on, 1 is added after the InStr func tion. T he returned value of
InStr plus the value 1 is us ed as the s tarting pos ition parameter
in the Mid func tion.

Figure 5 - 1 7 s hows how to s et up a query us ing thes e nes ted


func tions . T he value of 10 is arbitrarily us ed here as the length of
the las t name. L as t names vary in length, but us ing 1 0
c harac ters to s ort on all but guarantees the s ort will be in the
right order.

Figure 5-17. Using nested functions in a sort


Figure 5 - 1 8 s hows the res ult of the query. C lients are s orted by
las t name, within a s ingle field that c ontains full firs t and las t
names . N eat!
5.6.4. Hacking the Hack

Sorting on names is n't diffic ult when firs t and las t names are all
you have to work with. But what about middle names , titles , and
s uffixes ? H ow c an you handle thes e? L et's up the ante on this
hac k and inc lude a c us tom func tion in the query.

Figure 5-18. Clients sorted by last name


T he func tion we need will examine the names in the C lient field
to determine the pos ition of the s pac e. H ere's the c atc h: now
there c ould be more than one s pac e. M y name is Ken S.
Bluttman; that's two s pac es one on eac h s ide of the middle
initial. Some names have three, four, or even five s pac es . T he
func tion is meant to s imply figure out the bes t s pac e to us e; it
figures out the pos ition of that s pac e and tells the Mid func tion
where it is .

Firs t, you write the func tion in a V BA c ode module. To do this ,


from the databas e window, go to the M odules tab, and s elec t to
c reate a new module. E nter this c ode:

Function find_space(client_name As String)


Dim name_length As Integer
Dim space_loop As Integer
Dim space_count As Integer
Dim partial_name As String
Dim first_space_position As Integer
'count spaces in full name
space_count = 0
name_length = Len(client_name)
For space_loop = 1 To name_length
If Mid(client_name, space_loop, 1) = " " Then
space_count = space_count + 1
End If
Next space_loop
'parse the full name using assumptions in each Case
Select Case space_count
Case 0
'no spaces found!
'return 1 as the position
find_space = 1
Case 1
'a first name and last name
'split after first space
find_space = InStr(client_name, " ")
Case 2, 3
'assume a first name, Middle name, and last name (2 spaces)
'or a first name, Middle name, last name, and suffix (3 space
'split after second space
find_space = InStr(client_name, " ")
first_space_position = find_space
partial_name = _
Mid(client_name, find_space, name_length - find_space)
find_space = InStr(partial_name, " ") + first_space_position
Case Else
'difficult to make assumption on name structure
'split after first space
find_space = InStr(client_name, " ")
End Select
End Function

I n a nuts hell, the func tion takes a c lient name, c ounts how many
s pac es are in it, and then determines whic h s pac e is bes t. T he
pos ition of that s pac e is us ed in the Mid func tion as before.

I n the query grid, the c all to the func tion, named find_space, is
embedded in the Mid func tion, like this :

Mid([Client],find_space([Client])+1,10)
Figure 5 - 1 9 s hows how to s et up the query.

Figure 5-19. The Mid function, using the


find_space function
When the query runs , eac h c lient name is examined in the
find_space func tion. T he func tion returns the bes t s pac e pos ition,
and the names are s orted. Figure 5 - 2 0 s hows the res ults of
running the query.

Figure 5-20. Sorting by last name when middle


names and suffixes are present
L ooking c los ely, you will s ee that the s ort is n't without problems .
T he way the func tion is written, it as s umes that when there are
two s pac es , the format of the name is firs t name, las t name,
s uffix. T his works for a name s uc h as A lex A vakian I I I . T he
func tion as s umes the las t name s tarts after the firs t s pac e.

U nfortunately, a name s uc h as Tammy J ill A dams does n't end up


with the other las t names beginning with A . T he func tion
as s umes the bes t s pac e is the firs t, and the name is s orted as
though the las t name s tarts with J . Tammy's las t name s tarts
after the s ec ond s pac e. Sorry, Tammy!

Splitting names apart is traditionally a thorny problem. Some


names always c onfound the bes t intentions of a name- pars ing
routine. T hat mus t be why I keep getting c atalogs addres s ed to
M r. Ken.

5.6.5. See Also

"U s e a C us tom Func tion in a Q uery" [Hack #48]


Hack 45. Summarize Complex Data

Take advantage of Crosstab queries to get a view on


multif aceted data.

When you need to aggregate data that has more than a s imple
grouping s truc ture, Crosstabs are the way to go. "C reate
C onditional Subtotals " [Hack #29] s hows you how to us e groups
and c onditional s umming on a report. T hat works as long as the
c onditions don't lead to an overwhelming number of pos s ibilities .

T he example in that hac k us es five s tates and two years on


whic h to c reate s ubtotals . T he data model, though, has another
table in play: a table of pets (the model is a s imulation of a
veterinary prac tic e). T his c reates a large number of
pos s ibilities , s uc h as all c at vis its in N ew York in 2 0 0 3 , or all
dog vis its in P enns ylvania in 2 0 0 4 , and s o on. Figure 5 - 2 1
s hows the updated data model for this hac k.

Figure 5-21. The pets data model


T he data model inc ludes s even types of pets (bird, c at, dog,
ferret, hors e, monkey, and s nake), five s tates (C T, M A , N J , N Y,
and P A ), and two years of data (2 0 0 3 and 2 0 0 4 ). T his makes
7 0 pos s ible c ombinations . T he bes t way to s um up the number
of vis its in whic h all thes e c ombinations of c riteria are mixed and
matc hed is to us e a Crosstab query.

To get s tarted, we mus t put together a Select query to join the


different tables and return the fields needed in the Crosstab. N ote
that the Select query has a c alc ulated field that is olates the year
out of the D ateO fServic e field. Figure 5 - 2 2 s hows the des ign of
the Select query.

Figure 5-22. A Select query on which a Crosstab


will run
5.7.1. Introducing the Crosstab

A c c es s has a C ros s tab Q uery Wizard, whic h walks you through


c reating a Crosstab query. Figure 5 - 2 3 s hows the N ew Q uery
dialog box in whic h a Crosstab query is initiated.

Figure 5-23. Starting up the Crosstab Query


Wizard

I n this example, s elec t the qryStatesPetsDates query in the firs t


s c reen of the wizard, as s hown in Figure 5 - 2 4 .

I n the next s c reen, s elec t two fields as the rows . I n a Crosstab


query, the rows ac t as groups . N ote that at leas t two fields mus t
remain after you s elec t fields for the rows . Selec t the s tate and
pet type fields to be the row headings .
Figure 5-24. Selecting the Select query
I n the next s c reen, s elec t a field to be the c olumn field. Crosstabs
require at leas t one c olumn field. C hoos e Year here, as s hown in
Figure 5 - 2 5 (note that this figure s hows the third s c reen, not the
s ec ond).

Figure 5-25. Selecting Year as the column


heading
I n the las t field s elec tion s c reen, one field remains . Selec t the
type of aggregationin this c as e, C ount, as s hown in Figure 5 - 2 6 ,
bec aus e the purpos e is to c ount vis its . A ls o be s ure to unc hec k
the "Yes , inc lude row s ums " c hec kbox on the left. Keeping this
c hec ked returns a field of s ums bas ed on jus t c ombinations of
s tate and pet type (the row headings ) and that is n't our foc us
here; we're looking for the c ombination of s tate, pet type, and
year.

Figure 5-26. Selecting to return a count


When the query c ompletes , all the c ounts are available. Figure
5 - 2 7 s hows how the query pres ents s ums in all c ombinations of
s tate, pet type, and year.

Figure 5-27. The completed Crosstab query


T here are 3 5 rec ords by virtue of the fac t that s tate and pet type
are row headings , and year is a c olumn heading. T his s till
provides the 7 0 unique c ombinations bec aus e the two years ,
2 0 0 3 and 2 0 0 4 , eac h have their own c olumn.

5.7.2. See Also

"C reate C onditional Subtotals " [Hack #29]


Hack 46. Get All Combinations of Data

Remove the Join clause in a SQL statement to return a


Cartesian product (which returns all possible combinations).

L eaving the Join c laus e out of a SQ L s tatement returns a


number of rec ords equal to the produc t of the number of rec ords
in the tables . Taking two tables , for example, as long as one field
from either table is des ignated for output, the number of returned
rec ords in a Select query of this des ign is the produc t of the
c ounts of the two tables .

Behind the s c enes , the query is matc hing all c ombinations of the
data. I f eac h table has hundreds or thous ands of rec ords , the
returned number of rec ords c an be in the millions . T his c an be
dis as trous that is , unles s returning rec ords in this way is by
des ign. Why would you do this ? I t makes s ens e to do it to
explic itly return all the c ombinations . I f you need s uc h all-
inc lus ive matc hing, you don't have to bother with any V BA c ode;
jus t c reate a query that does it for you. Figure 5 - 2 8 s hows a
table with 1 2 people and another table with eight pos s ible
ac tivities .

Figure 5-28. Two unrelated tables


C reate a Select query with the two tables , and des ignate the
s ingle field from eac h table for output. Figure 5 - 2 9 s hows the
query des ign. N ote that the lac k of a relation line between the
tables is intentional.

Figure 5-29. A Select query of unrelated tables

A little tip- tap on a c alc ulator s hows 9 6 c ombinations of pers on


and ac tivity. Running the query returns the 9 6 rec ords , as s hown
in Figure 5 - 3 0 .

Figure 5-30. Returning the combined records


T he query res ults c an be c opied, exported, and s o on. T his is a
fas t and eas y way to get all c ombinations of data. G oing one
s tep further, a third table is added to the query. T his new table
c ontains parts of the day, in two rec ords : morning and afternoon.
Running the query returns the expec ted 1 9 2 rec ords , whic h is
the produc t of 1 2 x 8 x 2 . Figure 5 - 3 1 s hows the res ult.

Figure 5-31. Returning combinations on three


unrelated tables
A lthough it is n't effic ient to handle data in this unrelated way, at
leas t with regard to databas e work, a s et of c ombinations s uc h
as this makes for us eful reports , c hec klis ts , and s o on.
Hack 47. Don't Let Nulls Ruin Data
Summaries

When nulls are mixed in with valid data, incorrect results can
occur. Here are some guidelines to tame the beast.

When you are dealing with values in A c c es s , you might be


tempted to think that a blank field is s imply a blank field.
H owever, there is a differenc e between a blank field that is filled
in with an empty s tring and a blank field that is null. For example,
when you are looking at number fields , there is a differenc e
between a field with a 0 value and a field with a null value. T his
hac k helps you work with thes e nonvalue values .

T he firs t frus trating thing about nulls is that if you write a line
s uc h as this , every line will s how up as Not Blank, even if you
have null values :

IIF([Amount]=Null,"Blank","Not Blank")

T his oc c urs bec aus e in a Boolean expres s ion, any item


c ompared to Null returns False.

T here is an eas y way to deal with this , us ing a func tion available
in A c c es s c alled ISNULL. T his func tion returns a Boolean and
allows you to perform your tes t. H ere is how to rewrite the
previous example:
IIF(ISNULL([Amount],"Blank","Not Blank")

T hat c linc hes it. N ow, any enc ountered null is c onverted to
Blank.

5.9.1. Nulls in Number Fields

L et's as s ume you have a table with a field c alled A mount. You
are trying to determine the average of that field (as s ume als o
that the average does n't need to be weighted). I f you write a
query that attempts to determine the average value, the SQ L
might look like this :

SELECT Avg(tbl_Amount.Amount) AS AvgOfAmount


FROM tbl_Amount;

T his gives you the average amount of the values in that field.
H owever, if you have nulls for any of the values , the query will
ignore them. So, if your values are 8, null, 8, null, 8, null, the
average is 8. I f your values are 8, 0, 8, 0, 8, 0, the average is
4. D epending on the purpos e of the query, you might want to s ee
4 ins tead of 8.

I f you want to s ubs titute 0 for null, you c an try to do it with the
ISNULL func tion by writing a line s uc h as this :

IIF(ISNULL([Amount]),0,[Amount])
T here is a muc h eas ier way, though. T he NZ func tion available in
A c c es s requires two parameters : one for the value and the other
for the value if it is null. You c an us e this for both number and
s tring func tions . H ere is what the SQ L of the query looks like
us ing the NZ func tion:

SELECT Avg(NZ([Amount],0)) AS AverageofAmount


FROM tbl_Amount;

A s you c an s ee, this is more c ompac t than writing out IIF


s tatements to perform the s ame func tion.

N ext, let's look at an example of a s tring func tion. A s s ume you


live in an area where pine trees are popular, and you have a
s urvey in whic h you input the type of tree only if it is s omething
other than pine; otherwis e, you jus t input the number of trees
(bad des ign, but I 've s een wors e) and leave the tree type field
null.

N ow, as s ume that you have inherited this applic ation, and you
want to us e it in other areas of the c ountry. You want to update
all the null Tree_Type fields with P ine Tree. You c an do s o with
the NZ func tion. H ere is what the SQ L for this query looks like:

UPDATE tbl_TreeTypes SET tbl_TreeTypes.Tree_Type =


nz([Tree_Type],"Pine Tree");

T his will work, but you have to update every rec ord. So, if you
c an't us e tree_Type = Null, you might as k if you c an us e null for
c riteria in a query. You c an, us ing one of two methods . T he
eas ies t way is to us e IS NULL for the c riteria. T he previous query
looks like this us ing IS NULL:

UPDATE tbl_TreeTypes SET tbl_TreeTypes.Tree_Type = "Pine T


WHERE (((tbl_TreeTypes.Tree_Type) Is Null));.

5.9.2. Preventing Nulls

I t might be nec es s ary for you to prevent nulls and zero- length
s trings in your databas e in the firs t plac e. A good example for
this might be a name field or a ZI P c ode field. You c an do this
through either your table des ign or your data entry forms .

5.9.2.1 Table design to prevent nulls and


zero-length strings.

When you des ign your table, you c an s et s everal properties to


help you handle blank fields , as s hown in Figure 5 - 3 2 .

Figure 5-32. Setting field properties to control


nulls and zero-length strings
T he firs t is a property c alled Required. I f you enter Yes for the
Required property, you are telling A c c es s a value mus t be
entered in this field for it to be s aved. T his won't prevent
s omeone filling it with a zero- length s tring. Setting the Allow Zero
Length property to No forc es an entry other than a zero- length
s tring. I f you s ay Yes, and you jus t want to eliminate nulls (tes t
for blank by writing [Field]=""), you c an s et the Default Value
property to "".

I f you s et thes e two properties c orrec tly, you will have a value in
eac h field, and you won't have to deal with nulls in your
applic ation. T he s ame thing applies to number fields : there is a
Required property you c an s et to Yes, and there is als o a Default
Value property. N ormally, the Default Value property is s et to 0 in
a number field. H owever, if you want to ens ure that us ers enter a
value in this field and don't s imply s kip over it, you c an remove
the 0 in the default value field and s et the Required property to
Yes. T his ens ures that the rec ord is n't s aved until the us er puts
a value in the field (0 c an be entered unles s you have a
validation rule in plac e).

5.9.2.2 Form design to prevent nulls and


zero-length strings.

I f you don't have c ontrol over the table des ign, but you want to
ens ure the data entered is ac c urate, you c an do s o through
A c c es s forms . When you c reate a form in A c c es s , s everal
textbox properties are available that c an help you ens ure
meaningful data, as s hown in Figure 5 - 3 3 .

Figure 5-33. Controlling nulls through form


control properties

You c an s et the Default Value property to allow a zero- length


s tring if all you want is avoid a null.

You c an als o write c ode in the Lost Focus event. I t is important to


do this in the Lost Focus event bec aus e the Before Update event
won't fire if the field is jus t tabbed through, and the After Update
event fires after the field has ac tually been c hanged. H ere is
what that c ode might look like for a text box c alled TextBox1 :

Private Sub TextBox1_LostFocus( )


If IsNull(Me.TextBox1.Value) Then
MsgBox "You must enter a value in this field", vbOKOnly, "Im
Me.TextBox2.SetFocus
Me.TextBox1.SetFocus
End If
End Sub

You might be wondering why the Set Focus event is c alled twic e.
You mus t s et the foc us off of the text box and then bac k onto it;
otherwis e, it won't let you s et the foc us to the box. You might
als o be wondering why the c ode does n't us e the Validation Rule
property. T he validation rule run onlys when the field is c hanged,
s o if you s imply s kip a field, it won't run.

T here is a limitation to us ing the Lost Focus event if a us er us es


th mous e and does n't c lic k eac h field. You c an get around this
limitation by s etting the Cycle property on the O ther tab of the
Form P roperties dialog box to Current Record (as s hown in Figure
5 - 3 4 ) and then s etting the Navigation Buttons property to No on
the Format tab of the s ame dialog box (as s hown in Figure 5 - 3 5 ).

Figure 5-34. Setting the Cycle property to


Current Record
Figure 5-35. Setting the Navigation Buttons
property to No
O nc e you have done this , you c an c reate your own buttons to
allow us ers to move to the next rec ord, and you c an put your
validation text in there. I n all c as es , it is muc h eas ier to as s ign
thes e s ettings during table des ign, but many times you don't
have that c ontrol.

Michael Schmalz
Hack 48. Use a Custom Function in a
Query

Write a custom f unction to manipulate multiple data f ormats.

When you need to perform c omplex manipulation of data in a


query, it is often eas ier to write a func tion to perform the
manipulation. You c an avoid us ing c omplex func tions ins ide a
query and always write a us er func tion. H owever, it is bes t to us e
your judgment. I f you have a rather s imple c onc atenation of a
few fields , I s ugges t you write a us er func tion within your query.
But if you need to perform s omething c omplex and it is likely
that you will need to do it in other plac es in the applic ation,
c reating a new func tion will s ave you a lot of time.

5.10.1. Creating a New Function

To c reate a func tion, go to the M odules tab in A c c es s , and


c reate a new module. O nc e you are in the new module (you c an
als o go into D es ign view in an exis ting module), s elec t I ns ert
P roc edure. G ive it a name, s elec t Func tion as the Type,
and s elec t P ublic as the Sc ope. O nc e you have your func tion,
you c an plac e variables between the parenthes es . A fter the
parenthes es , give your func tion a type by typing A s datatype;
this ens ures that your func tion is returned in the datatype that
you expec t.
5.10.2. Manipulating Dates

D ates c ome from different s ys tems in many different formats ,


inc luding Y Y Y Y M M D D , M M /D D /Y Y Y Y, and M M D D Y Y Y Y. T he
problem c omes when you need to have the date in another
format, as happens when you import data from a mainframe or a
fixed- length text file in whic h the date is ac tually imported as
text. T his firs t example as s umes the format being imported is
Y Y Y Y M M D D or Y Y M M D D . I n this func tion, the s tring is brought
in as an argument and the Left, Right, and Mid func tions are
us ed with the CDate func tion to c reate the date:

Public Function GetDate(Dt As Variant) As Date


Dim MM As String
Dim DD As String
Dim YYYY As String
If VBA.InStr(1, Dt, "/") > 0 Then Dt = ""
Select Case VBA.Len(Access.Nz(Dt, ""))
Case 8
YYYY = VBA.Left(Dt, 4)
MM = VBA.Mid(Dt, 5, 2)
DD = VBA.Right(Dt, 2)
GetDate = VBA.CDate(MM & "/" & DD & "/" & YYYY)
Case6
YYYY = VBA.Left(Dt, 2)
MM = VBA.Mid(Dt, 3, 2)
DD = Right(Dt, 2)
GetDate = VBA.CDate(MM & "/" & DD & "/" & YYYY)
Case Else
GetDate = #1/1/1900#
End Select
End Function
N otic e that this func tion pas s es the s tring as a variant; this
allows it to tes t for a null value or trap an ac tual date. I f the
variable is dec lared as a s tring, a null value res ults in an error.
I n this c as e, if a real date, null value, or anything other than
Y Y M M D D or Y Y Y Y M M D D is pas s ed, it returns a date of
1 /1 /1 9 0 0 . You c an s et that date to be s omething els e. T he If
InStr … Then line tes ts to s ee if a s las h (/) is in the Dt variable. I f
it is , the proc edure s ets Dt to an empty s tring.

N ow, let's as s ume you need to take it the other way. A s s ume
you have a date field, and you need to turn it into a fixed- length
s tring. H ere is how the required func tion looks :

Public Function GetDateString(Dt As Date) As String


Dim MM As String
Dim DD As String
Dim YYYY As String

MM = VBA.Right(DatePart("m", Dt) + 100, 2)


DD = VBA.Right(DatePart("d", Dt) + 100, 2)
YYYY = VBA.DatePart("yyyy", Dt)

GetDateString = YYYY & MM & DD

End Function

T his func tion pas s es the variable as a date. I f a null or nondate


is pas s ed to the func tion, it returns #Error. T his res ult s imply
s hows how the func tion reac ts when an inappropriate date is
pas s ed to it.
A ls o notic e that the func tion us es the Right func tion along with
DatePart and then adds 100 to it. T his ens ures that the month and
date return two digits . I f you didn't do this , it might work on your
c omputer if you have dates s et with leading zeros , but it c ould
bomb on another c omputer. You c an als o us e this logic anytime
you need to put leading zeros in for a number. I f you need five
leading zeros , type right(x+100000,5).

Your next ques tion might be, "H ow do I us e thes e func tions ? "
You c an c all thes e func tions from within a query, jus t like you
would any other func tion. You c an als o us e them in forms ,
reports , and s o on. L et's as s ume you have a table c alled
tbl_P ers onalI nformation, and you have a field c alled H ireD ate
that is a date type. I f you need to have a field in a query that
formats the date as Y Y Y Y M M D D , write it in the query's D es ign
view, like this :

TextHireDate: GetDateString([HireDate])

T hat is all you need to do. T he date will be c onverted to a s tring


in the format that you need. I t is important to note that you
c ould do the s ame thing the func tion does right in the query.
H owever, by doing it that way, you have no way of eas ily reus ing
the logic els ewhere in the applic ation.

Michael Schmalz
Hack 49. Create Access Tables with SQL
Server Scripts

SQL Server writes scripts that create tables. With a little


editing, you can put them to work in A ccess.

So muc h attention is given to ups izing from A c c es s to SQ L


Server. T his makes s ens e bec aus e, after all, databas es tend to
grow, not s hrink. H owever, this hac k is n't c onc erned with data; it
has to do with des ign. E very s o often you might need to
duplic ate a SQ L Server s c hema in A c c es s . T his c ould be for the
very purpos e of preparing your A c c es s databas e for SQ L Server.

I f you are familiar with SQ L Server, you already know SQ L Server


E nterpris e M anager c an write SQ L create table s c ripts bas ed on
exis ting tables . I f this is all new to you, c ome along for the ride!

5.11.1. Walking Through Enterprise


Manager

E nterpris e M anager, s hown in Figure 5 - 3 6 , is the utility you us e


to manage SQ L Server.

T he P ets databas e is ac c es s ed in the left pane. T he databas e


c ontains various objec ts . T he tables of the P ets databas e are
lis ted in the right pane. M os t of the tables are s ys tem tables .
T he las t three tables tblA ppointments , tblC lients , and tblP ets are
us er tables . T hat means I c reated them; this is the s ame
paradigm we us e in A c c es s .

To generate a SQ L s c ript, right- c lic k the tblC lients table, and


s elec t A ll Tas ks G enerate SQ L Sc ript…, as s hown in Figure
5 - 3 7 . A fter you s elec t a des tination for the s c ript, a file is
c reated.

A text file is written with SQ L Servers pec ific SQ L s tatements .


Figure 5 - 3 8 s hows the generated s c ript opened in N otepad.

Figure 5-36. Exploring Enterprise Manager


Figure 5-37. Preparing to generate a SQL script
A s is , this s c ript won't work if it's run ins ide an A c c es s query.
T he pertinent part is in the middle, s tarting with the Create Table
s tatement. Create Table is rec ognizable SQ L in A c c es s . E ven s o,
the field types aren't c orrec t in A c c es s , s o we s till have to c lean
this up. Knowing what to do requires a little SQ L knowledge, but
if you haven't learned any yet, it's not a bad thing to get to know.

Figure 5-38. The generated script


P lenty of books are available on the
s ubjec t of SQ L . See the end of this hac k
for a s hort lis t.

O nc e you've c leaned up the SQ L , and it's ready for A c c es s , you


need to c all up a Data Definition query. Figure 5 - 3 9 s hows where
to ac c es s this s pec ial type of query in A c c es s .

Figure 5-39. Creating a Data Definition query


O nc e you s elec t the query type, you are left in a pane in whic h
SQ L is entered. Figure 5 - 4 0 s hows the pane with the edited SQ L
s c ript. N ow it is ready to run in A c c es s .

Sure enough, when this query runs , it c reates a new tblC lients
table. So, es s entially, not only is it pos s ible to rec reate a SQ L
s c hema in A c c es s , but you als o c an edit a SQ L Servergenerated
s c ript to get the job done.

Figure 5-40. A SQL script, ready to run in Access

5.11.2. See Also


SQL Pocket Guide (O 'Reilly)

SQL in a Nuts hell (O 'Reilly)


Hack 50. Use Wildcards in Queries

The Like operator comes in handy when you don't quite


remember how to spell a data item.

When you c an remember only a partial amount of information, a


great way to s earc h through your databas e rec ords is to us e the
SQ L Like operator. C ombining Like with wildc ards makes for
s ome powerful queries .

For example, imagine you have a databas e table filled with


c us tomer rec ords . You need to look up s omeone whos e las t
name s tarts with D e, and that's all you c an rec all about her
name.

Figure 5 - 4 1 s hows a query that us es the as teris k (* ) wildc ard to


find all c us tomers whos e las t name s tarts with D e. T he as teris k
is a plac eholder for any number of c harac ters . T herefore, running
this query returns all c us tomers with D e as the firs t two
c harac ters in their las t name.

L et's s ay ins tead that you remember the las t name s tarts with D
and is four c harac ters long. I n this c as e, the ques tion mark (?)
wildc ard c omes in handy. You us e the ques tion mark as a
plac eholder to repres ent a s ingle c harac ter. Figure 5 - 4 2 s hows
three ques tion marks being us ed to make up for three s pac es
after the letter D .

Figure 5 - 4 3 s hows the res ult of running the query. A ll c us tomers


with four- c harac ter las t names that s tart with D are returned.
U s ing wildc ards lets you really get to your data in c reative ways .
You might rec all the firs t and las t letter of a name, or even jus t
that a name is four c harac ters long, but you don't remember any
of the ac tual c harac ters ! I n s uc h as c as e, us ing a c riterion of
Like "????" returns all c us tomers with las t names that are four
c harac ters long, as s hown in Figure 5 - 4 4 .

Figure 5-41. Finding customers with an asterisk


wildcard
Figure 5-42. Finding customers using question
marks
Figure 5-43. Returning records based on the
wildcards

Figure 5-44. Returning all customers with a last


name four characters long
Hack 51. Get Cleaner Or-Based Criteria

A void using multiple rows in the query grid by using the In


operator.

T he A c c es s query grid is des igned for eas y query as s embly, and


it does a great job. Without a doubt, the grid has been an
es s ential learning tool that helps us unders tand and us e queries .

You c an us e the grid to c reate Or- bas ed c riteria in two ways .


Figure 5 - 4 5 s hows a typic al way to s et up a query. I n this c as e,
the query returns rec ords in whic h the s tate is any of s ix
pos s ible values . A s you c an s ee, however, if a few more s tates
were to be inc luded, it would bec ome nec es s ary to s tart s c rolling
vertic ally to work on the lis t of s tates .

Figure 5-45. Creating Or-based criteria


Figure 5 - 4 6 s hows an alternate way to s et up the Or c riteria. T he
s pec ified s tates are put on one row, with Or s tatements
throughout. H owever, this des ign als o s uffers from bec oming
unwieldy if more s tates are added. E ac h additional s tate being
added als o requires another Or operator, s o the expres s ion c an
bec ome quite long.

Figure 5-46. A long criteria statement


T he In operator is the s olution to this dilemma. T he In operator
is perfec t for es tablis hing Or- bas ed c riteria. Whereas in Figure
5 - 4 6 the inc lus ion of eac h s tate requires another Or operator,
only one In operator is nec es s ary, as s hown in Figure 5 - 4 7 .

Figure 5-47. Using the In operator


U s ing the In operator makes it eas y to add more s tates to the
c riteria. J us t make s ure you s eparate eac h s tate abbreviation
with a c omma.
Hack 52. Get Cleaner And-Based Criteria

Remove the need f or multiple A nd statements by combining the


In and Not operators.

Sometimes , c riteria are s et up to filter out c ertain rec ords


ins tead of inc luding them. T his revers al of logic makes s ens e in
s ituations in whic h you want to return mos t of the rec ords , but
not all of them. "G et C leaner O r- Bas ed C riteria" [Hack #51]
s hows how to us e the In operator to better manage Or bas ed
c riteria. When you s et up c riteria to be exc luded, however, us e
the And operator. For example, you might as k, "G ive me all
s tates , exc ept C alifornia and N ew M exic o."

Figure 5 - 4 8 s hows a query des ign that exc ludes s ix s tates from
the query res ults . A s new s tates are added to this lis t, an
additional And operator is required. E ventually, this method of
writing multiple Andoperators bec omes tires ome and you end up
having to s c roll to read through it all.

Figure 5-48. Using multiple And operators to


filter out records
T he In operator might c ome to mind as a way to reduc e the long
c riteria s tatement. H owever, the point is to not inc lude the
c riteria. T he s olution is to us e both the In and the Not operators .
Not is a logic al operator: it revers es a c ondition. I nc luding it with
an In operator res ults in a lis t of items not to inc lude, whic h
works perfec tly for this type of query.

Figure 5 - 4 9 s hows the improved query, in whic h the multiple And


s tatements are removed.

T he query returns the s ame res ults , with a les s - c luttered SQ L


s tatement.

Figure 5-49. Using Not and In together


Hack 53. Create an Outer Join

A ccess doesn't support making an outer join; here's a


workaround.

T he s tandard join between two tables returns rec ords that matc h
bas ed on the field or fields being s elec ted as keys . T his is c alled
an inner join. For example, a s tatement s uc h as "give me all
c us tomers and their s ales rec ords " us ually is interpreted to
mean return all the s ales rec ords and the c us tomers to whom
thos e rec ords belong.

Sometimes , though, a left or right join is reques ted. For example,


"give me all our c us tomers and any s ales they might have had"
is really a reques t for a left join. I n other words , return all the
rec ords from the left table (the c us tomers ) and any s ales
rec ords that go with them.

Figure 5 - 5 0 s hows how rec ords returned from a left join query
look. I n this example, there are more c us tomers than purc has e
date rec ords . Some of the c us tomers have no purc has es and
therefore have no data in the c olumn on the right.

A right join returns all rec ords from the table on the right and
only thos e rec ords from the table on the left that matc h on the
key. T he three types of joins inner, left, and rightare eas y to s et
up. T he inner one is the default when two tables are related.

You c an s et the join type in either the Relations hips window or in


a query des ign by double- c lic king direc tly on the line between
the two tables . Figure 5 - 5 1 s hows the J oin P roperties dialog box
that appears when the line is double- c lic ked. T he dialog
c ontains options for the three join types .

Figure 5-50. Returned records from a left join


query
Figure 5-51. Setting join properties
A s you c an s ee, there is no option to c reate an outer join, whic h
would return all the rec ords that matc h, plus the rec ords that
don't matc h from both tables . T he tric k to doing this is to s imply
as s emble the three types of available join queries into one
query. T his final query us es the Union operator to as s emble the
res ults from the other three queries .

A Union query works only with s traight SQ L s tatements . You c an


enter the SQ L direc tly into a new query or, to make it eas ier,
c opy the generated SQ L from the three join types and pas te it
into a new query. A ll you need to do is s tart the s ec ond and third
mini- SQ L Select s tatements with the Union operator in a new
query, like this :

SELECT tblCustomer2.LastName, tblSales2.PurchaseDate


FROM tblCustomer2 INNER JOIN tblSales2 ON
tblCustomer2.CustomerID = tblSales2.Customer_ID
Union
SELECT tblCustomer2.LastName, tblSales2.PurchaseDate
FROM tblCustomer2 LEFT JOIN tblSales2 On
tblCustomer2.CustomerID = tblSales2.Customer_ID
Union
SELECT tblCustomer2.LastName, tblSales2.PurchaseDate
FROM tblCustomer2 RIGHT JOIN tblSales2 ON
tblCustomer2.CustomerID = tblSales2.Customer_ID;

Figure 5 - 5 2 s hows the res ult of running the SQ L .

Figure 5-52. The results of an outer join


Both c olumns (eac h c omes from a different table) have
blanks where there was no matc hing rec ord in the other tableand
the rec ords that matc h are there as well.
Hack 54. Use Regular Expressions in
Access Queries

Sometimes wildcards aren't enough. With a little hacking, you


can use regular expressions in your queries.

A lthough A c c es s allows for s ome powerful s tring matc hing (s ee


"Wildc ard c harac ters and the L ike operator" in the A c c es s H elp
s ys tem), s ometimes you require an even more powerful s olution.
M ic ros oft added the ability to us e regular expres s ions bac k in
Vers ion 5 .0 of its Windows Sc ripting E ngine, bringing it up to par
with J avaSc ript. You c an us e this power ins ide an A c c es s query
as well.

A lthough the advanc ed details of regular expres s ions are


beyond the s c ope of this hac k, this example will get you s tarted
if you are new to the s ubjec t. I f you need more information, I
rec ommend the book Mas tering Regular Expres s ions (O 'Reilly).

I n many c as es it's pos s ible to work around the lac k of built- in


regular expres s ions us ing A c c es s 's wildc ard c harac ters and
multiple c alls to different s tring func tions , s uc h as Left, Mid,
Right, Len, and s o on. H owever, onc e you s ee what you c an do
with a s ingle c us tom func tion c all, you c an imagine the advanc ed
pos s ibilities and time s avings .

5.16.1. Creating the Custom Function


T he firs t thing we need to do is c reate a func tion that c an be
c alled from our A c c es s query that ties into the M ic ros oft
Sc ripting Runtime library.

T his hac k as s umes the mac hine you are


running has the lates t vers ion of
M ic ros oft's Sc ripting E ngine ins talled. I f
you are uns ure, vis it
http://www.mic ros oft.c om/s c ripting.

T he following c ode us es the CreateObject func tion s o that you


don't have to c hec k the Referenceeac h time the c ode is plac ed in
a new databas e:

Public Function RegExp(strString As String, _


strRegExp As String, Optional bolIgnoreCase As Boolean =
Boolean
Dim re As Object
Set re = CreateObject("vbscript.RegExp")
re.Pattern = strRegExp
re.IgnoreCase = bolIgnoreCase
If re.Test(strString) Then
RegExp = True
Else
RegExp = False
End If
End Function
T he func tion has two required parameters : the s tring being
matc hed agains t and the s tring that c ontains the regular
expres s ion. T he third, optional parameter tells the func tion
whether to matc h the regular expres s ion while ignoring the c as e;
the default won't ignore the c as e.

5.16.2. Creating an Example Query

A s an example, let's look at verifying part numbers by finding


thos e that don't matc h a given c riterion. M any times , you might
rec eive data from multiple people and platforms that needs to be
c leaned before going into a mas ter databas e. L et's s ay that part
numbers for a fac tory have the following c riteria:

T hey mus t s tart with a c apital P N or a c apital P.

T he next two pos itions mus t be numeric .

T he next pos ition mus t be a c apital letter (A Z).

T he next three to four pos itions mus t be numeric .

T he next five to s ix pos itions mus t be c apital letters


(A Z).
E xamples of part numbers that meet the c riteria inc lude
P N 1 2 W1 2 3 A BC D E and P 1 2 W1 2 3 A BC D E . E xamples that don't
meet the c riteria inc lude P N 1 2 W1 3 A BC D E (only two digits after
the W) and 1 2 W1 2 3 A BC D E (does n't s tart with P N or P ).

G iven the s et of c riteria for the part number, here's the regular
expres s ion:

"^(PN|P)[0-9][0-9][A-Z][0-9]{3,4}[A-Z]{5,6}$"

A s mentioned earlier, thes e regular expres s ions c an bec ome


quite overwhelming until you get us ed to them. I f you aren't
familiar with them, I s trongly rec ommend additional reading to
learn the full power of thes e expres s ions . To better unders tand
it, let's break down this expres s ion:

Tells the expres s ion to s tart at the beginning of the


s tring

(PN|P)

Says to matc h the c harac ters P N or P

[0-9][0-9]

Tells the expres s ion to matc h two digits , both in the


range 0 through 9
[A-Z]

Says to matc h a s ingle c harac ter A through Z

[0-9]{3,4}

Says to matc h a s ingle digit 0 through 9 at leas t three


times and a maximum of four times

[A-Z]{5,6}

Says to matc h a s ingle c harac ter A through Z at leas t


five times and a maximum of s ix times

Figure 5 - 5 3 s hows the layout for a query to find part numbers


that don't matc h our c riteria.

Figure 5-53. Calling the RegExp function from a


query
Running the query in Figure 5 - 5 3 returns the part numbers that
do not matc h our given c riteria s o that you c an review them
before plac ing them into a mas ter databas e. A lthough you c an
do this without tapping into the power of regular expres s ions , it
requires a muc h more involved s olution.

5.16.3. Hacking the Hack


A s you dis c over the power of regular expres s ions , you will find
them to be very robus t for all kinds of text proc es s ing. A nother
handy tric k is to us e them to verify text input on a form. To do
s o, c all the c us tom RegExp func tion from the BeforeUpdate event of
the text box. I f it returns false, s et the Cancel parameter variable
to true, whic h c lears the input on the text box.

You c an even add an advanc ed feature to your applic ation, whic h


allows the us er to do s earc hes bas ed on her own regular
expres s ions !

Steve Huff
6. Multiuser Issues
Sec tion 6 .1 . H ac ks 5 5 5 8

H ac k 5 5 . Tes t for D uplic ation

H ac k 5 6 . D is tribute a Split D atabas e with P redefined


Table L inks

H ac k 5 7 . Build a T ime- O ut Feature

H ac k 5 8 . I mplement U nique U s ernames


6.1. Hacks 5558
You c an deploy A c c es s databas es as s tandalone applic ations as
well as in s hared s ys tems . A lthough working with a s hared
databas e provides many benefits in terms of effic ienc y, is s ues
c an c rop up with regard to us ers ' ac tivities getting in the way of
eac h other's data. T his c hapter provides a few workarounds for
integrating A c c es s in a multius er environment while ens uring
data does n't get trampled. "Build a T ime- O ut Feature" [Hack
#57] c atc hes and c ompletes idle rec ord edits , thereby allowing
others to make c hanges . "Tes t for D uplic ation" [Hack #55]
s hows a way to validate data before us ers duplic ate eac h other's
entries . T he c hapter als o c overs a dis tribution method [Hack
#56] that makes it eas y to get a s plit databas e from your
development mac hine to your c lients , with the table links
already matc hing the network.
Hack 55. Test for Duplication

Bef ore you insert multiple entries into master tables in a busy
data-entry environment, you'll need a custom validation process
to avoid duplicated data.

J us t bec aus e a databas e is deployed on the s erver does n't mean


the entire applic ation mus t be in that s erver c opy. A c ommon
approac h is to put the data in the s erver databas e and dis tribute
the forms to the loc al c lient c omputers , ins ide another A c c es s
file. T his is a typic al A c c es s vers ion of a c lient/s erver
applic ation.

Bec aus e the c lient ins tallations are A c c es s databas es , us ing


tables in the c lient databas es opens up pos s ibilities . O ne us eful
tec hnique is to have new data entries go into loc al tables firs t,
and later to bulk- ins ert them into the mas ter table or tables on
the s erver.

T he heart of this tec hnique is that entry operators have loc al


tables that mirror the s erver tables . T heir forms are bound to the
loc al tables and all entry is done loc ally. A t the end of the day, or
at s c heduled times throughout the day, a proc es s runs that
takes the data out of the loc al tables and moves it to the s erver
tables .

H ere is where the advantage lies . T he ins ert proc es s gathers the
input from all the entry operators ' c lient tables and tes ts for
duplic ation before the ac tual ins ert into the s erver tables .
N ormally, s uc h an interim validation would be overkill bec aus e
data us ually is validated upon entry. H owever, the point here
is n't really to validate data in terms of c orrec t c ontent, but
rather, to s ee if duplic ate rec ords were entered during input.

T his is c ertainly a pos s ibility in a bus y c us tomer s ervic e, s ales ,


or telemarketing operation. For example, in an environment
where phone c alls c ome in, it is pos s ible that J ane plac es an
order for s omething and, an hour later, her hus band J oe plac es an
order for the s ame item. I f different operators handled the
hus band and wife, no one would be the wis er that this is a
duplic ate order. E ven the two c reated rec ords might not be
ac tual duplic ates bec aus e the firs t name is different in eac h
rec ord. But if a c us tom- des igned validation proc es s is us ed,
thes e two rec ords c an be flagged as duplic ates bec aus e at leas t
the addres s is the s ame in both rec ords .

You als o c an tes t for near duplic ation on the s erver, s o this begs
the ques tion: why bother with the s eparate table- entry
approac h? T he ans wer is performanc e. I f all entry goes s traight
to the s erver tables , and the c us tom duplic ation proc es s runs on
the larger tables , there c ould be s ome is s ues with s peed.

A nother is s ue to c ons ider is how far bac k in time to look for


duplic ates . With the loc al approac h, the tes t c ertainly is done at
leas t at the end of the day, if not during s c heduled times
throughout the day, all for that day's proc es s ing. I nvolving older
rec ords in the s erver tables is n't nec es s ary. A n order plac ed
twic e in one day is probably a duplic ate. A n order that res embles
one plac ed las t week or las t month is probably a repeat order.

Andrea Mos s
Hack 56. Distribute a Split Database with
Predefined Table Links

If you f ollow this interesting distribution game plan, users will


not have to link their local database f iles to the data tables on
the system.

T he tec hnique known as databas e s plitting, whic h involves a


tables - only bac k- end A c c es s file on a network s hare, c opies of
front- end A c c es s files (with forms , reports , and s o on) on eac h
us er's C:\ drive, and the us e of linked tables , has been around for
quite s ome time. T he benefits of s uc h c lient/s erver databas e
c onfigurations are widely known and doc umented.

H owever, it c an be a c hallenge to deal with s plit databas es


during periods of frequent updates , es pec ially during the
development phas e. For s ome us ers , their O ffic e ins tallation
does n't even inc lude the L inked Table M anager, s o they c an get
prompted for the O ffic e ins tallation C D when they attempt to
refres h and c hange links . O ther us ers might s imply be
unc omfortable or unfamiliar with how linked tables work.
Frequent relinking, es pec ially for us ers who were us ed to jus t
s haring an M D B from one loc ation, c an be problematic .

When frequent rounds of revis ions are being s ubmitted to end


us ers during the initial prototyping s tage of development, it
makes s ens e to keep the projec t in only one file. But onc e the
data model is s igned off, it's time to s plit the databas e. You c an
do this in two ways :
M ake a c opy of the databas e s o that you have two
identic al c opies . I n one c opy, delete the tables . I n the
other c opy, delete everything but the tables .

U s e the D atabas e Splitter utility (Tools D atabas e


U tilities ). T his automatic ally c reates an A c c es s file with
jus t the tables and, at the s ame time, removes the
tables from the databas e running the utility.

T hen the bac k- end databas e (the one with the tables ) goes on a
network s hare, while the front- end databas e is dis tributed to
us ers and is run from their P C s . T he problem is that the front-
end databas e mus t be linked to the tables in the bac k- end
databas e. Simulating thes e links on your development P C before
dis tributing the front end is the point here. I f you c an s et the
links s o that they are the s ame as thos e in the produc tion
environment, us ers will not have to deal with es tablis hing the
links thems elves from their P C s .

I n other words , you c an dis tribute the front end prelinked. A ll of


this is bas ed on the as s umption that a drive- mapping s tandard
is in plac e and that all us ers will have an identic al map path.

6.3.1. Copying the Network Drive to Your


Development Machine

T he SUBST D O S c ommand is all you need to c opy the network


drive to your development mac hine. O n your development
mac hine, you c an us e SUBST to c reate a map that matc hes the
one us ers need.
Firs t, c reate a direc tory on your c omputer that matc hes the
folder on the s hare where the bac k- end databas e will go. I f the
network path inc ludes s ubdirec tories , c reate a path that
matc hes that path s truc ture on your development mac hine.

T he s yntax for us ing SUBST requires the new drive letter and the
path it is s et to, like this :

SUBST <New Virtual Drive Letter:> <Path to map to that let

For example, if you have a s ubfolder named XYZ_Corp in your


C:\Clients folder, and you want to map that folder to an S:\ drive,
c lic k Start/Run; type command (Windows 9 8 , M e) or CMD (Windows
N T, 2 0 0 0 , XP ); c lic k O K; and enter this at the c ommand line:

SUBST S: C:\Clients\XYZ_Corp

Figure 6 - 1 s hows how you do this in the C ommand P rompt box.

Figure 6-1. Using SUBST


I f us ers are ac c es s ing a s ubfolder under the S: drive, c reate
matc hing folder names under the folder that was s ubs tituted to
the S: drive. P lac e the bac k- end databas e in the appropriate
folder.

N ow, when you are in the front- end databas e file and are linking
the bac k- end tables , brows e to your new S: drive to find the
bac k- end databas e, thereby keeping the links the s ame as what
the us ers need. When you s end your us ers an update, they
s hould not have to relink anything. T his new drive letter will even
s how up when you open M y C omputer. T he new virtual drive
letter will las t until the next time you res tart. I f you dec ide you
no longer need a virtual drive, you c an get rid of it with the /d
s witc h:

SUBST S: /d

I t's important to note that this removes the virtual mapping; it


does n't delete the folder or its files . A ls o, you c an't us e SUBST if
you already have a drive us ing that letter, s o if your keyc hain
flas h drive is us ing G:\, you have to s afely remove it before
SUBSTing a folder to us e the G: drive.

O f c ours e, this tec hnique works only in extremely s table


environments , where all us ers have the s ame drive letter
mapped to the given s hare. A lthough S: might work for your
primary c ontac t, other us ers of the applic ation might have a
different letter mapped to that loc ation, or they might not have
any letter mapped. T hey c ould be ac c es s ing the folder through
its U nivers al N aming C onvention (U N C ) name
(\\ServerName\ShareName). I f this is the c as e, you c an emulate
this on your P C as well as long as you are willing to rename your
P C to matc h the s erver's name (you c an always c hange it bac k
later).

6.3.2. Using UNC Instead

I f you want to us e U N C ins tead, you need to rename your


c omputer. Firs t, you need to know the s erver name at the c lient
s ite and the full path of folders and s ubfolders to the s hare that
will hold your bac k- end datafile. To rename your c omputer to
matc h the s erver, bring up your P C 's Sys tem P roperties by right-
c lic king M y C omputer and c lic king P roperties , or by double-
c lic king the Sys tem ic on in the C ontrol P anel to open the
Sys tem P roperties dialog box. Selec t the C omputer N ame tab,
and then c lic k the C hange button. When you s ee the C omputer
N ame C hanges dialog box, s hown in Figure 6 - 2 , type the des ired
name for the c omputer. I t will require a reboot to take effec t. O f
c ours e, this as s umes you will be c reating a name c onflic t on the
network. T he as s umption is that your development mac hine is n't
on the produc tion network. I f it is , you c an dis c onnec t your
c omputer during this proc es s .
Figure 6-2. Changing the name of the computer
So, if XY Z C orp.'s s erver, c alled ServerName, has a DeptShare
folder and a s ubfolder c alled DataFolder that will hold your
datafile, c hange your c omputer's name to ServerName. T hen,
c reate a folder named DeptShare off the root of your C: drive, and
c reate a s ubfolder c alled DataFolder ins ide the DeptShare folder.

O nc e the folder s truc ture is in plac e, brows e to the DeptShare


folder, rightc lic k in an empty area of the folder, and then c lic k
P roperties . Selec t the Sharing tab, and make s elec tions to s hare
the folder, as s hown in Figure 6 - 3 .

Figure 6-3. Sharing a folder


N ow go to M y N etwork P lac es , and c lic k A dd a N etwork P lac e.
C lic k N ext on the wizard's firs t s c reen, and the wizard will as k
you where to c reate the new network plac e; s elec t "C hoos e
another network loc ation." C lic k N ext, and in the I nternet or
N etwork A ddres s box, type \\ServerName\DeptShare. C lic k N ext;
Windows will as k what to c all the s hare. I f the name is n't already
in the box, type DeptShare for ServerN ame. C lic k O K, and then
c lic k Finis h.

Finally, bac k in your A c c es s applic ation file, delete all the linked
tables . T his time, when relinking the tables , make s ure to go
through M y N etwork P lac es /E ntire N etwork in the link dialog to
brows e to the datafile, or type \\servername\deptshare into the
dialog to brows e to the datafile. T his c aus es A c c es s to c reate
the links to us e the U N C naming c onvention. I f you us e the M y
C omputer s hortc ut to your s hare, A c c es s rec ognizes that it is
loc al and us es the C: drive path to c reate the link. To ens ure that
your link is us ing U N C , type this in the debug window:

?Currentdb.TableDefs("<your table name>").Connect

M ake s ure to put the name of one of the linked tables in the c ode
line where you s ee the <your table name> prompt. T he res pons e
s hould look like this :

;DATABASE=\\Servername\DeptShare\DataFolder\Project_dat.md
N ote that you will s ee the name of your databas e; you won't s ee
Project_ dat.mdb. I f you get the following res pons e, you need to
try again, making s ure you go through the entire network,
workgroup, c omputer name, and s hare name when brows ing to
your datafile:

;DATABASE=C:\DeptShare\DataFolder\Project_dat.mdb

O nc e this is c orrec t, end us ers at the c lient s ite s houldn't need


to relink, regardles s of whic h drive letter (if any) they have
mapped to the network loc ation.

Steve Conklin
Hack 57. Build a Time-Out Feature

Make sure your data is saved and available to others. Lock the
records when they're not being updated.

T he phone rings , or you are late to a meeting, or any number of


other dis trac tions pop up. I t happens to all of us . U nfortunately,
you s ometimes forget to c los e out of the file open on your P C .

I n a multius er databas e, this c an be a real nuis anc e. D epending


on the rec ord- loc king s c heme being us ed, if a rec ord is left in
the middle of an edit, other workers might not be able to make
c hanges to that rec ord. Figure 6 - 4 s hows the dreadful mes s age
a us er c an get when attempting to make a c hange to a rec ord
s omeone els e has left open.

Figure 6-4. A record that has been left in an


edited state
A lthough the mes s age in Figure 6 - 4 gives the s ec ond us er the
options he needs , it is better to not even s ee this mes s age, if it
c an be avoided. A produc tive meas ure for this s ituation is to
c los e a form in whic h no ac tivity is s ens ed after a period of time.
I n other words , if the firs t us er has not c ompleted any c hanges
to the rec ord within a s pec ified time, the form s hould jus t c los e.
C los ing the form ends the rec ord- editing proc es s , and the
c hanges are s aved automatic ally. T he alternative, to drop the
c hanges , is dis c us s ed at the end of this hac k.

6.4.1. It's About Time

Forms have an intrins ic timer c ontrol and Timer event. I f you're


familiar with V is ual Bas ic , you know how to ac tually plac e a
timer c ontrol on a form. I n A c c es s , the c ontrol is effec tively
already on the form, although you don't s ee it. I ns tead, you us e
the property s heet to s et the Interval property and to indic ate
what oc c urs in the On Timer event.

To dis play the property s heet, open the form in D es ign mode,
and pres s F4 on the keyboard. I f nec es s ary, make s ure the
property s heet is dis playing properties about the form its elf, not
about one of the c ontrols or s ec tions . Selec t Form from the drop-
down box at the top of the property s heet.

Figure 6 - 5 s hows the property s heet s et to dis play the


properties for the form. T he On Timer and Timer Interval
properties are found on both the E vent tab and the A ll tab.

You c an dis play the property s heet in a few


ways . You c an pres s F4 , or you c an pres s
A lt- E nter. You c an als o us e the V iew
P roperties menu, or jus t c lic k the
P roperties button on the Form D es ign
toolbar.

T he Interval property ac c epts values between 0 and


2,147,483,647 millis ec onds . A s etting of 1000 equals one s ec ond.
T he 10000 s etting s hown in Figure 6 - 5 is the equivalent of 1 0
s ec onds . By the way, the larges t s etting of 2,147,483,647 equals
almos t 2 5 days . Yes , you c an s c hedule an A c c es s event every
2 5 days !

T he On Timer event property links to either a mac ro or a c ode


proc edure. I n this example, a c ode proc edure was written. I 'll
explain the c ode s oon, but firs t, let's examine the form's des ign.

6.4.2. In Good Form

Figure 6 - 6 illus trates the form des ign, field lis t, and property
s heet. N otic e the text box c ontrol in the form header that is n't
bound to a field. T he property s heet is s et to dis play the
properties of the unbound box, txtT ime, and its Visible property
is s et to No. I n other words , when the form is in V iew mode, the
txtT ime text box won't be s een.

Figure 6-5. Setting the Timer Interval and On


Timer event
Figure 6-6. An unbound text box to hold a time
reference
T he txtT ime text box is n't meant to be us ed for entry. I ns tead, it
holds a s naps hot of the c omputer's c loc k time, at the moment
the form is ac tivated. To make this happen, you need to enter a
little c ode in the form's Activate event.

To get to the event c ode s tub, s elec t [E vent P roc edure] from the
drop- down menu to the right of O n A c tivate in the property s heet
and then c lic k the ellips es (…) button, as s hown in Figure 6 - 7 .
T his brings you to the form's c ode module, right at the s tart of
the Activate event. H ow's that for c onvenienc e?

Figure 6-7. Getting to an event code stub from


the property sheet
H ere is the c ode to enter in the Activate event:

Private Sub Form_Activate( )


Me.txtTime = Now
End Sub

T he On Timer event c ontains the DateDiff func tion, s et to tes t for


the differenc e in s ec onds between the es tablis hed form
ac tivation time and the c urrent time. From the property s heet,
s elec t [E vent P roc edure] in the drop- down menu to the right of
O n T imer. C lic k the ellips es button and enter this c ode:

Private Sub Form_Timer( )


If DateDiff("s", Me.txtTime, Now) > 5 Then
DoCmd.Close
End If
End Sub

T he firs t parameter of the DateDiff func tion indic ates whic h


interval to tes t; in this c as e, s is for s ec onds . T he func tion tes ts
if more than five s ec onds have laps ed between the time s tored in
the txtT ime text box and now. Bear in mind that there are two
values to c oordinate here: the timer interval and how many
s ec onds to tes t for.

T his example is s et up to tes t every 1 0 s ec onds if there is a


differenc e of five s ec onds , but you c an c hange thes e numbers .
For example, it might be eas ier on the us er if the timer interval is
3 0 s ec onds . T here is a balanc e of what makes s ens e here. I f
us ers are likely to edit the s ame rec ords often, make the interval
s horter.
T he Now() func tion returns the s ys tem
time. E very c omputer keeps an internal
c loc k running. When timing events , it's
nec es s ary to s tart with a bas eline time.
T he Now() func tion takes a s naps hot of the
time, whic h is then c ompared to a later
time (effec tively another s naps hot, but
later in time). Subtrac ting the firs t
s naps hot from the s ec ond s naps hot equals
the elaps ed time. I nc identally, the
c omputer c loc k is als o us ed in programs
that allow you to enter "today's date."
Sometimes , the c loc k needs to be res et.

I f we s topped here, the form would c los e 1 0 s ec onds after being


opened. T hat is , upon the firs t run of the On Timer event (whic h
oc c urs 1 0 s ec onds after the form is opened) a differenc e greater
than five s ec onds is found, and the DoCmd.Close line runs , c los ing
the form. But our goal is to c los e the form only when there is no
ac tivity, not jus t for the hec k of it.

T he key to making this hac k work is to add c ode to eac h c hange


event for the various text entry boxes on the form. T he form in
this example has text boxes for editing employee name,
department, title, and s o on. T he Change event for eac h text box
rec eives a s ingle line of c ode to update the txtT ime text box with
the c urrent time. I n other words , every time a c hange is made in
an entry text box the txtT ime text box (remember, this one is
invis ible) is res et to Now, like this :
Private Sub Department_Change( )
Me.txtTime = Now
End Sub

T he Change event fires eac h time a c harac ter is entered or


bac ks pac ed out of the text box. T herefore, as a us er types in
one of the entry text boxes , the txtT ime text box is c ons tantly
updated with the c urrent time. T hen, when the timer event fires ,
the DateDiff func tion returns a differenc e of les s than five
s ec onds , and the form s tays open. O nly when the form is left idle
does a differenc e greater than five s ec onds oc c ur, thereby
c los ing the form.

Figure 6 - 8 s hows how the form's c ode module s hould look after
thes e routines have been entered. I t's O K if your event routines
aren't in the s ame order.

Figure 6-8. The code that handles an inactive


form
6.4.3. Hacking the Hack

You c an implement this hac k in many different ways . So far, all


we know is how to res et the bas eline time eac h time a c harac ter
is entered with the keyboard. A ls o, the only ac tion after a period
of inac tivity has been to c los e the form. H ere are s ome other
ideas .

6.4.3.1 Reset the time when the mouse is


moved.

I n addition to c apturing keyboard entries as a way to res et the


time held in the invis ible text box, it makes s ens e to do this
whenever the mous e is moved as well. Some people are quic k on
the mous e, and jus t giving the mous e a pus h keeps the form
open. I n fac t, I often do this to keep my s c reens aver from
s tarting up.

A c c es s forms c an als o us e the MouseMove event. I ns ert c ode into


the MouseMove event in the s ame manner explained earlier. T he
purpos e of the c ode is the s ame, to res et the invis ible text box
to Now .

Private Sub Title_MouseMove(Button As Integer, _


Shift As Integer, X As Single, Y As Single)
Me.txtTime = Now
End Sub

A s long as the mous e is moved at leas t onc e every 1 0 s ec onds ,


the form will s tay open.

6.4.3.2 Let the user decide the timer


interval.

E ac h us er has his own way of working, not to mention his own


s peed of working. So, ins tead of hardc oding the timer's interval
value, why not let the us er dec ide what is bes t? To do this , you
have to build a way to let him s elec t the interval into the form (or
s omewhere els e, s uc h as in a preferenc es area). Figure 6 - 9
s hows how the form has been modified by adding a c ombo box.
T he c ombo box lets the us er s elec t from a lis t of pos s ible
values .

Figure 6-9. Letting the user decide how long to


wait before closing the form
T he c ode is updated as well. T he c ombo box has been named
c mbSec onds . I ts Row Source Type is s et to Value List, and the Row
Source is s et to the c hoic es 10, 20, 30, 40, 50 , and 60 . When the
us er s elec ts a value from the c ombo box, the c ombo box's Change
event fires to update the invis ible text box to the c urrent time.
A ls o, the form's Activate event now takes c are of es tablis hing a
default time to wait2 0 s ec onds in this c as e, as s hown in Figure
6 -1 0 .

I t's nec es s ary to have a default value to us e until or unles s the


us er s elec ts an interval. Finally, the number of elaps ed s ec onds
that are tes ted for is now always one fewer than the interval
s elec ted in the c ombo box. Figure 6 - 1 0 s hows the updated c ode
module.

Figure 6-10. Setting the Interval property with


the combo box Change event
6.4.3.3 Save the record but leave the
form open.

J us t bec aus e a rec ord is dis played in a form does n't nec es s arily
mean it is being edited. T he Dirty property is true if edits have
been made or false if no data has c hanged. You c an c hange the
c ode in the form's Timer event to tes t the Dirty property. I f it is
true , the rec ord is s aved, and a mes s age is pres ented, as s hown
in Figure 6 - 1 1 . I f Dirty is false, and no edit is oc c urring, nothing
happens . E ither way the form s tays open.

T he new c ode for the Timer event us es a pair of nes ted If


s tatements . Firs t, if the elaps ed time is greater than the
predetermined interval of the tes t, the s ec ond If s tatement
c omes into play. T he s ec ond If tes ts the Dirty property. I f true ,
the rec ord is s aved, and the mes s age is dis played:

Figure 6-11. The saved-edits message


Private Sub Form_Timer( )
If DateDiff("s", Me.txtTime, Now) > Me.cmbSeconds - 1 Then
If Me.Dirty Then
DoCmd.RunCommand acCmdSaveRecord
MsgBox "Edits have been saved!"
End If
End If
End Sub

I f you s tarted an edit and didn't c omplete it, the c ode c ompletes
it for you. I f an edit is initiated, no harm is done.

6.4.3.4 Close the form without saving the


record.

So far, eac h approac h in the hac k has been bas ed on s aving the
rec ord that is in the middle of an edit. T his is a judgment c all
bec aus e even if the pers on walked away from his work you don't
know for s ure whether to s ave his entry. To be on the s afe s ide,
the work is s aved.

O f c ours e, the argument exis ts to not s ave the edits . I t's eas y
to drop the edits with an Undo ac tion. H ere is a s nippet of
modified c ode that goes into the Timer event:

Private Sub Form_Timer( )


If DateDiff("s", Me.txtTime, Now) > 10 Then
If Me.Dirty Then
DoCmd.RunCommand acCmdUndo
DoCmd.Close
End If
End If
End Sub

E s s entially, if the rec ord is n't in its pris tine s tate (c onfirmed by
the Dirty property), the c ode runs an Undo c ommand and c los es
the form without s aving the rec ord. T his is jus t one way to
handle dropping a half- c ompleted edit.

A nother enhanc ement is to s ave the values out of the form and
into a temporary table or even a text file, and to leave a
mes s age box alerting the us er that his entry was dropped but
that he c an find his uns aved efforts at the plac e where you s aved
them.
Hack 58. Implement Unique Usernames

Even when A ccess Security isn't active, you can implement


unique usernames when all users are A dmin.

A c c es s Sec urity is great in many multius er s ituations bec aus e


you c an as s ign rights to groups and individuals . H owever,
s ometimes this is jus t a lot of overhead. I n a s mall group of
us ers who all us e the s ame objec ts , you don't get muc h added
value by implementing s ec urity.

T he downs ide of not us ing s ec urity is that all us ers are given the
name A dmin. You c an c onfirm this in an uns ec ured databas e by
going to the I mmediate window (C trl- G in the V B E ditor) and
typing the following:

?CurrentUser

T he CurrentUser property c ontains the logged- in name of the


us er. When s ec urity is on, eac h us er has a s pec ific name. When
s ec urity is off, all us ers are A dmin.

A n eas y way to us e s pec ific names in an uns ec ured databas e is


to firs t have us ers enter their names and then have the entered
names available throughout the s es s ion. T his tec hnique makes
s ens e only in a c onfiguration in whic h eac h us er works on a loc al
databas e with the forms . T he data tables remain on in a bac k-
end databas e on the s erver.

When a us er s tarts up her c lient- bas ed front end, s he is as ked to


enter her name. T his is jus t an eas y affair handled by an input
box. A loop keeps tes ting for her entry, and when s he is done,
the entry is handed off to the Tag property of the main form. T his
works bes t if the main form opens automatic ally when the us er
s tarts up the databas e. T his c ode goes into the main form's Open
event:

Private Sub Form_Open(Cancel As Integer)


Dim user_name As String
user_name = ""
Do Until user_name <> ""
user_name = InputBox("Please enter your name", "Enter Na
Loop
Me.Tag = user_name
End Sub

T hroughout the s es s ion, the pers on's name is always available


via this s imple referenc e bac k to the main form and its tag,
as s uming that the main form is named frmM ain. C hange the form
name to matc h yours :

Forms!frmMain.Tag

Bec aus e the applic ation is c onfigured in the way that eac h us er
is us ing a loc al vers ion of the main form, the referenc e to the Tag
property always returns the us er's unique name.

T hat's all it takes . By ac c es s ing the entered us ername in this


way, you c an us e the name in reports , populate field text boxes
with it, us e it in queries ; in other words , us e the name wherever
you need it in your applic ation.

Andrea Mos s
7. External Programs and
Data
Sec tion 7 .1 . H ac ks 5 9 7 1

H ac k 5 9 . I mport N onc ontiguous Ranges of D ata from


E xc el

H ac k 6 0 . U s e E xc el to Reorient A c c es s D ata

H ac k 6 1 . U s e E xc el Func tions I ns ide A c c es s

H ac k 6 2 . U s e Word to C ompare D ata in Two A c c es s


Tables

H ac k 6 3 . I mport Varied XM L D ata into A c c es s

H ac k 6 4 . E xport XM L D ata Sanely

H ac k 6 5 . Break T hrough V BA 's Trans formation Barrier

H ac k 6 6 . L everage SQ L Server P ower by C alling Stored


P roc edures

H ac k 6 7 . M anage Word D oc uments from A c c es s

H ac k 6 8 . U s e A c c es s as a Front E nd to M ySQ L

H ac k 6 9 . Send A c c es s D ata T hrough O utlook


A utomatic ally

H ac k 7 0 . C reate A c c es s Tables from O uts ide A c c es s

H ac k 7 1 . Write V BA with the M ac ro Rec order in Word


and E xc el
7.1. Hacks 5971
A c c es s is n't an is land of an applic ation, not by a long s hot. I t
integrates eas ily with many programs . O bvious ly, it s hares
c harac teris tic s with the other O ffic e produc ts , and it is relatively
eas y to inc lude Word, E xc el, O utlook, and P owerP oint files in
your A c c es s s olutions . Several hac ks in this c hapter do jus t
that. For example, "I mport N onc ontiguous Ranges of D ata from
E xc el" [Hack #59] and "U s e E xc el Func tions I ns ide A c c es s "
[Hack #61] involve integration with E xc el. "M anage Word
D oc uments from A c c es s " [Hack #67] s hows you how to us e
Word's objec t model to c reate a programmatic s olution, and
"U s e Word to C ompare D ata in Two A c c es s Tables " [Hack #62]
s hows you a neat way to us e Word independently to tes t your
data.

T his c hapter als o inc ludes hac ks on us ing XM L data, integrating


with M ySQ L , and us ing SQ L Server s tored proc edures . A ll in all,
the c hapter offers quite a bit, and us ing the tec hniques
pres ented here will c ertainly gain you an edge in your
development efforts .
Hack 59. Import Noncontiguous Ranges
of Data from Excel

A standard import lets you get only one data range at a time.
Here are a couple of workarounds to get you more.

When importing data from an E xc el workbook into A c c es s , you


c an s elec t to import a works heet or a range. You c an s elec t a
range only when the workbook inc ludes es tablis hed named
ranges . Figure 7 - 1 s hows the firs t s c reen of the I mport
Spreads heet Wizard. T his wizard appears after you s elec t File
G et E xternal D ata and s elec t to import from an E xc el file.

Whether you're importing a works heet or a range, the problem is


that you c an s elec t only one item in the lis t. U s ually, s ingle
works heets are imported bec aus e a wealth of data c an s it on a
s ingle works heet. Ranges are a different s tory. You might need to
import more than one range. I t's tedious to run the I mport
Spreads heet Wizard over and over again.

Figure 7-1. Importing data from Excel


7.2.1. Using Macros for Multiple Imports

A n eas y way around the one- range- at- a- time import is to c reate
a mac ro that us es multiple transferSpreadsheet ac tions . E ac h
oc c urrenc e of this ac tion imports a s ingle range, but you c an
c reate a s equenc e of them in a s ingle mac ro. You s hould
c ons ider whether the ranges are to be imported as new tables , or
whether the ranges are to be ac c umulated into a s ingle table.

7.2.1.1 Import Excel data into separate


tables

Figure 7 - 2 s hows a mac ro that imports five ranges into five


tables . E ac h import puts data into a s eparate A c c es s table.
E ac h table name is s pec ified in the Table Name argument of eac h
TRansferSpreadsheet ac tion. T he firs t five ac tions of the mac ro
delete the exis ting tables jus t before the imports . T he imports
plac e the E xc el data into tables with the s ame name as the
tables being deleted

H ere is a potential problem: if you don't delete the tables firs t,


the data is appended to the tables bec aus e they already exis t.
M os t likely you don't want to do this . D eleting the A c c es s tables
firs t guarantees that the tables are rec reated with jus t the newly
imported data.
Figure 7-2. A macro that creates separate
Access tables
You s et the ac tual transferSpreadsheet ac tions s uc h that eac h
addres s es a different range in the E xc el data. You s et this in the
Range argument, s hown in Figure 7 - 2 ; it's an ac c eptable way to
gather data from different E xc el ranges .

7.2.1.2 Import Excel data into a single


table

I f you want to c ombine the data from different E xc el ranges into


one A c c es s table, the Table Name argument of eac h
transferSpreadsheet ac tion s hould be identic al. You s till mus t
empty the des tination table firs t. I n this mac ro, you do s o with
the RunSQL ac tion, whic h runs a s imple Delete operation:

Delete * From Inventory_All

P rior to this , turn off warnings s o that the proc es s is n't


interrupted with a c onfirmation mes s age.

A fter the Delete operation, the transferSpreadsheet ac tions fill the


nowempty I nventory_A ll table. A ll the data is appended to the
table.

Figure 7 - 3 s hows how this mac ro is s truc tured.

Figure 7-3. A macro that populates one Access


table
7.2.2. Importing Noncontiguous Data from
Excel Without Using Ranges

M ac ros are handy but are limited in power. A s s hown in the


previous s ec tion, you c an import ranges eas ily enough. You c an
even import areas of a workbook by addres s . I n other words , you
c an enter A1:D15 to import part of an E xc el works heet. T hat's
about it, though. A mac ro c an't do anything muc h more
s ophis tic ated than that. T his is where s ome V BA c omes in
handy.

Figure 7 - 4 s hows an E xc el works heet. T he data c ons is ts of


produc t amounts broken out by years and quarters .

Figure 7-4. Excel data to be imported


To import, s ay, jus t the s ec ond- quarter figures for eac h year
requires a proc es s that tes ts eac h row to s ee if the quarter is Q2.
H ere is a c ode routine that does jus t that:

Sub get_excel()
Dim test_quarter As Integer
Dim conn As ADODB.Connection
Set conn = CurrentProject.Connection
Dim xl As Object
Set xl = GetObject("C:\Inventory\Inventory.xls")
'first delete existing records
conn.Execute "Delete * From Inventory_All"
With xl
With .Worksheets("Data")
For test_quarter = 2 To 25 'known row numbers on work
If .Cells(test_quarter, 2) = "Q2" Then
ssql = "Insert Into Inventory_All Values(
ssql = ssql & .Cells(test_quarter, 1) & "
ssql = ssql & "'" & .Cells(test_quarter,
ssql = ssql & .Cells(test_quarter, 3) & ", "
ssql = ssql & .Cells(test_quarter, 4) & "
ssql = ssql & .Cells(test_quarter, 5) & "
conn.Execute ssql
End If
Next test_quarter
End With
End With
xl.Close
Set xl = Nothing
MsgBox "done"
End Sub
T his c ode us es automation to c reate an E xc el objec t and s ets
the workbook to the objec t. I t then c yc les through the works heet
rows . A tes t s ees if the value in c olumn 2 is Q2. When this is
true, all five c olumns of the row are ins erted into the
I nventory_A ll A c c es s table.

O f c ours e, you c an alter the c ode to tes t on other c onditions .


A ls o, you don't have to hardc ode the Q 2 tes t. Figure 7 - 5 s hows
the A c c es s table populated with jus t the s ec ond- quarter
rec ords .

Figure 7-5. Populating the table with portions of


the Excel workbook
A little c ode c an go a long way. Setting the referenc e to the
E xc el workbook is a s imple proc es s with the GetObject func tion.
O nc e the routine is c onnec ted to a workbook, you c an do many
things with a little knowledge of E xc el's programmatic model.
Hack 60. Use Excel to Reorient Access
Data

Use Excel's Paste Special Transpose f eature to turn data on its


ear.

H ere's an eas y way to c hange c olumns to rows (or rows to


c olumns ; I gues s it all depends on how you look at it). Figure 7 -
6 s hows a table filled with s ome data. T he table c ontains 8 fields
and 1 0 0 rows of data.

Figure 7-6. Eight columns of data in a table


P erhaps your us er wants to view the data s o that eac h pers on's
rec ord is dis played vertic ally, whic h is n't an unc ommon reques t
with E xc el us ers . T his hac k s hows how to do jus t that: put this
data in E xc el, but turn it s ideways .

Firs t, s elec t all the data in the A c c es s table; then, pop over to
an open E xc el workbook. Find an empty works heet, and pas te
the data. N ote that for this example, I have purpos ely pas ted the
data in row 1 2 . You will s ee why in a moment. Figure 7 - 7 s hows
how the data landed in E xc el.

Figure 7-7. Access data pasted in Excel


U pon being pas ted, the data is in a s elec ted s tate. T hat's great!
J us t leave it as is , but if you los e the s elec tion, jus t s elec t it
again. T he next s tep is to c opy the data, by either s elec ting E dit
C opy or pres s ing C trl- C . C opying the data is a nec es s ary
s tep. I t might s eem that the data is already on the c lipboard. I t
is , but not in the way we need; therefore, the extra c opy from
within E xc el is nec es s ary.

N ow that the data is c opied in an E xc el format, c lic k in c ell A 1 .


T his removes the s elec ted s tate from the data, but that's okay
at this point. I n fac t for the next s tep, the data mus t be
des elec ted, and a s ingle c ell mus t be ac tive.

U s e the E dit P as te Spec ial menu to open the P as te Spec ial


dialog box, as s hown in Figure 7 - 8 .

Figure 7-8. The Paste Special dialog box


T here are few things to note in Figure 7 - 8 . A s already noted, the
data is des elec ted. C ell A 1 is the ac tive c ell. T he P as te Spec ial
dialog will pas te the c opied data in the next operation, but the
c ritic al point is that the Trans pos e box is c hec ked. T his
c hec kbox is near the bottom of the dialog box.

C lic king the O K button c ompletes the proc es s . Figure 7 - 9


s hows how the data s its at the top of the works heet. E arlier, I
made the firs t pas te in row 1 2 to give enough room for the
s ec ond pas te. We already knew there were eight fields of data,
and now they oc c upy eight works heet rows . A lthough not vis ible
in Figure 7 - 9 , the data goes 1 0 0 c olumns to the right.

T he data in the firs t pas te is no longer needed, s o you c an delete


it. A few formatting c hanges will make the data pres entable and
ready for work. Figure 7 - 1 0 s hows how the data looks after a
fac elift and how analys is is already being run on the data.

Figure 7-9. The transposed data


Figure 7-10. Working with the transposed data
Hack 61. Use Excel Functions Inside
Access

Expose powerf ul f unctions available in Excel to your A ccess


application.

E xc el has many powerful built- in func tions for s uc h things as


financ ial and s tatis tic al analys is . I f you want to do the s ame
type of analys is in A c c es s , you c an do one of the following three
things : purc has e an off- the- s helf c ode s olution, write your own
c ode for analys is , or us e automation to tap into E xc el's
func tions from ins ide A c c es s . T his hac k s hows you how to tap
into E xc el via automation and us e s preads heet func tions , s aving
you time and money over the other options .

T his hac k involves A c c es s working hand in hand with E xc el, s o


you need to make s ure E xc el is ins talled on the mac hine on
whic h your databas e will be running. T his is a s afe as s umption in
mos t c orporate environments .

7.4.1. A Simple Excel Function

E xc el's FV (future value) func tion c alc ulates the value of an


inves tment at s ome time in the future bas ed on periodic ,
c ons tant payments and on a c ons tant interes t rate. T he
following V BA func tion takes the s ame parameters as E xc el's FV
works heet func tion and returns the s ame res ult as if you were
us ing the future value func tion right in E xc el:

Public Function FV(dblRate As Double, intNper As Integer, _


dblPmt As Double, dblPv As Double, _
intType As Integer) As Double
Dim xl As Object
Set xl = CreateObject("Excel.Application")
FV = xl.WorksheetFunction.FV(dblRate, intNper, dblPmt, dblPv,
Set xl = Nothing
End Function

T he WorksheetFunction property of E xc el's Application objec t is


key to c alling E xc el func tions from c ode, whether in A c c es s or
even direc tly in E xc el's V BA environment. With this property,
nearly every E xc el works heet func tion is available to build into a
s olution.

Figure 7 - 1 1 s hows a form that takes input from a us er and c alls


the FV func tion from the C alc ulate Future Value button.

Figure 7-11. Calling the FV function from a form


C lic king the C alc ulate Future Value button exec utes the
following c ode:

Private Sub cmdFV_Click( )


Dim dblFV As Double
dblFV = FV(txtRate / 12, txtNper, txtPmt, dblPv, frmType)
MsgBox "FV = " & dblFV, vbInformation, "Future Value"
End Sub

T he cmdFV_Click event c alls the FV func tion and dis plays the
mes s age box s hown in Figure 7 - 1 2 . You c an modify the c ode to
write the s olution bac k to a table or to dis play it els ewhere on
the form objec t as needed.

Figure 7-12. Message box displayed from the


cmdFV_Click event

7.4.2. An Excel Function with an Array


Parameter

T he example of c alc ulating a future value required five


parameters to be pas s ed into E xc el, and with the magic of
automation, we got the res ult bac k. H owever, what would happen
if one of thos e parameters were an array, as many are in E xc el?

I f an E xc el func tion requires an array or table array, you c an


pas s it an array or a multidimens ional array c reated in A c c es s
and get bac k the needed res ult. L et's look at the c ode you'd us e
to c all E xc el's perc entile works heet func tion, whic h returns the
kth perc entile of values that you s pec ify from a given array of
values :

Public Function Percentile(strTbl As String, strFld As String, k A


As Double
Dim rst As ADODB.Recordset
Dim dblData() As Double
Dim xl As Object
Dim x As Integer
Set xl = CreateObject("Excel.Application")
Set rst = New ADODB.Recordset
rst.Open "Select * from " & strTbl, CurrentProject.Connection,
adOpenStatic
ReDim dblData(rst.RecordCount - 1)
For x = 0 To (rst.RecordCount - 1)
dblData(x) = rst(strFld)
rst.MoveNext
Next x
Percentile = xl.WorksheetFunction.Percentile(dblData, k)
rst.Close
Set rst = Nothing
Set xl = Nothing
End Function
With this func tion, we pas s the table name and field name to be
read into the A c c es s array, whic h in return is pas s ed into
E xc el's perc entile func tion along with the kth perc entile value
that we are looking for in the array of values . I t's worth noting
that you c an pas s the func tion a query name ins tead of a table,
depending on the applic ation's requirements .

Figure 7 - 1 3 s hows a form that dis plays a s ubform that is bound


to the tblD ata table and dis playing the SampleD ata field in
datas heet mode.

Figure 7-13. Calling the percentile function from


a form
T his s ample c alc ulates the 3 0 th perc entile from the lis t 1, 2, 3,
4, 5, 14, 13, 13, 16, 15, 16, 156 when the us er c lic ks the
C alc ulate P erc entile button. C lic king the C alc ulate P erc entile
button exec utes the following c ode:

Private Sub cmdPercentile_Click()


Dim dblPercentile As Double
dblPercentile = Percentile("tblData", "SampleData", txtK)
MsgBox "Percentile = " & dblPercentile, vbInformation, "Percen
End Sub

T his c ode produc es the mes s age box in Figure 7 - 1 4 .

Figure 7-14. The message box displayed from


the cmdPercentile_Click event
A s noted previous ly with the FV func tion, you c an write this
return value bac k to a table or dis play it on the form. You c an
als o c all the FV func tion or Percentile func tion from a query or
us e it on a report.

7.4.3. Other Excel Spreadsheet Functions

You c an c all more than 1 0 0 func tions us ing the WorksheetFunction


method of the E xc el objec t via automation. Keep in mind that
s ome are redundant with built- in A c c es s func tions , s uc h as
E xc el's ISNUMBER and A c c es s 's ISNUMERICM, and others , s uc h as
ISERR and ISNA, aren't of muc h us e unles s you are doing s ome
other advanc ed s preads heet automation.

You als o have to c ons ider whether the overhead of automation is


ac c eptable in your applic ation. I t might not be as effic ient as a
well- written c us tom func tion. H owever, it c an be a huge
times aver if you don't have time to write your own c us tom
func tions s uc h as the Percentile func tion.

Steve Huff
Hack 62. Use Word to Compare Data in
Two Access Tables

Look f or discrepancies the easy way, using Word's Document


Compare utility.

Sometimes , you have to c ompare data in two A c c es s tables .


U s ually you do this when you have one table that derives from
two different c opies of the databas e. T he data might differ
between the tables ; for example, s ome data has been updated in
one table, and now you need to unc over the dis c repanc ies .

You c an do this in a c ouple of ways . You c an us e s ome queries ,


but if there are many fields , query des ign c ould be diffic ult.
A nother option is to write c ode to read through both tables and
identify the differenc es . T his works but it als o takes a bit of time
to get the c ode working c orrec tly.

H ere's a great alternative: Word has a built- in feature that


c ompares two doc uments and highlights the differenc es .

T he firs t thing you need to do is export the A c c es s tables as


text files . Word then us es thes e to run a c omparis on. Figure 7 -
1 5 s hows the two tables already s aved as text. A s you c an s ee,
they appear identic al.

Figure 7-15. Two tables saved as text files


I n Word, open one of the text files . T hen, us e the Tools
C ompare and M erge D oc uments menu item to brows e to the
s ec ond text file. A s s hown in Figure 7 - 1 6 , you have options for
how to c ompare and merge the doc uments . I always c hoos e
"M erge into new doc ument." T hat way, I know the original files
aren't altered.

A new doc ument is c reated, but you immediately run into a


problem. Word's s pellc hec ker and grammar c hec ker will flag
nearly everything as inc orrec t bec aus e the export from A c c es s
c reates rec ords with no s pac e breaks . T his is c orrec t for the
data, but not as far as Word is c onc erned. So, the next thing to
do is turn off the s pellc hec ker and grammar c hec ker in Word's
O ptions dialog, as s hown in Figure 7 - 1 7 . By the way, the firs t
opened text file didn't flag any errors bec aus e it was s till a text
file. T he new merged doc ument, on the other hand, is a proper
Word doc ument.

Figure 7-16. Setting up the document


comparison
Figure 7-17. Turning off spellchecker and
grammar checker in Word
O nc e you c an s ee the doc ument for what it is , you c an s ee
plac es where the data does n't matc h bec aus e the data is
formatted with s trikethroughs , as s hown in Figure 7 - 1 8 .

Sc rolling through this data is a breeze. You c an quic kly s ee


where the data is different and dec ide what to do about it.

Andrea Mos s

Figure 7-18. Identifying unmatched data


Hack 63. Import Varied XML Data into
Access

A ccess is pretty good at importing simple XML data, but


sometimes you want to import data that isn't precisely the way
A ccess expects it to be.

A c c es s lets you import data from XM L files into its tables . For
example, let's c ons ider a databas e c ontaining a table that
defines a lis t of books . Figure 7 - 1 9 s hows the D es ign view for
this table. I t inc ludes s ix fields of three different types .

To begin, the table c ontains a few books , as s hown in Figure 7 -


20.

T he eas ies t way to s ee the XM L format A c c es s expec ts to


rec eive when it imports data to this table is to export s ome of
the data, whic h you c an do by s elec ting a table in the databas e
and then s elec ting E xport... from the File menu. I n this c as e, the
XM L format we'll need to let A c c es s import automatic ally looks
like the data that was jus t exported as XM L . I n other words ,
exporting rec ords into XM L s hows the XM L node s truc ture any
XM L data being imported bac k in s hould have. E xample 7 - 1
s hows the exported data.

Figure 7-19. A simple table to which we'll import


data
Figure 7-20. Test data in the books table

Example 7-1. New data for import

<?xml version="1.0" encoding="UTF-8"?>


<dataroot>
<books>
<ISBN>0596002637</ISBN>
<Title>Practical RDF</Title>
<Tagline>Solving Problems with the Resource Description Framework<
<Short_x0020_Description>The Resource Description Framework (RDF)
for describing and interchanging metadata on the Web.</Short_x0020
<Long_x0020_Description>The Resource Description Framework (RDF) i
for describing and interchanging metadata on the Web - anything fr
catalogs and worldwide directories to bioinformatics, Mozilla inte
structures, and knowledge bases for artificial intelligence projec
x0020_Description>
<PriceUS>39.95</PriceUS>
</books>
<books>
<ISBN>0596003838</ISBN>
<Title>Content Syndication with RSS</Title>
<Tagline>Sharing Headlines and Information Using XML</Tagline>
<Short_x0020_Description>RSS is sprouting all over the Web, connec
and providing news feeds.</Short_x0020_Description>
<Long_x0020_Description>RSS is sprouting all over the Web, connect
providing news feeds. Originally developed by Netscape in 1999, RS
stand for RDF Site Summary, Rich Site Summary, or Really Simple Sy
an XML-based format that allows Web developers to create a data fe
supplies headlines, links, and article summaries from a web site</
Description>
<PriceUS>29.95</PriceUS>
</books>
<books>
<ISBN>0596002912</ISBN>
<Title>XPath and XPointer</Title>
<Tagline>Locating Content in XML Documents</Tagline>
<Short_x0020_Description>Referring to specific information inside
can be like looking for a needle in a haystack: how do you differe
information you need from everything else?</Short_x0020_Descriptio
<Long_x0020_Description>Referring to specific information inside a
can be like looking for a needle in a haystack: how do you differe
information you need from everything else? XPath and XPointer are
related tools that play a key role in XML processing by allowing d
find these needles and manipulate embedded information.</Long_x002
<PriceUS>24.95</PriceUS>
</books>
</dataroot>

T he s truc ture begins with the dataroot element, though A c c es s


does n't ac tually c are what that c ontainer element's name is . T he
books element tells A c c es s this information goes into the books
table, and the ISBN, Title, Tagline, and other elements ins ide
eac h books element go to fields in the books table. T he only tric k
is in the Short Description and Long Description fields , whic h,
bec aus e XM L won't ac c ept s pac es in tag names , A c c es s prefers
to s ee as Short_x0020_Description and Long_x0020_Description.
A c c es s does n't c are what order the fields c ome in, but it will
rec ognize them only if they're c hild elements , not attributes .

To get s tarted, s elec t G et E xternal D ata from the File menu, and
then s elec t I mport. T he dialog box s hown in Figure 7 - 2 1 will
appear.

You might need to s elec t XM L from the "Files of type" drop- down
menu at the bottom bec aus e the dialog initially defaults to
A c c es s formats . Selec t a file, and c lic k I mport. T he I mport XM L
dialog box s hown in Figure 7 - 2 2 will appear.

You c an c lic k the plus s ign to the left of the books if you want to
ins pec t their s truc ture. I f you jus t c lic k O K, A c c es s c reates a
new table c alled books 1 (or whatever number avoids a c onflic t)
to import the XM L into A c c es s without c onflic ting with the prior
XM L table.

Figure 7-21. Initial Import dialog box


Figure 7-22. Import dialog box showing
structure of XML documents

T hat might be perfec tly fine bec aus e it gives you a c hanc e to
c ompare the new data with the old before merging the two.
A c c es s provides two more options , however: one that lets you
jus t c reate a new table bas ed on the s truc ture of the XM L file,
and another that lets you append the data in the XM L file to an
exis ting table. I n this c as e, we know the new books are different
from the old books , s o c lic k O ptions , and s elec t A ppend D ata to
E xis ting Table(s ), as s hown in Figure 7 - 2 3 .

I f you c lic k O K now, the extra books will be added to the exis ting
books table, as s hown in Figure 7 - 2 4 .

A c c es s refus es to import XM L data, whic h c aus es a c onflic t with


exis ting key relations hips . For example, if you import that s ame
doc ument again in the s ame way, you'll be rewarded with the
I mportE rrors table s hown in Figure 7 - 2 5 .

Figure 7-23. Import dialog box showing more


complex structure of XML documents and append
options
Figure 7-24. The results of importing a
document and appending its data

Figure 7-25. The results of importing a


document and appending its data when the data
is already there
U s ing the Trans form… button s hown in Figure 7 - 2 3 , you c an als o
perform c onvers ions , whic h make it eas ier to import data that
does n't arrive in a form that meets A c c es s 's expec tations . For
example, s uppos e information about a new book arrived in the
form s hown in E xample 7 - 2 .

Example 7-2. ch0812.xml, an attribute-bas ed XML document for


import

<update>
<books ISBN="0596003277" Title="Learning XSLT" Tagline="A Hands-On
Introduction to XSLT and XPath" Short_x0020_Description="A gentle
introduction to the complex intricacies of XSLT" Long_x0020
_Description="A gentle introduction to the complex intricacies of
XSLT and XPath, walking through the spec from simple work to
complex." PriceUS="34.95" />
</update>
I n E xample 7 - 2 , all data is s tored in attributes , and A c c es s
won't even look at attributes during an import. To get this
information into A c c es s , you need to us e a trans formation, s uc h
as the generic one s hown in E xample 7 - 3 , whic h c onverts all
attributes to c hild elements .

Example 7-3. ch0813.xs l, a s tyles heet for trans forming attributes


into elements

<?xml version="1.0" encoding="UTF-8"?>


<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XS
<!--Derived from recipe 6.1 of Sal Mangano's XSLT Cookbook-->

<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="ye

<xsl:template match="@*">
<xsl:element name="{local-name(.)}" namespace="{namespace-uri(..
<xsl:value-of select="."/>
</xsl:element>
</xsl:template>

<xsl:template match="node( )">


<xsl:copy>
<xsl:apply-templates select="@* | node( )"/>
</xsl:copy>
</xsl:template>

</xsl:stylesheet>

When applied to E xample 7 - 2 , the s tyles heet in E xample 7 - 3


produc es the res ult s hown in E xample 7 - 4 , whic h A c c es s c an
import eas ily.

A gain, A c c es s does n't c are what the root


element's name is ; update is an
appropriate des c ription for human
c ons umption.

Example 7-4. An "elementized" vers ion of the data in Example 7-2

<?xml version="1.0" encoding="UTF-8"?>


<update>
<books>
<ISBN>0596003277</ISBN>
<Title>Learning XSLT</Title>
<Tagline>A Hands-On Introduction to XSLT and XPath</Tagline>
<Short_x0020_Description>A gentle introduction to the complex intr
XSLT</Short_x0020_Description>
<Long_x0020_Description>A gentle introduction to the complex intri
and XPath, walking through the spec from simple work to complex.</
Description>
<PriceUS>34.95</PriceUS>
</books>
</update>

I f you tell A c c es s to import ch0812.xml, the file s hown in


E xample 7 - 2 , you won't have muc h to c hoos e from in the I mport
XM L dialog box, as s hown in Figure 7 - 2 6 .

Figure 7-26. Access's initial reaction to the


document that stores data in attributes

I f you c hoos e O ptions Trans form…, you c anadd the


s tyles heet, muc h as you did for the export trans formation. A dd
the s tyles heet to the lis t of trans formations , and s elec t c h0 8 1 3 ,
as s hown in Figure 7 - 2 7 .

When you c lic k O K, A c c es s applies the trans formation to the


doc ument, modifying the dis play of c omponents you s ee and
produc ing the res ult in Figure 7 - 2 8 .

I n this c as e, the table already exis ts , s o be s ure to s elec t


A ppend D ata to E xis ting Table(s ). When you c lic k O K, the data
from E xample 7 - 1 is added to the books table, as s hown in
Figure 7 - 2 9 .

Trans formations are a powerful tool in pretty muc h any area of


XM L development. U s ing a bit of XSLTadmittedly, a bit
c hallenging to learnyou c an c onvert the s truc tures you have into
the s truc tures A c c es s expec ts .

Figure 7-27. Selecting a stylesheet for


transformation
Figure 7-28. A transformed document ready for
import
7.6.1. See Also

"E xport XM L D ata Sanely" [Hack #64]

"Break T hrough V BA 's Trans formation Barrier" [Hack


#65]

Simon St. Laurent

Figure 7-29. The result of importing a


transformed document
Hack 64. Export XML Data Sanely

Working around the thorny issue of exporting related data to


XML.

E xporting a s ingle table to XM L produc es s ome eas ily reus able


data. E xporting multiple tables to XM L , however, might not
produc e data that other applic ations c an us e; it all depends on
how you s truc tured your tables and relations hips . You c an s olve
this problem in two ways : res truc ture your data or us e a query to
export data that's been unnormalized.

For our initial example, we'll s tart with a databas e c ontaining a


table that defines a lis t of books . Figure 7 - 3 0 s hows the D es ign
view for that table. I t inc ludes s ix fields of three different types .

Figure 7-30. A simple table for export


For the initial tes ts , this table c ontains jus t a little bit of
information. E xporting mature tables with thous ands of rec ords
c an quic kly produc e large XM L files definitely us eful in real life
but diffic ult for initial analys is . Figure 7 - 3 1 s hows a partial view
of the c ontent in the tes t table.

Figure 7-31. Test data in the books table

E xporting this table to XM L involves a few s teps , mos t of whic h


will be familiar to developers who have exported information from
A c c es s databas es before. T he proc es s s tarts by s elec ting the
books table in the databas e, then s elec ting E xport… from the File
menu. T he dialog box s hown in Figure 7 - 3 2 will appear, and you'll
need to s elec t XM L (* .xml) from the "Save as type" drop- down
box.

Figure 7-32. Selecting the destination for the


export
When you perform the export, A c c es s might ac tually c reate
more files than jus t the XM L file, but they'll all appear in the
s ame direc tory together with the XM L . O nc e you c lic k the E xport
button, a s mall dialog box with bas ic options , s hown in Figure 7 -
3 3 , appears .

Figure 7-33. Basic export options

For now, we'll ac c ept the defaults and jus t c lic k O K. T his res ults
in two files : books .xml and books .xs d. T he books .xml file c ontains
the information from the table, and books .xs d c ontains an XM L
Sc hema des c ription of that c ontent, annotated with a bit of
information s pec ific to A c c es s and its J et databas e engine.

T he books .xml file, s hown in E xample 7 - 5 , reflec ts the s truc ture


and c ontent of the original table c los ely.

Example 7-5. A s imple table export

<?xml version="1.0" encoding="UTF-8"?>


<dataroot xmlns:od="urn:schemas-microsoft-com:officedata" xmlns:xs
generated="2003-03-26T13:49:17">
<books>
<ISBN>0596005385</ISBN>
<Title>Office 2003 XML Essentials</Title>
<Tagline>Integrating Office with the World</Tagline>
<Short_x0020_Description>Microsoft has added enormous XML function
Excel, and Access, as well as a new application, Microsoft InfoPat
gets readers started in using those features.</Short_x0020_Descrip
<Long_x0020_Description>Microsoft has added enormous XML functiona
Excel, and Access, as well as a new application, Microsoft InfoPat
gets readers started in using those features.</Long_x0020_Descript
<PriceUS>34.95</PriceUS>
</books>
<books>
<ISBN>0596002920</ISBN>
<Title>XML in a Nutshell, 2nd Edition </Title>
<Tagline>A Desktop Quick Reference</Tagline>
<Short_x0020_Description>This authoritative new edition of XML in
provides developers with a complete guide to the rapidly evolving
Short_x0020_Description>
<Long_x0020_Description>This authoritative new edition of XML in a
provides developers with a complete guide to the rapidly evolving
Serious users of XML will find topics on just about everything the
including fundamental syntax rules, details of DTD and XML Schema
transformations, and APIs used for processing XML documents. Simpl
the only references of its kind among XML books.</Long_x0020_Descr
<PriceUS>39.95</PriceUS>
</books>
<books>
<ISBN>0596002378</ISBN>
<Title>SAX2</Title>
<Tagline>Processing XML Efficiently with Java</Tagline>
<Short_x0020_Description>This concise book gives you the informati
effectively use the Simple API for XML, the dominant API for effic
processing with Java.</Short_x0020_Description>
<Long_x0020_Description>This concise book gives you the informatio
effectively use the Simple API for XML, the dominant API for effic
processing with Java.</Long_x0020_Description>
<PriceUS>29.95</PriceUS>
</books>
</dataroot>

T his doc ument's root element, dataroot, is the only piec e of this
doc ument s pec ific to A c c es s :

<dataroot xmlns:od="urn:schemas-microsoft-com:officedata" xmlns:xs


xsd" generated="2003-03-26T13:49:17">

I t makes a names pac e dec laration for the od prefix, whic h is n't
ac tually us ed in this doc ument, and it inc ludes a pointer to the
XM L Sc hema des c ribing this doc ument's s truc ture. Bec aus e the
element names us ed here aren't in any names pac e, the
doc ument us es the xsi:noNamespaceSchemaLocation attribute to
identify the s c hema that s hould be us ed for all the elements in
this doc ument that have no names pac e. I t als o inc ludes one
s mall bit of metadata in the generated attribute, that identifies
the time and date when this XM L doc ument was c reated.

T he dataroot element c ontains three c hild books elements , eac h


indic ating a row in the books table. T heir c ontents map fairly
s imply to the names and values of the table c olumns :

<books>
<ISBN>0596002920</ISBN>
<Title>XML in a Nutshell, 2nd Edition</Title>
<Tagline>A Desktop Quick Reference</Tagline>
<Short_x0020_Description>This authoritative new edition of XML in
provides developers with a complete guide to the rapidly evolving
</Short_x0020_Description>
<Long_x0020_Description>This authoritative new edition of XML in a
provides developers with a complete guide to the rapidly evolving
Serious users of XML will find topics on just about everything the
including fundamental syntax rules, details of DTD and XML Schema
XSLT transformations, and APIs used for processing XML documents.
put, this is the only references of its kind among XML books.</Lon
Description>
<PriceUS>39.95</PriceUS>
</books>

T he only s ignific ant variation here involves the c olumn names ,


whic h inc lude s pac es . I ns tead of Short Description, now we have
Short_x0020_Description, following a c onvention M ic ros oft
developed for repres enting s pac es in XM L element names .

XM L forbids s pac es in element names


bec aus e they make it diffic ult to s eparate
the element name from the attributes , s o
A c c es s us es _x0020_, the U nic ode hex
number, for the s pac e.

E xporting individual tables is us eful, but s ometimes you might


want to export multiple tables and pres erve the relations hips
between them. A c c es s allows you to export a s et of tables ,
though it works mos t eas ily when only two tables are involved.

7.7.1. Exporting from Tables in a One-to-


Many Relationship

For our firs t example, we'll add a table that c ontains information
about (very fic tional) promotions for various books . Figure 7 - 3 4
s hows what this table looks like.

Figure 7-34. The promotions table


T he promotions table links to the books table through its
BookI D field, as s hown in Figure 7 - 3 5 .

Figure 7-35. Relationship between the books


and promotions tables
E xporting this pair of tables takes a few more s teps bec aus e
A c c es s lets you c hoos e how the export works . T he c hoic e of
whic h table is the bas e table makes a big differenc e in the export
res ults , s o the following examples will export it both ways .

We'll s tart by exporting the books table again, but this time, we'll
s elec t M ore O ptions from the dialog box s hown in Figure 7 - 3 6 .
Figure 7-36. Basic export options

C lic king M ore O ptions brings up a larger dialog with a lot more
c hoic es , as s hown in Figure 7 - 3 7 .

I n this c as e, all the information we need is on the firs t (D ata)


tab. C hec king the P romotions box and c lic king the O K button
tells A c c es s to export both the books table and the linked
rec ords of the promotions tablein this c as e, all of them. E xample
7 - 6 s hows an abbreviated vers ion of the export, with the new
c ontent from the promotions table in bold.

Figure 7-37. The full version of the Export XML


dialog box
Example 7-6. Exported linked tables
<?xml version="1.0" encoding="UTF-8"?>
<dataroot xmlns:od="urn:schemas-microsoft-com:officedata" xmlns:xs
generated="2003-03-31T16:37:01">
<books>
<ISBN>0596005385</ISBN>
<Title>Office 2003 XML Essentials</Title>
<Tagline>Integrating Office with the World</Tagline>
<Short_x0020_Description>...</Short_x0020_Description>
<Long_x0020_Description>...</Long_x0020_Description>
<PriceUS>34.95</PriceUS>

<promotions>
<PromotionID>1</PromotionID>
<BookID>0596005385</BookID>
<Name>Palm civet bonus</Name>
<Venue>Anywhere interested</Venue>
<Description>A stuffed-animal palm civet,
lovingly screen-printed to match the cover,
with every copy of the book.</Description>
<Cost>10000</Cost>
</promotions>
<promotions>
<PromotionID>3</PromotionID>
<BookID>0596005385</BookID>
<Name>Key chains</Name>
<Venue>Conferences</Venue>
<Description>keychains adorned with lovely palm civets
and the title of the book.</Description>
<Cost>1000</Cost>
</promotions>

</books>
<books>
<ISBN>0596002920</ISBN>
<Title>XML in a Nutshell, 2nd Edition</Title>
<Tagline>A Desk top Quick Reference</Tagline>
<Short_x0020_Description>...</Short_x0020_Description>
<Long_x0020_Description>...</Long_x0020_Description>
<PriceUS>39.95</PriceUS>
</books>
<books>
<ISBN>0596002378</ISBN>
<Title>SAX2</Title>
<Tagline>Processing XML Efficiently with Java</Tagline>
<Short_x0020_Description>...</Short_x0020_Description>
<Long_x0020_Description>...</Long_x0020_Description>
<PriceUS>29.95</PriceUS>

<promotions>
<PromotionID>2</PromotionID>
<BookID>0596002378</BookID>
<Name>Free filters</Name>
<Venue>Online/Safari</Venue>
<Description>Bonus SAX filters, open source-licensed,
for developers who visit the SAX2 book site.</Description>
<Cost>0</Cost>
</promotions>

</books>
</dataroot>

T he general pattern here is muc h like the original export of the


books table, exc ept that zero or more promotions elements whos e
BookID holds the s ame value as the c ontaining books element's
ISBN elementnow appear ins ide eac h books element. T his works
the s ame way that zero or more books elements appeared ins ide
the dataroot element. A ll the table c olumns are lis ted ins ide
eac h promotions element, making it eas y to rec ons truc t the
information in the promotions table or to treat the information as
a c omplete s et of information about eac h book. T here's no need
to rec ons truc t the original tables and c alc ulate primary
key/foreign key links .

A s s oon as you s tep beyond the one- to- many relations hip,
however, this kind of s imple c ontainment will fail you.

7.7.2. Exporting from Tables in a Many-to-


Many Relationship

A many- to- many relations hip, implemented with an intermediary


table, as s hown in Figure 7 - 3 8 , produc es XM L that mos t likely
will be us eful only if s omeone reimports it into A c c es s and works
with it there.

A c c es s lets you travers e this relations hip in an XM L export, as


s hown in Figure 7 - 3 9 . T his time, the export us es a [Lookup Data]
element to indic ate that s imply nes ting the data in the XM L
doc ument s truc tures is n't going to work. O ne- to- many
relations hips are repres ented us ing c ontainment, and many- to-
one relations hips are repres ented as s eparate piec es . I n this
c as e, the many- to- many relations hip inc ludes both of thos e
c hoic es .

Figure 7-38. Related tables with a many-to-


many relationship, expressed as two one-to
many relationships
Figure 7-39. Exporting related tables with a
many-to-many relationship
[Lookup Data] provides a warning that reas s embling s ome of
thes e relations hips is going to require extra lookup work on the
part of the c ons uming applic ation.

I f you reimport this data into A c c es s , it'll


do that work, s o this might not be a
problem.

E xample 7 - 7 s hows the res ults of this export.

Example 7-7. A many-to-many export combining containment and


lookup

<?xml version="1.0" encoding="UTF-8"?>


<dataroot xmlns:od="urn:schemas-microsoft-com:officedata" xmlns:xs
generated="2003-04-01T21:01:50">
<books>
<ISBN>0596005385</ISBN>
<Title>Office 2003 XML Essentials</Title>
<Tagline>Integrating Office with the World</Tagline>
<Short_x0020_Description>...</Short_x0020_Description>
<Long_x0020_Description>...</Long_x0020_Description>
<PriceUS>34.95</PriceUS>
<authorBookLink>
<bookISBN>0596005385</bookISBN>
<authorID>1</authorID>
</authorBookLink>
</books>
<books>
<ISBN>0596002920</ISBN>
<Title>XML in a Nutshell, 2nd Edition</Title>
<Tagline>A Desktop Quick Reference</Tagline>
<Short_x0020_Description>...</Short_x0020_Description>
<Long_x0020_Description>...</Long_x0020_Description>
<PriceUS>39.95</PriceUS>
<authorBookLink>
<bookISBN>0596002920</bookISBN>
<authorID>3</authorID>
</authorBookLink>
<authorBookLink>
<bookISBN>0596002920</bookISBN>
<authorID>4</authorID>
</authorBookLink>
</books>
<books>
<ISBN>0596002378</ISBN>
<Title>SAX2</Title>
<Tagline>Processing XML Efficiently with Java</Tagline>
<Short_x0020_Description>...</Short_x0020_Description>
<Long_x0020_Description>...</Long_x0020_Description>
<PriceUS>29.95</PriceUS>
<authorBookLink>
<bookISBN>0596002378</bookISBN>
<authorID>2</authorID>
</authorBookLink>
</books>
<authors>
<AuthorID>1</AuthorID>
<GivenName>Simon</GivenName>
<FamilyName>St.Laurent</FamilyName>
<FullName>Simon St.Laurent</FullName>
</authors>
<authors>
<AuthorID>2</AuthorID>
<GivenName>David</GivenName>
<FamilyName>Brownell</FamilyName>
<FullName>David Brownell</FullName>
</authors>
<authors>
<AuthorID>3</AuthorID>
<GivenName>Elliotte</GivenName>
<FamilyName>Harold</FamilyName>
<FullName>Elliotte Rusty Harold</FullName>
</authors>
<authors>
<AuthorID>4</AuthorID>
<GivenName>Scott</GivenName>
<FamilyName>Means</FamilyName>
<FullName>W. Scott Means</FullName>
</authors>
</dataroot>

N ow eac h books element c ontains one or more authorBookLink


elements , eac h holding an authorID element. T he value of that
authorID element maps to an authorID element ins ide an authors
element. I f the data is going bac k into A c c es s , this is fine, but if
it's going to another applic ationE xc el, perhaps , or an XSLT
trans formation into H T M L for a brows erthis is n't muc h fun.

T his might feel like a c as e in whic h it would make s ens e to s tore


repetitive (nonnormalized) data in the tables , but fortunately,
there's a better option: exporting a query ins tead of a table.

7.7.3. Using a Query to Tame the Export


By thems elves , queries don't provide nes ted views , but they
c ertainly make it eas ier to pres ent s ome kinds of
informationnotably, many- to- many relations hips . T he mec hanic s
of exporting queries are muc h like thos e of exporting s ingle
tables , and the res ults are s imilar.

A c c es s s upports SQ L queries , obvious ly,


bec aus e that's at the heart of its
func tionality. A c c es s does n't, however,
s upport other s tandards for querying, s uc h
as XQ uery.

To demons trate, let's export a SQ L query named books ByA uthor,


whic h us es the books , authors , and authorBookL ink tables to
c reate a lis t of books s orted by author. T he SQ L for the query
expres s es the relations hips an XM L proc es s or working with the
linked table export would otherwis e have to deal with:

SELECT authors.GivenName, authors.FamilyName, books.ISBN, books.Ti


FROM books INNER JOIN (authors INNER JOIN authorBookLink ON author
= authorBookLink.authorID) ON books.ISBN = authorBookLink.bookISBN
ORDER BY authors.FamilyName;

T he interfac e for exporting a query is the s ame as the interfac e


for a table, exc ept there is no option for exporting linked
information. When you export a query, all the information you
want to export mus t be in that query. E xporting the query
produc es the res ult s hown in E xample 7 - 8 .

Example 7-8. An exported query

<?xml version="1.0" encoding="UTF-8"?>


<dataroot xmlns:od="urn:schemas-microsoft-com:officedata" xmlns:xs
xsd" generated="2003-04-02T14:47:59">
<booksByAuthor>
<GivenName>David</GivenName>
<FamilyName>Brownell</FamilyName>
<ISBN>0596002378</ISBN>
<Title>SAX2</Title>
</booksByAuthor>
<booksByAuthor>
<GivenName>Elliotte</GivenName>
<FamilyName>Harold</FamilyName>
<ISBN>0596002920</ISBN>
<Title>XML in a Nutshell, 2nd Edition</Title>
</booksByAuthor>
<booksByAuthor>
<GivenName>Scott</GivenName>
<FamilyName>Means</FamilyName>
<ISBN>0596002920</ISBN>
<Title>XML in a Nutshell, 2nd Edition</Title>
</booksByAuthor>
<booksByAuthor>
<GivenName>Simon</GivenName>
<FamilyName>St.Laurent</FamilyName>
<ISBN>0596005385</ISBN>
<Title>Office 2003 XML Essentials</Title>
</booksByAuthor>
</dataroot>
J us t as in a tabular repres entation of the query, information
repeats notably, the I SBN and title of XML in a Nuts hell, whic h has
two authors . I f you're s ending data to an applic ation that lac ks
A c c es s 's apprec iation for relations between tables , this
approac h will probably work muc h more eas ily.

7.7.4. See Also

"I mport Varied XM L D ata into A c c es s " [Hack #63]

"Break T hrough V BA 's Trans formation Barrier" [Hack


#65]

Simon St. Laurent


Hack 65. Break Through VBA's
Transformation Barrier

Strange but true: A ccess supports XSLT transf ormation on input


when you use the GUI, but not when you automate the process
with VBA . The same goes f or output. Fortunately, you can work
around this by calling the MSXML parser directly.

T he examples in "I mport Varied XM L D ata into A c c es s " [Hack


#63] give s ome ideas for how to get information into your
A c c es s tables even if the data arrives in a format other than the
s imple element- only form A c c es s expec ts . H owever, if s uc h data
arrives on a regular bas is , you probably don't want to be c lic king
through forms every time you need to import more data.

U nfortunately, c onverting thes e s teps to an automated V BA


proc es s is a c hallenge bec aus e the ImportXML func tion does n't
provide a plac e for any trans formations . A s it turns out, neither
does the ExportXML func tion.

T he s yntax of the ImportXML func tion looks like this :

Application.ImportXML (DataSource, ImportOptions)

I t takes only a data s ourc e, the name and path of the XM L file to
import, and an options c ons tant acAppendData, acStructureAndData
(the default), or acStructureOnly. T here is no option for an XSLT
trans formation. Similarly, the ExportXML func tion looks like this :

Application.ExportXML (ObjectType, DataSource, DataTarget,

T he PresentationTarget argument does have s omething to do with


trans formation, but it's only for output. I t identifies where
A c c es s will put a s tyles heet for turning the XM L into H T M L
bas ed on its own expec tations , not yours .

You c an get around thes e problems in two ways . Firs t, you c an


write s ome c us tom c ode. T he import vers ion will ins tantiate an
XM L pars er (probably M SXM L ), read the c ontent from the
doc ument however you deem appropriate, and then us e A D O ,
D A O , or SQ L Update queries to put the data in the databas e. T he
export vers ion will read data from the databas e and write it to an
M SXM L D O M tree as nec es s ary.

T his might be appropriate if you have c omplic ated c as es , but it's


a lot of c ode for what's mos t likely a s imple problem, and you
c an't tes t how it works (or reus e that work) outs ide of A c c es s .

A more likely approac h, if you c an s tand working with XSLT, is to


add a s tep before the import or after the export that performs an
extra trans formation. Bec aus e A c c es s does n't let you pas s
objec ts to the import or get objec ts from the export, you need to
work with temporary files to produc e the res ults you want.
C onveniently, you c an us e the s ame func tion for both c as es .

A s imple vers ion of this func tion looks like this :

Private Sub Transform(sourceFile, stylesheetFile, resultFile)

Dim source As New MSXML2.DOMDocument30


Dim stylesheet As New MSXML2.DOMDocument30
Dim result As New MSXML2.DOMDocument30

' Load data.


source.async = False
source.Load sourceFile

' Load style sheet.


stylesheet.async = False
stylesheet.Load stylesheetFile

If (source.parseError.errorCode <> 0) Then


MsgBox ("Error loading source document: " & source.parseError.r
Else
If (stylesheet.parseError.errorCode <> 0) Then
MsgBox ("Error loading stylesheet document: " & _
stylesheet.parseError.reason)
Else
' Do the transform.
source.transformNodeToObject stylesheet, result
result.Save resultFile
End If
End If

End Sub

T he transform func tion takes three arguments : the path of a


s ourc e file holding the original XM L , the path of a s tyles heet file
holding the XSLT that will be us ed to trans form it, and the path to
whic h the res ulting doc ument s hould be s aved. Typic ally, you'll
want to c all transform before us ing A c c es s 's native ImportXML
func tion or after you've us ed the ExportXML func tion.

For example, you might import XM L files to a table direc tly with
this c all:

Application.ImportXML "http://simonstl.com/ora/updateBook.

But if that XM L file s tored the data as attributes , and you wanted
to apply a trans formation to that data before you imported it into
A c c es s , you might do this ins tead:

Transform "http://simonstl.com/ora/updateBook.xml", _
"C:\xslt\attsToElem.xsl", _
"C:\temp\tempImport.xml"
Application.ImportXML "C:\temp\tempImport.xml", acAppendData

Similarly, you c an apply a trans formation after you exported


data, turning it into H T M L :

Application.ExportXML acExportTable, "books", "C:\temp\tempExport.


Transform "C:\temp\tempExport.xml", _
"C:\xslt\booksToHTML.xsl", _
"C:\export\exportedBooks.html"

Writing XM L doc uments out to files and then repars ing them is n't
effic ient by any means , but it patc hes a gap left by the A c c es s
A P I for importing and exporting XM L . U nles s you're dealing with
huge volumes of data, or doing this proc es s ing c ons tantly, us ers
of your databas es aren't likely to notic e a big differenc e. I mport
and export are us ually pretty s low operations anyway.
7.8.1. See Also

"I mport Varied XM L D ata into A c c es s " [Hack #63]

"E xport XM L D ata Sanely" [Hack #64]

Simon St. Laurent


Hack 66. Leverage SQL Server Power by
Calling Stored Procedures

Get a leg up on perf ormance when using SQL Server data.

D evelopers c reating A c c es s applic ations that are front ends to


SQ L Server databas es have two c hoic es for their applic ation
type. T he M ic ros oft- rec ommended c hoic e is to us e an A c c es s
data projec t (A D P ), whic h is direc tly tied to the SQ L Server
databas e. T his native- mode O L E D B c onnec tion res ults in a
lighter- weight, better- performing front end that c an direc tly us e
SQ L views , s tored proc edures , and us er- defined func tions . I t
als o lets developers des ign objec ts direc tly on the s erver (no
need to us e E nterpris e M anager).

D es pite thes e advantages , many s ituations forc e developers to


us e O D BC linked tables in a traditional A c c es s M D B file. N ot
the leas t of thes e is the ability to c reate loc al tables in the M D B
(in an A D P, even the Switc hboard I tems table mus t be on the
s erver) and the ability to c onnec t to other data s ourc es (s uc h as
other A c c es s databas es , E xc el s preads heets , text files , and s o
on). J us t bec aus e you c hoos e to us e an M D B as a front end
does n't mean you have to give up the s erver- s ide proc es s ing
power of SQ L Server s tored proc edures .

7.9.1. Hooking Up with ODBC


When an A c c es s M D B is us ing O D BC links to SQ L Server, all
data proc es s ing is done on the c lient s idethat is , within A c c es s
on the works tation. I f a lis tbox on a form gets filtered by a
c ombo box s elec tion, all the rec ords are returned over the
network to A c c es s and A c c es s applies the filter. A lternatively,
the us e of s tored proc edures c an inc reas e performanc e in your
A c c es s M D Bs by s hifting the filtering to the s erver. Stored
proc edures are powerful bec aus e they c ombine the data- joining
c apabilities of A c c es s queries or SQ L views with the ability of
V BA proc edures to ac c ept parameters and to loop and proc es s
data.

T- SQ L , M ic ros oft SQ L Server's vers ion of the SQ L language, is


s omewhat different from the J et (A c c es s 's ) flavor of SQ L . I t is
als o muc h different from V BA . H owever, if you c an c reate A c c es s
queries and write V BA func tions , you c an learn to write SQ L
s tored proc edures . I t is n't diffic ult to bec ome good enough in T-
SQ L to inc reas e the performanc e of your applic ations . Whether
you ins tall M SD E (the lite vers ion of SQ L Server that s hips with
M ic ros oft O ffic e) or SQ L Server its elf, you c an look at the s tored
proc edures within the N orthwind databas e to get s tarted.

T he A D O library is one way to exec ute s tored proc edures in


A c c es s . You do this in V BA by exec uting a Command objec t whos e
c ommand text is the s tored proc edure name. Firs t it is
nec es s ary to open a Connection objec t on the SQ L Server
databas e. T he c ode in E xample 7 - 9 exec utes the
CustOrdersOrders s tored proc edure that s hips with N orthwind,
s ending in the muc h- abus ed customerid ALFKI to fill an A D O
rec ords et with all the orders belonging to A lfreds Futterkis te.

Example 7-9. Running a s tored procedure

Dim cn As ADODB.Connection
Dim sp As ADODB.Command
Dim rs As ADODB.Recordset
Set cn = New ADODB.Connection
cn.ConnectionString = CurrentDb.TableDefs("dbo_customers").Connect
cn.Open
Set sp = New ADODB.Command
sp.ActiveConnection = cnSQL
sp.CommandType = adCmdStoredProc
sp.CommandText = "CustOrdersOrders"
sp.Parameters.Refresh
sp.Parameters("@customerid") = "ALFKI"
Set rs = sp.Execute

A c c es s , however, c an't us e A D O rec ords ets in c ertain


s ituations . A lthough A c c es s us es A D O more and more with
every new vers ion releas e, A c c es s 2 0 0 3 s till has deep ties to
D A O , s o muc h s o that M ic ros oft put bac k a default referenc e to
D A O in V BA , after not inc luding it in A c c es s 2 0 0 2 (XP ). A data-
entry form bound to a linked table will have an underlying
rec ords et that is n't A D O , but rather, is D A O . C ontrols s uc h as
c ombo boxes or lis tboxes , on unbound or D A O - bound forms ,
require their rec ords ets to be D A O as well.

7.9.2. Creating a Pass-Through Query

A c c es s c an tap into s tored proc edure power and get a D A O


rec ords et filled with data via a s tored proc edure us ing an
underutilized feature known as a Pass-Through query. C reating a
Pass-Through query is relatively s traightforward, and the res ults
returned are in a D A O rec ords et, appropriate for us e in any
A c c es s objec t or c ontrol that c an us e a query as its data
s ourc e.
To c reate a Pass-Through query, s elec t Q ueries in the D atabas e
window, and c lic k N ew. C lic k D es ign V iew, and then c lic k O K.
C lic k C los e on the Table lis t to go direc tly into D es ign view. O n
the Q uery menu, c lic k SQ L- Spec ific , and then c lic k P as s -
T hrough, as s hown in Figure 7 - 4 0 .

Figure 7-40. Creating a Pass-Through query


T he query des igner will s witc h to SQ L view and allow only SQ L
s tatements to be entered. E nter CustOrdersOrders 'ALFKI' in the
SQ L view of the query des igner. C lic k Save, and name the query
qry_C us tO rders O rders _pt.

A t this point, A c c es s does n't know where to pas s this query. O n


firs t exec ution, you are prompted for the data s ourc e c onnec tion
to us e: c C hoos e the s ame data s ourc e you us ed to link your
SQ L tables . A fter c hoos ing the appropriate data s ourc e, A c c es s
s ends the SQ L s tring c ontained in the query to the s erver, and
SQ L runs the s tored proc edure and returns the res ults to
A c c es s , as s hown in Figure 7 - 4 1 .

Figure 7-41. Data returned from SQL Server via


a stored procedure
Steve Conklin
Hack 67. Manage Word Documents from
Access

Tap into the Word object library to copy A ccess data directly
into a Word document.

A s is the c as e with all M ic ros oft O ffic e produc ts , Word has a


s ignific ant number of expos ed objec ts and methods to work with,
and bec oming familiar with a dec ent number of thes e is a
c hallenge worth undertaking.

T his hac k c reates a proc edure that plac es data from A c c es s


into a table in a Word doc ument. T he c onc epts here als o apply to
other Word manipulations . P erhaps this will be your s pringboard
into a new avenue of O ffic e development.

7.10.1. Hooking into Word

I n an A c c es s c ode module, we're going to plac e a routine to


work with an exis ting Word doc ument. To make this a little eas ier,
we'll s et a referenc e to Word's objec t library. We'll do this ins ide
the A c c es s V B E ditor, us ing the Tools Referenc es menu
and the Referenc es dialog box, as s hown in Figure 7 - 4 2 . N ote
that your vers ion number of the Word library might differ, s o us e
whatever you have.
Figure 7-42. Setting a reference to the Word
object library
7.10.2. The Code

T he next thing to do is enter the c ode. T his mus t go into an


A c c es s c ode module:

Sub Access_to_Word()
Dim conn As ADODB.Connection
Set conn = CurrentProject.Connection
Dim recset As ADODB.Recordset
Set recset = New ADODB.Recordset
Dim row_num As Integer
Dim col_num As Integer
Dim word_doc As Object
'Assumes Word doc is in same path - change name and path as needed
Set word_doc = GetObject(Application.CurrentProject.Path & "\Custo

'get data from Access table


recset.Open "Select * From Customers Where State='OR'", _
conn, adOpenKeyset, adLockOptimistic
'get the record count - used to create Word Table
recset.MoveLast
recset.MoveFirst

With word_doc
'navigate to Word bookmark and create table
'the number of table rows matches the recordset row count
'the number of table columns matches the number of recordset field
.Bookmarks("Customers").Select
.Tables.Add Range:=Selection.Range, _
NumRows:=recset.RecordCount, NumColumns:=recset.Fields.Co
For row_num = 1 To recset.RecordCount
For col_num = 1 To recset.Fields.Count
.Tables(.Tables.Count).Cell(row_num, col_num).
Select Selection.TypeText recset.Fields(col_num - 1)
Next col_num
'next database record
recset.MoveNext
Next row_num
End With
recset.Close
Set recset = Nothing
Set word_doc = Nothing
MsgBox "done"
End Sub

H ere are s ome highlights of this c ode:

T he A c c es s data is gathered into a rec ords et.

T he GetObject func tion is referenc ed to the exis ting Word


doc ument. N ote that this example as s umes the
databas e and the doc ument are in the s ame direc tory.
A ls o, the name of the doc ument is hardc oded, but you
c an c hange this as nec es s ary.

T he doc ument has a prees tablis hed bookmark named


C us tomers . T his is us ed as a guide to where to c reate
the table.
A Word table is c reated, and its row and c olumn
dimens ions matc h thos e of the rec ords et. T his ens ures
the new Word table is exac tly the c orrec t s ize to hous e
the data.

T he Word table is populated c ell by c ell by looping


through the rec ords et. A n outer loop c yc les through the
rec ords et rows , and in eac h row an inner loop c yc les
through eac h field.

7.10.3. The Data Has Landed Intact

A fter running this c ode, the doc ument has a table with the data,
as s hown in Figure 7 - 4 3 . N ote that there is no c onnec tion bac k
to A c c es s ; the data is jus t es s entially part of the Word
doc ument.

N ote that this s implis tic example as s umes a number of things :


the bookmark exis ts , there is no exis ting table, and the A c c es s
table is n't too large in terms of rows and fields to make the Word
table too dens ely pac ked.

N onetheles s , this hac k s erves as a brief introduc tion to tapping


into Word objec ts . Bec aus e the referenc e has been s et to the
library, you c an now us e the O bjec t Brows er in A c c es s to review
Word's objec ts .

Figure 7-43. The Access data in a Word table


Hack 68. Use Access as a Front End to
MySQL

MySQL is a widely used open source database program that


of ten runs on Linux web servers, and A ccess makes a great
f ront end f or data entry and reporting.

M ySQ L is a wildly s uc c es s ful open s ourc e SQ L databas e that


runs on mos t L inux- bas ed web s ervers . I t's the perfec t databas e
to s tore information for us e in databas e- driven web s ites
bec aus e you c an us e the P H P programming language to read the
data from M ySQ L and dis play it on web pages . I n fac t, the
c ombination of L inux, A pac he (an open s ourc e web s erver that
runs on L inux), M ySQ L , and P H P is s o popular that it's known by
its initials : L A M P.

H owever, M ySQ L does n't hold a c andle to A c c es s when it c omes


to forms and reports . M ySQ L has no built- in form editor or report
writer. I ns tead, you type c ommands at a c ommand line or write
programs (us ually as part of P H P - bas ed web pages ) to enter,
edit, and dis play information.

But who wants to c reate programs that dis play data entry forms
for eac h table? T his is where A c c es s c omes in. A c c es s c an add
a friendly fac e to your M ySQ L databas e. I n the s ame way an
A c c es s databas e c an link to tables in another A c c es s databas e,
you c an link to tables in a M ySQ L databas e on your web s erver
over a L A N or the I nternet. O nc e the tables are linked, you (or
your us ers ) c an us e A c c es s forms to enter or edit the M ySQ L
data and A c c es s reports to dis play it.

7.11.1. Installing the MySQL Tools

M ySQ L does have a Windows - bas ed utility you c an us e for


c reating and editing the s truc ture of the tables in your M ySQ L
databas es . T he older vers ion of the program is c alled M ySQ L
C ontrol C enter, and it has been replac ed by M ySQ L Q uery
Brows er. You c an download either program from the M ySQ L web
s ite (http://dev.mys ql.c om/downloads /) for free. T he manual for
M ySQ L Q uery Brows er is available online at
http://dev.mys ql.c om/doc / query brows er/en/, or you c an
download it as a P D F or Windows H elp file.

When you s tart M ySQ L Q uery Brows er, you s pec ify the s erver on
whic h M ySQ L runs , your M ySQ L us ername, and your pas s word.
O nc e c onnec ted, you s ee a lis t of the tables for whic h you have
ac c es s permis s ion, and you c an view or edit the data or
s truc ture of the tables , as s hown in Figure 7 - 4 4 .

Figure 7-44. MySQL Query Browser


M ySQ L Q uery Brows er is us eful, but it's not the tool to give to
your databas e us ers . For example, you c an't c reate forms with
data validation, c ombo boxes , or s ubforms , and you c an't c reate
formatted reports . For this , you need A c c es s .

For A c c es s to c onnec t to a M ySQ L databas e, you need to ins tall


the M ySQ L C onnec tor/O D BC driver (als o c alled the M ySQ L
O D BC or M yO D BC driver). T his driver lets A c c es s
c ommunic ate with M ySQ L via O pen D ataBas e C onnec tivity
(O D BC ). You c an download the M ySQ L O D BC driver from
(http://dev.mys ql.c om/downloads /) for free. A fter you ins tall it, a
new option appears when you link to external tables from an
A c c es s databas e.

You don't need to ins tall M ySQ L Q uery Brows er on every


c omputer on whic h your A c c es s databas e will run; you need it
only if you plan to us e it to look at or c hange the s truc ture of the
tables in your M ySQ L databas e. But every c omputer on whic h
your A c c es s databas e runs needs the M ySQ L O D BC driver
ins talled bec aus e A c c es s us es the driver every time you open a
linked M ySQ L table.

I f you plan to c onnec t to a M ySQ L databas e over the I nternet,


your reques ts will probably need to pas s through one or more
firewalls . T he M ySQ L O D BC driver c ommunic ates over port
3 3 0 6 , s o this port mus t be open on all the firewalls between your
c omputer and the M ySQ L s erver. You c an s pec ify a different port
when you c reate the link from A c c es s to M ySQ L , in c as e your
M ySQ L s erver is c onfigured to us e a nons tandard port number.

7.11.2. Linking to MySQL Tables


O nc e you've got the M ySQ L O D BC driver ins talled, linking
A c c es s to M ySQ L tables requires two s teps : making s ure the
tables c ontain the right fields and making the link. For the
A c c es s /M ySQ L link to work right when editing data into the
tables , eac h table to whic h you link needs to have the following
two fields (the names of the fields don't matter):

AutoNumber

I n M ySQ L , this is an INT (integer) field of s ize 11 with the


UNSIGNED and AUTO INC (auto inc rement) options s elec ted.
T his field mus t be the primary key for the table.

Date

T his field is updated automatic ally any time the rec ord
is edited. I n M ySQ L , this is a TIMESTAMP field.

M os t tables have thes e two fields anyway; good databas e des ign
s ugges ts us ing an A utoN umber field as the primary key for mos t
tables . H owever, if your M ySQ L tables don't have thes e fields ,
you need to us e M ySQ L Q uery Brows er or s ome other tool to
add them.

C reating the links in A c c es s is a s nap. C hoos e File G et


E xternal D ata L ink Tables to dis play the L ink dialog box.
Set the file type to O D BC D atabas es , and you s ee the Selec t
D ata Sourc e dialog box, whic h lis ts O D BC databas es you've
us ed before, in the form of D ata Sourc e N ame (D SN ) files that
c ontain the c onnec tion information for the databas e. I f you are
opening a table in a databas e you've us ed before, c hoos e the
D SN file for the databas e, c lic k O K, and c hoos e the tables to
link.
I f you are linking to a M ySQ L databas e for the firs t time, c lic k
the N ew button in the Selec t D ata Sourc e dialog box, c hoos e
M ySQ L O D BC D river from the driver lis t (it's near the end), c lic k
N ext, s pec ify a name for the D SN file you are c reating to s tore
the c onnec tion information, and c lic k Finis h. You'll s ee the
M ySQ L O D BC D river D SN C onfiguration dialog box, s hown in
Figure 7 - 4 5 .

Figure 7-45. Specifying connection information


for a MySQL database
Fill in the hos tname of the M ySQ L s erver, the name of the
databas e that c ontains the tables to whic h you want to link, and
your M ySQ L us ername and pas s word. I f your M ySQ L s erver
does n't c ommunic ate over port 3 3 0 6 (the default), enter the
port number, too. I f you want to make s ure your c onnec tion
information is c orrec t, c lic k Tes t D ata Sourc e, and A c c es s will
try to c onnec t to the M ySQ L databas e and tell you whether it
s uc c eeded.

When you c lic k O K, A c c es s dis plays the L ink Tables dialog box
(the s ame dialog box you us e when linking to tables in other
A c c es s databas es ). H owever, in addition to the lis t of tables , you
c an s elec t the Save P as s word c hec kbox. T his option is
mis named bec aus e A c c es s s tores the M ySQ L pas s word no
matter what; this c hec kbox ac tually c ontrols whether it s tores
the M ySQ L us ername. I f you don't s elec t this option, you have
to enter the M ySQ L us ername eac h time your A c c es s databas e
makes its initial c onnec tion to the M ySQ L databas e.

I f you have any s ec urity c onc erns about


the information in the table, don't c hec k
the Save P as s word c hec kbox when you
c reate a link to a M ySQ L table. I f you s ave
both the M ySQ L us ername and pas s word
in the A c c es s databas e, anyone who c an
open the A c c es s databas e c an make
c hanges to the information in your M ySQ L
databas e.

L inked tables from M ySQ L databas es appear on the Tables lis t


in the A c c es s D atabas e window with a blue- green globe ic on
rather than the us ual box ic on. You c an't c hange the s truc ture of
linked tables , and you c an't c reate relations hips that enforc e
referential integrity between tables , but otherwis e, you c an us e
the data jus t as if it were in your A c c es s databas e. I f you
c hange the s truc ture of a table in the M ySQ L table, be s ure to
relink it by c hoos ing Tools D atabas e U tilities L inked
Table M anager.

7.11.3. Hacking the Hack

When you s pec ify the information about a M ySQ L databas e,


A c c es s c reates a D SN file and s tores it in the C:\Program
Files \Common Files \ODBC\Data Sources folder (as s uming Windows
is ins talled on your C: drive). Strangely, A c c es s als o s tores the
information in the databas e (M D B) file, s o it does n't read this
D SN file again after it c reates it. I f you s et up an A c c es s
databas e with M ySQ L links and then take the databas e to
another mac hine, all you need is the M ySQ L O D BC driver
ins talled. You don't need to bring along the D SN file, too.

A c c es s s tores the M ySQ L c onnec tion information as a


c onnec tion s tring that looks like this (the line breaks after eac h
s emic olon are inc luded for readability only):

ODBC;
DRIVER={MySQL ODBC 3.51 Driver};
DESC=;
DATABASE=financial;
SERVER=data.gurus.com;
UID=odbc-margy;
PASSWORD=ziasti;
PORT=;
OPTION=;
STMT=;
TABLE=Categories

To s ee the c onnec tion s tring for a linked table, s elec t the table
in the D atabas e window, c lic k D es ign, c lic k Yes when A c c es s
points out that the table s truc ture will be read- only, right- c lic k
anywhere in the D es ign window, and c hoos e P roperties from the
menu that appears . A s you c an s ee, both the us ername and the
pas s word (if you have c hos en to s ave the pas s word) appear in
plain texts o muc h for s ec urity. You c an't edit the c onnec tion
s tring bec aus e the table s truc ture c an't be edited.

I f you open a D SN file with N otepad or another text editor, you


s ee the s ame c onnec tion s tring, but without the s emic olons . You
c an edit the D SN file, but it won't affec t exis ting linked tables ; it
affec ts only tables that you link us ing the D SN file in the future.

7.11.4. See Also

M ySQ L doc umentation at http://dev.mys ql.c om/doc /

M ySQ L C onnec tor/O D BC driver doc umentation at


http://dev.mys ql.
c om/doc /mys ql/en/O D BC _C onnec tor.html

P H P doc umentation at http://www.php.net/doc s .php


Margaret Levine Young
Hack 69. Send Access Data Through
Outlook Automatically

Implement bulk emailing of your data by tapping into Outlook


objects.

T he purpos e of mos t databas es is to s tore and report


information. O ften, it is nec es s ary to s end the reports that are
generated by a databas e to multiple us ers . T his does n't have to
be a manual proc es s . By automating M ic ros oft O utlook from
A c c es s V BA , it is pos s ible to automatic ally generate reports
and s end them via email.

T he firs t item you need to determine is whether you are going to


s end emails only through your addres s book. I f you dec ide to do
that, you don't need to adjus t any of the default s ettings in
O utlook. I f, however, you want to s end to any addres s through
your applic ation, you need to make a c hange in O utlook.

By default, O utlook automatic ally c hec ks the email addres s es


when you s end an email. When you are doing this in an
automated fas hion, you will have errors to deal with if an email
addres s does n't exis t in your addres s book. To s hut off this
feature in O utlook, go to the Tools O ptions dialog.

O n the O ptions dialog, s hown in Figure 7 - 4 6 , c lic k the E - mail


O ptions button in the P referenc es tab, and then c lic k the
A dvanc ed E - mail O ptions button s hown in Figure 7 - 4 7 .
T his ac tion brings up a dialog box with three s ec tions : "Save
mes s ages ," "When new items arrive in my I nbox," and "When
s ending a mes s age," as s hown in Figure 7 - 4 8 .

T he "When s ending a mes s age" s ec tion c ontains a c hec kbox for


"A utomatic name c hec king," as s hown in Figure 7 - 4 8 . C hec k
the box if you want O utlook to c hec k addres s es , and unc hec k it
if you want to s imply s end the mes s ages without c hec king.

N ow that you have determined how you want O utlook to handle


addres s es , you are ready to build email func tionality into your
applic ation. A lthough you will eventually want to have reports
bas ed on parameterized queries that go to different us ers , this
example s hows how to s end individual reports to multiple
rec ipients .

Figure 7-46. Outlook's Options dialog


I t s hould be noted that to deal with the inc reas ing number of
problems with virus es , O utlook prompts the us er to allow ac c es s
to the addres s book and to s end the mes s ages . A lthough this
prevents you from s ending email unattended, it is c ertainly muc h
eas ier than doing everything manually every time. I n older
vers ions of O utlook, you c an s end multiple emails unattended.

To ac c omplis h the email tas k, c reate a table c alled tbl_E mail


with two text fields : E mail_A ddres s (5 0 c harac ters ) and
Report_N ame (2 5 c harac ters ). You c an make the fields larger if
it is warranted. I f you us e automatic name c hec king, you jus t
need to put in the dis play name of the people you want to s end
the mes s ages to in the E mail_A ddres s field. I f you aren't us ing
automatic name c hec king, you need to enter the full email
addres s . P ut in two or three rec ords for your tes t.

I n a normal applic ation environment, you would want this to be


driven from a form; however, this example s imply s ends all the
emails through a proc edure.

Figure 7-47. The Advanced E-mail Options


dialog
To c reate the proc edure, go to the M odules tab in A c c es s , and
c lic k N ew. O nc e you are in a blank module, go to I ns ert
P roc edure, make s ure the radio boxes for Sub and P ublic are
s elec ted, and fill in SendO utlookE mail in the N ame text box.
T his c reates the s hell for your proc edure.

N ow you need to c reate a referenc e to M ic ros oft O utlook. D o


this by going to Tools Referenc es and c hec king the box for
the vers ion of O utlook that you have. N ow you c an referenc e the
O utlook objec t model. I f you us e a vers ion of A c c es s other than
A c c es s 2 0 0 3 , you might need to c hec k the box for M ic ros oft
D ata A c c es s O bjec ts (mine is M ic ros oft D A O 3 .6 O bjec t
L ibrary). N ow you are ready to begin c oding.

7.12.1. The Code

T he c ode is s hown in E xample 7 - 1 0 .

Figure 7-48. Changing how Outlook handles


names and email addresses
Example 7-10.Acces s VBA code to s end email

Public Sub SendOutlookEmail()


Dim db As DAO.Database
Dim ReportRs As DAO.Recordset
Dim EmailRS As DAO.Recordset

Dim olApp As Outlook.Application


Dim olMail As Outlook.MailItem

Dim EmailColl As Collection


Dim varEmail As Variant
Dim FileName As String

' Outlook only allows one instance to be open at a time,


' so you can call it with New and it will use the instance
' that you already have open. I suggest having Outlook open
' already so you are not prompted for user name or password.
Set olApp = New Outlook.Application
Set db = CurrentDb
Set ReportRs = db.OpenRecordset( _
"Select Report_Name from tbl_Email Group by Report_Name")

ReportRs.MoveFirst

While Not ReportRs.EOF


Set EmailColl = New Collection
Set EmailRS = db.OpenRecordset( _
"Select Email_Address from tbl_Email Where Report_Name = " & "
ReportRs.Fields(0).Value & """" & ";")
EmailRS.MoveFirst
While Not EmailRS.EOF
EmailColl.Add EmailRS.Fields(0).Value
EmailRS.MoveNext
Wend

EmailRS.Close
Set EmailRS = Nothing

Set olMail = olApp.CreateItem(olMailItem)


olMail.subject = "Monthly Report"
For Each varEmail In EmailColl
olMail.Recipients.Add varEmail
Next
olMail.Body = "Your Monthly Report is attached"
FileName = "C:\Reports\" & ReportRs.Fields(0).Value & ".rtf"
DoCmd.OutputTo acReport, ReportRs.Fields(0).Value, _
acFormatRTF, FileName
' If you had multiple attachments, you could add them one at a t
olMail.Attachments.Add FileName, olByValue, 1, "Monthly Report"
olMail.Send

Set olMail = Nothing


Set EmailColl = Nothing

ReportRs.MoveNext
Wend

ReportRs.Close
Set ReportRs = Nothing

Set olApp = Nothing


' You can close Outlook with olApp.Quit - but since I suggested
' that you keep it open I am not closing it here

Set db = Nothing
End Sub

When you run the c ode, you will quic kly bec ome annoyed at the
number of prompts you rec eive. A s s tated earlier, this is muc h
better than doing it manually, but there has to be a better way.

T he c ode requires s everal variables for the


O utlook objec ts and data objec ts ; s ee
E xample 7 - 1 0 for thes e items . T his
example als o takes advantage of the
Collection objec t; however, you c an s kip
that s tep and jus t us e the rec ords et. T he
main reas on the c ode us es the Collection
objec t is that, in my produc tion- automated
email applic ations , I pas s Collections to
the email proc edure for the report names
and the email addres s es . T his lets me us e
that s ame proc edure in other M ic ros oft
O ffic e applic ations s uc h as E xc el or Word,
where I might not be us ing rec ords ets . T he
proc edure s aves the reports in a direc tory
c alled C:\Reports ; if this direc tory does n't
exis t on your s ys tem, you c an c reate the
direc tory, or you c an plac e the reports in a
different direc tory.
7.12.2. An Easier Way

N ow that you are familiar with the items to s end emails through
O utlook, here is an eas ier way to handle it. M os t likely this will
be helpful only for large jobs bec aus e it requires two- s teps .

T he adjus ted A c c es s proc edure in E xample 7 - 1 1 c hanges the


original c ode from E xample 7 - 1 0 to s ave the email ins truc tions
in an ADO.Records et XM L file. O utlook then proc es s es this file.
You will need to c reate a referenc e to A D O in both the O utlook
and A c c es s V BA environments .

Example 7-11. Creating an XML file from an ADO records et

Public Sub CreateOutlookXML()


Dim db As DAO.Database
Dim ReportRs As DAO.Recordset
Dim EmailRS As DAO.Recordset

Dim saveRS As ADODB.Recordset


Set saveRS = New ADODB.Recordset

saveRS.Fields.Append "Email_Address", adVarChar, 50, adFldFixed


saveRS.Fields.Append "File_Name", adVarChar, 50, adFldFixed
saveRS.Open
Dim FileName As String

Set db = CurrentDb
Set ReportRs = db.OpenRecordset( _
"Select Report_Name from tbl_Email Group by Report_Name")

ReportRs.MoveFirst
While Not ReportRs.EOF
FileName = "C:\Reports\" & ReportRs.Fields(0).Value & ".rtf"
Set EmailRS = db.OpenRecordset( _
"Select Email_Address from tbl_Email Where Report_Name = " & """
ReportRs.Fields(0).Value & """" & ";")
EmailRS.MoveFirst
While Not EmailRS.EOF
saveRS.AddNew
saveRS.Fields(0).Value = EmailRS.Fields(0).Value
saveRS.Fields(1).Value = FileName
saveRS.Update
EmailRS.MoveNext
Wend

EmailRS.Close
Set EmailRS = Nothing

DoCmd.OutputTo acReport, ReportRs.Fields(0).Value, _


acFormatRTF, FileName

ReportRs.MoveNext
Wend

saveRS.Save "C:\Reports\EmailFile.xml", adPersistXML


saveRS.Close
Set saveRS = Nothing
ReportRs.Close
Set ReportRs = Nothing

Set db = Nothing
End Sub

T his proc edure takes advantage of a dis c onnec ted A D O


rec ords et. With A D O , you c an c reate a rec ords et on- the- fly
without c onnec ting to a databas e. I n addition, you might als o
notic e that this proc edure c reates all the files O utlook will s end
later. I f you want to, you c an have a s tep that runs at the
beginning of the proc es s to c reate the XM L file with no rec ords
and then have multiple proc edures run that c ontinue to add to
the XM L file to be proc es s ed by O utlook at a partic ular time.

7.12.3. Macros in Outlook

N ext, you need to c reate the O utlook proc edure. To make this
work, you need to add a mac ro to your O utlook environment. I n
O utlook, s elec t Tools M ac ros V is ual Bas ic E ditor,
and c lic k the ThisOutlookSession objec t in the P rojec t E xplorer.
O nc e there, enter the c ode in E xample 7 - 1 2 .

Example 7-12. Proces s ing the ADO records et in Outlook

Public Sub EmailTest()


Dim mi As MailItem
Dim varitm As Variant
Dim adors As ADODB.Recordset
Set adors = New ADODB.Recordset
adors.Open "C:\Reports\EmailFile.xml"
adors.MoveFirst
While Not adors.EOF
Set mi = Application.CreateItem(olMailItem)
mi.Recipients.Add adors.Fields(0).Value
mi.Subject = "Monthly Report"
mi.Body = "Your monthly report is attached."
mi.Attachments.Add adors.Fields(1).Value, olByValue, 1, "Monthly R
mi.Send
Set mi = Nothing
adors.MoveNext
Wend
adors.Close
Set adors = Nothing
End Sub

T his s ends all your emails without prompting you eac h time.
A lthough it c reates a two s tep proc es s , you will apprec iate not
having to c lic k through eac h mes s age. T his is partic ularly us eful
if you have a s ignific ant number of emails to s end. I f nec es s ary,
you c an s tore additional fields for Subject and Body in the
rec ords et and have thos e als o bec ome dynamic .

T he one downs ide of this proc edure is that it s ends an individual


email for eac h rec ord. You c an update it to go through the
rec ords et and determine if emails c an be grouped; however, this
is unlikely to be nec es s ary. I n addition, you c an als o c reate
multiple XM L files for eac h email to be s ent and have the
proc edure c yc le through all the XM L files and then move them
when it is c ompleted (I implemented s uc h a proc edure for a
c lient onc e).

You will need to s ave this proc edure us ing the Save ic on from
the V is ual Bas ic E nvironment if you want to us e it again. A ls o,
depending on your s ec urity s ettings , you might be prompted to
enable this mac ro eac h time you open O utlook and attempt to
us e it.

U s ing either approac h will c ertainly help you tac kle your A c c es s
projec ts and help automate s ending emails . I f you need to s end
jus t a mes s age to us ers , you c an us e the firs t proc edure and
eliminate the lines related to attac hments . I n either c as e, the
power of us ing V BA in M ic ros oft O ffic e applic ations s hould be
evident.

Michael Schmalz
Hack 70. Create Access Tables from
Outside Access

You don't have to be in A ccess to use A ccess.

H ere's the s c enario: you have an E xc el s olution that needs to


populate an A c c es s table. T he table will be a new table in the
databas e. You might think the table needs to exis t before you
c an populate it via A D O or s ome other means , s o you manually
c reate the table and then go bac k to E xc el and run the routine
that populates the table.

A c tually, you don't have to go to A c c es s and c reate the table.


J us t c reate it direc tly from c ode while in E xc el. I t's a s imple
matter, really; you jus t need to work with the A D O X library.

I n E xc el, s et a referenc e to A D O X by us ing the Tools


Referenc es menu and s etting the referenc es in the Referenc es
dialog box, as s hown in Figure 7 - 4 9 .

Figure 7-49. Setting a reference to ADOX


7.13.1. The Code

I t's now jus t a matter of whipping up a little c ode that us es the


A D O X programmatic model. I n an E xc el c ode module, enter this
c ode:

Dim cat As New ADOX.Catalog


Dim tbl As New ADOX.Table
Dim db_file_path As String
'change path and database!
db_file_path = ActiveWorkbook.Path & "\abc.mdb"
'connect to the Access database
cat.ActiveConnection = "Provider=Microsoft.Jet.OLEDB.4.0;" & _
"Data Source=" & db_file_path

'Create a table
With tbl
.Name = "Prospects"
' First, fields are appended to the table object
' Then the table object is appended to the Tables collection
.Columns.Append "Name", adVarWChar
.Columns.Append "Company", adVarWChar
.Columns.Append "Phone", adVarWChar
.Columns.Append "Address", adVarWChar
End With

cat.Tables.Append tbl

Set cat = Nothing


MsgBox "Table created!"
J us t be s ure to c hange the hardc oded databas e name and path.
T his c ode c reates the P ros pec ts table in the A c c es s databas e.
T he table will have four fields . T he Append method of the Columns
property takes the name of the field and the field type as a
c ons tant. T he c ons tants for the field types differ from what you
s ee in A c c es s , although they s erve the s ame purpos e. To s ee all
the c ons tants , us e the O bjec t Brows er and filter it to jus t s how
the A D O X library, as s hown in Figure 7 - 5 0 .

Figure 7-50. Displaying ADOX constants


7.13.2. Hacking the Hack

T his hac k us es A D O X, whic h is an available external library. You


jus t as eas ily c an run the c ode in this hac k from Word,
P owerP oint, O utlook, and other programs . T he point is that you
c an c reate and manipulate A c c es s databas e tables even when
A c c es s is n't running.
Hack 71. Write VBA with the Macro
Recorder in Word and Excel

Take advantage of autogenerated code to speed up your coding


ef f orts.

L et's s ay you need to work with Word or E xc el from within


A c c es s . A nd let's s ay the projec t involves writing V BA for Word
or E xc el that will be us ed from A c c es s via automation. Well, you
don't have to c limb as s teep a learning c urve as you might think.
T hat's bec aus e both Word and E xc el c an generate V BA c ode
automatic ally.

To make this work, you firs t turn on the M ac ro Rec order, perform
c ertain ac tions in the applic ation, and then s top the rec order.
J us t s elec t Tools M ac ro Rec ord N ew M ac ro to s tart
the rec order, as s hown in Figure 7 - 5 1 .

Figure 7-51. Starting to record an Excel macro


With the rec order running, you c an perform a few ac tions of
entering data and c reating a c hart. A fter s topping the rec order
(while it is rec ording, a toolbar with a Stop button is vis ible), the
c ode is in an E xc el c ode module. Figure 7 - 5 2 s hows an example
of the c ode E xc el generates .

U s ually, you'll need to work with the generated c ode. I t will have
hardc oded c ell referenc es that might not make s ens e for your
applic ation.

Figure 7-52. Excel autogenerated VBA code


H owever, us ing the M ac ro Rec order, you c an generate mos t of
what you need and then jus t edit it to make it work right. C ode
s uc h as this that works in E xc el will run fairly well from A c c es s
when a referenc e is s et to the E xc el library (everything s aid here
als o applies to Word). You will need to make s ome c hanges , but
this is s till a big times aver.

Kirk Lamb
8. Programming
Sec tion 8 .1 . H ac ks 7 2 9 1

H ac k 7 2 . Store I nitial C ontrol Selec tions for L ater


Rec all

H ac k 7 3 . Write C ode Fas ter by Turning O ff Syntax-


C hec king

H ac k 7 4 . Subs titute D omain A ggregate Func tions for


SQ L A ggregate Func tions

H ac k 7 5 . Shrink Your C ode with Subroutines

H ac k 7 6 . Shrink Your C ode with O ptional A rguments

H ac k 7 7 . P rotec t P rogramming C ode from C urious


U s ers

H ac k 7 8 . Build a Sec ret D eveloper Bac kdoor into Your


A pplic ations

H ac k 7 9 . H elp U s ers D rill D own to a Rec ord

H ac k 8 0 . P revent U s ers from D is abling Your Startup


O ptions

H ac k 8 1 . I nform U s ers of a L ong P roc es s

H ac k 8 2 . A llow U s ers to C hoos e a Bac k- E nd D atabas e

H ac k 8 3 . O verride the T imeout I nterval

H ac k 8 4 . Save Values from U nbound C ontrols for L ater


Rec all
H ac k 8 5 . Sort Rec ords Randomly

H ac k 8 6 . Bulk- U pdate C ontrols on a Form

H ac k 8 7 . P rovide C omplete XM L C ontrol to A ny Vers ion


of A c c es s

H ac k 8 8 . U s e C us tom E numerations

H ac k 8 9 . C onvert Text to the D es ired C as e

H ac k 9 0 . C reate a C ode L ibrary

H ac k 9 1 . A utomatic ally C hec k for D atabas e Table


U pdates
8.1. Hacks 7291
V BA , A D O , D A O , SQ L , XM L : developers s ubs is t in a world of
ac ronyms . T he great thing about all thes e tec hnologies is how
well they work together in our c us tom s olutions . You might be
familiar with jus t a handful of thes e tec hnologies , but when you
finis h reading this c hapter, you will walk away with a bit more
knowledge than when you s tarted.

I n this c hapter, you'll find hac ks that optimize c ode by reduc ing
the number of lines and reduc ing repetitive routines . "Subs titute
D omain A ggregate Func tions for SQ L A ggregate Func tions "
[Hack #74] s hows you how to reduc e c ode by avoiding SQ L
func tions . O ther hac ks s how you how to protec t your c ode and
keep us ers out of the des ign elements . O ne of thes e, "P rotec t
P rogramming C ode from C urious U s ers " [Hack #77], s hows you
how to apply pas s word protec tion to your c ode.

T he c hapter als o c ontains hac ks that enhanc e the us er


experienc e. For example, "H elp U s ers D rill D own to a Rec ord"
[Hack #79] helps us ers quic kly find a des ired rec ord from a
large number of rec ords . A nd "Save Values from U nbound
C ontrols for L ater Rec all" [Hack #84] gives us ers a way to
rec reate form s elec tions from earlier s es s ions .
Hack 72. Store Initial Control Selections
for Later Recall

The Tag property is a great place to store data about controls.


Here's how to put it to good use.

C ombo boxes and lis tboxes are great for pres enting us ers with
c hoic es . A s the us er makes s elec tions in a c ombo box or
lis tbox, the value is available to us e in c ode s imply by referring
to the c ontrol's Value property:

If IsNull(cmbLevel) Then
MsgBox "No Value Selected"
Else
MsgBox cmbLevel.Value
End If

You c an even refer only to the c ontrol its elf and leave the Value
property off, like this :

If IsNull(cmbLevel) Then
MsgBox "No Value Selected"
Else
MsgBox cmbLevel
End If
N ote that both c ode s nippets begin with a tes t for a null. T he
lis tbox or c ombo box might initially be null, s o it is good prac tic e
to inc lude a way to avoid bombing out if this is the c as e.

A s us ers c lic k away and make s elec tions , a lis tbox or c ombo
box's value c hanges to reflec t the us er's las t s elec tion. But what
if you have to rec all an earlier s elec tion or perhaps the firs t
s elec tion the us er made? A us er might have forgotten what he
firs t s elec ted and wants to return to that value. You c an build
into your applic ation a way to do this , but it will be for naught
unles s you s tored the initial value.

O f c ours e, you c an keep the initial value s tored in a table, but


that's extra work. I ns tead, this hac k s hows you how to s tore a
c ontrol's initial value right in the c ontrol its elf! Both the lis tbox
and the c ombo- box c ontrols have a Tag property. T his property
has no purpos e other than to ac t like a little c omment area for
A c c es s objec ts .

I n c as e you are wondering about the


OldValue property, here is the s kinny on
that. OldValue s tores the unedited value
while the value is being edited. T his is like
a before- and- after pic ture of an edit, but it
works only for the mos t rec ent c hange. You
c an't us e OldValue to rec all a value from
s everal c hanges bac k.
Figure 8 - 1 s hows a form with a c ombo box, a lis tbox, and a
button. Both the c ombo box (c mbL evel) and the lis tbox
(lis tRegion) have their Row Sourc e Type s et to Value L is t. T he
c ombo box has four Row Sourc e items : Beginner, I ntermediate,
A dvanc ed, and E xpert. T he lis tbox has five Row Sourc e items :
E as t, Wes t, N orth, South, and C entral. O riginal s elec ted values
for thes e c ontrols are s tored in the Tag property.

T hes e c ontrols are unbound. When the form opens , they are
blank. I n the form's Open event, the Tag property of eac h c ontrol
is s et to a zero- length s tring. H ere is the c omplete c ode:

Figure 8-1. A form with a combo box, listbox,


and button
Private Sub Form_Open(Cancel As Integer)
cmbLevel.Tag = ""
listRegion.Tag = ""
End Sub

Private Sub cmbLevel_AfterUpdate( )


If cmbLevel.Tag = "" Then
cmbLevel.Tag = cmbLevel
End If
End Sub

Private Sub listRegion_AfterUpdate( )


If listRegion.Tag = "" Then
listRegion.Tag = listRegion
End If
End Sub

Private Sub cmdReset_Click( )


cmbLevel = cmbLevel.Tag
listRegion = listRegion.Tag
End Sub

T he firs t time a s elec tion is made in either c mbL evel or


lis tRegion, the Tag property is tes ted to s ee if it is a zero- length
s tring. I f it returns true, the Tag property updates to the c ontrol's
c urrent value. T his ac tivity oc c urs in the AfterUpdate event
bec aus e the new lates t value is required. T he lates t value is the
c urrent one after the update.

T he tes t for a tag with a zero- length s tring is s uc c es s ful only the
firs t time it is run. E very time after that, a new s elec tion is made
in a c ontrol; therefore, the tes t fails bec aus e the Tag property
has a valuethe original s elec ted value.
T he Res et button, when c lic ked, s imply s ets both the c ombo box
and the lis tbox to the original s elec tions by s etting them to their
own Tag properties .

T his hac k s hows you only how to s tore the initial s elec tions .
What you ac tually do with them is a func tion of your applic ation.
T he great thing about this hac k is that it is eas ily portable.
Bec aus e no tables are involved, you c an c opy the c ode into
other c ontrols ' event s tubs and jus t c hange the name of the
c ontrol referenc e in the c ode.
Hack 73. Write Code Faster by Turning
Off Syntax-Checking

Make sure A ccess doesn't pester you with warning messages


while you're writing code.

I f you're like me, s ometimes your fingers c an't keep up with your
brain. O ften, when I am writing c ode, I am s everal lines ahead in
my thoughts , and I 'm typing as fas t as I c an to keep up. T he las t
thing I want is to los e my train of thought, but if I make a few
typing errors or leave out a keyword or variable, A c c es s pops up
a s c olding mes s age.

T he error mes s age in Figure 8 - 2 is a perfec t example. I am in


the mids t of c oding a Select Case s tatement. I don't know what
the expres s ion is yet, but I do have in mind what the ac tual
c as es are, and I want to get them down fas t. But no, A c c es s
hangs me up on the Select Case line.

I know I left s omething out! I did this on purpos e, and I will


return and enter it in a little while! T his interruption is jus t
annoying.

So, I have learned to turn off thes e annoying mes s ages as I


enter lengthy c hunks of c ode. Figure 8 - 3 s hows the O ptions
dialog. I n the V B editor, us e Tools O ptions to dis play the
dialog box.

O n the E ditor tab of the dialog, unc hec k the A uto Syntax C hec k
c hec kbox. D oing s o is the key to fas t c oding. When you turn off
the s yntax c hec k, you c an type without interruption. E rrors will
s till be flagged by their c olor c oding (lines with errors will c hange
c olor), but no more pes ky mes s age boxes will appear that you
have to c lear.

Figure 8-2. A pesky warning message


Figure 8-3. Turning off Auto Syntax Check
O f c ours e, it's a good idea to turn s yntax- c hec king bac k on
when you finis h entering c ode!
Hack 74. Substitute Domain Aggregate
Functions for SQL Aggregate Functions

Reduce the amount of code you enter and still get the same
results.

Within V BA c ode, it is a c ommon prac tic e to tap into the A D O


objec ts and us e s ome SQ L to query data in the databas e.
Bec aus e SQ L is the de fac to s tandard for querying data,
following this route is unders tandable. H owever, s ometimes you
don't need to query data in this way.

For example, if you need to proc es s individual rec ords , us ing


A D O and SQ L makes s ens e. A rec ords et is c reated that is
typic ally s c rolled through us ing the MoveNext method within a Do
Until loop or s imilar c ons truc t.

O n the other hand, A D O and SQ L are s ometimes us ed jus t to


get an aggregate value from a s et of rec ords . I n this s ituation,
the individual rec ords are of no c onc ern. I ns tead, you're looking
for a s ummary, s uc h as a s um, a c ount, or an average.

8.4.1. The Code

E xample 8 - 1 s hows a routine that us es A D O and SQ L to return


the s um of s ome invoic e amounts .
Example 8-1. Us ing ADO and SQL to return a s um

Sub get_SQL_Sum( )
Dim conn As ADODB.Connection
Set conn = CurrentProject.Connection
Dim rs As New ADODB.Recordset
rs.Open "Select Sum(Amount) As SumOfAmount From Invoices" & _
" Where InvoiceDate=#12/10/04#", _
conn, adOpenKeyset, adLockOptimistic
MsgBox rs.Fields("SumOfAmount")
rs.Close
Set rs = Nothing
Set conn = Nothing
End Sub

T he SQ L s tatement inc ludes the SQ L aggregate Sum func tion.


A ls o, the s um of the amounts is from a s et of rec ords filtered to
a s ingle invoic e date of 1 2 /1 0 /0 4 . T he c ode in E xample 8 - 1
requires c reating A D O objec ts and then des troying them
afterward (by s etting them to Nothing).

You c an boil down all this to a s ingle line us ing a domain


aggregate function.

8.4.2. Boiling Down the Code

D omain aggregate func tions provide the s ame res ults as SQ L


aggregate func tions . H owever, whereas you need to s omehow
embed SQ L aggregate func tions into a SQ L s tatement, you c an
c ode domain aggregates independently.
E xample 8 - 2 s hows how a s hort routine us ing the DSum domain
aggregate func tion replac es the c ode in E xample 8 - 1 .

Example 8-2. Us ing DSum to return the s um

Sub get_Domain_Sum( )
Dim amount As Single
amount = DSum("[Amount]", "Invoices", "[InvoiceDate] = #12/10/04
MsgBox amount
End Sub

O ther than dimens ioning the amount variable and us ing a


mes s age box to dis play the res ult, the c ode requires jus t one
s tatement:

amount = DSum("[Amount]", "Invoices", "[InvoiceDate] = #12

T he arguments handed to DSum are the field to s um, the domain (a


table or Select query), and any filtering. T he third argument
works in the s ame manner as the SQ L Where c laus e.

You c an even enter c omplex c riteria for the third argument. For
example, this line of c ode returns the s um of amount when the
invoic e date is 1 2 /1 0 /0 4 , the c us tomer is A nders on, and the
loc ation is either C hic ago or D allas :

amount = DSum("[Amount]", "Invoices", "[InvoiceDate] = #12/10/0


Customer]='Anderson' And ([Location]='Chicago' or [Location]='D
8.4.3. Domain Aggregate Functions

T here are s everal domain aggregate func tions :

DAvg

Returns the average of the values in the field in the firs t


argument.

DCount

Returns the c ount of rec ords .

DLookup

Returns the value of the firs t field in the firs t rec ord that
matc hes bas ed on the c riteria in the third argument.

DFirst and DLast

Returns the value of the field in the firs t argument from


the firs t or las t rec ord.

DMin and DMax

Returns the minimum or maximum value of the field in


the firs t argument from among the rec ords .
DStDev and DStDevP

Returns the s tandard deviation of the values in the field


in the firs t argument.You us e DStDev with a s ample from a
population. You us e DStDevP with the full population.

DSum

Returns the s um of the values in the field in the firs t


argument.

DVar and DVarP

Returns the varianc e among the values in the field in the


firs t argument.You us e DVar with a s ample from a
population. You us e DVarP with the full population.

A ll the domain aggregate func tions work with the s ame three
arguments : the field being evaluated, the domain, and the
c riteria. L ook up thes e func tions in the A c c es s H elp s ys tem if
you want more information. I ntegrating them into your
proc edures is a great way to retrieve quic k s ummaries of data
with jus t s ingle lines of c ode.
Hack 75. Shrink Your Code with
Subroutines

Say goodbye to long and dif f icult-to-maintain code by placing


repetitive processing into subroutines.

A ll applic ations live and grow. Func tionality begets func tionality.
A s us ers s tart banging away at your firs t delivered applic ation,
they s c ream for more features . A s you add thes e features , the
amount of c ode c an grow. O ften, routines get c opied, and then a
c ouple of literals , variable names , or c riteria get c hanged. You
end up with c ode that has a number of s imilar routines .

E xample 8 - 3 s hows three identic al routines , with the exc eption


that eac h addres s es a different s tate.

Example 8-3. Multiple nearly identical routines

Sub get_NY_records( )
'
'get New York customers
'
Dim conn As ADODB.Connection
Set conn = CurrentProject.Connection
Dim recset As ADODB.Recordset
Set recset = New ADODB.Recordset
recset.Open "Select * From Customers Where State='NY'", conn
Do Until recset.EOF
''Process records here
recset.MoveNext
Loop
recset.Close
Set recset = Nothing
End Sub

Sub get_CT_records( )
'
'get Connecticut customers
'
Dim conn As ADODB.Connection
Set conn = CurrentProject.Connection
Dim recset As ADODB.Recordset
Set recset = New ADODB.Recordset
recset.Open "Select * From Customers Where State='CT'", conn
Do Until recset.EOF
''Process records here
recset.MoveNext
Loop
recset.Close
Set recset = Nothing
End Sub

Sub get_MA_records( )
'
'get Massachusetts customers
'
Dim conn As ADODB.Connection
Set conn = CurrentProject.Connection
Dim recset As ADODB.Recordset
Set recset = New ADODB.Recordset
recset.Open "Select * From Customers Where State='MA'", conn
Do Until recset.EOF
''Process records here
recset.MoveNext
Loop
recset.Close
Set recset = Nothing
End Sub

T he c ode in E xample 8 - 3 is frightfully redundant. You c an


optimize c ode s uc h as this by c reating a s ubroutine that takes
an argument. You plac e the c ode that is identic al in all the
routines into the s ubroutine and then us e the argument to pas s
the partic ular individual value to the s ubroutine to run the
proc es s .

I n E xample 8 - 3 the only differentiating item in the c ode is the


s tate, s uc h as in this s tatement, whic h s elec ts M as s ac hus etts
(MA) rec ords :

recset.Open "Select * From Customers Where State='MA'", conn

E xample 8 - 4 s hows how to c hange the c ode by us ing a


s ubroutine. N ow the repetitive c ode is plac ed in a s eparate
s ubroutine named get_state_records. T he s ubroutine takes a
s tring argument, named s tate.

Example 8-4. The repetitive code placed into a s ubroutine

Sub get_NY_records( )
'get New York customers
get_state_records "NY"
End Sub

Sub get_CT_records( )
'get Connecticut customers
get_state_records "CT"
End Sub

Sub get_MA_records( )
'get Massachusetts customers
get_state_records "MA"
End Sub

Sub get_state_records(state As String)


Dim conn As ADODB.Connection
Set conn = CurrentProject.Connection
Dim recset As ADODB.Recordset
Set recset = New ADODB.Recordset
recset.Open "Select * From Customers Where State='" & state & "'
Do Until recset.EOF
''Process records here
recset.MoveNext
Loop
recset.Close
Set recset = Nothing
End Sub

N ow, eac h individual routine, s uc h as get_MA_records, s imply


c alls the generic s ubroutine and pas s es the s tate initials as the
argument. T his is done in a s ingle line of c ode:

get_state_records "MA"

T he generic get_state_records s ubroutine takes the pas s ed


argument and us es it in the SQ L s tatement that opens the
rec ords et:

recset.Open "Select * From Customers Where State='" & stat

You c an eas ily s ee that the c ode in E xample 8 - 4 is s horter than


the c ode in E xample 8 - 3 .

Years ago, programmers would boas t about how many thous ands
of lines of c ode they had written. I s uppos e now the fas hion is for
programmers to talk about how many lines of c ode they avoided
writing!
Hack 76. Shrink Your Code with Optional
Arguments

Put subroutines to even more general use by accepting dif f erent


numbers of arguments.

"Shrink Your C ode with Subroutines " [Hack #75] s hows you how
to reduc e c ode by us ing a generic s ubroutine. T his hac k takes
that c onc ept a s tep further. Subroutines c an take optional
arguments . C alling routines are required only to s upply
arguments that aren't optional. T he optional ones are, well,
optional.

E xample 8 - 5 s hows a handful of routines and the s ubroutine


they c all.

Example 8-5. A s et of routines that call a s ubroutine

Sub get_NY_records( )
'get New York customers
get_state_records "NY"
End Sub

Sub get_CT_records( )
'get Connecticut customers
get_state_records "CT"
End Sub

Sub get_MA_records( )
'get Massachusetts customers
get_state_records "MA", "Boston"
End Sub

Sub get_state_records(state As String, Optional city As String)


Dim conn As ADODB.Connection
Set conn = CurrentProject.Connection
Dim recset As ADODB.Recordset
Set recset = New ADODB.Recordset
If state = "MA" Then
recset.Open "Select * From Customers Where State='" & state _
& "' And City='" & city & "'", conn
Else
recset.Open "Select * From Customers Where State='" & state &
End If
Do Until recset.EOF
''Process records here
recset.MoveNext
Loop
recset.Close
Set recset = Nothing
End Sub

T he s ubroutine takes two arguments : state, whic h is required,


and city, whic h is optional. T he Optional keyword is plac ed in
front of the argument name:

Sub get_state_records(state As String, Optional city As String)


N o keyword is available for s pec ifying that
an argument is required. A ll arguments are
required unles s you s pec ific ally s et them
as optional with the Optional keyword. N ote
als o that optional arguments mus t c ome
after all the required arguments . You c an
have multiple optional arguments , but you
mus t prec ede eac h one with the Optional
keyword.

L et's as s ume the requirements have c hanged; now, any NY or CT


rec ords c an be us ed, but for M as s ac hus etts (MA), we need only
rec ords for whic h the c ity is Bos ton.

O ne way to ac c ommodate this new requirement is to us e


s ubroutines : one that ac c epts a s ingle s tate argument and one
that ac c epts two arguments , for the s tate and the c ity. T he
func tionality would work, but there would be more c ode.

H owever, by inc orporating the c ity as an optional argument, you


c an us e the s ingle s ubroutine for all the s tates . M as s ac hus etts
rec ords are ac c es s ed by the two arguments being pas s ed:

get_state_records "MA", "Boston"

N ew York and C onnec tic ut rec ords are ac c es s ed with the s ingle
required s tate argument:

get_state_records "CT"
We've modified the s ubroutine to handle two types of queries ,
one with a s ingle c riterion and one that us es both c riteria:

If state = "MA" Then


recset.Open "Select * From Customers Where State='" & s
Else
recset.Open "Select * From Customers Where Stat
End If

A nd that's it! By jus t altering the exis ting s ubroutine a bit, we


don't need to c reate a s ec ond s ubroutine.
Hack 77. Protect Programming Code
from Curious Users

Your code is valuable. A dd password protection to your modules.

I think you'll agree that developed c ode is valuable, in terms of


the time and expens e it took to c reate it as well as what would
be involved to rec reate it. So, like any valuable as s et, a little
protec tion goes a long way. O f c ours e, I would be remis s if I
didn't mention that the firs t thing you s hould do is have a bac kup
s ys tem in plac e. But even if you do have a bac k- up s ys tem, if
the c ode in a produc tion s ys tem gets trampled s omehow, it s till
c os ts the c ompany to take down the s ys tem to fix the c ode.

L uc kily, there is an eas y way to protec t your c ode. T his hac k


prevents all but thos e who know the pas s word from s eeing your
c ode. To add this protec tion, you mus t ac c es s the Tools
P roperties menu in the V B editor, as s hown in Figure 8 - 4 . I n this
example, the menu item is C us tomers P roperties bec aus e my
c ode projec t is named C us tomers . Your menu item will reflec t
the name of your c ode projec t.

I nitially, the name of the c ode projec t is


s et to the name of the databas e. You c an
c hange this in the G eneral tab on the
P rojec t P roperties dialog box (Tools
P roperties ).

Figure 8-4. Accessing the project properties


O n the P rotec tion tab in the P rojec t P roperties dialog box,
s elec t the option to protec t the projec t and es tablis h a
pas s word. E nter a pas s word, and c lic k O K, as s hown in Figure 8 -
5 . D on't forget that pas s words are c as e- s ens itive.

Figure 8-5. Protecting the project


You won't s ee any c hange until you c los e the databas e and then
reopen it. When you go to the V B editor, the lis t in the P rojec t
window will be c ollaps ed to the top level of the projec t its elf. I f
you try to open the lis t (by c lic king the plus s ign), a box in whic h
to enter the pas s word will pop up, as s hown in Figure 8 - 6 .

Figure 8-6. Requiring a password to see the


code
T his is as far as you c an get without knowing the pas s word. T he
protec tion ac tually helps in two ways : not only is the c ode
protec ted from tampering, but als o it is invis ible. T herefore, your
c ode is protec ted in terms of intellec tual rights as well. I f you
developed s ome exc ellent routines that you hold dear, you c an
relax knowing that others c an't even view them, muc h les s c opy
them.
Hack 78. Build a Secret Developer
Backdoor into Your Applications

Keep users out of your design while letting yourself in the easy
way.

Some us ers are a little too c urious for their own good. L eft to
their own devic es , they will explore your databas e's des ign. A
few approac hes exis t to c ombat this . You c an turn on s ec urity,
c onvert the databas e to a read- only .mde vers ion, or jus t remove
the ability to get to the des ign elements .

T his las t option, although not as robus t as us ing A c c es s


s ec urity, is a c ommon way to keep us ers on the up and up.
Setting the following options in Startup O ptions , s hown in Figure
8 - 7 , helps make it hard for us ers to get into the databas e
des ign:

Set the opening form.

U nc hec k the D is play D atabas e Window c hec kbox.

U nc hec k the A llow Built- in Toolbars c hec kbox.

U nc hec k the A llow Toolbar/M enu C hanges c hec kbox.


Figure 8-7. Changing startup options

I n the form des ign, you c an implement the following additional


s ettings :

Set the Shortcut Menu property to No.


Set A llow D es ign C hanges to D es ign V iew O nly.

A s s ign a c us tom menu bar that does n't provide a way to


ac c es s des ign elements .

T his will keep mos t us ers foc us ed on their work. U nfortunately, it


might als o mean you jus t made it more diffic ult for you to get
into the databas e des ign. T his hac k builds a hidden s hortc ut
that only you, the developer, know about. T his s hortc ut takes
you direc tly to the databas e's des ign elements .

Figure 8 - 8 s hows a form in D es ign mode. A c ommand button is


s trategic ally plac ed where you wouldn't think to c lic k. You c an
s ee it in the upper- lef c orner of the form. I t does n't look like a
regular button, although it is . T he transparent property has been
s et to Yes, whic h makes it look flat. A ny c aption dis appears as
well when the button is trans parent. T herefore, it really appears
as jus t the outline of a rec tangle.

Figure 8-8. A form with a transparent button


T he button has c ode ins ide its double- c lic k event. A double-
c lic k event is never really us ed for buttons bec aus e everyone
expec ts that a s ingle button c lic k will perform an ac tion. When
the form is in V iew mode, the button is invis ible. I ts Visible
property is s et to true, but the trans parenc y overrides that.

N ote that the mous e pointer does n't c hange when the us er
pas s es it over the invis ible button. T he button is enabled,
though. A us er c ould ac c identally c lic k it without knowing it.
T herefore, us ing the double- c lic k event ins tead of the s ingle-
c lic k event s erves as a s afeguard. T his approac h is n't foolproof,
but it does keep s tray c lic ks from running any c ode.

8.8.1. The Code

When you double- c lic k the trans parent button, the following c ode
runs to as k for a pas s word:

Dim pw As String
pw = InputBox$("Enter Password", "Password")
If pw = "Access Rocks!" Then
DoCmd.SelectObject acTable, , True
Else
MsgBox "Incorrect Password"
End If

I f the pas s word entered is c orrec t, the databas e window is


dis played.

Figure 8 - 9 s hows the form in V iew mode after the button has
been double- c lic ked. T he button its elf is n't vis ible; you need to
know where it is to double- c lic k it.
Figure 8-9. Entering a password to open the
database window
Hack 79. Help Users Drill Down to a
Record

Facilitate browsing through a lengthy customer database by


f irst grouping customers.

Sometimes us ers need to s pend a lot of time finding a s pec ific


c us tomer from a long lis t of c us tomer rec ords . For example,
loading c us tomer names into a c ombo box or a lis tbox requires
your us ers to do a lot of s c rolling.

H ere's a tec hnique that removes s ome of this drudgery. T he firs t


s tep is to provide a way to lis t s maller groups of c us tomers .
Figure 8 - 1 0 s hows a form in whic h four buttons (on the left,
ins ide the Brows e C us tomers frame) lead to c us tomers whos e
las t names s tart with A F, G L , M R, and SZ.

Figure 8-10. Browsing customers within


alphabetic groupings
Segregating the c us tomers into four groups is s ubjec tive. You
c an apply this tec hnique to even s ingle letters ; in other words ,
you c an have 2 6 buttons . T he more c us tomers there are, the
s maller the groupings s hould be. Keep in mind that the point is
to pres ent s maller lis ts of c us tomers for us ers to brows e
through. I f you have thous ands of c us tomers , a grouping s uc h as
A F s till res ults in a rather lengthy lis t.

C lic king any of the four buttons opens a form that is filtered to
c us tomers whos e las t name falls into the indic ated grouping. For
example, the c ode in the Click event for the A F button looks like
this :

Private Sub cmdAF_Click( )


DoCmd.OpenForm "frmBrowseCustomers", acNormal, , _
"CustomerLastName Like'A*' or CustomerLastName Like 'B*' or " &
"CustomerLastName Like'C*' or CustomerLastName Like 'D*' or " &
"CustomerLastName Like'E*' or CustomerLastName Like 'F*'"
End Sub

T he OpenForm method opens the frmBrows eC us tomers form


filtered to the appropriate c us tomers . Figure 8 - 1 1 s hows the
form opened with the A F c us tomers .

Figure 8-11. A filtered set of customers on a


form
T he form in Figure 8 - 1 1 really s erves jus t as a brows e feature.
U s ers s till don't s ee the full c us tomer rec ord. To s ee the full
rec ord, the us er mus t double- c lic k any rec ord s hown on the
brows e form, whic h opens the full c us tomer rec ord. T he form that
dis plays c us tomer details is filtered to the partic ular rec ord
s elec ted in the form s hown in Figure 8 - 1 1 .

To make this happen, the key is nec es s ary. T he key, an


A utoN umber type named CustomerID in this example, is on the
brows e form, but it is hidden. Figure 8 - 1 2 s hows the des ign of
the brows e form. I n D es ign mode, you c an s ee the key field.

A ll the fields in the brows e form have c ode in their double- c lic k
events . T he c ode c alls a s ubroutine named open_customer, whic h
opens the c us tomer detail form. T his time, the OpenForm method
us es the key to open the detail form. Figure 8 - 1 3 s hows the
c ode behind the brows e form.

D ouble- c lic king a rec ord in the brows e form pulls up the
c us tomer's details . Figure 8 - 1 4 s hows the c us tomer detail form.

To s ummarize, when working with a large number of c us tomers


(or with other types of data, for that matter), us ers c an get to the
des ired detail rec ords pretty quic kly by grouping the rec ords
into s maller brows eable s ets .T hen, from the interim brows e form,
they c an view the full details . U s ing this tec hnique makes it
eas y to get to a detail rec ord with jus t a few c lic ks , without
having to s c roll through a long lis t.

Figure 8-12. The design of the browse form


Figure 8-13. The code behind the browse form
Figure 8-14. A form with the customer detail
Hack 80. Prevent Users from Disabling
Your Startup Options

Stop users f rom being able to hold down the Shif t key to get to
the database window.

A fter s pending all that time developing your databas e


applic ation and then s etting the s tartup options , the las t thing
you want is for s omeone to be able to s imply hold down the Shift
key during s tartup and then mes s around with your applic ation.
T his hac k explores two different ways to prevent this : dis abling
the Shift key c ode for A c c es s databas es (M D Bs ) and dis abling
the Shift key c ode for A c c es s D ata P rojec ts (A D P s ).

8.10.1. Access MDB

With M D B files , the hac k works by adding a property c alled


AllowBypassKey to the databas e objec t. Setting the property to
False dis ables the Shift key, and c hanging it bac k to TRue enables
it again. You need to dec ide on an event to trigger the value
c hange. I t c ould be when a s pec ific us er logs into the databas e
or, as in this c ode example, when a file named AllowByPas s .txt is
in the s ame direc tory as the databas e:

Public Function DetermineByPass( )


If Len(Dir(CurrentProject.Path & "\AllowByPass.txt")) = 0
ChangeProperty "AllowBypassKey", DB_BOOLEAN, False, Tr
Else
ChangeProperty "AllowBypassKey", DB_BOOLEAN, True, Tru
End If
End Function
Public Function ChangeProperty(strPrpName As String,_
varPrpTy
varPrpVa
bolDDL a

Dim prp As Variant


Const conPrpNotFoundError = 3270
On Error GoTo ChangePrp_Err
CurrentDb.Properties(strPrpName) = varPrpValue
ChangeProperty = True
ChangePrp_Bye:
Exit Function
ChangePrp_Err:
If Err = conPrpNotFoundError Then 'Property not found
Set prp = CurrentDb.CreateProperty(strPrpName, _

CurrentDb.Properties.Append prp
Resume Next
Else
'Unknown Error
MsgBox Err.Description
ChangeProperty = False
Resume ChangePrp_Bye
End If
End Function

A fter the c ode c hec ks for the AllowByPas s .txt file, it c alls the
ChangeProperty func tion and s ets it to False if the file is n't found
or to TRue if it is found.

Firs t the ChangeProperty func tion attempts to s et the value


pas s ed to it in varPrpValue to the strPrpName property, whic h in
this c as e is AllowBypassKey. A n error number (3270) oc c urs the
firs t time this func tion is c alled with a new property bec aus e that
property has not yet been appended to the data- bas e's
properties c ollec tion. T he func tion traps for this error number
and c reates the property with the error- handling routine.

T his example us es CurrentDB s o that you don't need to manually


s et a referenc e to the D A O library for A c c es s 2 0 0 0 and 2 0 0 2
(A c c es s 2 0 0 3 has the referenc e s et by default). I t is worth
noting that the CurrentDB method es tablis hes a hidden referenc e
to the M ic ros oft D A O 3 .6 objec t library when us ed in a 2 0 0 0 or
2 0 0 2 M D B file.

T he las t parameter to the ChangeProperty func tion (bolDDL) s ets


the DDL parameter of the AllowBypassKey property. T he DDL
parameter determines if the property c an be altered via
automation. By s etting this parameter to true, you prevent
s omeone with V BA c oding experienc e from res etting the
parameter by automation.

O nc e the c ode is in plac e in an A c c es s module, you need to


make s ure it gets run when the databas e s tarts up. T he bes t way
to do this is to us e the AutoExec mac ro. T he AutoExec mac ro is
nothing more than a regular mac ro with a s pec ial name. A c c es s
automatic ally runs it bas ed on its name. You s hould note that
the AutoExec mac ro gets exec uted after any form is opened from
the Startup properties . T his is s omething to c ons ider if you are
us ing the Startup properties and you plac e any events in the
mac ro other than a c all to the DetermineByPass func tion.

O nc e you've s et up the AutoExec mac ro to c all your func tion and


the s tartup form is in plac e (c alled from either the AutoExec
mac ro or the Startup properties ), open the databas e without
holding down the Shift key. T his allows the AllowBypassKey
property to be added to the databas e for the firs t time.

I f you open the A c c es s databas e with the AllowByPas s .txt file in


the s ame direc tory as the A c c es s M D B, it s ets the
AllowBypassKey property to true but does n't allow the bypas s the
firs t time the databas e is opened. T he A c c es s databas e mus t be
opened a s ec ond time while holding down the Shift key to bypas s
the Startup properties , inc luding the AutoExec mac ro, bec aus e
now the AllowBypassKey internal property is s et to TRue. Removing
the AllowByPas s .txt file from the databas e's direc tory res ets the
property to False the next time the databas e is s tarted up
without holding down the Shift key, thereby preventing the next
us er from bypas s ing the Startup properties and the AutoExec
mac ro.

I f you us e A c c es s 2 0 0 3 , and you don't have your mac ro


s ec urity s etting s et to L ow, you need to hold down the Shift key
when c lic king the O pen c ommand button of the A c c es s Sec urity
Warning s c reen s hown in Figure 8 - 1 5 .

Figure 8-15. Access 2003 security warning


8.10.2. Access ADP

Bec aus e mos t A D P s us e A D O only, the developers at M ic ros oft


provided a way to dis able the Shift key without referenc ing a
D A O library direc tly or indirec tly. T his method of adding the
AllowBypassKey is muc h s impler, but unfortunately, it works only
with A D P projec ts . T he following s ample c ode works identic ally
to the c ode for an A c c es s M D B, without the need for a func tion
s uc h as ChangeProperty:

Public Function DetermineByPass( )


If Len(Dir(CurrentProject.Path & "\AllowByPass.txt")) = 0 Then
CurrentProject.Properties.Add "AllowBypassKey", False
Else
CurrentProject.Properties.Add "AllowBypassKey", True
End If
End Function

A lthough this method for s etting the AllowBypassKey property is


muc h s horter and arguably eas ier to us e with an A D P, it does
have a drawbac k. You have no way of s etting the DDL parameter
to prevent s omeone from c hanging your AllowBypassKey property
with automation.

8.10.3. Be Careful

T his tec hnique of dis abling the Shift key is powerful; make s ure
you don't loc k yours elf out of your databas e. You might want to
c ons ider c hanging the DDL parameter in the s ample c ode for the
A c c es s M D B to False s o that you c an us e remote automation to
res et it if you need to. I f you do loc k yours elf out, you c an
always c reate a new databas e and import all the objec ts over to
the new databas e, then res et your Startup properties and
replac e the databas e you got loc ked out of with the new c opy
you jus t made.

Steve Huff
Hack 81. Inform Users of a Long Process

While your code is conquering a long looping process, users


might think their system has crashed, unless you provide some
visual clue that a process is running.

When a us er c lic ks a button to run a proc es s , and that proc es s


takes a while to c omplete, the us er won't know if the proc es s is
s till running or if the s ys tem has c ras hed. J us t imagine it: you
c lic k a button on a form and … nothing. A minute or two later, s till
nothing. M aybe even 5 or 1 0 minutes later, the s ys tem is s till
unres pons ive.

T his is nerve- wrac king for the us er s itting in front of his


c omputer. H e has to weigh whether he s hould try the break key
or let the s ys tem c ontinue to look like it is hung up. O n the other
hand, if he s tops a proc es s that was running s moothly after all,
he will jus t have to s tart it all over again. U gh!

D on't leave your us ers in this predic ament. You know that
1 0 0 ,0 0 0 rec ords are being proc es s ed and it takes a while. But
your us ers might not know this and get frus trated waiting.

T his hac k takes advantage of the SysCmd method. With SysCmd,


you c an write mes s ages in the s tatus bar during C P U - intens ive
proc es s ing, even with s c reen refres h turned off. Figure 8 - 1 6
s hows a form (a rather s imple one, I admit). T he button has been
c lic ked, and the proc es s is c hugging away. N otic e the s tatus bar
in the lower- left c orner of the s c reen; a c ontinuous ly updated
mes s age is being generated there.
Figure 8-16. Providing a feedback message in
the status bar

T he mes s age in the s tatus bar s ays , "P roc es s ing 2 7 4 1 of


8 5 0 0 ." T hinking like a programmer, you probably realize that to
have the total number of rec ords being proc es s ed in the
mes s age means a rec ord c ount property is being us ed.

8.11.1. The Code

H ere is the c ode behind the button:

Private Sub cmdProcessSales_Click()


Dim conn As ADODB.Connection
Set conn = CurrentProject.Connection
Dim recset As New ADODB.Recordset
Dim total_records As Long
Dim record_num As Long
record_num = 0
recset.Open "Select * From SalesRecords", conn, adOpenKeyset,
adLockOptimistic
total_records = recset.RecordCount
Do Until recset.EOF
recset.Fields("NetSales") = recset.Fields("Sales") - recset.
Fields("Costs")
record_num = record_num + 1
feedback_msg = "Processing " & record_num & " of " & total_rec
SysCmd acSysCmdSetStatus, feedback_msg
recset.MoveNext
Loop
recset.Close
Set recset = Nothing
SysCmd acSysCmdClearStatus
End Sub

T he c ode c reates a rec ords et and loops through it. P rior to the
looping, the RecordCount property populates the total_records
variable. D uring the looping, another variable, record_num, is
inc remented. T hes e two variables are us ed to c reate the
mes s age:

feedback_msg = "Processing " & record_num & " of " & total

T hen the mes s age is us ed in the SysCmd method:

SysCmd acSysCmdSetStatus, feedback_msg


Finally, at the end of the proc es s ing, the c ode c lears the s tatus
bar by giving SysCmd a c lear s tatus flag:

SysCmd acSysCmdClearStatus

8.11.2. Hacking the Hack

P roviding a feedbac k mes s age of the type des c ribed here is


helpful in gauging the length of a proc es s . I f the feedbac k
c ons is ted of jus t the number of rec ords proc es s ed without
indic ating the total number to be proc es s ed, you s till would not
know how long the proc es s will take to c omplete. For example, a
s imple mes s age of "P roc es s ing 2 4 7 1 " does n't let you know if
you are halfway done, are nearly done, or have hardly even
begun.

O f c ours e, the mes s age format of "P roc es s ing X of XX" works
only in a loop. O ther long proc es s es might not be bas ed on a
loop. A c omplex query c an take time, es pec ially when it needs to
work on many rec ords . I t is n't pos s ible to break into a query in
the s ame way, s o the thing to do is to put the time that the
proc es s s tarted in the s tatus bar.

T he Now func tion returns the time from the s ys tem c loc k. By
dis playing that in the s tatus bar, you're at leas t telling us ers
when the proc es s s tarted s o that they c an c ompare the s tart
time to the c loc k time in the s ys tem tray at the right of the
Windows tas kbar.
Hack 82. Allow Users to Choose a Back-
End Database

Store ODBC connection strings in a table so they are ready to go


when needed.

C ertain s ys tem applic ations provide more than one databas e


you c an interac t with. A s long as the s truc ture of the various
databas es and/or their tables is bas ed on a c ommon s c hema, it
is pos s ible to s wap the bac k ends in and out. A n eas y way to do
this is to provide the available c onnec tions in a lis t in whic h
us ers s elec t the databas e to us e.

Figure 8 - 1 7 s hows a table that s imply c ontains O D BC


c onnec tion s trings .

O n the form in whic h us ers s elec t a c onnec tion, they jus t s ee


the friendly names , provided in the table's s ec ond c olumn (the
firs t c olumn s erves as a way to s ort the lis t). Figure 8 - 1 8 s hows
the form and the c ombo box from whic h a c onnec tion is s elec ted.

Figure 8-17. A table filled with ODBC connection


strings
Figure 8-18. Selecting a database connection
T he value of the c ombo box is us ed in c ode to s et the
Connectionproperty for an A D O c onnec tion objec t, like this :

Dim conn As ADODB.Connection


Set conn = New ADODB.Connection
conn.ConnectionString = Me.cmbConnections
conn.Open

I n this manner, us ers c an eas ily c hange the databas e they are
working with at will.
Hack 83. Override the Timeout Interval

In a busy network environment, a little patience is sometimes


necessary.

When us ing A c c es s to c onnec t to an external databas e,


performanc e depends on a number of fac tors . O ne of thes e is the
networks pec ific ally in regard to bandwidth and traffic . Bandwidth
might be c ons tant, but add in the dynamic of network traffic , and
you often don't know what to expec t.

To be on the s afe s ide of having your proc es s ing c omplete, even


if it takes a while longer, you c an turn off the A D O CommandTimeout
property. T he default is for a timeout to oc c ur after 3 0 s ec onds .
T his means that if the s erver has not c ommunic ated bac k to
your applic ation in 3 0 s ec onds , your applic ation as s umes a
res pons e is n't c oming. You might then s ee a timeout mes s age
s uc h as the one s hown in Figure 8 - 1 9 .

Figure 8-19. A timeout expired error message


Setting the timeout interval to 0 turns off this property. Your
applic ation will then wait indefinitely for a res pons e. A s s uming
an A D O c onnec tion objec t named conn is being us ed, this is how
you apply the s etting:

conn.CommandTimeout = 0

T he drawbac k to this approac h is that you really might wait


forever. By the very nature of turning off the timeout, you have no
s pec ific period of reas onable time to expec t for the proc es s ing
to c omplete. I f s omeone pulled the plug on the s erver, you might
not know it. I f it makes s ens e, though, you c an experiment with
different s ettings . N ote that the value us ed is in s ec onds , s o a
value of 300 s ets the timeout interval to five minutes . Knowing
the wait is no more than five minutes for s uc c es s or failure might
make more s ens e than waiting indefinitely.
Hack 84. Save Values from Unbound
Controls for Later Recall

Give users a way to automatically recreate the way a f orm was


set up so that they don't have to reenter inf ormation

E very time a form is c los ed, the values in unbound c ontrols are
los t (this is n't always s tric tly true, but it generally is ).

I magine a form that is filled with many unbound c ontrols . A us er


makes s everal s elec tions and expec ts to need to reus e the
s ame s elec tions another time. Saving the values in the unbound
c ontrols , and making them identifiable and rec allable, c an be a
big times aver. L et's c all this a s cheme.

Saving the values from unbound c ontrols does n't make them
bound to anything. T he values are s aved to a table but only by
c reating c ode to do s o. Figure 8 - 2 0 s hows a form with three
unbound lis tboxes .

Figure 8-20. A form in which schemes of control


values are saved
A s elec tion has been made in eac h lis tbox, a s c heme name has
been entered, and the A dd/U pdate button has been c lic ked. T his
has c reated a s c heme that s tores the values from the lis tboxes .

8.14.1. The Code


T he c ode behind the A dd/U pdate button looks like this :

Dim conn As ADODB.Connection


Set conn = CurrentProject.Connection
Dim rs As ADODB.Recordset
Set rs = New ADODB.Recordset

ssql = "Insert into tblSchemes Values("


ssql = ssql & "'" & Me.txtSchemeName & "', "
ssql = ssql & "'" & Me.listOffices & "', "
ssql = ssql & "'" & Me.listItems & "', "
ssql = ssql & "'" & Me.listDeliveryMethod & "')"

'delete scheme first


delete_ssql = "Delete * From tblSchemes Where Scheme='" & schemena
conn.Execute (delete_ssql)

'now insert scheme


conn.Execute (ssql)

conn.Close
Set conn = Nothing

Me.lstSchemes.Requery
Exit Sub
err_end:
MsgBox Err.Description

N ote that s ome of the c ode has been removed. C hec king for
nulls and s uc h is n't s hown here, to keep the foc us on the point of
the hac k.
8.14.2. Running the Code

T he values in the lis tboxes , along with the s upplied name of the
s c heme, are ins erted as a rec ord into the tblSc hemes table
s hown in Figure 8 - 2 1 .

Figure 8-21. The control values stored in a table

A s more and more s c hemes are s aved, the lis tbox of s c hemes
(on the left) fills up. From this lis t, a s c heme c an be reentered on
the form. Figure 8 - 2 2 s hows the numerous s c hemes now
available.

Figure 8-22. Many schemes to choose from


T he L oad and D elete buttons work with the table rec ords and the
form. L oad populates the unbound c ontrols with values s tored in
the table for whic hever s c heme is s elec ted in the s c heme
lis tbox. D elete s imply deletes the appropriate rec ord from the
table.
8.14.3. Hacking the Hack

T he final thing to c ons ider is what to do when unbound lis tboxes


and c ombo boxes are s et to multis elec t. T his allows more than
one item to be s elec ted. I n this s ituation, you're s toring
relational data. For example, one s c heme c an have more than
one s elec tion in a lis tbox. To handle this , you literally c reate a
s et of related tables . O ne holds the general s c heme information,
and the other holds a rec ord for every value in the lis tbox. T he
tables relate on a keythe s c heme name.

Figure 8 - 2 3 s hows how this works .

Figure 8-23. Saving schemes that have multiple


selections per control
T he s ec ondary table s tores the s c heme name, the name of the
c ontrol, the value of the c ontrol, the item's pos ition in the lis t,
and whether the item is s elec ted. E very lis t item is s tored, along
with its pos ition and its s elec ted s tate. When a s c heme is
reloaded, the various parameters are needed to rec reate the lis t
and to reflec t whic h items were s elec ted.
Hack 85. Sort Records Randomly

Get a unique sort of records whenever you need one.

Rec ords in a table are always in s ome kind of order. A primary


key or other index might have been applied. E ven when all
indexes are removed, the rec ords are in the order in whic h the
table rec eived them.

A hac k is available for getting a true random s ort of the rec ords .
L iterally s ort them on random values ! To get this to work, you
add an extra field to the table. You then populate the field with
randomly generated values . L et's look at s ome c ode:

Sub random_sort_field()
Dim conn As ADODB.Connection
Set conn = CurrentProject.Connection
Dim ssql As String
Dim recset As New ADODB.Recordset
Dim tbl As String
tbl = "tblCustomers" ' the table name could be passed as an argu

ssql = "Alter Table " & tbl & " Add Column RandomSort Long"
'may already have field so trap error
On Error Resume Next
conn.Execute ssql

Randomize
recset.Open "select * From " & tbl, conn, adOpenDynamic, adLockO
Do Until recset.EOF
recset.Fields("RandomSort") = Int(Rnd() * 50000)
recset.MoveNext
Loop
recset.Close
Set recset = noting
conn.Close
MsgBox "done"
End Sub

T he tabletblC us tomers in this examplerec eives a new field


named RandomSort. H owever, the field might already be there
from the las t time this c ode was run, s o an On Error s tatement
prec edes the operation:

ssql = "Alter Table " & tbl & " Add Column RandomSort Long"
'may already have field so trap error
On Error Resume Next
conn.Execute ssql

T he c ode then c yc les through the table, and the RandomSort


field is populated with random values us ing the RND func tion:

recset.Fields("RandomSort") = Int(Rnd() * 50000)

N ow, the tblC us tomers table c an be s orted on the RandomSort


field, as s hown in Figure 8 - 2 4 .

E ac h time the routine runs , the values in the RandomSort field


c hange, thereby providing a new s ort.

Figure 8-24. Randomly sorted records


Hack 86. Bulk-Update Controls on a
Form

Tap the Controls property to make f ast property changes.

When working with a form's des ign, typic ally you s et properties
for c ontrols with the property s heet. You c an s elec t s ingle
c ontrols one by one and s et properties , or you c an s elec t a
handful of c ontrols and update them together. When you us e the
latter method, the number of properties lis ted on the property
s heet s hrinks to jus t thos e that are c ommon to all the s elec ted
c ontrols .

T his works fine for the mos t partthat is , s etting properties to


multiple c ontrols is n't diffic ult. But what if you had to s ee what a
property is before dec iding whether to c hange it? I n this
s ituation, the property s heet won't help you. When multiple
c ontrols are s elec ted, the property s heet c an't dis play individual
c ontrol properties . I t dis plays a property's value if all the
s elec ted c ontrols have the s ame value for the property, but that
is n't always the c as e.

8.16.1. Accessing Control Properties in


Code

L uc kily, A c c es s has a Controls property that belongs to the form


objec t, and a Properties property for the c ontrols thems elves .
T his makes it pos s ible to read and write c ontrol properties . To
s tart, E xample 8 - 6 s hows a routine that reads c ontrol
properties .

Example 8-6. Reading control properties

Sub list_control_properties()
Dim form_name As String
Dim ctl As Control
Dim list_type As Integer
Dim prop_num As Integer

form_name = "frm1" ' change as needed!


DoCmd.OpenForm (form_name), acDesign

list_type = 2 ' change to use with Select Case below

Select Case list_type


Case 1
' list names and values of properties for single control
With Forms(form_name).Controls(1)
For prop_num = 0 To .Properties.Count - 1
Debug.Print .Name & ": " & _
.Properties(prop_num).Name & ": " & _
.Properties(prop_num).Value
Next
End With
Case 2
' list value of entered property for all controls on form
With Forms(form_name)
For Each ctl In Forms(frm_name).Controls
On Error Resume Next
Debug.Print ctl.Name & ": Caption=" & _
ctl.Properties("Caption")
Next
End With
End SelectEnd Sub

T he routine in E xample 8 - 6 has two variations of reading c ontrol


properties , whic h you c an c hoos e by s etting the list_type
variable to 1 or 2.A Select Case s tatement us es this number to
run one or the other c ode s nippet. When the list_type variable is
s et to 1, the properties of a s ingle c ontrol are read. I n partic ular,
eac h property's name and value are written to the I mmediate
window. T he s ingle c ontrol is referenc ed in E xample 8 - 6 s imply
as .Controls(1). You c an enter the name of an ac tual c ontrol
ins tead by putting the name in quotes .

Figure 8 - 2 5 s hows the res ults of returning all the property


names and values for a s ingle c ontrol.

When the list_type variable is s et to 2 , all the c ontrols on the


form are tapped. A s ingle property that is entered into this line
indic ates whic h property to return:

Debug.Print ctl.Name & ": Caption=" & ctl.Properties("Caption")

Figure 8-25. Returning a list of control


properties
A ls o note the On Error Resume Next s tatement before this line.
T his line is inc luded bec aus e not all properties exis t for all
c ontrols . I n this example, the Caption property is being
ac c es s ed, but s ome c ontrols , s uc h as text boxes , don't have
c aptions . T he error trap keeps the proc edure going, and only
c ontrols that have the indic ated property make it into the
I mmediate window, as s hown in Figure 8 - 2 6 .

N ote that the routine in Figure 8 - 2 6 addres s es a form in the


c ode. T he form mus t be open for the c ode to work, pres umably in
D es ign mode bec aus e the point of this hac k is to make bulk
des ign c hanges . T he DoCmd s tatement takes c are of opening the
form in D es ign mode; jus t provide the name of your form. I n the
following line, replac e frm1 with the name of your form:

form_name = "frm1" ' change as needed!

8.16.2. Changing Properties the Easy Way

E xample 8 - 7 s hows a routine that c hanges the ForeColor


property to red for all text boxes on the form. To work with a
s ingle type of c ontrol, the c ode tes ts the ControlType property. I f
the c ontrol type matc hes the enumeration value, the property is
updated.

Figure 8-26. Returning the Caption property for


controls that have one
Example 8-7. Working with a s ingle control type

Sub update_controls()
Dim ctl As Control
Dim form_name As String
form_name = "frm1"
DoCmd.OpenForm (form_name), acDesign
With Forms(form_name)
For Each ctl In Forms(frm_name).Controls
If ctl.ControlType = acTextBox Then
ctl.Properties("ForeColor") = vbRed
End If
Next
End With
End Sub

You c an c hoos e to c hange a property for all the c ontrols on a


form or for partic ular c ontrol types . I f you're working with the full
s et of c ontrols , it's a good idea to us e an On Error Resume Next
s tatement s o that the proc es s won't bomb when a c ontrol
does n't have the partic ular property.

T he c ode in E xample 8 - 7 addres s es text boxes only. Figure 8 -


2 7 s hows how to us e the O bjec t Brows er to find the members of
the acControlType enumeration.

Figure 8-27. Reviewing control-type constants


Hack 87. Provide Complete XML Control
to Any Version of Access

Use the MSXML Parser to make XML native to your applications.

Support for XM L has been growing through s uc c es s ive A c c es s


releas es , but c omplete XM L c ontrol s till is n't available. For
ins tanc e, A c c es s 2 0 0 3 s upports importing and exporting XM L ,
but even s o, the level of func tionality is limited. For example,
you c an't import attributes (a type of XM L data).

Referenc ing an external XM L pars er not only improves the XM L


s upport, but als o provides the s ame level of s upport to any
vers ion of A c c es s . T his is pos s ible bec aus e the pars er is an
independent piec e of tec hnology. A s long as you c an referenc e
it, you c an us e it to its fulles t.

8.17.1. Referencing the Parser

I n the A c c es s V B editor, us e the Tools Referenc es menu to


open the Referenc es dialog box, s hown in Figure 8 - 2 8 .

Sc roll through the lis t, and find M ic ros oft XM L . T he referenc e will
inc lude a vers ion number; any vers ion will do. I f you are c urious
how the pars er vers ions differ, vis it M ic ros oft's web s ite
(http://www.mic ros oft.c om).
Figure 8-28. Adding a reference to the XML
parser
I f you don't find the M ic ros oft XM L
referenc e on your c omputer, download the
M SXM L pars er from M ic ros oft
(http://www.mic ros oft.c om/xml).

With the referenc e s et, you c an work with XM L in many


s ophis tic ated ways .T his hac k is n't the plac e to learn how to us e
the pars er (s ee the end of the hac k for s ome res ourc es ).
I ns tead, we'll preview us ing the pars er to load XM L data and
ins ert it into an A c c es s table. A long the way, we'll ac c omplis h a
c ouple of tric ks : filtering XM L data and loading attributes .

A n XM L file filled with employee information has been prepared,


as s hown in Figure 8 - 2 9 .

8.17.2. The Code

I n an A c c es s c ode module, the following c ode has been entered.


T his c ode us es objec ts available from the pars er referenc e:

Sub read_xml()
On Error GoTo err_end
Dim conn As New ADODB.Connection
Set conn = CurrentProject.Connection
Dim xmlobj As DOMDocument
Dim xml_list As IXMLDOMNodeList
Dim xml_node As IXMLDOMNode
Set xmlobj = New DOMDocument
xmlobj.async = False
xmlobj.Load "C:\Employees.xml"
Set xml_list = xmlobj.selectNodes _
("Employees/Department/Employee")
For Each xml_node In xml_list
ssql = "Insert Into tblEmployees Values (" & _
xml_node.childNodes(0).Text & ", '" & _
xml_node.childNodes(1).Text & "', '" & _
xml_node.parentNode.Attributes(0).Text & "')"
conn.Execute ssql
Next
MsgBox "done"
err_end:
MsgBox Err.Description
End Sub

Figure 8-29. The Employees.xml file


T he XM L file is loaded into the xmlobj objec t variable:

xmlobj.Load "C:\Employees.xml"

Typic al XM L objec ts are nodes and node lis ts . A lis t is a


c ollec tion of nodes . T he ac tual nodes are the employee
elements , whic h are c hildren of the department nodes :

Set xml_list = xmlobj.selectNodes _


("Employees/Department/Employee")

E mployee nodes have two c hildren: EmployeeID and Name. T hes e


c hild elements and the parent department element are the bas is
from whic h a SQ L Insert s tatement is c reated.

ssql = "Insert Into tblEmployees Values (" & _


xml_node.childNodes(0).Text & ", '" & _
xml_node.childNodes(1).Text & "', '" & _
xml_node.parentNode.Attributes(0).Text &

A fter the routine runs , the tblE mployees table is populated with
the XM L data, as s hown in Figure 8 - 3 0 .
Figure 8-30. The XML data now in Access
So, in jus t a s hort routine, we've ac c omplis hed two things that
are typic ally taken for granted as being impos s ible. O ne is that
now, only A c c es s 2 0 0 3 c an work with XM L in a robus t way, and
the other is that attributes c an't be imported. T he routine in this
hac k will work with any vers ion of A c c es s that referenc es the
pars er and c learly has no problem putting an attribute's value
into an A c c es s table.

8.17.3. See Also

"U s e A c c es s as an XM L D atabas e"[Hack #95]

XML Hacks (O 'Reilly)

Office 2003 XML(O 'Reilly)


Hack 88. Use Custom Enumerations

Use f amiliar names, instead of memorizing equivalent numbers,


to avoid errors and speed up coding.

H aving a lis t of properties and methods appear while typing


really helps when you're c oding. For example, entering
Application in a c ode module and then entering a period opens up
a lis t of methods and properties that belong to the Application
objec t.

You c an us e this s ame helpful fac ility to provide you with


c ons tants that are partic ular to your projec t or bus ines s . Take,
for example, an applic ation that has to take the department c ode
into ac c ount for s ome s pec ific proc es s ing. E ac h department has
a unique c ode number, but the numbers thems elves are
meaningles s . T his makes the c ode numbers diffic ult to
remember.

T hat is where a s et of enumerated variables c omes in handy. N ot


only c an you give names to the numeric al c ode numbers , but the
names bec ome available in a lis t while typing.

Figure 8 - 3 1 s hows a V BA c ode module. I n the dec laration


s ec tion, I us ed the Enum s tatement to c reate the variables . I
gave a name to the bloc k (DepartmentCodes in this example) and
us ed the End Enum s tatement to end the bloc k.

Figure 8-31. Using Enum for more efficient


coding
Within the Enum bloc k, I as s igned eac h department's c ode
number to its name. N ow, when c oding within a proc edure, all I
have to do is type DepartmentName and enter a period. T he
enumerated variables appear, and I c an s elec t the one I need.
By s elec ting one, I 'm really s elec ting the department c ode
number, but I no longer have to memorize the numbers . T his
reduc es errors bec aus e hones tly, up until I us ed this tec hnique I
c ould not remember if Advertising was 200 or 600. T hankfully, by
us ing Enum, I 'm able to let go of remembering s uc h things and
c an c onc entrate on the more important as pec ts of my projec ts .
Hack 89. Convert Text to the Desired
Case

Have any text string be returned in uppercase, lowercase, or


proper case.

O ne of the oc c as ional requirements thrown at a developer is the


ability to c hange the c as e of the text. T his is n't a really diffic ult
problem. A ll you have to do is us e the UCase or LCase func tions ,
whic h return a text s tring as all c apital letters or all lowerc as e
letters , res pec tively.

H owever, no func tion is available for returning proper cas e (a.k.a.


mixed cas e or s entence cas e): text in whic h eac h word s tarts with
an upperc as e letter, with the res t of the word in lowerc as e.

M ic ros oft Word has the ability to return proper c as e, but A c c es s


does n't. While you're waiting for the two development teams at
M ic ros oft to get together on this , here is a func tion that returns
all three c as e types : upper, lower, and proper. T he func tion takes
two arguments : the text to be c onverted and the type of
treatment to apply.

8.19.1. The Code

When c onverting to upper- or lowerc as e, the func tion s imply


us es the res pec tive built- in UCase or LCase func tion. Why reinvent
the wheel?

To c onvert text to proper c as e requires a looping proc es s . I f you


think about it, all you need to do is apply UCase or LCase to eac h
c harac ter in the text. T he tric k is to know whic h letters get whic h
treatment.

Function change_case(txt As String, case_type As String) As String


Select Case case_type
Case "Upper"
change_case = UCase(txt)
Case "Lower"
change_case = LCase(txt)
Case "Proper"
'create proper case
Dim space_flag As Boolean
space_flag = False
'first letter is alway uppercase
change_case = UCase(Left(txt, 1))
For test_case = 2 To Len(txt)
If Mid(txt, test_case, 1) = " " Then
space_flag = True
change_case = change_case & LCase(Mid(txt, test_case, 1))
Else
If space_flag = True Then
change_case = change_case & UCase(Mid(txt, test_case, 1)
space_flag = False
Else
change_case = change_case & LCase(Mid(txt, test_case, 1)
End If
End If
Next test_case
End Select
End Function
To s tart, the firs t letter of the s tring bec omes upperc as e. T hat
one is a given. T hen, a loop c yc les through the res t of the text
s tring. A c harac ter c an be a s pac e. When a s pac e is
enc ountered, a flag is s et to true. When a nons pac e c harac ter is
enc ountered, one of two things c an happen:

I f s pace_flag is true

T he c harac ter c omes direc tly after a s pac e, s o c hange


the c harac ter to upperc as e, and s et space_flag to false.

I f s pace_flag is fals e

T he c harac ter followed another nons pac e c harac ter.


T herefore, the c harac ter being evaluated is n't the firs t
letter of a word, s o c hange it to lowerc as e.

N ote that you don't need to tes t whether a c harac ter is upper- or
lowerc as e while it is being evaluated. I f it follows a s pac e, it
ends up as upperc as e, regardles s of the c as e in whic h it was
typed. T he s ame approac h holds true for c harac ters that don't
follow a s pac e: they are s et to lowerc as e regardles s .

8.19.2. Running the Code

T he change_case func tion needs two arguments . You c an s pec ify


them from field c ontrols , from c ode, or even from within the
I mmediate window. Figure 8 - 3 2 s hows how the func tion is c alled
from a form.

A text box c ontains the s tring of text, a lis tbox offers the three
c as e types , and a c ommand button c alls the func tion with this
little s nippet of c ode:

Private Sub Command2_Click()


Dim z As String
z = change_case(Me.Text1, Me.List1)
MsgBox z
End Sub

Figure 8-32. Returning text in a selected case


Hack 90. Create a Code Library

Make your f avorite custom f unctions available in all your


databases.

A s a developer, I find I often c reate and us e the s ame func tions


on a number of different projec ts . Too many times I have had to
hunt down s ome c ode I wanted to reus e. T his has taken a good
amount of time bec aus e not only do I have to rec all where I us ed
the c ode before, but I als o need to remember whic h c omputer it
is on!

T he s olution to this c haos is to put all the c ode in one databas e


and us e the databas e as a c ode library. A good way to do this is
to c lean up the c ode and as s emble it in one or more c ode
modules , in a s ingle databas e. M ake all the c ode routines be
func tions s o that they return values . T hen, jus t s ave the
databas e and c los e it. Finally, c hange the extens ion of the
databas e to.mda.

Figure 8 - 3 3 s hows a module filled with func tions . T his module is


in the A c c es s file that has the extens ion c hanged to .mda.

With a c hanged extens ion, now the file is rec ognized as an add-
in, as s hown in Figure 8 - 3 4 .

From a regular A c c es s databas e, the c ode library is referenc ed.


I n the V B editor, us e Tools Referenc es to open the
Referenc es dialog box. From the dialog, brows e to the loc ation of
the s aved c ode library. I t might be nec es s ary to c hange "Files of
type" in the A dd Referenc e dialog to A dd- ins , as s hown in Figure
8 -3 5 .

Figure 8-33. A module of code functions


Figure 8-34. Recognizing the file as an add-in
N ow, the func tions in the c ode library are available in the regular
databas e. Figure 8 - 3 6 s hows how they are lis ted in the O bjec t
Brows er.

O ver time, as you c reate more and more routines , the value of
keeping them in one plac e bec omes a real times aver. You never
know when you will need to us e an algorithm you c reated years
ago.

Figure 8-35. Referencing the add-in


Figure 8-36. Viewing the custom functions in the
Object Browser
Hack 91. Automatically Check for
Database Table Updates

Pull updated objects f rom a master database when your


database opens.

O ne of the is s ues you fac e with a dis tributed applic ation is how
to propagate updated tables to the us er c ommunity. T his
happens when you mus t proc es s new data or when lookup lis ts
have new values that have table data as the s ourc e.
Redis tributing the entire databas e is one way to go, although
that dis rupts the workflow. I n that s c enario, us ers mus t s top
what they are doing, get the new file, and s ave it s omewhere.
E ven when that proc es s is automated, you c an't be s ure
s omeone is c urrently us ing the databas e you are about to
overwrite.

I ns tead, here's a great way to have us ers ' applic ations update
thems elves . T he update oc c urs when a us er s tarts up her loc al
c opy of the databas e. A c ode routine c hec ks the databas e's
tables agains t thos e in a mas ter databas e on the network. When
a table in the mas ter databas e is found to be newer, it is c opied
into the us er's databas e.

8.21.1. Running Code at Startup


E ac h us er's loc ally ins talled databas e c ontains a table named
tblTableVers ions that has two fields : one c ontains the names of
the tables in the databas e, and the other has the las t modified
date of eac h table. When the databas e is opened, a c ode routine
opens the mas ter databas e and c ompares the modified date of
the tables in the mas ter databas e with the rec ords in the
tblTableVers ions table.

You might wonder why we don't jus t c ompare the modified dates
of the tables thems elves in the loc al databas e. Why is a table
kept with the modified dates ? T he reas on is a s afeguard: us ers
might alter the tables loc ally, thereby c hanging the las t modified
date on loc al tables . T he point is to c hec k if the mas ter
databas e c ontains updated tables that have not been us ed yet.
T he dates in the loc al tblTableVers ions table are the modified
dates of tables in the mas ter databas efrom the las t time any
partic ular table was c opied.

Figure 8 - 3 7 s hows the tblTableVers ions table. Two tables have


dates that are in D ec ember 2 0 0 4 .

Figure 8-37. Keeping track of the last modified


date
8.21.2. The Code

When the loc al databas e opens , the AutoExec mac ro c alls the
following get_ updates func tion, whic h therefore runs upon
s tartup:

Function get_updates()
On Error GoTo err_end
Dim update_db As String
update_db = "G:\UpdateDB.mdb"

Dim cat As New ADOX.Catalog


Dim tbl As New ADOX.Table
Dim conn As ADODB.Connection
Set conn = CurrentProject.Connection
Dim local_tbl As String
Dim current_object_date As Date
' Open the catalog
cat.ActiveConnection = "Provider=Microsoft.Jet.OLEDB.4.0;" & _
"Data Source=" & update_db
For Each tbl In cat.Tables
If Left(tbl.Name, 4) <> "MSys" Then
current_object_date = _
DLookup("[ModifiedDate]", "tblTableVersions", _
"[TableName] = '" & tbl.Name & "'")
If tbl.DateModified > current_object_date Then
DoCmd.DeleteObject acTable, tbl.Name
DoCmd.TransferDatabase acImport, "Microsoft Access", _
update_db, acTable, tbl.Name, tbl.Name
'store new date
conn.Execute ("Update tblTableVersions Set ModifiedDate=#"
tbl.DateModified & "# Where TableName='" & tbl.Name & "
End If
End If
Next
Set cat = Nothing
Set conn = Nothing
MsgBox "done"
Exit Function
err_end:
MsgBox Err.Description
End Function

8.21.3. Running the Code


A mixture of V BA , A D O X, and A D O works together to c hec k for
updates . A n A D O X c atalog objec t is c reated and is s et to the
mas ter databas eG:\ UpdateDB.mdb in this example. A ll the
tables in the mas ter databas e are examined; however, s ys tem
tables aren't inc luded. A ll s ys tem tables s tart with M Sys [Hack
#15].

T he DateModified property of eac h table in the mas ter databas e


is c hec ked agains t the loc ally s tored date for the s ame named
tables . When it finds that a mas ter databas e table has a newer
modified date, DoCmd deletes the table in the loc al databas e and
imports the new table from the mas ter databas e. T hen, a SQ L
Update s tatement updates the date for the table in the tblTable-
N ames table.

A fter the routine c ompletes , the us er c an go about her bus ines s


as us ual. T he applic ation works as always bec aus e the table
names have not c hanged.

T he only c aveat with this hac k is that it is us eful to update


tables that don't s hare in a relations hip. Related tables need to
have the relations hip broken before you delete and then
rees tablis h them. T herefore, this hac k is perfec t for tables that
c ontain the s ourc e for lookup lis ts or that don't partic ipate in a
relations hip. You c an develop additional c ode to tes t for
relations hips and handle them appropriately.
9. Third-Party Applications
Sec tion 9 .1 . H ac ks 9 2 9 5

H ac k 9 2 . D oc ument Your D atabas e with Total A c c es s


A nalyzer

H ac k 9 3 . Build an A pplic ation Shell with E Z A pplic ation


G enerator

H ac k 9 4 . L oad Your D atabas e with Tes t D ata

H ac k 9 5 . U s e A c c es s as an XM L D atabas e
9.1. Hacks 9295
M uc h of the foc us in A c c es s books involves how to manage
building an applic ationworking with tables , queries , forms , and
reports and applying s olutions with mac ros and c ode. H owever,
onc e you've c ompleted all that work, you might need a tool to
doc ument your databas e [Hack #92]. Similarly, when you c reate
an applic ation, you might not be c ons idering all the
management, func tionality, and utilities that c an go into your
databas e. Fortunately, a produc t is available that fles hes this
out for you [Hack #93].

O f c ours e, data is the point of a databas e. H ow often have you


des igned a databas e and then had to find s ome data to tes t it
with? You c an enter data manually, or you c an us e a produc t that
c reates data for you [Hack #94].

I n addition to all this , this c hapter als o s hows you how to build
an applic ation without any tables . "U s e A c c es s as an XM L
D atabas e" [Hack #95] s hows how to run an XM L databas e from
A c c es s , reading from and writing to an external XM L file.
Hack 92. Document Your Database with
Total Access Analyzer

Get the f ull nuts-and-bolts skinny on your database.

E ven a s imple A c c es s databas e has a lot in it. J us t take a table


of data, a form, a few c ontrols , and a report, and the number of
properties is in the hundreds . A large databas e has an
unimaginable number of items in it.

N ow, imagine a utility that lets you drill down anywhere in your
databas e and unc over nuggets of information you probably didn't
even know about. E nter the Total A c c es s A nalyzer by FM S, I nc .
(http://www.fms inc .c om). T his outs tanding produc t tells you
everything about your databas e. I t leaves nothing out.

9.2.1. Running the Analyzer

A fter you ins tall the A nalyzer, it is available as an add- in.


Regardles s of whic h databas e you have open, jus t go to the
Tools A dd- I ns menu to find the A nalyzer. T he A nalyzer runs
from a main form, s hown in Figure 9 - 1 .

Figure 9-1. Total Access Analyzer


To get s tarted, c lic k the D oc ument button. T his runs the
D oc umentation Wizard, whic h walks you through s elec ting whic h
items to doc ument. Figure 9 - 2 s hows the firs t s c reen of the
D oc umentation Wizard.

A fter making a s elec tion, c lic k the N ext button to bring up the
wizard's s ec ond s c reen, s hown in Figure 9 - 3 .

O n this s c reen, you c an s elec t to doc ument relations hips ,


doc ument s ec urity, and generate field c ros s referenc es that
s how you where fields are us ed throughout the databas e, among
other things . G enerating field c ros s referenc es is a great feature
bec aus e although it's good to get details about a field, it's even
better to know where the field's data is pres ented (on forms and
reports ).

T he doc umentation proc es s works by writing the res ults to an


external file. I n the third wizard s c reen, s hown in Figure 9 - 4 , you
c an s elec t where this external file goes (or jus t ac c ept the
default loc ation) and then s c hedule the doc umentation.

Figure 9-2. Documentation Wizard, step 1


Figure 9-3. Documentation Wizard, step 2
T he doc umentation proc es s begins when you c lic k the Finis h
button. I t c an take a moment or two, depending on the s ize of the
databas e.
9.2.2. Viewing the Documentation

To view the doc umentation, c lic k the E xplore button bac k on the
main form, s hown in Figure 9 - 1 . T he D oc umentation E xplorer
opens , as s hown in

Figure 9-4. Documentation Wizard, step 3

Figure 9 - 5 . I n the E xplorer, you s elec t objec ts in the left


explorer pane, and the details are s hown in the right pane.

Figure 9-5. Exploring the documentation


A t this point you have a wealth of information to s ift through. You
c an look up everything about your databas e and its objec ts ; the
E xplorer's layout makes this eas y. For example, as s hown in
Figure 9 - 6 , you c an s ee all the proc edures in a module and their
dec larations all next to eac h other and eas y to c ompare. J us t try
doing that in a c ode module.

9.2.3. Errors and Suggestions

N ot only does Total A c c es s A nalyzer doc ument the databas e,


but it als o goes further to identify problems and offer
s ugges tions . Figure 9 - 7 s hows

Figure 9-6. Exploring a module


where a potential problem has been flagged. I tend to forget to
s et the O ption E xplic it s etting in my modules . Well, I gues s I
c an't get away with that anymore!
Figure 9-7. Exploring an error

Figure 9 - 8 s hows a lis t of s ugges tions the A nalyzer has


as s embled. Suc h s ugges tions are really us eful for making an
applic ation the bes t it c an be.

Figure 9-8. Exploring suggestions


Total A c c es s A nalyzer is a great produc t not only for lis ting all
the objec ts , properties , methods , and attributes about a
databas e, but als o for finding out what you c an do to improve
them. O ther options from the main form inc lude s earc hing for
partic ular items in the doc umentation and printing the
doc umentation.
Hack 93. Build an Application Shell with
EZ Application Generator

Let your f ingers do the walking through the process of creating


an A ccess application.

Why go through the drudgery of putting together a databas e from


s c ratc h c reating a s plas h s c reen, integrating s ec urity and help,
c reating a report generator, and morewhen a great produc t is
available that c an do it for you? E Z A pplic ation G enerator by
D atabas e C reations , I nc . (http://www. databas ec reations .c om)
c reates a framework for an applic ation. You s till c reate your data
tables , forms , and other databas e objec ts , but in the end, you
will have a c omplete applic ation that has it all, from A to Z.

T he E Z A pplic ation G enerator Wizard runs through nearly a


dozen s c reens that c over all the bas es , from adding applic ation
information, logos , and other graphic s , right through to s etting
up s ec urity, error trapping, and advanc ed s earc h features .

To get s tarted, c reate a new databas e. You c an c reate your


tables , forms , reports , and other databas e objec ts now, or
anytime after the applic ation s hell is c ompleted. T he E Z
A pplic ation G enerator s tarts when you s elec t it from the A dd-
I ns lis t.

T he firs t s c reen, s hown in Figure 9 - 9 , ac c epts the applic ation


title and other general information.
Figure 9-9. Entering general application
information
I n the next s c reen, s hown in Figure 9 - 1 0 , you s elec t the images
to us e for the s plas h s c reen, the logo, and the ic on.

I n the next s c reen, s hown in Figure 9 - 1 1 , you s elec t a theme for


the s witc hboard. Boy, is this eas y! To think of all thos e years I
did this by hand!

Several more s c reens follow, in whic h you c an s et up titles , tips


of the day, legal agreements , and other us eful applic ation items .
A long the way, s ec urity and error handling are initiated.

T he s c reen in Figure 9 - 1 2 lets you s elec t to inc lude a c alendar,


c loc k, and c alc ulator, as well as s c heduling options .

When the E Z A pplic ation G enerator Wizard finis hes , the


databas e is populated with the objec ts needed to run your
applic ation. You c an ac c es s and run all tas ks , inc luding your own
databas e objec ts , from a s witc hboard. A ll the utilities you
s elec ted in the E Z A pplic ation G enerator Wizard are available
from the s witc hboard as well, as s hown in Figure 9 - 1 3 .

Figure 9-10. Selecting graphics


Figure 9-11. Selecting a switchboard theme
D atabas e C reations offers other developer- friendly produc ts as
well. V is it the c ompany's web s ite for more information.

Figure 9-12. Selecting utilities


Figure 9-13. Running the application in a
switchboard
Hack 94. Load Your Database with Test
Data

Use predesigned test data that matches your tables and f ields.

Building a databas e applic ation is one thing, but tes ting it is


another. Sometimes you c an get your hands on arc hived
produc tion data and us e it to tes t an applic ation before it is
releas ed. O ther times , though, you have to c reate your own tes t
data.

A lthough putting a few rec ords into your tables to make s ure
everything works ac c ordingly c an reas s ure you that your
databas e applic ation works , loading it with hundreds or
thous ands of rec ords lets you really s ee how your applic ation will
perform when it's releas ed.

Rec ords 2 G o (http://www.rec ords 2 go.c om) is a data- generation


produc t that lets you define s c hemas and then produc es data to
your s pec ific ations . T he c reated data is s aved into an external
text or XM L file, whic h you c an import into A c c es s . You c an
make any number of rec ords us ing this tool; I have us ed
Rec ords 2 G o to make rec ords numbering in the hundreds of
thous ands .

Figure 9 - 1 4 s hows how Rec ords 2 G o works . You have a handful


of s ettings to input, inc luding how many rec ords to make, what
the c reated file is named, and where it will be s aved. T he form's
main foc us is the grid in whic h you define a table layout.
Figure 9-14. Creating an XML data set
You c an c reate two types of files : text and XM L . Figure 9 - 1 4
s hows an XM L s c hema being s et up. Whether you're c reating a
text file or an XM L file, eac h row in the grid repres ents a field. O n
eac h row, you enter the field name, field type, and other
parameters . You c an us e s tandard field types s uc h as numbers ,
dates , and textor you c an us e field types from a s et of predefined
data. T he predefined data offers name and addres s field type
information.

You c an us e wildc ards to c ontrol how data is c reated. For


example, when you s elec t the Text type, you c an enter a phras e,
whic h will be c reated as is , or you c an us e s pec ific wildc ards to
repres ent random numbers or c harac ters . I n the s pec ific ation in
Figure 9 - 1 4 , pound s igns (#) indic ate random number c reation.

Figure 9 - 1 5 s hows the XM L data generated from the


s pec ific ation s hown in Figure 9 - 1 4 . T he predefined Full Name
attribute c reated jus t that: XM L attributes of names . T he
E mployeeI D field is filled with random numbers , and the hire
dates fall within the range in the s pec ific ation.

Figure 9-15. The created XML data


Rec ords 2 G o is great for produc ing large s ets of data. Figure 9 -
1 6 s hows a s pec ific ation for making 1 5 ,0 0 0 text rec ords . T he
tas k was c ompleted in 3 3 s ec onds .

Figure 9-16. 15,000 records in 33 seconds


Figure 9 - 1 7 s hows the c reated text file. O ne of the options is
whether to inc lude a header row. O ther options inc lude s elec ting
the delimiter and the text qualifier.

Figure 9-17. Test data ready to be imported into


Access
A great fac et of the produc t is that you c an s ave and load
s pec ific ations . I n other words , when you c reate a data
s pec ific ation in the grid, you c an s ave it s o that you c an us e it
whenever you need it. You c an move rows around in the grid, add
rows , delete rows , and s o on. You even c an us e jus t s ome of the
rows and leave others outwithout having to delete them.
Hack 95. Use Access as an XML
Database

Have A ccess work as a f ront end to your XML data.

A few hac ks throughout this book (s ee the lis t at the end of this
hac k) explore XM L us age with A c c es s . T his hac k pres ents the
c rowning ac hievement: a c omplete XM L databas e. To c larify, this
hac k s hows you how A c c es s c an read from and write to XM L files
and have the data appear on a form for viewing and editing. T he
form has the requis ite databas e func tionality: brows e, add a
rec ord, update a rec ord, and delete a rec ord.

T he power behind making this work is to inc orporate the M SXM L


pars er from M ic ros oft. V is it http://www.mic ros oft.c om/xml and
s ee "P rovide C omplete XM L C ontrol to A ny Vers ion of A c c es s "
[Hack #87] for an introduc tion to getting and us ing the pars er.
T he pars er is the key to getting A c c es s to do more than s imple
XM L imports and exports .

Figure 9 - 1 8 s hows the form us ed in the applic ation. T he


dis played rec ord is from an XM L file. T he form has P revious and
N ext buttons for navigating through rec ords , as well as U pdate
and D elete buttons . T he N ew button is for entering new rec ords ,
and the Save button is us ed for s aving new rec ords to the file.

Figure 9-18. Displaying data from an XML file


T he data is c ompletely external, but it does n't c ome from a
table. T his applic ation c ontains no tables , whether linked or
c onnec ted with A D O or O D BC . I n fac t, this applic ation c ontains
nothing exc ept this one form.
9.5.1. The Code

T he following c ode behind the form takes c are of all data


management:

Option Compare Database


Public xmlobj As DOMDocument
Public xml_list As IXMLDOMNodeList
Public record_num As Integer
Public file_name As String

Private Sub cmdDelete_Click()


Dim xml_node As IXMLDOMElement
Set xml_node = xmlobj.documentElement.childNodes(record_num)
xmlobj.documentElement.removeChild xml_node
xmlobj.Save file_name
reload_file
End Sub

Private Sub cmdNew_Click()


Me.txtEmployeeID = ""
Me.txtEmployeeName = ""
Me.txtHireDate = ""
Me.txtRecordNum = ""
End Sub

Private Sub cmdNext_Click()


If record_num < xml_list.length - 1 Then
record_num = record_num + 1
Else
record_num = 0
End If
load_record
End Sub
Private Sub cmdPrevious_Click()
If record_num > 0 Then
record_num = record_num - 1
Else
record_num = xml_list.length - 1
End If
load_record
End Sub

Private Sub cmdSave_Click()


Dim xml_node As IXMLDOMElement
If Me.txtEmployeeID = "" Or Me.txtEmployeeName = "" Or _
Me.txtHireDate = "" Then
MsgBox "Must fill in all three fields"
Exit Sub
End If
Set xml_node = xmlobj.createElement("Employee")
xml_node.setAttribute "EmployeeID", Me.txtEmployeeID
xml_node.setAttribute "EmployeeName", Me.txtEmployeeName
xml_node.setAttribute "HireDate", Me.txtHireDate
xmlobj.documentElement.appendChild xml_node
xmlobj.Save file_name
reload_file
End Sub

Private Sub cmdUpdate_Click()


xmlobj.documentElement.childNodes(record_num) _
.Attributes(0).nodeValue = Me.txtEmployeeID
xmlobj.documentElement.childNodes(record_num) _

.Attributes(1).nodeValue = Me.txtEmployeeName
xmlobj.documentElement.childNodes(record_num) _
.Attributes(2).nodeValue = Me.txtHireDate
xmlobj.Save file_name
reload_file

End Sub

Private Sub Form_Open(Cancel As Integer)


file_name = "C:\EmployeeData.xml"
Set xmlobj = New DOMDocument
xmlobj.async = False
xmlobj.Load file_name
Set xml_list = xmlobj.selectNodes _
("Employees/Employee")
'load first record
record_num = 0
load_record
End Sub

Sub load_record()
Me.txtEmployeeID = _
xml_list.Item(record_num).Attributes(0).nodeValue
Me.txtEmployeeName = _
xml_list.Item(record_num).Attributes(1).nodeValue
Me.txtHireDate = _
xml_list.Item(record_num).Attributes(2).nodeValue
Me.txtRecordNum = record_num + 1
End Sub

Sub reload_file()
xmlobj.Load file_name
Set xml_list = xmlobj.selectNodes _
("Employees/Employee")
'load first record
record_num = 0
load_record
End Sub
9.5.2. Loading the XML File

When the form opens , a public XM L variable (xmlobj) is s et to the


loaded XM L file, whic h res ides in memory. A lis t of nodes
(xml_list) holds the E mployee rec ords , and the firs t rec ord is
dis played in the form:

Private Sub Form_Open(Cancel As Integer)


file_name = "C:\EmployeeData.xml"
Set xmlobj = New DOMDocument
xmlobj.async = False
xmlobj.Load file_name
Set xml_list = xmlobj.selectNodes _
("Employees/Employee")

'load first record


record_num = 0
load_record
End Sub

9.5.3. Browsing Records

I n XM L lingo, the length property is the s ame as the count


property in V B. When the N ext or P revious buttons are c lic ked, a
public variable, record_ num, is c ompared with the number of XM L
rec ords . I f the record_num variable hits the total c ount as a res ult
of c lic king N ext, it res ets to 0 . I f the record_num variable hits 0
as a res ult of c lic king P revious , it res ets to the number of
rec ords . C lic king N ext or P revious c ompletes with a c all to the
load_record routine:

Private Sub cmdNext_Click()


If record_num < xml_list.length - 1 Then
record_num = record_num + 1
Else
record_num = 0
End If
load_record
End Sub

Private Sub cmdPrevious_Click()


If record_num > 0 Then
record_num = record_num - 1
Else
record_num = xml_list.length - 1
End If
load_record
End Sub

T he load_record routine s imply fills the c ontrols on the form with


the data from the XM L rec ord that is pos itioned at the record_num
number:

Sub load_record()
Me.txtEmployeeID = _
xml_list.Item(record_num).Attributes(0).nodeValue
Me.txtEmployeeName = _
xml_list.Item(record_num).Attributes(1).nodeValue
Me.txtHireDate = _
xml_list.Item(record_num).Attributes(2).nodeValue
Me.txtRecordNum = record_num + 1
End Sub

9.5.4. Updating a Record

When data is c hanged while on the form, the U pdate button mus t
be c lic ked to s ave the c hanges bac k to the original file. T he
proc es s here is to update the node (the employee rec ord) in the
file with the form values . T he Employee node is a c hild of
documentElement Employees. T he values aren't s aved until the Save
method runs on xmlobj. A fter that, the file is reloaded, and this
las t s tep res ets the form bac k to the firs t rec ord (an alternative
is to leave the form dis playing the updated rec ord):

Private Sub cmdUpdate_Click()


xmlobj.documentElement.childNodes(record_num) _
.Attributes(0).nodeValue = Me.txtEmployeeID
xmlobj.documentElement.childNodes(record_num) _
.Attributes(1).nodeValue = Me.txtEmployeeName
xmlobj.documentElement.childNodes(record_num) _
.Attributes(2).nodeValue = Me.txtHireDate
xmlobj.Save file_name
reload_file

End Sub
9.5.5. Deleting a Record

To delete a rec ord s et a node variable (xml_node) to the employee


rec ord. T hen, the removeChild method of its parent deletes it:

Private Sub cmdDelete_Click()


Dim xml_node As IXMLDOMElement
Set xml_node = xmlobj.documentElement.childNodes(record_num)
xmlobj.documentElement.removeChild xml_node
xmlobj.Save file_name
reload_file
End Sub

A s with other file c hanges , the Save method is nec es s ary.

9.5.6. Adding a New Record

T he N ew and Save buttons work together to add a rec ord to the


XM L file. T he N ew button s imply c lears the form, and new
employee information c an be entered. T he Save button runs the
c ode that s aves a new rec ord.

A fter validating that all text boxes c ontain data, a new element
is c reated. A ttributes are s et to the form values , and the
element, along with its attributes , are s aved us ing the
appendChild method. T he Save method follows , and the file is
reloaded (now it c ontains the new rec ord):

Private Sub cmdSave_Click()


Dim xml_node As IXMLDOMElement
If Me.txtEmployeeID = "" Or Me.txtEmployeeName = "" Or _
Me.txtHireDate = "" Then
MsgBox "Must fill in all three fields"
Exit Sub
End If
Set xml_node = xmlobj.createElement("Employee")
xml_node.setAttribute "EmployeeID", Me.txtEmployeeID

xml_node.setAttribute "EmployeeName", Me.txtEmployeeName


xml_node.setAttribute "HireDate", Me.txtHireDate
xmlobj.documentElement.appendChild xml_node
xmlobj.Save file_name
reload_file
End Sub

9.5.7. See Also

"I mport Varied XM L D ata into A c c es s " [Hack #63]

"E xport XM L D ata Sanely" [Hack #64]

"Break T hrough V BA 's Trans formation Barrier" [Hack


#65]

"P rovide C omplete XM L C ontrol to A ny Vers ion of


A c c es s " [Hack #87]
10. The Internet

Sec tion 1 0 .1 . H ac ks 9 6 1 0 0

H ac k 9 6 . E xport a Report as H T M L

H ac k 9 7 . U s e a Brows er I ns ide A c c es s

H ac k 9 8 . P ull the H T M L Sourc e C ode from a Web Site

H ac k 9 9 . D ownload Files U s ing the Web Brows er


C ontrol

H ac k 1 0 0 . U s e a Smart Tag to O pen a Web P age


10.1. Hacks 96100
Web tec hnologies and the I nternet are ingrained in s o muc h of
what we do that extending the Web to work with our databas e
applic ations makes perfec t s ens e. A lthough at firs t A c c es s
might not s eem like a web- s avvy produc t, with a few hac ks
A c c es s c an work well with the Web.

T his c hapter begins by s howing how to s ave a report as H T M L


[Hack #96]. T his lets you pos t your data to a web s ite. Better
yet, how about bringing the I nternet into your databas e? "U s e a
Brows er I ns ide A c c es s " [Hack #97] s hows you how to us e a web
brows er right within your A c c es s forms . N eed to get data from a
web s ite? J us t FT P it over [Hack #99].
Hack 96. Export a Report as HTML

Preview your reports on web pages to reach a larger audience.

P reviewing s ummarized data is a key bus ines s need, and


A c c es s handles it quite well. To reac h a large audienc e, you c an
print, email, s ave to Word, and s o on. A nother option is to s ave a
report to a web page.

When you open an A c c es s report, one of the s elec tions on the


File menu is E xport. A handful of output types are available, and
one of them is H T M L . Figure 1 0 - 1 s hows a report being exported
as H T M L .

Saving the report as an H T M L file opens up a number of


pos s ibilities . T he likely ac tion is to view the report in a web
brows er. Figure 1 0 - 2 s hows the report previewed in I nternet
E xplorer. T he H T M L file is no longer a part of the databas e, s o
you mus t open it in the brows er us ing the File O pen menu.

You c an upload the H T M L file to a web s erver and make it


available public ly, or you c an upload it to a c ontrolled intranet
group. O pening the file in N otepad or another plain- text editor
reveals the ac tual H T M L c ode, as s hown in Figure 1 0 - 3 .

Figure 10-1. Selecting to export a report as


HTML
Figure 10-2. Viewing the exported data in a web
browser
N ote the highlighted c ontents of the H T M L title tag. Referring
bac k to Figure 1 0 - 2 , you c an s ee that the H T M L file is named
Sightings ByState.html. But the title in Figure 1 0 - 3 jus t s ays
Report1 . Report1 was the name of the report in A c c es s , s o
that's the title us ed in the I nternet E xplorer titlebar in Figure
1 0 - 2 . We'll have to c hange the title in N otepad and dis play the
file again in the brows er.

Figure 10-3. Viewing the source HTML


Figure 1 0 - 4 s hows a s lightly edited vers ion of the H T M L report.
T he title has been c hanged, and a few formatting c hanges are in
plac e.

Figure 10-4. An updated view


T hough the H T M L c ode was edited direc tly in N otepad, you c an
open the H T M L file with any H T M L editor, s uc h as D reamweaver
or FrontP age.
Hack 97. Use a Browser Inside Access

Place the Microsof t web browser on a f orm to coordinate data


and the Web.

A c c es s tables have a hyperlink field type, whic h s tores a U RL


that, when c lic ked, opens your c omputer's brows er and dis plays
the s ite. T his is us eful, but to view the databas e information and
the web s ite together is a c hallenge. You might have to reduc e
the s ize of both A c c es s and the brows er s o that they fit together
on your c omputer s c reen.

T his hac k puts the brows er right on the A c c es s form. To view the
web s ite at the s tored U RL , you jus t load the brows er with the
U RL addres s , and the web s ite appears on the form.

Figure 1 0 - 5 s hows a form in D es ign mode. T he M ic ros oft Web


Brows er c ontrol (one of the items in the lis t of M ore C ontrols
that is available from the button on the toolbox) has been plac ed
on the form.

Figure 10-5. Selecting the Microsoft Web


Browser control
Figure 1 0 - 6 s hows the form in V iew mode.
T he brows er c ontrol dis plays the web s ite lis ted in the c urrent
rec ord. T his oc c urs when you c lic k the D is play Web Site button,
whic h has this line of c ode in its Click event:

Me.WebBrowser1.Navigate URL:=Me.website

T he web brows er c ontrol has the Navigate method, and it


navigates to the s upplied U RL . I n this example, the U RL c omes
from the webs ite field on the form, from the c urrent rec ord. O f
c ours e, you c an feed a U RL to the web brows er c ontrol in other
ways . T his is jus t one example.

Figure 10-6. Viewing a web site on the form


J us t like any c ontrol, you c an plac e multiple web brows er
c ontrols on a form. Figure 1 0 - 7 s hows s uc h an arrangement.

Figure 10-7. Multiple browsers on a form


T his might s eem exc es s ive, but ac tually, you might have a good
reas on for us ing multiple brows ers . For example, an applic ation
that integrates with c urrent events c an us e this . Financ ial
applic ations c ould keep tabs on different s ec urities markets .
T he brows er c ontrols c ould even be dis playing the output of
webc ams .
Hack 98. Pull the HTML Source Code
from a Web Site

Integrate web data into your application.

"U s e a Brows er I ns ide A c c es s " [Hack #97] s hows you how to


us e the M ic ros oft Web Brows er c ontrol to dis play a web page.
T his hac k takes that func tionality a s tep further and s hows how
to get to the s ourc e c ode. Being able to ac c es s the s ourc e c ode
makes it pos s ible to extrac t data from a web s ite.

Figure 1 0 - 8 s hows a web s ite being dis played in the brows er


c ontrol, and a mes s age box dis plays the s ite's H T M L .

Figure 10-8. Reading the HTML source from a


web site
T he M ic ros oft Web Brows er c ontrol has an extens ive
programmatic model. V is it
http://ms dn.mic ros oft.c om/library/default.as p?
url=/works hop/brows er/prog_brows er_node_entry.as p
for more information.

T he H T M L is returned with this line of c ode:

MsgBox Me.WebBrowser1.Document.documentElement.innerhtml

T he programmatic model for the web brows er c ontrol follows the


doc ument objec t model (D O M ). A s the brows er dis plays a web
s ite, documentElement and its c hild nodes bec ome available. I n
this example, the full H T M L is ac c es s ed with the innerhtml
property. Bec aus e the H T M L is ac c es s ible, you c an pas s it to
any routine you want. For example, you c an have a routine that
looks for H T M L tables from whic h to pull data or that s earc hes
through the H T M L for keywords , and s o on.
Hack 99. Download Files Using the Web
Browser Control

FTP f iles without ever leaving your database.

"U s e a Brows er I ns ide A c c es s " [Hack #97] and "P ull the H T M L
Sourc e C ode from a Web Site" [Hack #98] s how you how to
brows e the Web from an A c c es s form and how to retrieve the
s ourc e H T M L c ode. T his hac k s hows you how to us e the Web
Brows er c ontrol to pull files from an FT P s ite.

T he File Trans fer P rotoc ol (FT P ) is c ommonly us ed to move files


to and from a web s erver. O ften, the s ite is pas s word- protec ted,
s o to try out this hac k, you need the rights to ac c es s an FT P
s ite.

10.5.1. Placing the Web Browser Control


on a Form

A s dis c us s ed in "U s e a Brows er I ns ide A c c es s " [Hack #97],


you c an plac e the M ic ros oft Web Brows er c ontrol on a form. T he
Navigate method takes a U RL addres s to navigate to. I n this
hac k, an FT P U RL is s upplied ins tead of an H T T P U RL .

U s ing a line of c ode s uc h as the following, the Web Brows er


c ontrol opens a login dialog (s hown in Figure 1 0 - 9 ):
Me.WebBrowser0.Navigate URL:="ftp.logicstory.com"

Figure 10-9. Entering a username and password


for the FTP site
A fter the login is c omplete, the Web Brows er c ontrol dis plays the
s ite's c ontents . You c an view, c opy, delete, and rename objec ts
depending on your us er permis s ions , as s hown in Figure 1 0 - 1 0 .

Figure 10-10. Viewing contents of the web site


When you s elec t a file to c opy, the C opy I tems dialog dis plays
the files ys tem on the loc al c omputer. A direc tory is s elec ted
where the c opied file will be pas ted, as s hown in Figure 1 0 - 1 1 .

Figure 10-11. Selecting the local directory to


save the copied file
10.5.2. Uploading Files

A s noted earlier, when s elec ting a file to download, the "C opy
this item" link opens the C opy I tems dialog box. A lthough no
equivalent link is available for initiating an upload, you c an
upload files to the FT P s ite. A ll you have to do is s elec t the file
on the loc al mac hine, and then c lic k onc e in the Web Brows er
c ontrol and us e the pas te keyboard s hortc ut (C trl- V ). Figure 1 0 -
1 2 s hows the progres s of a c opy operation, c onfirming that a file
is being c opied to the s ite.

Figure 10-12. Copying a file to the remote


server
Hack 100. Use a Smart Tag to Open a
Web Page

Quickly review web inf ormation when you need to see it.

D evelopers have mixed feelings about s mart- tag tec hnology.


Some c reate s ophis tic ated s mart- tag s olutions , and others s hun
their us e. I f you're open to us ing s mart- tag tec hnology, here is a
s imple way to us e a s mart tag to open a web page.

Firs t, a s mart tag is as s oc iated with a table field. Figure 1 0 - 1 3


s hows a s mart tag being as s oc iated with the C os t field in the
tblServic es table. A fter the s mart tag is s elec ted, the field's
s mart tag is available to c lic k.

Figure 10-13. Selecting smart tags


Figure 1 0 - 1 4 s hows the table open in D atas heet view. I n eac h
row, the C os t field dis plays a s mall purple triangle in the lower-
right c orner. H overing your mous e pointer over the triangle
opens the link to the s mart tag.

Figure 10-14. Accessing the smart tag from the


Cost field

C lic king the s mart tag initiates the ac tion as s oc iated with the
s mart tag. I n this example, the ac tion is to open a web page to
dis play s ome additional bus ines s information.

Figure 1 0 - 1 5 s hows the web page that opens when the s mart tag
is c lic ked.

Figure 10-15. A web site accessed from a smart


tag
I n mos t databas e applic ations , us ers don't work direc tly with
tables ; ins tead, they do all their work through forms . Figure 1 0 -
1 6 s hows that the s mart tag in the underlying table is available
on a form as well. From here, too, c lic king the s mart tag opens
the web page.

Figure 10-16. Smart tags at work on a form

To s ummarize, a s mart tag c an bring information from the Web to


the A c c es s applic ation. T he mec hanic s of c reating s mart tags
are beyond our s c ope here, but if you vis it
(http://www.mic ros oft.c om and s earc h for s mart tags , you will
turn up many artic les on their development and us e.
Colophon
O ur look is the res ult of reader c omments , our own
experimentation, and feedbac k from dis tribution c hannels .
D is tinc tive c overs c omplement our dis tinc tive approac h to
tec hnic al topic s , breathing pers onality and life into potentially
dry s ubjec ts .

T he tool on the c over of Acces s Hacks is a flour s ifter. E ver s inc e


humans firs t produc ed flour, they have c ons truc ted s ifters to
refine it. T he V ikings us ed round, c up- s haped s ieves utilizing
hors ehair fiber. I n preindus trial times , whole wheat was ground in
a mill, then s ifted through s uc c es s ively finer bolting c loths to
get various grades of flour, from dark whole- wheat to almos t
white for the wealthy. T he bran removed while bolting was us ed
by the miller to feed his lives toc k or was s old to others as feed.

Something s imilar to this proc es s is s till us ed in modern mills .


Wheat firs t gets broken and s eparated by plain s ifters and
purifiers two advanc ed s ifting mec hanis ms . D uring this proc es s
the c ours e outer bran s kins are s ifted from the inner white
portions , c alled endos perm or s emolina. G radually the s emolina
is milled down into s mooth, powdery flour. T he c lean bran, wheat
feed, and flour are eac h c ollec ted in s eparate c hannels by a
large number of different mac hines . N o hand even touc hes the
wheat until it leaves the mill.

Today flour is generally pres ifted, but s ifting with a hand rotary
c rank or elec tric s ifter is rec ommended before baking to remove
lumps and further aerate flour, making it livelier for kneading.

M ary A nne Weeks M ayo was the produc tion editor and
proofreader, and A udrey D oyle was the c opyeditor for Acces s
Hacks . D arren Kelly provided quality c ontrol. J ohnna D ins e wrote
the index.
H anna D yer des igned the c over of this book, bas ed on a s eries
des ign by E die Freedman. T he c over image is an original
photograph by P hotoSpin P ower P hotos . Karen M ontgomery
produc ed the c over layout with A dobe I nD es ign C S us ing
A dobe's H elvetic a N eue and I T C G aramond fonts .

D avid Futato des igned the interior layout. T his book was
c onverted by Keith Fahlgren to FrameM aker 5 .5 .6 with a format
c onvers ion tool c reated by E rik Ray, J as on M c I ntos h, N eil Walls ,
and M ike Sierra that us es P erl and XM L tec hnologies . T he text
font is L inotype Birka; the heading font is A dobe H elvetic a N eue
C ondens ed; and the c ode font is L uc as Font's T heSans M ono
C ondens ed. T he illus trations that appear in the book were
produc ed by Robert Romano and J es s amyn Read us ing
M ac romedia FreeH and M X and A dobe P hotos hop C S. T his
c olophon was written by Lydia O nofrei.
Index

[SYMBOL] [A ] [B] [C] [D] [E] [F] [G] [H] [I] [J]
[K] [L] [M] [N] [O] [P] [Q] [R] [S] [T] [U] [V ] [W]
[X] [Z]
Index

[SYMBOL] [A ] [B] [C] [D] [E] [F] [G] [H] [I] [J]
[K] [L] [M] [N] [O] [P] [Q] [R] [S] [T] [U] [V ] [W]
[X] [Z]

.bmp files
.jpg files
.tif files
Index

[SYMBOL] [A] [B] [C] [D] [E] [F] [G] [H] [I] [J]
[K] [L] [M] [N] [O] [P] [Q] [R] [S] [T] [U] [V ] [W]
[X] [Z]

Action queries
actions
Admin user
ADO (ActiveX Data Objects)
ADO library

ADOX library
datatypes

references

table creation
alignment
And-based criteria
apostrophes
Append query

appending
across databases

apostrophes and

failed operations

Paste Append

records
log table

records to log table

applications
personalization

third-party
MSXML parser

Total Access Analyzer


arguments
audit logs
AutoExec macro

preferences and
AutoKeys macro

AutoNumber field
custom values
seeding
Index

[SYMBOL] [A ] [B] [C] [D] [E] [F] [G] [H] [I] [J]
[K] [L] [M] [N] [O] [P] [Q] [R] [S] [T] [U] [V ] [W]
[X] [Z]

Back Style property


back-end databases
backdoor building
Before Update event
Before_Update event

browsers
file dow nload
brow sers, Web
bulk updates
by letter
Index

[SYMBOL] [A ] [B] [C] [D] [E] [F] [G] [H] [I] [J]
[K] [L] [M] [N] [O] [P] [Q] [R] [S] [T] [U] [V ] [W]
[X] [Z]

Can Shrink property


Cartesian products

cascading
updates
relationships and
cascading updates
case conversion
characters 2nd 3rd 4th 5th 6th
cleanup, databases
clocks

code
arguments and

passw ord protection


subroutines and
code library creation
coding
command-line
Compact on Close option
compacting databases
comparing tables
compatibility
conditional
conditional formatting

highlighting and
Conditional Formatting dialog box
conditional macro actions
conditional subtotals 2nd 3rd 4th 5th
constants

controls
custom

information about

listboxes
populating

sorting

Tag property and


unbound
copying betw een tables
CreateObject function
creates

creating
tables
outside Access

w ith SQL Server scripts


Crosstab queries
Crosstab Query Wizard
CurrentUser property
custom functions
Index

[SYMBOL] [A ] [B] [C] [D] [E] [F] [G] [H] [I] [J]
[K] [L] [M] [N] [O] [P] [Q] [R] [S] [T] [U] [V ] [W]
[X] [Z]

data changes
cascading deletes
Data Definition query
data entry
data sources
data summaries
data transfer
Database Splitter utility
database splitting

database window
groups 2nd

objects

plain
databases
appending across

compacting

size

size limits
datatypes
DateDiff function

dates
functions and

header

deleting
shortcuts
descriptions
desktop shortcuts to databases

dialog boxes
Conditional Formatting

Edit Relationships

New Query

Sorting and Grouping


DLookup function
Document Compare utility (Word)

domain aggregate functions


aggregate functions and
dow nloading files
Index

[SYMBOL] [A ] [B] [C] [D] [E] [F] [G] [H] [I] [J]
[K] [L] [M] [N] [O] [P] [Q] [R] [S] [T] [U] [V ] [W]
[X] [Z]

Edit Relationships dialog box


embedded reports
Enter event

entering
text
additional

entering text
additional

tab order
Enterprise Manager
enumerations

events
Before Update

Enter

forms

MouseMove

Excel
functions 2nd 3rd 4th

FV function

importing from 2nd 3rd 4th 5th

macro recorder

Paste Special Transpose feature 2nd

w orksheet imports

Excel data
importing
into separate tables
Execute method
Export Text Wizard

exporting
to XML 2nd 3rd 4th 5th 6th 7th 8th 9th 10th 11th 12th
expressions
subtotals
EZ Application Generator
Index

[SYMBOL] [A ] [B] [C] [D] [E] [F] [G] [H] [I] [J]
[K] [L] [M] [N] [O] [P] [Q] [R] [S] [T] [U] [V ] [W]
[X] [Z]

Favorites group

fields
AutoNumber
custom values

default values

Number

number fields

Text
Find Unmatched Query w izard
focus
formatting

forms
custom controls

events

keyboard shortcuts

lists

personalization

Window s Media Player


frmKeyboardShortcuts form
function keys

functions
CreateObject

creating

custom

dates

DLookup

domain aggregate

Excel 2nd 3rd 4th 5th

ISNULL

Len
Mid
FV function (Excel)
Index

[SYMBOL] [A ] [B] [C] [D] [E] [F] [G] [H] [I] [J]
[K] [L] [M] [N] [O] [P] [Q] [R] [S] [T] [U] [V ] [W]
[X] [Z]

graphics
groups

adding

database w indow

Favorites

macros
Index

[SYMBOL] [A ] [B] [C] [D] [E] [F] [G] [H] [I] [J]
[K] [L] [M] [N] [O] [P] [Q] [R] [S] [T] [U] [V ] [W]
[X] [Z]

hiding objects

HTML
source code
Index

[SYMBOL] [A ] [B] [C] [D] [E] [F] [G] [H] [I] [J]
[K] [L] [M] [N] [O] [P] [Q] [R] [S] [T] [U] [V ] [W]
[X] [Z]

images

importing
Excel
noncontiguous data

separate tables

single table

XML data 2nd 3rd 4th 5th 6th 7th 8th 9th
In operator 2nd
inner joins
Insert operation (SQL)
Insert statement

inserting
apostrophes and
insertion point
intellectual property
Interval property
ISNULL function
Index

[SYMBOL] [A ] [B] [C] [D] [E] [F] [G] [H] [I] [J]
[K] [L] [M] [N] [O] [P] [Q] [R] [S] [T] [U] [V ] [W]
[X] [Z]

Join clause

joins
left

right
junction tables
Index

[SYMBOL] [A ] [B] [C] [D] [E] [F] [G] [H] [I] [J]
[K] [L] [M] [N] [O] [P] [Q] [R] [S] [T] [U] [V ] [W]
[X] [Z]

keyboard shortcuts

data entry
keystrokes
Index

[SYMBOL] [A ] [B] [C] [D] [E] [F] [G] [H] [I] [J]
[K] [L] [M] [N] [O] [P] [Q] [R] [S] [T] [U] [V ]
[W] [X] [Z]

left join
Len function
libraries creating code libraries
Like operator
Limit To List property
line numbers in reports
lines in reports

listboxes
populating

Union query and


lists 2nd

items

Limit To List property


popularity
locking records
log table
low ercase text
Index

[SYMBOL] [A ] [B] [C] [D] [E] [F] [G] [H] [I] [J]
[K] [L] [M ] [N] [O] [P] [Q] [R] [S] [T] [U] [V ]
[W] [X] [Z]

macro recorder (Word

macros
AutoExec
personalization and

AutoKeys

conditional actions

groups

Outlook
main form

many-to-many relationships
exporting to XML and
method
Mid function

MouseMove
OnTimer
clock and
MouseMove event
movie playing
MSXML parser

XML and

XSLT transformation and

multiple users
record locking

time-out feature

mouse and

usernames

MySQL
Access as front end 2nd 3rd 4th

linking to tables

tools installation
Index

[SYMBOL] [A ] [B] [C] [D] [E] [F] [G] [H] [I] [J]
[K] [L] [M] [N] [O] [P] [Q] [R] [S] [T] [U] [V ] [W]
[X] [Z]

naming conventions 2nd


navigation
navigation form
netw ork drive
New Query dialog box
New Data argument
Not operator
null values

number fields
defaults

nulls in
Index

[SYMBOL] [A ] [B] [C] [D] [E] [F] [G] [H] [I] [J]
[K] [L] [M] [N] [O] [P] [Q] [R] [S] [T] [U] [V ] [W]
[X] [Z]

object-use log

objects
descriptions

groups

multiple groups

shortcuts

unused
ODBC

On Timer event
clocks and

slideshow s and
On Timer property
OnTimer
MouseMove
slideshow s

open

Timer
open event

operators
In

Like

Not
Options dialog box
Or-based criteria
outer joins

Outlook
macros

sending Access data


Index

[SYMBOL] [A ] [B] [C] [D] [E] [F] [G] [H] [I] [J]
[K] [L] [M] [N] [O] [P] [Q] [R] [S] [T] [U] [V ]
[W] [X] [Z]

Page Break controls


page count
Page Dow n key
Page Up key
pages
pass-through queries
passw ords in code
Paste
Paste Special Transpose feature (Excel)

personalization
preferences
Picture property
plain database w indow

populating
sorting
multiple sources and
populating lists 2nd 3rd 4th

alphabetically

Union query and

preferences
applying
Prefix Characters property
printing reports and closing
processes, length

properties
CurrentUser

hidden objects and

Limit To List

On Timer

Picture
Index

[SYMBOL] [A ] [B] [C] [D] [E] [F] [G] [H] [I] [J]
[K] [L] [M] [N] [O] [P] [Q] [R] [S] [T] [U] [V ] [W]
[X] [Z]

queries
Action

Crosstab

Data Definition

functions

pass-through

regular expressions in

Select

Union queries

XML exports and

query grid
And-based criteria

Or-based criteria

sorting on any character 2nd 3rd 4th 5th 6th 7th 8th 9th 10th
Index

[SYMBOL] [A ] [B] [C] [D] [E] [F] [G] [H] [I] [J]
[K] [L] [M] [N] [O] [P] [Q] [R] [S] [T] [U] [V ] [W]
[X] [Z]

randomly sorting records


read-only command-line sw itch

records
Admin user

grouping

locking

random

separate sorted
alphabetically

sorting
separate alphabetically
unmatched

updates
Records2Go
references
regular expressions in queries

relationships
cascading updates

many-to-many
junction tables
remember

reports
embedded

line numbers

lines

w hitespace
Response argument
RIGHT JOIN
right join
Rnd function
Row Source
Row Source property
row s
RunCode action
Running Sum
Running Sum property
running sums
Index

[SYMBOL] [A ] [B] [C] [D] [E] [F] [G] [H] [I] [J]
[K] [L] [M] [N] [O] [P] [Q] [R] [S] [T] [U] [V ] [W]
[X] [Z]

sampling records
scripts

scrolling
Page Dow n key

security
backdoor building

hiding data

user edits

usernames
Select queries
SelStart

Tag, control details and


SelStart property
sending Access data through Outlook 2nd 3rd 4th 5th
SendKeys statement
separate sorted records alphabetically 2nd 3rd
shaded lines in reports
shortcut keys

shortcuts
desktop to databases

keyboard shortcuts

objects
simple
slideshow s
smart tags
Snapshot View er
Snapshot View er Control
sorting 2nd 3rd

any character 2nd

Excel's Past Special Transpose feature 2nd 3rd


Sorting and Grouping dialog box
source code

split data
w orking w ith
splitting databases distributing split

SQL Server
aggregate functions

Enterprise Manager

ODBC and

scripts

stored procedures
startup
storage, preferences
stored procedures (SQL)
subroutines
subtotals 2nd

sums
subtotals
syntax-checking
system tables 2nd 3rd 4th
Index

[SYMBOL] [A ] [B] [C] [D] [E] [F] [G] [H] [I] [J]
[K] [L] [M] [N] [O] [P] [Q] [R] [S] [T] [U] [V ]
[W] [X] [Z]

tab controls
tab order

tables
copying betw een

creating
speed

MySQL

number of 2nd 3rd 4th

updates
Tag property

testing
third-party applications for 2nd
text
case conversion

length
Text fields
the

third-party
applications
EZ Application Generator

third-party applications
EZ Application Generator

MSXML parser

softw are testing

Total Access Analyzer


tiling
time
time-out feature

mouse and

user-determined
timeouts, overriding intervals
Timer event
timers

to XML
exporting
queries and
Top predicate
Total Access Analyzer
tracking object use

triggers
form events and
Index

[SYMBOL] [A ] [B] [C] [D] [E] [F] [G] [H] [I] [J]
[K] [L] [M] [N] [O] [P] [Q] [R] [S] [T] [U] [V ] [W]
[X] [Z]

unbound controls
UNC (Universal Naming Convention)

renaming computer

Union query
listbox population
unmatched records
unused objects

updates
controls

records

tables
uppercase text
usernames
users
editing data

process length
USys
Index

[SYMBOL] [A ] [B] [C] [D] [E] [F] [G] [H] [I] [J]
[K] [L] [M] [N] [O] [P] [Q] [R] [S] [T] [U] [V ] [W]
[X] [Z]

values
custom

VBA
conditional formatting and
versions
video

viewing
Snapshot View er Control and
Index

[SYMBOL] [A ] [B] [C] [D] [E] [F] [G] [H] [I] [J]
[K] [L] [M] [N] [O] [P] [Q] [R] [S] [T] [U] [V ] [W]
[X] [Z]

w atermarks
Web brow sers

file dow nload


Web pages
w hitespace in reports
w ildcards in queries
Window s Media Player

wizards
Export Text

Find Unmatched Query

Word
documents

table comparison and


w orks
WorksheetFunction property (Excel)
w orksheets (Excel)
Index

[SYMBOL] [A ] [B] [C] [D] [E] [F] [G] [H] [I] [J]
[K] [L] [M] [N] [O] [P] [Q] [R] [S] [T] [U] [V ] [W]
[X] [Z]

XML
exporting to 2nd 3rd 4th 5th 6th 7th 8th

importing data 2nd 3rd

MSXML parser and


XSLT transformation
Index

[SYMBOL] [A ] [B] [C] [D] [E] [F] [G] [H] [I] [J]
[K] [L] [M] [N] [O] [P] [Q] [R] [S] [T] [U] [V ] [W]
[X] [Z]

zero-length strings
Credits

A bout the A uthor

C ontributors

A c knowledgments
About the Author
Ken Bluttman has been hac king around with A c c es s for years .
H aving honed his programming s kills bac k when the P C was jus t
bec oming a hous ehold item, Ken found A c c es s a joy to us e, even
bac k in earlier vers ions .

Bes ides A c c es s , Ken exc els at E xc el and the other O ffic e


produc ts , as well as SQ L Server, web development, and
V B/V B.N E T development. N o wonder he rarely s leeps .

Ken is als o the author of Developing Micros oft Office Solutions


(A ddis on Wes ley) and Excel Formulas and Functions for Dummies
(Wiley), as well as numerous print and web- bas ed artic les .

O n the pers onal s ide, Ken is a mus ic ian and a nature lover. H ave
guitar, will travel to the woods . Ken lives in N ew York with his
wife, s on, dog, and s everal amphibians .

V is it Ken at his web s ite: http://www.bluttman.c om.


Contributors
T he following people c ontributed their hac ks , writing, and
ins piration to this book:

Steve C onklin is an independent s oftware developer and


the owner of U ltra D .N .T. (D evelopment, N etworks , and
Training) Tec hnology C ons ulting, loc ated in Q ueens ,
N ew York. H e s pec ializes in A c c es s , V is ual
Bas ic /V B.N E T, and M S- SQ L Server development, and is
releas ing a line of P oc ketP C applic ations for the mobile
profes s ional. Steve has written s everal artic les for
Acces s /VB/SQL Advis or magazine and he teac hes
M ic ros oft Windows and O ffic e c ours es at a N ew York
C ity c ommunity c ollege. H e is available for development
work and c an be reac hed at U ltraD N T @ H otmail.c om.

Steve H uff has been developing A c c es s databas e


applic ations for more than nine years . H e has a
c omputer s c ienc e degree from N orthern Kentuc ky
U nivers ity, where he is taking night c ours es toward a
mas ter's degree in information s ys tems . H e has been
developing M ic ros oft O ffic e s olutions as a c ons ultant
working for SA RC O M for more than s even years . Steve
lives in Kentuc ky with his wife, M elis s a. You c an reac h
him through his web s ite: http://www.huffs .us .

Kirk L amb has been dabbling with A c c es s for many


years . A lthough his expertis e is in boating, he knows a
good databas e when he s ees one. Kirk lives with his wife,
D ill, in Was hington s tate.

A ndrea M os s firs t got involved with A c c es s when s he


des igned a s ys tem to trac k ins uranc e c laims . Sinc e
then, s he has applied her artis tic s kills to des igning
layout and c olor s c hemes for various G U I s , inc luding
A c c es s forms and web s ites . A long the way, s he has
pic ked up a few A c c es s tric ks of her own.

M ic hael Sc hmalz works in banking and performs


bus ines s and tec hnology c ons ulting in a variety of
indus tries . H e has been a tec hnic al editor for O 'Reilly on
M ic ros oft O ffic e books . M ic hael has a degree in financ e
from P enn State. H e lives with his wife and daughter in
P enns ylvania.

Simon St.L aurent is a web developer, network


adminis trator, c omputer- book author, and XM L
troublemaker living in I thac a, N ew York. H is books
inc lude XML: A Primer, XML Elements of Style, Building XML
Applications , Cookies , and Sharing Bandwidth. H e is a
c ontributing editor to XM L hac k.c om and an oc c as ional
c ontributor to XM L .c om.

M argaret L evine Young has us ed s mall c omputers s inc e


the 1 9 7 0 s . She graduated from U nix on a P D P - 1 1 to
A pple D O S on an A pple I I to D O S, Windows , and U nix
on a variety of mac hines . She has done all kinds of jobs
that involve explaining to people that c omputers aren't
as mys terious as they might think, inc luding managing
the us e of P C s at C olumbia P ic tures , teac hing s c ientis ts
and engineers what c omputers are good for, and writing
and c owriting c omputer manuals and books , inc luding
Unders tanding Javelin PLUS, The Complete Guide to PC-File,
UNI X for Dummies , and The I nternet for Dummies . M argy
has a degree in c omputer s c ienc e from Yale U nivers ity
and lives with her hus band and two c hildren in Vermont.
Acknowledgments
T his book is a c ollaborative effort. M y thanks go to the
c ontributors for providing great hac ks that I am s ure you will
enjoy as muc h as I have.

Spec ial thanks and apprec iation go to my editor, M itc h Tulloc h.


M itc h has s tuc k with me through the thic k and thin of getting
this projec t c ompleted. H is patienc e and pers everanc e have
been awes ome. M itc h would like to thank M T S C ommunic ations
I nc . (http://www.mts .c a) for providing I nternet s ervic es for
hos ting his web s ite (http://www.mtit.c om).

T hanks to M ic hael Sc hmalz for tec h- reviewing the material and


keeping on my bac k about early binding vers us late binding and
other pertinent topic s dear to our profes s ion.

T hanks to Brian Sawyer and the great O 'Reilly team. T hanks to


all of you.

T hanks to my agent, N eil Salkind, and the Studio B team. N eil


c alled me one day las t year to s ee if I would be interes ted in
writing Acces s Hacks . O f c ours e! A nd that's how I met M itc h
Tulloc h.

T hanks to the s taff at D atabas e C reations , I nc .


(http://www.databas ec reations .c om) and FM S, I nc .
(http://www.fms inc .c om) for providing c opies of their outs tanding
produc ts .

L as t but not leas t, thanks to my wife G ayla and s on M atthew.


Working on a book is always s tres s ful, and they have been real
troopers in giving me s pac e and time to c omplete the book. I t's
c ute to s ee a s even- year- old bec oming s uc h a c omputer pro.
O ften, M atthew will s it on my lap and watc h what I am typing.
N ow he is an A c c es s expert in his own right.
Preface
A c c es s really is an amazing produc t. I ts power is vas t, and yet
its maintenanc e is low. I n fac t, in mos t ins tallations it s its on
the des ktop and is maintenanc e- free. I t's flexible enough to be
us ed by one pers on or to run an entire c ompany. I t's a rapid
applic ation development (RA D ) tool that outs hines other s uc h
tools (s uc h as V is ual Bas ic ) in time to development and eas e of
us e.

A c c es s is als o a c omplete databas e applic ation s ys tem. I t


inc orporates both the bac k- end and front- end elements of a
databas e, thereby eliminating the need to us e two produc ts to
get your work done. E ven s o, its flexibility allows an A c c es s
databas e file to be jus t a bac k end or jus t a front end. A c c es s
c an c ontrol data in external databas e s ys tems s uc h as SQ L
Server and O rac le.

N eed I s ay more? I don't think you need any c onvinc ing to know
what a great produc t A c c es s is . E ither you are us ing it already,
or you are about to s tart. Well, here is s ome great news : this
book is going to s how you even more ways to us e A c c es s .
Whether it's how to run Union queries , play video files in A c c es s ,
view web s ites within A c c es s , or even c ontrol A c c es s from
another produc t, there are hac ks here to tic kle every fanc y.

Acces s Hacks lets you move beyond the familiar tables , forms ,
and reports paradigm and get new ins ights into making your
databas e applic ations more valuable and exc iting. I t's my
pleas ure to s how you new ways to work with your favorite
databas e produc t. So, fire up your c omputer, and let's get
s tarted!
Why Access Hacks?
T he term hacking has a bad reputation in the pres s . T hey us e it
to refer to s omeone who breaks into s ys tems or wreaks havoc
with c omputers as their weapon. A mong people who write c ode,
though, the term hack refers to a "quic k- and- dirty" s olution to a
problem, or a c lever way to get s omething done. A nd the term
hacker is taken very muc h as a c ompliment, referring to s omeone
as being creative, having the tec hnic al c hops to get things done.
T he H ac ks s eries is an attempt to rec laim the word, doc ument
the good ways people are hac king, and pas s the hac ker ethic of
c reative partic ipation on to the uninitiated. Seeing how others
approac h s ys tems and problems is often the quic kes t way to
learn about a new tec hnology.
How to Use This Book
Acces s Hacks is not meant to be a s equential read, although I
won't c omplain if you read it s traight through, from c over to
c over! T he book c ontains 1 0 0 hac ks , and eac h s tands on its
own merit. You c an read them in any order. Some hac ks have a
c ommon theme with other hac ks , in whic h c as e the flow is duly
noted. O ther than that, jus t dig in, and s ee what interes ts you.
O ne group of hac ks might be what you need for today's projec t,
and another group might be what you need tomorrow.
How This Book Is Organized
E ac h c hapter in Acces s Hacks c enters on a fac et of A c c es s . I n
this way, you c an foc us on areas in whic h you need a little
ins piration. I f you need help with queries and SQ L , go to C hapter
5 . I f you want to learn s ome programming tric ks , go to C hapter
8 . I n partic ular, here is what you'll find in eac h c hapter:

C hapter 1 , Core Acces s

T he firs t c hapter c overs the bas ic s , from organizing


databas e objec ts to working with data. I n this c hapter,
you'll find nuggets about helping us ers , overc oming
vers ion inc ompatibility, and even how to work with any
amount of data.

C hapter 2 , Tables

Tables are the c ore objec t of any databas e. I n this


c hapter, you'll find hac ks that s how you how to move
data between tables and how to res et A utoN umbering to
begin with a number of your c hoic e. A ls o, you will learn
what s ys tem tables are and how to have them s tay out of
your way.

C hapter 3 , Entry and Navigation

T his c hapter foc us es on us ers ' needs . Bes ides s toring


data, a databas e s ys tem needs to make it eas y for us ers
to manage the data. C hapter 3 is c hoc k- full of hac ks
that improve how us ers work with forms , whic h of c ours e
are the mos t c ommon databas e objec ts us ers interac t
with.

C hapter 4 , Pres entation

O nc e data is entered and s tored, the res t of the equation


involves reporting. T his c hapter s hows you new ways to
work with reports . L earn how to us e a watermark, provide
s ophis tic ated s orting, and provide c onditional totals .
D on't forget to c hec k the hac ks on c reating a s lide s how
and playing videos !

C hapter 5 , Queries and SQL

Running queries is a big part of databas e work. M any of


the hac ks in this c hapter take you beyond the bas ic s of
the query des ign grid. I mmers e yours elf in the SQ L
language its elf as you dis c over Union queries , us ing the
In and Not operators , and how to us e c us tom func tions in
queries . T here is even a hac k that enc ourages you to
query unrelated tables to return all c ombinations of data
in two fields .

C hapter 6 , Multius er I s s ues

C ertain is s ues exis t only in a s hared environment. I n


this c hapter, you'll find hac ks that provide workarounds
for c ommon problems . L earn how to end an unattended
edit and how to dis tribute a databas e with no has s les .
C hapter 7 , External Programs and Data

A c c es s is eas y to integrate with other programs and


protoc ols . T his c hapter s hows you many ways to us e
A c c es s with other produc ts , inc luding E xc el, Word,
M ySQ L , and SQ L Server. I f you have an inkling of how to
work with XM L data, this c hapter inc ludes hac ks for that.
T here is even a hac k that s hows you how to c reate
A c c es s tables without running A c c es s .

C hapter 8 ,Programming

T his c hapter provides a number of programming


tec hniques . I t inc ludes hac ks for optimizing c ode,
writing fas ter c ode, and protec ting c ode. O ther hac ks
provide minis olutions , s uc h as a way to drill down to a
s pec ific rec ord and to provide feedbac k during a long
proc es s .

C hapter 9 , Third-Party Applications

T his c hapter previews a few third- party produc ts that


make your databas e work a breeze. L earn about
produc ts that c reate a databas e framework, doc ument
your databas e, and even provide data. L as t but not
leas t, this c hapter provides an overview of a c omplete
XM L- bas ed applic ation s olution.

C hapter 1 0 , The I nternet


T he hac ks in this c hapter s how you how to c reate H T M L
files from A c c es s . With jus t a little editing with an H T M L
tool or in a text editor you c an turn an A c c es s report
into the format you need. You'll find hac ks in this c hapter
for putting a web brows er direc tly on an A c c es s form.
N eed to c hec k your online inves tments ? You c an do s o
without leaving the databas e.
Conventions Used in This Book
T he following is a lis t of the typographic al c onventions us ed in
this book:

Plain text

I ndic ates options , queries , and options entered us ing


A c c es s 's graphic al us er interfac e (G U I ), inc luding table
titles , c ell identifiers , named ranges , menu titles , menu
options , menu buttons , and keyboard ac c elerators (s uc h
as A lt and C trl).

I talics

I ndic ates U RL s , filenames , filename extens ions , and


direc tory/folder names . For example, a path in the
files ys tem appears as /Developer/ Applications .

Constant width

Shows c ode examples , the c ontents of files , c ons ole


output, as well as the names of variables , c ommands ,
func tions , mac ros , s tatements , c ommand- line queries ,
and other c ode exc erpts .

Constant width bold


H ighlights portions of c ode, typic ally new additions to
old c ode.

Cons tant width italic

U s ed in c ode examples and tables to s how s ample text


to be replac ed with your own values .

Color

T he s ec ond c olor indic ates a c ros s referenc e within the


text.

You s hould pay s pec ial attention to notes s et apart from the text
with the following ic ons :

T his is a tip, s ugges tion, or general note.


I t c ontains us eful s upplementary
information about the topic at hand.

T his is a warning or note of c aution, often


indic ating that your money or your privac y
might be at ris k.
T he thermometer ic ons , found next to eac h hac k, indic ate the
relative c omplexity of the hac k:

beginner moderate expert


Using Code Examples
T his book is here to help you get your job done. I n general, you
c an us e the c ode in this book in your programs and
doc umentation. You do not need to c ontac t us for permis s ion
unles s you're reproduc ing a s ignific ant portion of the c ode. For
example, writing a program that us es s everal c hunks of c ode
from this book does not require permis s ion. Selling or dis tributing
a C D - RO M of examples from O 'Reilly books does require
permis s ion. A ns wering a ques tion by c iting this book and
quoting example c ode does not require permis s ion. I nc orporating
a s ignific ant amount of example c ode from this book into your
produc t's doc umentation does require permis s ion.

We apprec iate, but do not require, attribution. A n attribution


us ually inc ludes the title, author, publis her, and I SBN . For
example: "Acces s Hacks by Ken Bluttman. C opyright 2 0 0 5
O 'Reilly M edia, I nc ., 0 - 5 9 6 - 0 0 9 2 4 - 0 ."

I f you feel your us e of c ode examples falls outs ide fair us e or the
permis s ion given here, feel free to c ontac t us at
permis s ions @oreilly.com.
Safari Enabled

When you s ee a Safari® E nabled ic on on


the c over of your favorite tec hnology book, that means the book
is available online through the O 'Reilly N etwork Safari Books helf.

Safari offers a s olution that's better than e- books . I t's a virtual


library that lets you eas ily s earc h thous ands of top tec h books ,
c ut and pas te c ode s amples , download c hapters , and find quic k
ans wers when you need the mos t ac c urate, c urrent information.
Try it for free at http://s afari.oreilly.c om.
How to Contact Us
We have tes ted and verified the information in this book to the
bes t of our ability, but you might find that features have c hanged
(or even that we have made mis takes ! ). A s a reader of this book,
you c an help us improve future editions by s ending us your
feedbac k. P leas e let us know about any errors , inac c urac ies ,
bugs , mis leading or c onfus ing s tatements , and typos that you
find anywhere in this book.

P leas e als o let us know what we c an do to make this book more


us eful to you. We take your c omments s erious ly and will try to
inc orporate reas onable s ugges tions into future editions . You c an
write to us at:

O 'Reilly M edia, I nc .
1 0 0 5 G ravens tein H wy N .
Sebas topol, C A 9 5 4 7 2
(8 0 0 ) 9 9 8 - 9 9 3 8 (in the U .S. or C anada)
(7 0 7 ) 8 2 9 - 0 5 1 5 (international/loc al)
(7 0 7 ) 8 2 9 - 0 1 0 4 (fax)

To as k tec hnic al ques tions or to c omment on the book, s end


email to:

bookques tions @ oreilly.c om

T he web s ite for Acces s Hacks lis ts examples , errata, and plans
for future editions . You c an find this page at:

http://www.oreilly.c om/c atalog/ac c es s hks

For more information about this book and others , s ee the


O 'Reilly web s ite:
http://www.oreilly.c om
Got a Hack?
To explore H ac ks books online or to c ontribute a hac k for future
titles , vis it:

http://hac ks .oreilly.c om
1. Core Access
Sec tion 1 .1 . H ac ks 1 1 2

H ac k 1 . H elp U s ers Find the O bjec ts T hey N eed

H ac k 2 . P ers onalize Your A c c es s A pplic ation

H ac k 3 . Work Fas t and A void Typos

H ac k 4 . O ptimize D ata C hanges

H ac k 5 . Trans fer D ata Between Vers ions of A c c es s

H ac k 6 . O rganize and E nhanc e Your M ac ros

H ac k 7 . Rid Your D atabas e of C lutter

H ac k 8 . P rotec t Valuable I nformation

H ac k 9 . Work with A ny A mount of D ata

H ac k 1 0 . Find D atabas e O bjec ts in a Snap

H ac k 1 1 . U s e a J unc tion Table

H ac k 1 2 . Stop the D atabas e from G rowing


1.1. Hacks 112
A c c es s is us ed in many different s ituations , in many different
ways , by a divers e group of people. Some are novic es , while
others have been us ing a s ingle c us tom A c c es s s olution for
years . Still others are s ophis tic ated us ers who want to take
advantage of the applic ation's bells and whis tles , or they are
A c c es s developers who make thos e bells and whis tles ring and
s ing.

O ne thing all us ers and developers want is for A c c es s to s upport


their needs as effic iently as pos s ible. T his c an require a little
c us tomization or a down- right minis olution that's implemented
ins ide A c c es s and helps to s upport the purpos e of the overall
s olution.

T his c hapter inc ludes a c ollec tion of hac ks you c an implement


direc tly within A c c es s . You'll find hac ks des igned to help general
us ers by making their experienc e more rewarding and more
effic ient. You'll als o find hac ks a power us er c an implement to
expand the value of his c us tom A c c es s applic ation. You'll even
find hac ks that only a developer c an implement. A little V BA
goes a long way here.
Hack 1. Help Users Find the Objects
They Need

Place shortcuts to pertinent objects in custom groups so that


users don't have to wade through all the database objects.

T he A c c es s databas e window c an be overwhelming to s ome


us ers . Tables , queries , forms , reports ; determining where to find
objec ts you need within thes e objec t c ollec tions is n't exac tly a
us er- friendly proc es s . Bes ides , s ometimes a us er needs jus t a
handful of objec ts to c omplete his work. A nd yet he might be
c onfronted with c ons iderably more objec ts than he needs .

L uc kily, the A c c es s databas e window allows you to c reate


c us tom groupings in whic h you c an plac e s hortc uts to only the
des ired objec ts . J us t as the Windows des ktop has s hortc uts to
folders , files , and applic ations , A c c es s lets you make s hortc uts
to your databas e objec ts . A nd it's a c akewalk to do s o!

1.2.1. The Plain Database Window

Your A c c es s applic ation might open to a navigation, or main,


form. From there, us ers c lic k their way through the applic ation.
But not all applic ations are made in this way. Figure 1 - 1 s hows
the plain databas e window in all its unimpres s ive glory. Some
applic ations open to this func tional but ineffic ient window.
Figure 1-1. The standard Access database
window

O f c ours e, you c an get to all the objec ts you need from here:
c lic k the Q ueries tab to find the queries you want to run, or c lic k
the Reports tab to find the reports you want to run. But you c an
avoid this drudgery. O ne great thing about the databas e window
is the ability to make your own groups . I n fac t, it is c lear that the
databas e window does s eparate obj ects from groups . I n Figure 1 -
1 , on the left s ide of the databas e window, you c an s ee a c lear
dis tinc tion of groups in the bottom half of the window.

1.2.2. Using Groups

By default, there is one Favorites group, in whic h you c an plac e


s hortc uts to objec ts . I t's eas y to do; jus t find the objec t
wherever it exis ts within the various tabs , and then c lic k and
drag it to the Favorites group. Figure 1 - 2 s hows the res ult of
doing jus t that. T he Favorites group has been filled with
s hortc uts to s ome of the databas e objec ts . N ote that thes e are
s hortcuts . T he original objec ts are s till where they belong within
the objec t c ollec tions . You c an delete a s hortc ut in the Favorites
group, and the original objec t remains .

Figure 1-2. Placing shortcuts in the Favorites


group
C learly, us ing the Favorites group lets you foc us us er ac tivity!
H owever, you c an als o go a s tep further by adding additional
groups for even better organization. H ow about a group for eac h
us er or type of us er? For example, data entry operators and
s upervis ors might us e the s ame databas e applic ation, but with
different objec ts ; the data entry operators might us e c ertain
forms , and s upervis ors or managers might us e queries and
reports to s ee overall ac tivity.

I t's eas y to add a new group. J us t right- c lic k in the G roups area,
s elec t N ew G roup from the lis t of options , and give the group a
name. A t this point you c an drag objec ts to the new group.
Figure 1 - 3 s hows how two new groups have been added to the
applic ation. E ac h has its own lis t of s hortc uts .

A nother good point about groups is that the s ame objec ts c an


res ide in more than one group. I f you have a reas on to plac e a
s hortc ut to a partic ular report in three different groups , A c c es s
won't hold you bac k. I n fac t, you c an even c opy s hortc uts from
one group to another.

Figure 1-3. Creating and using a custom group


Hack 2. Personalize Your Access
Application

Build personalization f unctionality so that users can set up the


application in ways that work best f or them.

T here is no reas on to limit all us ers to us ing an A c c es s


applic ation in the s ame way. I t's eas y to overlook this c apability
bec aus e A c c es s allows you to des ignate only one opening form
in its s tartup options that is , unles s you tap into its databas e-
opening events . T hen, you c an c hoos e whic h form will open, what
its properties are, and more. You c an effec tively make all fac ets
of the applic ation unique to a partic ular individual or profile. H ere
are a few items you c an tailor this way:

Forms

Spec ify the opening form, how it's dis played, and what
func tionality it inc ludes

D ata s ourc es

Spec ify whic h pers onalization tables , internal or


external, are needed for eac h us er's tas ks
Reports

Show or hide details

T his hac k s hows you how to us e the AutoExec mac ro to run an


opening func tion that delivers a pers onalized interfac e to the
us er. For this to work, you mus t firs t c reate a databas e table to
s tore us er preferenc es , and then, when the databas e s tarts up,
you mus t be able to identify the us er to the databas e. You c an do
this in a number of ways : for ins tanc e, a pop- up input box c an
as k for a name or initials (pos s ibly with a pas s word), a
c ommand- line s witc h c an provide the us er identity, or, if the
A c c es s s ec urity model is in us e, the us er I D c an be made
available through the CurrentUser property.

1.3.1. Storing Preferences

U s er preferenc es are s tored in a table that has a field for eac h


pers onalization fac et. You determine whic h features to
pers onalize. For example, a L ong datatype field c an s tore the
preferred bac kground c olor, a text field c an s tore the name of the
preferred opening form, and s o on. Figure 1 - 4 s hows s uc h a
table, aptly named C us tomized, with a few preferenc es filled in.
T he field name indic ates the preferenc e, and the ac tual value in
the field is the s etting.

Figure 1-4. A table to hold single user


preferences
T his table is perfec t for databas es that are dis tributed to loc al
c lient mac hines . I n this c onfiguration, only one us er us es an
ins tanc e of the databas e. T herefore, the table is s truc tured to
s tore the preferenc es of jus t a s ingle us er. A key point about the
table is that it always has jus t a s ingle rec ord. T hat s ingle
rec ord c ontains a field for eac h pers onalized item.

I n a s hared databas e c onfiguration (s uc h as when all us ers are


us ing a network c opy), the table needs to have an additional field
to identify eac h us er. T he number of rec ords this table ends up
c ontaining matc hes the number of us ers , plus onethat is , one
rec ord per us er, plus a default rec ord for the A dmin us er. Figure
1 - 5 s hows this s truc ture.

I t's a good idea to leave a rec ord for the


A dmin us er. T his is the default A c c es s
us er ac c ount and is pres ent even when the
s ec urity model is n't us ed. When no
s ec urity login is us ed, the CurrentUser
property defaults to Admin.

Figure 1-5. A table to hold multiple user


preferences

A ll that's left to c omplete this hac k is to give us ers a way to


s elec t their preferenc es . N o, us ers aren't expec ted to enter s uc h
a c ryptic thing as the numeric al repres entation of a c olor! So,
we'll us e a form (what els e! ) to c apture preferenc es . T his unique
form s erves to jus t manage preferenc es ; it has no other
interac tion with the databas e. Figure 1 - 6 s hows the s truc ture of
s uc h a form.

Figure 1-6. A form in which users select their


preferences
O nc e the s elec tions are made on the form, the Save P referenc es
button writes the preferenc es to the table. For a s ingle- us er
table, a s imple SQ L ins ert does the tric k, like this :

Update Customized Set FormBackGroundColor=8454143, FontSiz


OpeningForm='Receivables', ShowReportDetails='No'

For the multius er c onfiguration, the extra field is in the SQ L


s tatement:

Update Customized Set FormBackGroundColor=8454143, FontSiz


OpeningForm='Main Form', ShowReportDetails='Yes' Where Use

T hes e SQ L s tatements are as s embled us ing the values of the


form c ontrols . A c tiveX D ata O bjec ts (A D O ) is us ed to update
the values in the table. A fter the SQ L s tatement is as s embled,
the Execute method of the Connection objec t runs the update:

Private Sub cmdSave( )


On Error GoTo err_end
Dim conn As ADODB.Connection
Set conn = CurrentProject.Connection
Dim ssql As String
ssql = "Update Customized Set " & _
"FormBackGroundColor=" & _
Me.groupFormColor & ", " & _
"FontSize='" & _
Choose(Me.groupFontSize, "Small", "Large") & "', "
"OpeningForm='" & Me.lstForms & "', " & _
"ShowReportDetails='" & _
Choose(Me.groupReportDetail, "Yes", "No") & "'"
conn.Execute ssql
conn.Close
Set conn = Nothing
MsgBox "Preferences updated!"
Exit Sub
err_end:
conn.Close
Set conn = Nothing
MsgBox Err.Description
End Sub

1.3.2. Applying the Preferences

J us t s toring the preferenc es does nothing, s o let's c rac k this


applic ation open a little wider. O ne of the preferenc es s elec ts
whic h form to dis play at s tartup. T he AutoExec mac ro is us ed here
to run a func tion that us es the las t s aved preferenc e s etting. A s
before, if this is a s ingle- us er ins tallation, one type of table is
us ed, but in a multius er c onfiguration, the us ername plays a role.

H ere are two func tions that c an be c alled by the AutoExec mac ro.
T he AutoExec mac ro's RunCode ac tion is us ed with the func tion
name as the parameter. I n either c as e, the DLookup func tion grabs
the opening form preferenc e and opens that form. T he differenc e
is in whether the DLookup func tion filters to a us ername. I n the
firs t func tion, it does n't, but in the s ec ond func tion, it does :

Function open_up_single( )
On Error GoTo err_end
Dim myform As String
myform = DLookup("OpeningForm", "Customized")
If Not IsNull(myform) Then
DoCmd.OpenForm myform
Else
DoCmd.OpenForm "Switchboard"
End If
Exit Function
err_end:
MsgBox Err.Description
End Function

Function open_up_multi_user( )
'On Error GoTo err_end
Dim myform As String
Dim username As String
myform = _
DLookup("OpeningForm", "Customized", "UserName ='" & _
CurrentUser & "'")
If Not IsNull(myform) Then
DoCmd.OpenForm myform
Else
DoCmd.OpenForm "Switchboard"
End If
Exit Function
err_end:
MsgBox Err.Description
End Function

N ote that an If…Else bloc k handles opening the default


Switchboard form in c as e a null value is returned.

You need to implement how to us e other types of preferenc es ,


s uc h as inc luding report details or us ing a different font s ize,
when and where it makes s ens e for the given preferenc e. For
example, here's how you c an c hange the bac kground c olor of a
form in the open event of the form:

Private Sub Form_Open(Cancel As Integer)


Me.Detail.BackColor = DLookup("FormBackGroundColor", "Cust
End Sub

1.3.3. Using the Hack

A ll that's left now is to dec ide how to handle opening the


c us tomization form. You c an make this ac tion available on a
toolbar, via a menu, or via a mac ro. A great idea is to put it into a
c us tom group of c ommonly us ed objec ts . See "H elp U s ers Find
the O bjec ts T hey N eed" [Hack #1] to learn about making
c us tom groups .
Hack 3. Work Fast and Avoid Typos

Save time and avoid mistakes by using simple keystrokes f or


entering the date, time, or other commonly used entries.

T he mous e is nic e, but nothing beats getting around an


applic ation fas ter than keyboard s hortc uts . C trl- C for c opy, C trl-
V for pas te, and s o on, are pretty familiar. H ow about keyboard
s hortc uts for entering the date, time, and other data? U s ing
thes e s hortc uts will s ave valuable time when you are in a rus h to
finis h a projec t. A nd how often are you not in a rus h?

1.4.1. Know Thy Shortcuts

Table 1 - 1 s ummarizes us eful keyboard s hortc uts to us e within


your A c c es s applic ations . T his is n't an exhaus tive lis t of
keyboard s hortc uts not by a long s hot! You c an us e the A c c es s
H elp s ys tem to find all the s hortc uts . T he ones pres ented in
Table 1 - 1 are s pec ific s hortc uts for entering data.

Table 1-1. Keyboard shortcuts for entering data


Keyboard
A ction
shortcut
E nter the c urrent time. C trl- :

E nter the c urrent date. C trl- ;

I ns ert data from the s ame field in the previous


C trl- '
rec ord.

C trl- A lt-
I ns ert the default value for the field.
s pac ebar

I ns ert a new line in a text or memo field. C trl- E nter

E nter a new rec ord. C trl- +

P as te the c ontents of the Windows c lipboard. C trl- V

T hes e s hortc uts are quite handy. H ave you ever forgotten the
c urrent date when you had to enter it in a field? Well, all you need
to remember now is the keyboard s hortc ut to enter the c urrent
date. Whic h brings us to the next point…

1.4.2. Remember Where to Reference


Shortcuts
I t takes time to memorize a group of s hortc uts , s o the next bes t
thing is to boil it down to memorizing jus t one. T he twis t here is
to have the lis t of keyboard s hortc uts available on a form that
you c an eas ily dis play us ingyou gues s ed ita keyboard s hortc ut.
H owever, you need to c reate this s hortc ut.
Using the AutoKeys Macro

T he AutoKeys mac ro lets you as s ign databas e ac tions to


c us tom keyboard s hortc uts . You c an as s ign ac tions to
the func tion keys , to key c ombinations s uc h as C trl- A ,
and to the I ns ert and D elete keys . You mus t follow a
s tric t s yntax, however: a c arat (^) repres ents the C trl
key, and a plus s ign (+) repres ents the Shift key. You
enter regular keys verbatim, and you enc los e func tion
keys and s pec ial keys (I ns ert and D elete) in brac es
({}). H ere are a few examples :

^A s ets an ac tion to C trl- A .

{F9} s ets an ac tion to the F9 func tion key.

+{F9} s ets an ac tion to Shift- F9 .

{INSERT} s ets an ac tion to the I ns ert key.

When you s et a c us tom s hortc ut to an exis ting default


s hortc ut, the c us tom s hortc ut overrides the default one.
T herefore, you c an override c ommon keyboard
s hortc uts , s uc h as C trl- V (pas te), and ins tead provide
your own.

T he s yntax s tatements are plac ed in the M ac ro N ame


c olumn, and the appropriate ac tions are s et in the
A c tion c olumn. T he only other requirement is that the
mac ro is ac tually named AutoKeys.

Figure 1 - 7 s hows a form that lis ts the keyboard s hortc uts . T he


form is bas ed on a table that holds the s hortc uts and their
des c riptions in two res pec tive fields . A n alternative is to jus t
us e label c ontrols in whic h the s hortc uts and des c riptions have
been entered.

Figure 1-7. A quick-reference form for keyboard


shortcuts
A func tion key is eas y enough to remember. F9 is a good one to
target bec aus e it is n't c ommonly us ed. By c ontras t, F1 is n't a
great c hoic e bec aus e it's the s tandard for entering the H elp
s ys tem. To es tablis h a c us tom keyboard s hortc ut, us e the
s pec ial AutoKeys mac ro. T he AutoKeys mac ro is ac tivated at
s tartup in the s ame fas hion as the AutoExec mac ro.

Figure 1 - 8 s hows the AutoKeys mac ro s et up with a few c us tom


keyboard s hortc uts . P res s ing F9 opens the frmKeyboardShortcuts
form s hown in Figure 1 - 7 .

Figure 1-8. Using the AutoKeys macro to set up


custom keyboard shortcuts
Hack 4. Optimize Data Changes

A void having to propagate data changes manually throughout


related tables by establishing cascading updates and deletes.

T he one c ons tant you c an c ount on is c hange. Why not plan for
this eventuality in your applic ations ? Take a real example: a
c us tomer c hanges her name. I f you are in bus ines s long enough,
this is s omething you will need to ac c ommodate in your
databas e.

D ata c hanges need to be propagated in two ways . I f the data


is n't us ed as a table key or a foreign key, you need to c hange the
data in all the plac es it res ides in your tables . H opefully, your
data res ides in only one plac e! A c orrec tly modeled databas e
holds a piec e of data, s uc h as a c us tomer name, in jus t one
plac e. I f you do have s uc h a piec e of data in a few plac es ,
however, you pres umably have a reas on for doing this .
A pplic ations grow over time and often are handled by a
s uc c es s ion of developers . I t happens .

I f you have databas e applic ations in whic h the s ame data is


found all over the plac e, a brus h up on data modeling is in order.

What about data that exis ts in table keys ? T his c an be a


frus trating c hange to propagate if many c hild tables us e the data
as the foreign key. T hat is , it will be frus trating unles s you plan
your relations hips with c as c ading updates .

When c reating relations hips between tables , one option is to


es tablis h c as c ading updates . Figure 1 - 9 s hows two tables of
data. T he tblC us tomers table on top has c us tomer information.
T he values in the key field, C us tomerI D , are the initials of the
ac tual c ompany names . I n the lower tblI nvoic es table, the
C us tomerI D s erves as the foreign key.

Figure 1-9. Related tables


Figure 1 - 1 0 c onfirms the relations hip between the tables . A line
leads from the C us tomerI D field in tblC us tomers to the
C us tomerI D field in tblI nvoic es . T he number 1 on the
tblC us tomers table s ide of the line indic ates that tblC us tomers
is the parent table. T he infinity s ymbol ( ) above where the line
meets the tblI nvoic es table indic ates that tblI nvoic es is the
c hild table. T his is a one-to-many relations hip. T he tblI nvoic es
table has other relations hips as well.

Figure 1-10. The Relationships window


T he E dit Relations hips dialog box, s hown in Figure 1 - 1 1 , is
where you s et relations hips . To open the dialog in the
Relations hips window, double- c lic k on a line that c onnec ts two
tables . N ote the C as c ade U pdate Related Fields c hec kbox.
When this box is c hec ked, c hanging the value in the key field of
the parent table automatic ally c hanges the values in the related
field in the c hild table. T his is a good thing! When a c us tomer
c hanges her name, all you have to do is c hange the value in the
key. A ll the oc c urrenc es of the value in the c hild table
automatic ally c hange to the new value.
I n the example s hown in Figure 1 - 9 , if Bes t E quipment c hanges
its name to Bes t Tools , the C us tomerI D value s hould be
c hanged to BT. M aking this c hange onc e in the tblC us tomers
table automatic ally updates all related rec ords in the
tblI nvoic es table.

A nother option in the E dit Relations hips


dialog box (Figure 1 - 1 1 ) is to es tablis h
c as c ading deletes . T he C as c ade D elete
Related Rec ords s etting ens ures that when
you delete a rec ord in the parent table, all
related rec ords in c hild tables are als o
deleted. When this option is n't s et, you
have to delete the rec ords in the c hild
table firs t, and then you c an delete the
rec ords in the parent table.

Figure 1-11. Selecting to use cascading updates


and deletes
I f the option to have c as c ading updates is n't s et, you have to
c hange eac h table's rec ords s eparately. You c an do that only if
you remove the relations hip firs t bec aus e A c c es s does n't let
you c hange the values in the key field in either table if there are
any related rec ords . Trying to make an update in that way is
quite mes s y.
Hack 5. Transfer Data Between Versions
of Access

Say goodbye to version incompatibility issues.

M ic ros oft has releas ed more than half a dozen vers ions of
A c c es s over the years . Some people and organizations buy into
eac h upgrade, s ome s kip around, and s ome hold on for dear life
to the one they have been us ing s inc e the previous c entury! T he
vers ion does n't matter when you or your organization work in a
vac uum, but when you exc hange data with external c ompanies ,
vers ion inc ompatibility c an rear its ugly head.

L et's s ay you have A c c es s 2 0 0 3 and you s end a databas e filled


with your orders to a vendor. T he vendor has A c c es s 9 5 . U h- oh!
T he vendor c an't open your databas e.

O ne of the rec ent data tec hnologies initiated throughout the


c omputing world is the us e of XM L and other platform- neutral
protoc ols . T his purportedly removes data inc ompatibility. XM L is
nic e, but only the mos t rec ent vers ions of A c c es s c an read XM L .

T he way to s hare data is via a tried- and- true, low- key, low- tec h
method: export and s ave your data as text. A lthough they vary
in terms of how text c an be s aveddelimited, type of delimiter
c harac ter, text qualifier, fixed- width, and s o onall vers ions of
A c c es s c an read and write text files . Figure 1 - 1 2 s hows the
E xport Text Wizard, in whic h you s et your text export options .
When you initiate to export an A c c es s table or query and s elec t
text as the type, the wizard s tarts up.

Figure 1-12. Using the Export Text Wizard


A dmittedly, exporting and importing text is n't an ideal approac h,
es pec ially when you have to export or import many tables of
data. But it s ure beats los ing bus ines s bec aus e your c lient c an't
open your databas e.

XM L has paved the way for eas y data exc hange among vers ions
and s ys tems . XM L s upport is dec ent enough in A c c es s 2 0 0 3 ,
les s s o in A c c es s 2 0 0 2 and A c c es s 2 0 0 0 . I f working with text
files jus t does n't s eem right for your needs , you c an always us e
XM L . A s eparate external XM L pars er does the tric k.

1.6.1. See Also

"P rovide C omplete XM L C ontrol to A ny Vers ion of


A c c es s " [Hack #87]

"U s e A c c es s as an XM L D atabas e" [Hack #95]


Hack 6. Organize and Enhance Your
Macros

Optimize and reduce the number of macros using the optional


name and condition columns.

M ac ros are often us ed for s mall automations us ually for tas ks


that aren't too c omplex or s ophis tic ated bec aus e V BA is
available to handle the heavy proc es s ing. L et's think that way no
more. A c tually, mac ros c an handle a dec ent amount of intelligent
proc es s ing and, in fac t, have a c ondition- tes ting ability s imilar
to the If…Then s truc ture in V BA . T his hac k s hows you how to
trans form a s ingle mac ro into a multipurpos e workhors e.

1.7.1. Conditional Macro Actions

M ac ros have but a s ingle mandatory c olumn: the A c tion c olumn.


A mac ro c an have one or more ac tions . H owever, mac ros als o
have an optional C ondition c olumn, in whic h a little entry c an go
a long way toward adding s ome punc h to the proc es s . When
you're des igning a mac ro, us e the V iew menu to dis play the
C ondition c olumn.

A c ondition c an tes t a field value, evaluate the res ult returned


by a func tion, and even us e the returned value from a mes s age
box. C onditions als o c an us e Boolean logic , inc orporating
and/or- type logic in the c ondition tes ting.

Figure 1 - 1 3 s hows a mac ro in whic h a s eries of ac tions oc c ur


when the mac ro is run. A few of the ac tions run only when their
c ondition is met. For ins tanc e, the End of Month func tion and the
E nd of M onth report are inc luded in the proc es s ing only when it
is the firs t day of the month (pres umably tallying up figures
about the month that jus t ended). U s ing the D ay and Now
func tions takes c are of tes ting for the firs t day of the month.

T he E mployee Bonus report runs only when a c ondition tes ted


with a DLookup func tion is TRue.

T he unc onditional ac tions in the mac ro always run. E ven when


the ac tions with unmet c onditions are pas s ed over, the mac ro
c ontinues to run and does n't s top prematurely.

1.7.2. Creating Macro Groups

M ac ros c an als o be organized into groups , known as macro


groups . By c reating mac ro groups , you c an reduc e the number of
overall mac ros and keep s imilar mac ro ac tions together in one
plac e. T he key differenc e between a mac ro and a mac ro group is
the us e of the optional M ac ro N ame c olumn.

When you're des igning mac ros , us e the V iew menu to dis play the
M ac ro N ame c olumn. Figure 1 - 1 4 s hows a mac ro group named
RunReport. T he mac ro group handles the tas k of opening a
number of individual reports . A n important point, though, is that
thes e reports won't open at the s ame time. E ac h mac ro name
exis ts as a s eparate mac ro within the larger group.

Figure 1-13. Using conditions in a macro


Figure 1-14. Using the Macro Name column
When a partic ular ac tion needs to be initiated, you us e the name
of the mac ro group, a dot qualifier, and the name in the M ac ro
N ame c olumn, like this :
DoCmd.RunMacro "RunReport.Inventory Status"

T he point where the ac tion s tarts is the row with the mac ro
name. Suc c es s ive ac tions will run until another mac ro name is
enc ountered. N ot all rows require a value in the M ac ro N ame
c olumn. T his is the beauty of mac ro groups . O ne c ohes ive
des ign hous es any number of s maller ac tion s ets . T he benefit is
a c leaner and eas ier- to- manage mac ro implementation.
Hack 7. Rid Your Database of Clutter

Implement an object-use log to clean up an overloaded


database by analyzing user actions and then deleting never-used
objects.

Some A c c es s databas e applic ations jus t get plain ugly. I f you


have ever brows ed through a databas e with dozens and dozens
of forms and reports , you know what I am referring to. T his is
often the res ult of a us er c ommunity turned loos e: forms for
every point and purpos e; a report for eac h day of the week; and
then s ome.

A dding ins ult to injury, you c an't eas ily tell whic h objec ts the
us ers are ac tually us ing. L uc kily, there is a way to reign in the
applic ation and reduc e the c lutter.

T he goal is to find out whic h objec ts are no longer being us ed.


O ften, us ers c reate forms or reports that they us e onc e and
never look at again. O nc e you've identified whic h objec ts are no
longer being us ed, you c an delete them from the databas e. T his
will likely improve the performanc e of the databas e and c ertainly
reduc e its memory footprint after you c ompac t it. T he tric k to
deleting unus ed objec ts is to c reate a lis t of objec ts that are
being us ed and then to delete the objec ts that didn't make it on
the lis t.

1.8.1. Tracking Object Use


A ll forms and reports c ontain an open event. By putting a s imple
c ode routine into all open events , you c an populate a log with the
names of the objec ts being opened. Before you do this , you need
to c reate a log table to s tore the objec t names . T his does n't
need to be fanc y; indeed, the log table c an have jus t a s ingle
field to s tore the names . O ptional fields c an s tore a times tamp,
the type of objec t, and s o forth.

Figure 1 - 1 5 s hows the des ign of s uc h a table. I t c ompris es two


fields : one c aptures the objec t name, and the other c aptures the
objec t type. T he table rec eives a rec ord eac h time an objec t is
opened.

To append a rec ord to the log table, an objec t mus t have a little
bit of c ode in its open event. H ere is a s nippet that would go into
the open event of a form named Customers:

Private Sub Form_Open(Cancel As Integer)


Dim conn As ADODB.Connection
Set conn = CurrentProject.Connection
Dim ssql As String
ssql = "Insert Into tblObjectLog Values ('Customers', 'Form'
conn.Execute ssql
conn.Close
Set conn = Nothing
End Sub

Figure 1-15. A table for logging objects as they


are opened
When the form is opened, a rec ord is written into the log with the
form's name and objec t type. You s hould put s imilar c ode into
the open event of all forms and reports . T hen let your us ers us e
the databas e again, and watc h the log table begin to fill up. A fter
a reas onable amount of timea week, a month, whatever makes
s ens eexamine the log table. You will s ee numerous entries . I f a
times tamp field was not us ed, you will s ee quite a number of
duplic ate rec ords . U s e a Select query with a Group By aggregate
c laus e to view the res ults without s eeing duplic ates .

1.8.2. Identifying Unused Objects

Figure 1 - 1 6 dis plays a query of the O bjec t log table. T he lis ted
objec ts repres ent the definitive lis t of objec ts us ers are opening.
You c an c ompare this lis t to the full lis t of forms and reports in
the databas e, and you c an s afely delete the forms and reports
that aren't on the lis t as long as you're c omfortable that enough
time has pas s ed. D on't forget to c ompac t the databas e after
deleting the objec ts !

Figure 1-16. Reviewing used database objects


1.8.3. Hacking the Hack

P art of this hac k c onc erns the nec es s ity to add c ode to the
opening routine of all the forms and reports . What a manual
has s le! H owever, you c an automate this tas k. H ere is an
example of c ode that updates the open events of all the reports in
the databas e:

Public Sub insert_open_report_event()


' !! Make sure all reports are closed before running !!
Dim rpt As AccessObject
For Each rpt In CurrentProject.AllReports
DoCmd.OpenReport rpt.Name, acViewDesign
With Reports(0).Module
On Error Resume Next
open_proc_start = .ProcBodyLine("Report_Open", vbext_pk
If Error <> 0 Then
'has no open event, so create one
Err.Clear
open_proc_start = .CreateEventProc("Open", "Report")
End If
.InsertLines open_proc_start + 1, _
"Dim conn as ADODB.Connection"
.InsertLines open_proc_start + 2, _
"Set conn =CurrentProject.Connection"
.InsertLines open_proc_start + 3, _
"Dim ssql as String"
.InsertLines open_proc_start + 4, _
"ssql = ""Insert Into tblObjectLog Values('" & _
Reports(0).Name & "', 'Report')"""
.InsertLines open_proc_start + 5, _
"conn.Execute ssql"
.InsertLines open_proc_start + 6, _
"conn.Close"
.InsertLines open_proc_start + 7, _
"Set conn = Nothing"
End With
DoCmd.Close acReport, Reports(0).Name, acSaveYes
Next
MsgBox "All Reports Updated"
End Sub

T his c ode routine works with the module behind the report. T his
is ac tual V BA that writes V BA kinda neat! Bas ic ally, eac h report
is opened in D es ign mode; c ode is then ins erted into the report's
c ode module. You c an develop a s imilar routine to work with
forms , too; you'll need to addres s the AllForms c ollec tion ins tead
of the AllReports c ollec tion.
Hack 8. Protect Valuable Information

Protect your data using the read-only command-line switch so


that users can't edit the data.

C reating a des ktop s hortc ut to a databas e provides a behind-


the- s c enes benefit. Spec ific ally, you c an us e c ommand- line
s witc hes that are uns een by all but the tec hnic ally c urious .

I n this manner, it is eas y to s et up a s hortc ut to open a databas e


in read- only mode and, thus protec t your data and des ign
elements . To do this , add the /ro s witc h at the end of the target
s tring in the des ktop s hortc ut. N ote that the full target s tring
is n't jus t the path to the databas e; it needs to s tart with the path
to the A c c es s exec utable, followed by the databas e path,
followed by the s witc h.

U s ing A c c es s 2 0 0 3 , whic h by default is in the s tandard Program


Files /Micros oft Office/Office 11direc tory, the full target s tring
looks like this :

"C:\Program Files\Microsoft Office\OFFICE11\MSACCESS.EXE"


"C:\Sales Summaries\Sales2005.mdb" /ro

When the des ktop s hortc ut is c lic ked, the databas e opens in
read- only mode. A c onfirmation mes s age is pres ented at
s tartup, s hown in Figure 1 - 1 7 . D ata c an't be added, deleted, or
edited.

T his is a great way to dis s eminate information without c onc ern


for data integrity is s ues . D is tributing the databas e applic ation in
s uc h a way that a des ktop s hortc ut is c reated or updated
guarantees that the databas e opens in jus t the way you
intended.

Figure 1-17. A reminder about the read-only


status

1.9.1. But Just in Case

O f c ours e, a half- s avvy us er c an jus t s tart up A c c es s and open


the databas e via the O pen dialog, thus bypas s ing the des ktop
s hortc ut. T he databas e is then opened in full read/write mode,
unles s a gotc ha is in plac e to prevent this .
To handle this , you c an plac e a s imple SQ L Insert operation in
the databas e's opening routine, and you c an inc lude an extra
table in the databas e for jus t this purpos e. I f the operation
s uc c eeds , the us er is warned to us e the des ktop s hortc ut (as
s hown in Figure 1 - 1 8 ), and the databas e c los es .

Figure 1-18. Catching users who try to skip


using the desktop shortcut

1.9.2. The Code


H ere's the routine that tes ts a SQ L Insert:

Public Function test_mode()


On Error GoTo err_end
Dim conn As ADODB.Connection
Set conn = CurrentProject.Connection
Dim ssql As String
ssql = "Insert Into tblModeTest Values ('test')"
conn.Execute ssql
conn.Close
Set conn = Nothing
'if got this far then database is not in read only mode
'tell user, then quit
MsgBox "Database must be opened through desktop shortcut
DoCmd.Quit
Exit Function
err_end:
'all is well, an error was expected
End Function

T here is a twis t to this : the databas e is s uppos ed to be in read-


only mode, s o the optimal outc ome is that the operation will fail
ins tead of s uc c eed. A n error trap is implemented in the routine,
s o if the error is triggered, all is well, and no ac tion is taken. I f
the ins ert s uc c eeds , the warning is dis played, and the Quit
method c los es the databas e. T his routine s hould be c alled by
the AutoExec mac ro s o that it runs immediately when the
databas e opens .
Hack 9. Work with Any Amount of Data

Plan a multiple-database architecture to house any amount of


datagigabytes, even terabytes!

T he only s ize limit in A c c es s is that a table c an't c ontain more


than 1 G B of data. Well, if that's it, there is a lot of opportunity
here. A c c es s is n't c ut out for inc redibly large s tores of data,
granted, but that's not the point. I f SQ L Server or O rac le is n't
going to be ins talled at your plac e of bus ines s for another year,
take advantage of A c c es s 's flexible arc hitec ture to work with
any amount of data.

T he tec hnique is s imply to make a plan for how to s truc ture the
data among multiple databas e files and tables . T here is no rule
that s ays an A c c es s applic ation mus t res ide c ompletely in a
s ingle A c c es s file. A n A c c es s applic ation c an be s plit into a
front end and a bac k end. T hat is , the forms , reports , and queries
s tay in the front end, and the data its elf is put into a s eparate
file. T he data tables are then linked to the front end. T his is
s tandard fare, the quintes s ential c lient/s erver in its s imples t
exec ution, s hown here in Figure 1 - 1 9 .

Figure 1-19. A simple front-end/back-end


configuration
1.10.1. Splitting Up Data

T here is no reas on to be limited to a s ingle file on the bac k end.


T he organization of and fac ts about the data will drive the
dec is ions c onc erning how it c an be pars ed into s maller data
s tores . For example, if you are working with a large c us tomer
bas e, you c an s plit the data from one data table into 2 6
tables one for eac h letter of the alphabet. Figure 1 - 2 0 s hows
s uc h a c onfiguration.

Figure 1-20. Using multiple databases on the


back end
A n alternative is to s plit a c us tomer lis t by c ity, s tate, provinc e,
or other geographic delimiter. A gain, this allows you to take an
overwhelmingly large s et of data and turn it into manageably
s maller (albeit s till large) s tores of data.

Splitting the data is the key to this hac k. A nalyze the data, and
c ome up with a game plan. P erhaps the data is date- bas ed. You
c an s plit it up by month, day, or whatever makes s ens e.

1.10.2. Working with Split Data

T here is an unwelc ome s ide effec t to s plitting data. I n a


relational s ys tem, you los e the s implic ity of relying on the
es tablis hed relations hips when data is s plit out to additional
tables . P ic ture this : you have a mas ter table of c us tomers and a
related table of c us tomer purc has es . You s plit the c us tomers
into 1 0 s maller tables . What happens to the relations hip? You
c an work around this problem in two ways .

O ne way is to relate the purc has es table to all 1 0 mas ter tables .
T he other is to leave out the relations hips altogether and
ins tead inc orporate behind- the s c enes proc es s ing to wed data
bac k together as needed by front- end ac tivity.

T his is n't as c omplic ated as it might s ound. I n a nuts hell, V BA


and A D O work together to find c us tomers and purc has es that
matc h bas ed on whatever c riteria are being s elec ted in the front
end. A workable approac h to find purc has es that matc h a
c riterion is to c reate a rec ords et or array of rec ords from the
purc has es table, and then run thes e rec ords agains t the 1 0
c us tomer tables while looking for a matc h on the key fields . N o,
this is n't an eloquent or partic ularly effic ient way of proc es s ing
data. H owever, it enables A c c es s to work with gigabytes or more
of data, and that is the meas ure of s uc c es s in this c as e.
Hack 10. Find Database Objects in a
Snap

Use the description property to prevent users f rom being


overwhelmed by sif ting through cryptic-sounding f orms, queries,
and reports.

M any of us follow naming c onventions when c reating databas e


objec ts . A mong the developer c ommunity, we have c ome to
rec ognize and take for granted that tbl, frm, rpt, and other
prefixes are part and parc el of our work. For example, tblStaff is
a table, frmA dmin is a form, and rptC ontac ts is a report.

H owever, when you c omplete a databas e with s everal objec ts


that are named in this way, it's a c hallenge to the average
databas e us er to unders tand the names . Figure 1 - 2 1 s hows a
perfec t example of a databas e with s everal forms .

T here is a way to res olve this dilemma, and it does n't mean
developers have to c hange their naming habits . A ll databas e
objec ts c an be given a des cription. T he bes t thing is that you c an
enter des c riptions for objec ts direc tly in the databas e window
without having to open an objec t in D es ign mode.

I n the databas e window, jus t right- c lic k an objec t, and from the
menu that appears , c lic k P roperties . A s mall dialog box opens
for you to enter a natural- s ounding des c ription, as s hown in
Figure 1 - 2 2 .
A fter you enter des c riptions for all the objec ts , jus t be s ure to
lis t the databas e objec ts in L is t view ins tead of I c ons view. T his
makes the des c riptions vis ible. Figure 1 - 2 3 s hows how the
group of forms in Figure 1 - 2 1 is now unders tandable.

Figure 1-21. Cryptic form names that can stump


a user
Figure 1-22. Entering a description
A neat thing about this approac h is that you c an even us e a
warning mes s age s o that us ers know not to open an objec t. T his
is partic ularly helpful in the c as e of s ubforms . U s ers s houldn't
open s ubforms direc tly bec aus e they appear ins ide other forms .
T he des c ription tells us ers not to open them.

Figure 1-23. Selecting a form by its description


Kirk Lamb
Hack 11. Use a Junction Table

Correctly model a many-to-many relationship.

I t's eas y to fall into the trap of as s uming all relations hips are of
the one-to-many type. I t's true that many data relations hips do
follow the one- to- many paradigm. For example, one pers on has
zero or more telephone numbers . H owever, not all data is meant
to be modeled in this way.

A perfec t example of data that appears to fit the one- to- many
model, but does n't, is the relations hip between ins truc tors and
s tudents . O n the one hand, one ins truc tor does have many
s tudents , thereby proving a one- to- many relations hip exis ts . O n
the other hand, one s tudent has many ins truc tors whic h is als o a
one- to- many relations hip. So, what is the problem?

Figure 1 - 2 4 s hows one way to model ins truc tors and s tudents .
T he ins truc tor table oc c upies the one s pot and the s tudent table
oc c upies the many s pot. I ns truc tors and s tudents get together
for appointments . T his model works but emphas izes that
ins truc tors are of a different level than s tudents , whic h might not
be true.

Figure 1-24. An inefficient one-to-many


relationship
I n Figure 1 - 2 4 , the s tudent table is als o required to have the
ins truc tor I D as the foreign key. T his is ac c eptable, but now look
at the appointments table; it c ons iders appointments as
belonging to s tudents , but appointments belong to both
ins truc tors and s tudents .

Figure 1 - 2 5 s hows how to res olve the dilemma in the data


model. Bec aus e appointments belong to both ins truc tors and
s tudents , that is how the model s hould look. T he appointments
table s erves as a j unction table between ins truc tors and
s tudents .

A junc tion table bec omes the many table for two or more other
tables . A ll the key fields of the one tables bec ome foreign keys in
the junc tion table. A ny other pertinent fields are inc luded in the
junc tion table. I n this example, the junc tion table has fields for
the date and time the ins truc tor and s tudent will meet. A ls o, the
s tudent table no longer has the ins truc tor I D as a foreign key. I n
this example, ins truc tors and s tudents have no hierarc hy;
therefore, it makes s ens e that one does n't s erve as a many to
the other.

Figure 1-25. A better model


Hack 12. Stop the Database from
Growing

Use the Compact on Close option to keep a database f rom


getting too big.

A c c es s databas es are notorious for their ability to grow in s ize.


T his is es pec ially true as data is moved in and out. For example,
when a databas e applic ation regularly imports data, proc es s es
the data, and then exports it bac k out, the databas e c an bec ome
huge, on the order of s everal megabytes in s ize. T his c an be the
c as e even when the data moving in and out is of a reas onable
s ize.

To s hrink the databas e bac k to the s ize it s hould be, you need to
compact the databas e. H owever, expec ting us ers to c ompac t
their databas es is n't a great idea, es pec ially if your us ers aren't
tec hnic ally s avvy. L uc kily, A c c es s inc ludes an option to
c ompac t a databas e when it is c los ed. T his option was not
available in older vers ions of A c c es s , but it is available in
A c c es s 2 0 0 2 and A c c es s 2 0 0 3 .

Figure 1 - 2 6 s hows the O ptions dialog box (Tools O ptions )


with the G eneral tab on top. N ote the C ompac t on C los e
c hec kbox.

A c c es s is unlike other produc ts , s uc h as SQ L Server, in that it


does n't allow you to c ontrol the s ize of the databas e. Setting the
databas e to c ompac t eac h time it c los es removes what has
traditionally been a rec urring problem with A c c es s .

Figure 1-26. Selecting to compact on close


2. Tables
Sec tion 2 .1 . H ac ks 1 3 1 8

H ac k 1 3 . C reate an A utoN umber Field with a C us tom


Value

H ac k 1 4 . C opy D ata Between Tables Without an A ppend


Q uery

H ac k 1 5 . Steer C lear of Sys tem Tables

Sec tion 1 6 . H ide Sens itive I nformation

H ac k 1 7 . Simulate Table Triggers

Sec tion 1 8 . C reate Tables Fas ter


2.1. Hacks 1318
Were it not for tables , we would have no plac e to s tore data!
Tables are s traightforward; they c ompris e rows and c olumns , or
rec ords and fields . So, what is there to hac k?

Table des ign is one area in whic h a little c us tomization goes a


long way. C hanging the default datatype and other properties
s peeds up development time. Without this intervention, text
fields default to 5 0 c harac ters . I s this a good s ize? T hat
depends on your projec t.

H ave you ever wis hed you c ould implement triggers the way SQ L
Server does ? You c an! U s e the inherent events available to
forms to get the s ame res ults .

N eed to hide data? T here's a hac k for that, too!


Hack 13. Create an AutoNumber Field
with a Custom Value

The A utoNumber f ield doesn't need to begin with a value of 1.


You can override A ccess's def ault autonumbering scheme to
better suit your requirements.

A great feature that A c c es s brings to the table- c reation proc es s


is the A utoN umber field. T his field type plac es a value of 1 in the
firs t rec ord and automatic ally inc reas es the value by 1 as
rec ords are added. I t does n't c ontain any s ignific ant or
meaningful data. I ts bas ic purpos e is to bec ome the key field
and thereby provide uniquenes s to the data rec ords .

J us t plop a field into the table des ign, and des ignate it as an
A utoN umber field. Typic ally s uc h a field has a name with I D or
N um in it, s uc h as C us tomerI D or Rec ordN um. N ote that a table
c an have only one A utoN umber field.

A ll in all, A utoN umber is a great feature, but there is one gotc ha:
the value always s tarts at 1 . O ften, this is n't an is s ue bec aus e
the field value really is unimportant. T he fac t that the values are
unique is what matters more. But what if you need to us e a s elf-
inc rementing number that s tarts at a different value? C an you do
this ? O f c ours e!

2.2.1. Seeding AutoNumber with a Number


of Your Choice

T he A utoN umber field type does n't have a property to s pec ify
the s tarting value. Figure 2 - 1 s hows a table des ign. A s you c an
s ee, the firs t field is an A utoN umber field, and its addres s able
properties fill the lower- left area of the table des ign window. N ote
that you have nowhere to input a default s tart value.

Figure 2-1. AutoNumber, an incrementing field


type
To be c lear, the table does c ontain a N ew Values property, but all
it tells you is whether new values are inc remented or are
random. I t tells you nothing about s tarting the inc rement at a
value of your c hoic e. So, the firs t rec ord will have a value of 1 in
the field, the s ec ond rec ord will have a value of 2 in the field, and
s o on.

To override the default s tarting value of 1 , you c an us e an Append


query to ins ert a different s tarting value. A fter you have
des igned the table and are ready to us e it, you mus t get the
initial value in plac e. Figure 2 - 2 s hows an Append query that
s pec ific ally plac es a value in the A utoN umber field. T hat is , one
rec ord gets added to the table, with the A utoN umber field
rec eiving the des ignated value.

Figure 2-2. Using a query to set the beginning


AutoNumber value
N ote that you enter this query in the SQ L view. T hat's bec aus e
it's not obvious how to enter this query in the des ign view (the
query grid), in whic h Append queries are typic ally us ed to append
one table to another. T his operation works by appending a value
to a field in a table with no other table involved (in a pinc h, you
c an des ign another table jus t to hold the value, but you don't
have to do s o).

Figure 2 - 3 s hows the res ults of running the Append query. T he


E mployees table was empty but now it c ontains its firs t rec ord,
and the A utoN umber value for that rec ord is 1 0 0 .

Figure 2-3. The first record with the designated


starting AutoNumber value

N ote that no other fields have been populated yet. I f any other
fields mus t have a value, you mus t populate them with
appropriate values in the query. For example, you c an modify the
query to populate additional fields , s uc h as E mployee and T itle,
like this :
INSERT INTO Employees (EmployeeID, Employee, Title)
VALUES (100, 'John Smith', 'Supervisor');

Figure 2 - 4 s hows the res ult of applying this new query. O n the
s urfac e, this s eems to take c are of two birds with one
s tones tarting the A utoN umber with a value of your c hoic e, yet
without us ing a dummy rec ord (as in Figure 2 - 3 ) to do s o.
H owever, this approac h c an be problematic . I t's a little odd to
populate the firs t rec ord in this manner and then to populate all
s ubs equent rec ords via forms , proc es s ing, or other methods .
T he point is that it probably is n't prac tic al to populate the firs t
rec ord in any method that differs from how other rec ords are
ins erted.

Figure 2-4. Using a query to fill the AutoNumber


field, along with other fields

But how c an you get the firs t rec ord to have the des ired s tarting
A utoN umber value without having a dummy rec ord as the firs t
rec ord in your table? T he twis t is to s till populate the firs t rec ord
us ing a query, but to populate the A utoN umber field with a value
of one les s than the real s tarting value. You c an then delete this
rec ord, and the A utoN umber will inc rement as s ub- s equent
rec ords are added. T his means the firs t real data rec ord, entered
in whatever way your s ys tem handles it, will have the firs t value
of c hoic e. T he A utoN umber will jus t inc rement from there, as
expec ted.

I n the example s hown in this hac k, the query needs to populate


the table with a s ingle rec ord, in whic h a value of 9 9 is given to
the A utoN umber. T hat rec ord is then deleted (manually or
otherwis e; it does n't matter how). When the firs t real data rec ord
is added, it will have an A utoN umber value of 1 0 0 .

2.2.2. Hacking the Hack

You c an res et the value of the A utoN umber field whenever you
want. T his does n't c hange the exis ting rec ords . I t lets new
rec ords be numbered s tarting from a new initial value. You jus t
run the Append query, as s hown in Figure 2 - 2 (adjus ted as
dis c us s ed to handle any other required fields ), but res et the
value to one that is higher than the highes t value already in the
table. For example, if the las t rec ord entered in the table has a
value of 2 2 0 , res et the c ount to s omething higher. O bvious ly,
you would s kip the next inc remental number; otherwis e, there
would be no reas on to rees tablis h where the inc rement begins .

T his offers an interes ting option for managing your data. What if
a s eries of rec ords in a table has s ome unobvious but related
attribute? For example, you c an res et the A utoN umber field to a
new s tarting value at the s tart of eac h year. T hen, eac h year's
data will be eas y to dis tinguis h. For example, all rec ords in the
year 2 0 0 5 might lie within the 5 ,0 0 0 5 ,9 9 9 range, all rec ords for
2 0 0 6 within the 6 ,0 0 0 6 ,9 9 9 range, and s o on.
Hack 14. Copy Data Between Tables
Without an Append Query

Use Paste A ppend to easily copy data across tables.

A c c es s us ers often us e an Append query to append rec ords from


one table to another. I n a produc tion environment in whic h data
is always being s huffled around, us ing Append queries c an
bec ome tedious . E ac h time you des ign one, you have to matc h
the fields of the des tination table with the fields of the s ourc e
table. T his is eas y when the fields have the s ame name, but it
takes manual intervention when the field names differ.

I f you have des igned and s aved an Append query definition, and
the s ourc e and des tination tables never c hange in name or
s truc ture, all is well for you. H owever, if even a s ingle extra
c harac ter is mis plac ed or is mis s ing in the field names , the
query either bombs or as ks you to fill in the value for the
unidentifiable field. N either is an option you c an live with.

H ow c an you deal with thes e ac c idents waiting to happen?


Fortunately, you c an c opy data between tables in another way:
us e P as te A ppend.

2.3.1. Appending Across Tables

A pas te method unique to A c c es s , P as te A ppend appends the


c ontents of the c lipboard to a databas e table. T he data has to
matc h the table in s truc ture, but it does not need to have
matc hing field names . T hat right there improves on the tedious
data entry involved when us ing the query grid. To be fair to
Append queries , they do have an advantage of their own: an Append
query c an us e c riteria to append filtered s ets of rec ords . P as te
A ppend, on the other hand, jus t appends everything. H owever, if
the need to apply c riteria is n't an is s ue, P as te A ppend has the
advantage.

Figure 2 - 5 s hows two tables : one c ontains exis ting c us tomers ,


and the other c ontains a lis t of leads that have to be added to
the lis t of exis ting c us tomers . T he rec ords in the tblL eads table
need to be added to the tblC us tomers table. T he field names
aren't the s ame, although the field types and purpos es matc h.

T he s imples t thing to do is to s elec t all the rec ords in tblL eads


(C trl- A ). C opy the rec ords , go to the tblC us tomers table, and
us e the E dit P as te A ppend menu to enable P as te A ppend,
as s hown in Figure 2 - 6 .

Figure 2-5. Appending similar data from one


table to another
Figure 2-6. Using Paste Append
N ote that the rec ords are appended without c onc ern for the field
names . H owever, an alternative method is available that is
eas ier s till: the table with the rec ords to be appended (tblL eads
in this example) does n't even have to be open! J us t s elec t and
c opy the table while it is c los ed. D o this direc tly in the databas e
window. T hen, open the table that rec eives the rec ords
(tblC us tomers in this example), and us e the P as te A ppend menu
item as before.

T his method has an is s ue, though. When the field names are the
s ame, the method works like a c harm. H owever, if at leas t one
field name is different, the method s till works , but the field
names of the table being c opied from might be ins erted as a
rec ord!

O f c ours e, by nature, field names are text- bas ed, s o if the table
rec eiving the append c ontains fields that aren't text- bas ed,
P as te A ppend won't pas te the field names . You might get an
error about the datatype being wrong, but this is O K. Strange but
true!

Finally, even when you know a rec ord will appear that c ontains
field names ins tead of data, the P as te A ppend method s till might
be preferable to c reating an Append query bec aus e it is us ually
muc h eas ier to delete a s ingle rec ord from a table than it is to
des ign a new query from s c ratc h.

2.3.2. Appending Across Databases

T he tec hniques in this hac k work not jus t within a s ingle


databas e applic ation, but als o ac ros s databas es . I n other words ,
you c an s elec t and c opy rec ords from a table in one databas e
and then append thes e rec ords to a table in a different databas e.
H owever, both databas es mus t be open to make this pos s ible.
Figure 2 - 7 s hows two databas es , s ide by s ide on the des ktop.

Figure 2-7. Appending data across databases

T he tblL eads table, in the databas e on the left, is s imply being


dragged over to the open tblC us tomers table in the databas e on
the right. T he tblL eads table is effec tively being c opied, not
moved; the original s tays in the firs t databas e. L etting go of the
mous e button c ompletes the append operation.
Hack 15. Steer Clear of System Tables

A void incorrect results by leaving system tables out of your


table count and def inition routines.

H ow many tables are in your databas e? You might think finding


this out is as eas y as c ounting how many tables are lis ted on the
Tables tab of your databas e window. To that I res pond, "Try
again! "

A c c es s us es a number of s ys tem tables to c ontrol its own


internal workings . U s ually, thes e additional tables are hidden,
but they are there nonetheles s . Figure 2 - 8 s hows a databas e
with s ome tables .

Figure 2-8. Tallying the tables


I t looks like this databas e c ontains eight tables , does n't it?
L et's try getting a c ount in a different way. I n the V B E ditor,
ac tivate the I mmediate window (C trl- G ). T hen, enter the
following c ode s nippet and pres s the E nter key:

?Application.CurrentData.AllTables.Count

Figure 2 - 9 s hows the c ode and its res ults in the I mmediate
window. For the databas e in Figure 2 - 8 , the res ult is 1 5 , s o
A c c es s is telling us the databas e ac tually c ontains 1 5 tables ,
although only eight are vis ible on the Tables tab.

Figure 2-9. Counting all the tables


T he c ode s nippet tells the truth, however: this databas e does
indeed c ontain 1 5 tables . T he ones you c ouldn't s ee before are
the s ys tem tables . L et's dis play them!

Bac k in the databas e proper (not the V B E ditor), us e the Tools


O ptions menu to dis play the O ptions dialog box. Selec t the
V iew tab. A s s hown in Figure 2 - 1 0 , one of the options in the
Show area is to dis play s ys tem objec ts . Selec t this c hec kbox to
make the s ys tem tables vis ible.

Figure 2-10. Selecting to show system objects


N ow, looking at the Tables tab in Figure 2 - 1 1 , you c an s ee the
s ys tem tables .

N ote that all the s ys tem table names s tart with M Sys . T his is
ac tually a us eful attribute about thes e tables bec aus e it makes
it eas y to remove them from a table c ount.

Figure 2-11. Displaying all tables, including


system tables
2.4.1. The Code

But why does any of this matter? O ne reas on is that an


applic ation might need to iterate through all the tables in a
databas eperhaps to add a property, to look for a field or data, to
alter the table s truc ture in s ome way, and s o on. I n s uc h
c irc ums tanc es , the s ys tem tables mus t be avoided. Fortunately,
a s imple c ode routine eas ily handles this by purpos ely avoiding
all tables that have names beginning with M Sys , as follows :

Sub count_tables()
'list tables in database
Dim table_num As Integer
Dim tbl_count As Integer
With Application.CurrentData
For tbl_count = 1 To .AllTables.Count
If Left(.AllTables(tbl_count - 1).Name, 4) <> "MSys" Then
Debug.Print .AllTables(tbl_count - 1).Name
End If
Next tbl_count
End With
End Sub

T his c ode routine c yc les through all the tables in the databas e
and writes the name of eac h table to the debug (I mmediate)
window, as long as the table's name does n't s tart with M Sys . To
us e this routine, replac e the table names with any partic ular per-
table proc es s ing you need.

2.4.2. Running the Code

Figure 2 - 1 2 s hows the output of this routine. T he I mmediate


window is filled with jus t the pertinent applic ation data tables ,
and that's exac tly what we need.

Figure 2-12. Listing just the data tables


By is olating the data tables from the s ys tem tables in this way,
you c an work with the data tables how ever you want, without
worrying about c ras hing your applic ation.
16. Hide Sensitive Information

Name tables with the USys pref ix to prevent them f rom being
visible.

H ere's a quic k and eas y hac k to hide data from prying eyes . O f
c ours e, any A c c es s guru with enough notc hes in his belt will
figure this one out. But ordinary us ers ? N ot likely. You c an hide
your tables us ing this approac h and s till retain full func tionality
in your applic ation. Q ueries , forms , reports , mac ros , and c ode
will s till work, but anyone viewing the Tables tab won't find the
tables you des ignate as hidden.

To do s o, prefix your table names with U Sys . T his ac ts as a flag


to A c c es s to treat the tables as a quas i- mix of s ys tem and us er
tables , and the ability is built in to hide or dis play them. Figure
2 - 1 3 demons trates this proc edure: a form is open and is c learly
dis playing data, but the Tables tab in the databas e window has
no tables !

Figure 2-13. A form based on a hidden table


T he form in Figure 2 - 1 3 has the rec ord s ourc e property s et to
the U Sys C lients table. I n the Tools O ptions V iew
menu, you'll find a s etting for dis playing s ys tem objec ts , as
s hown in Figure 2 - 1 4 . N ote that c hec king to dis play s ys tem
objec ts makes U Sys tables vis ible.

Figure 2 - 1 5 s hows all the s ys tem objec ts in their glory. T he


U Sys tables are there, as well as the M Sys tables [Hack #15].

T he prefix is n't c as e- s ens itive. You c an


us e U SY S, U Sys , us ys , and s o on; they all
work to differentiate a table.

2.5.1. An Alternative

A nother way to hide objec ts in your databas e is to right- c lic k a


databas e objec t, whic h then dis plays a menu that inc ludes a
P roperties option. Selec ting this dis plays a P roperties dialog, as
s hown in Figure 2 - 1 6 . C hec king the H idden c hec kbox hides the
objec t.

Figure 2-14. Selecting to display USys-prefixed


tables
Figure 2-15. Displaying all USys and MSys
tables
To dis play hidden objec ts , s imply c hec k "H idden objec ts " in the
Show s ec tion of the O ptions dialog box, as s hown previous ly in
Figure 2 - 1 4 . But note that between prefixing objec t names with
U Sys and s etting the hidden attribute, you've got enough
c apability to be a little s mart and a little dangerous . J us t
bec aus e you c an't s ee objec ts does n't mean they aren't there!

Figure 2-16. Setting the Hidden attribute


2.5.2. Hacking the Hack

A lthough this hac k s howed you how to hide tables and, therefore,
avoid giving us ers ac c es s to raw data, you c an hide other
databas e objec ts as well. J us t prefix the names of queries ,
forms , reports , and s o on, with U Sys , and they magic ally
dis appear. O r, s et the hidden attribute in the P roperties dialog. I t
helps to write down the names firs t!

A really c ool tric k is to us e the U Sys prefix, or to s et the hidden


attribute, for all databas e objec ts . A s a res ult, anyone viewing
the tabs in the databas e window will s ee abs olutely nothing. By
s etting the Startup form to a form prefixed with U Sys , you c an
get the entire applic ation running. A s long as you are fully aware
of how all the objec ts are named, you c an c reate a c omplete
applic ation without a s ingle vis ible objec t in the databas e
window tabs . O f c ours e, the objec ts bec ome vis ible when they
are opened, but by taking the c orrec t meas ures to keep us ers
out of your des ign elements , you c an dis tribute an invis ible
databas e.
Hack 17. Simulate Table Triggers

Incorporate the same f unctionality as SQL Server or Oracle in


your A ccess application.

A c c es s 2 0 0 3 and earlier vers ions don't s upport table events . A


trigger is a table event that you c an fire on an ins ert, an edit, or a
delete ac tiona valuable func tion. A us eful example is to c atc h an
edit before it c ompletes and to s tore the original datathat is ,
s tore the original rec ord s omewhere els e, s uc h as in a bac kup
table. T his leaves you with a data audit trail. I f for s ome reas on
the edited data is problematic , you c an rec all the original data.

T his logic applies to deletes as well. U s ing triggers , you c an


hook into a delete and arc hive the data ins tead of jus t dis c arding
it. I n the c as e of ins erts (s uc h as new rec ords being added to a
table), data c an be validated before being allowed into the table.

U nfortunately, A c c es s does n't let you do any of this direc tly


from the point of view of the table its elf. But you can do all of this
when working through forms . Forms have plenty of events to
hook into, and you c an handle s imilar func tionality as traditional
triggers by working through forms ins tead of tables .

2.6.1. Setting Up an Audit Log

To demons trate how all this works , let's add a new table to a
databas e to mirror an exis ting data table and c reate an audit log
of c hanges to the data table. We'll do this by us ing two additional
fields : one to s tore the type of operation and one to s tore a
times tamp. Figure 2 - 1 7 dis plays two tables : the data table
(tblC lients ) and a table to s tore rec ords from the firs t table jus t
prior to them being edited or deleted (tblC lients A uditL og).

H ere are a c ouple of points to c ons ider:

T he log table c ontains two additional fields : A c tion and


T imes tamp.

T he C lientI D field is the primary key in the data table,


but it is purpos ely not s et as a primary key in the log
table. T his is bec aus e the log table might hold multiple
rec ords that pertain to the s ame c lient (and therefore
the s ame C lientI D ).

2.6.2. Checking Out the Form Events

N ow you c an us e a s tandard form to view, add, edit, and delete


rec ords from the data table. Figure 2 - 1 8 s hows a typic al form
bas ed on the tblC lients table.

Figure 2-17. Using an audit log table to store


records
Figure 2-18. Inserts, updates, and deletes, done
with a form
O f c ours e, there is s ome c ode behind this form. Two events are
tapped: the Before Update event and the Delete event. Before
Update handles both ins erts and updates , and Delete handles
deletes . I n partic ular, when an ins ert is made, the Before Update
event validates the data (i.e., it c hec ks to s ee if there is a las t
name). I f the validation fails , the Cancel property is s et to true,
whic h c aus es the event to abort.

When an update (an edit) is made, the rec ord rec eiving the
c hange is written to the log table, prior to the c hange. T his
means the original data is kept intac t. When a delete is made,
the rec ord that is to be deleted is als o written to the log table,
prior to the c hange.

2.6.3. The Code

H ere is the c ode behind the form. T he Action field in the log table
rec eives one of two values : Update or Delete. T he two event
routines us e a c ommon func tion (build_sql):

Private Sub Form_BeforeUpdate(Cancel As Integer)


On Error GoTo err_end
Dim ssql As String
Dim conn As ADODB.Connection
Set conn = CurrentProject.Connection
If NewRecord = False Then
ssql = build_sql(ClientID, "Update")
conn.Execute ssql
conn.Close
Set conn = Nothing
Else
If IsNull(ClientLastName) Or ClientLastName = "" Then
MsgBox "Must provide name"
Cancel = True
End If
End If
Exit Sub
err_end:
MsgBox Err.Description
End Sub

Private Sub Form_Delete(Cancel As Integer)


On Error GoTo err_end
Dim ssql As String
Dim conn As ADODB.Connection
Set conn = CurrentProject.Connection
ssql = build_sql(ClientID, "Delete")
conn.Execute ssql
Exit Sub
err_end:
MsgBox Err.Description
End Sub

Function build_sql(client_id As Long, operation As String) As S


build_sql = "Insert Into tblClientsAuditLog Values ("
build_sql = build_sql & ClientID & ", "
build_sql = build_sql & "'" & _
DLookup("ClientFirstName", "tblClients", "ClientID=" & _
client_id) & "', "
build_sql = build_sql & "'" & _
DLookup("ClientLastName", "tblClients", "ClientID=" & _
client_id) & "', "
build_sql = build_sql & "'" & _
DLookup("ClientAddress1", "tblClients", "ClientID=" & _
client_id) & "', "
build_sql = build_sql & "'" & _
DLookup("ClientState", "tblClients", "ClientID=" & _
client_id) & "', "
build_sql = build_sql & "'" & _
DLookup("ClientCity", "tblClients", "ClientID=" & _
client_id) & "', "
build_sql = build_sql & "'" & _
DLookup("ClientZip", "tblClients", "ClientID=" & _
client_id) & "', "
build_sql = build_sql & "'" & _
DLookup("ClientPhone", "tblClients", "ClientID=" & _
client_id) & "', "
build_sql = build_sql & "'" & operation & "', "
build_sql = build_sql & "#" & Now() & "#)"
End Function

2.6.4. Running the Code

T he c ode runs when ins erts , updates , and deletes are made
us ing the form. N o partic ular additional ac tion, s uc h as c lic king
a button, is required. T he log table fills up with rec ords as us ers
do their thing. T he log table keeps trac k of all the c hanges and
even s tores multiple c hanges per c lient. T he build_sql func tion
c reates an Insert SQL s tatement. T he s tatement will inc lude
either Update or Delete as one of the values being written, the
differenc e being whic h routine c alled the func tion (and pas s ed
the word Update or Delete as an argument). T he SQ L s tring is
handed bac k to the c alling routine, from where the ins ert is run.

T his c ertainly c an be us eful in the real world. For example, s ay a


c lient moves (an addres s edit), gets married (a name edit),
s tarts buying from your c ompetitor (a delete! ), and s o on. Figure
2 - 1 9 s hows the res ulting log table with s ome rec ords . E ac h
rec ord dis plays the ac tion and the times tamp.

T he methods us ed in this hac k s imulate what SQ L Server,


O rac le, and other databas e produc ts provide in the way of
triggers . L et's not allow the elite of the databas e world to believe
A c c es s is n't up to s nuff!

Between the c lient table and the log table are multiple rec ords ,
and there is a s equenc e to the rec ords , thanks to the wonderful
times tamp.

Figure 2-19. Records copied to the audit log


table before being updated or deleted

2.6.5. Hacking the Hack


T his hac k was written with the advantage of knowing the table
s truc ture and field types . T herefore, I knew ahead of time to
plac e s ingle quotes around text values in the build_sql func tion.
When adapting this hac k, you will probably know ahead of time
what type of data to expec t, but if you don't, you c an tap into the
A D O X library to determine datatypes .

A D O X provides a way to read through eac h field in a table and


determine its type (as well as other properties ). T he following is
a bas ic routine to read through a s ingle table and have the name
and type returned for eac h field:

Sub get_fields()
Dim cat As ADOX.Catalog
Set cat = New ADOX.Catalog
Dim fld As ADOX.Column
cat.ActiveConnection = CurrentProject.Connection
For Each fld In cat.Tables("tblClients").Columns
Debug.Print fld.Name & " " & fld.Type
Next
Set cat = Nothing
End Sub

N ote that to us e the A D O X library, you mus t s et a referenc e. A s


with other referenc es , go to the V B E ditor and us e the Tools
Referenc es menu to dis play the Referenc es dialog box,
s hown in Figure 2 - 2 0 . T he library is named Micros oft ADO Ext. 2.7
for DDL and Security. C hec k the appropriate c hec kbox, and c lic k
O K to c los e the dialog.

T he routine returns a numeric al c ons tant for eac h field type. For
example, a value of 2 0 2 indic ates a Text field type (although
properly noted as a variable- width c harac ter field in A D O X
lingo). U s e the O bjec t Brows er to view the DataTypeEnum
c ons tants to s ee what the numbers repres ent. T his c ollec tion of
datatype c ons tants is available onc e the referenc e to A D O X is
s et. Figure 2 - 2 1 s hows the O bjec t Brows er zeroed in on the lis t
of datatype c ons tants . For any c ons tant, you c an s ee the
numeric al repres entation at the bottom of the O bjec t Brows er.

Figure 2-20. Setting a reference to ADOX


Figure 2-21. Reviewing datatype constants
U s ing Select Case or a s et of If s tatements inters pers ed with the
A D O X c ode in this s ec tion, you c an write a routine that does n't
rely on knowing the field types ahead of time.
18. Create Tables Faster

Optimize table design by changing the design def aults to match


your needs.

A Text field is 5 0 c harac ters . A N umber field is a long integer,


and the default value is 0 . Sound all too familiar? H ow often have
you gone out of your way to alter thes e defaults ? Well, with this
hac k, you no longer need to.

I n the O ptions dialog box (Tools O ptions ), on the


Tables /Q ueries tab, you'll find s ettings for s elec ting the default
s ize for text fields , the default number type (integer, long, s ingle,
etc .) for number fields , and even the overall default type. Figure
2 - 2 2 s hows this dialog box and the s ettings .

Figure 2-22. Changing field defaults


I n Figure 2 - 2 2 , the default Text field s ize has been c hanged to
1 0 0 . T his means that as new text fields are added to the des ign
of a table, they will default to a s ize of 1 0 0 . A ls o, the default
N umber type has been s et to Single. A s new number fields are
added, they default to the Single datatype. T he overall default
field type is s et to N umber; therefore, as new fields are entered
into a table des ign, they default to a N umber field typeand that
type will be of the Single number type.

A ltering thes e des ign defaults c an be quite us eful. I f, for


example, you are des igning a table that predominantly c ontains
dates , s et the default field type to D ate/T ime, and s ave yours elf
a lot of field- type s elec tion. A s you enter new fields , they will
default to D ate/T ime. You will need to adjus t only the minority of
fields that aren't of this type.

2.7.1. Setting Default Values

T he s ettings in the O ptions dialog box c ontrol field type s ettings


but offer nothing to indic ate default values . I n other words , you
c an s elec t Single as the default number type, but you c an't
s pec ify that the field defaults to a value of 1 .2 5 (for example) as
new rec ords are added to the table.

H owever, a s etting is available in whic h you c an indic ate a


default value. T he field in the third row of the table being
des igned in Figure 2 - 2 3 has been manually s et to 1 .2 5 , and this
bec omes the default value for the field.
Figure 2-23. Setting a default field value
2.7.2. The Code

What if 1 0 0 other fields need to be s et to s uc h a default value?


M y fingers hurt jus t thinking about the manual entry that would
be involved! A utomating this tas k will be a lifes averwell, at leas t
a finger s aver. A little c ode to the res c ue!

Sub change_field_defaults()
Dim cat As ADOX.Catalog
Set cat = New ADOX.Catalog
Dim fld As ADOX.Column
cat.ActiveConnection = CurrentProject.Connection
For Each fld In cat.Tables("myNewTable").Columns
If fld.Type = adSingle Then
fld.Properties("Default").Value = 1.25
End If
Next
Set cat = Nothing
End Sub

T his c ode us es the A D O X library [Hack #17] to work with the


fields in the des ignated table. I n this example, the table name is
hardc oded, but it c ertainly c an be pas s ed as an argument. T his
c ode example c yc les through the fields , and when a field type is
des ignated as Single (indic ated by the adSingle c ons tant), its
default value is s et to 1 .2 5 .
You c an expand this c ode routine to s et default values for all
pos s ible field types . E ven more, you c an s et default values for
c ombinations of field types and field names . For example, a field
named loc al_rate c an be a s ingle field type, whic h you c an s et it
to a default value of .2 5 ; likewis e, you c ab s et a field named
national_rate, als o a Single datatype, to have a default value of
.5 .
3. Entry and Navigation
3.1. Hacks 1927
A n applic ation's s uc c es s often res ts on us er ac c eptanc e. With
this in mind, it makes s ens e to plan how to make your
applic ation's front- end experienc e as vis ually pleas ing and
us er- friendly as pos s ible.

Sometimes us er experienc e is overlooked. D evelopers c an


s pend oodles of time des igning fields and tables , s etting up
relations hips , writing tric ky SQ L , and s o on. D oes this mean
anything to the typic al us er? N ot one whit!

L et's fac e it. A c c es s is more than jus t a databas e. I t is a


databas e with built- in front- end tools . Tables are the c ore of a
databas e, but the ability to c reate forms and reports is the main
job of a development platform. A c c es s has both, s o let's make
the bes t of both.

T he hac ks in this c hapter have been drummed up with the


ordinary us er in mind. E ntering data is a major us er ac tivity, and
thes e hac ks make this often mind- numbing ac tivity a little more
pleas ant.
Hack 19. Help Users Navigate Through
Long Forms

Use Page Break controls and command buttons so that users


won't have to scroll through long data-entry f orms.

I nformation is wonderful. T he more you know, the more you c an


do and plan forunles s you are the one s tuc k entering the data.
T hen, all you c an plan on is a lot of typing and mous ing around.

Figure 3 - 2 s hows an entry form, and a rather long one at that.


T his entry form c ontains more entry fields than c an reas onably
fit ons c reen, as evidenc ed by the s c rollbar on the right of the
form. A nyone us ing this form will need to s c roll or tab through to
the fields on the bottom.

Figure 3-1. A form that takes a lot of entries


A tab c ontrol is great for managing a lot of
c ontrols on a form. H owever, this hac k is
bas ed on a real- life s ituation. I onc e
worked on a projec t in whic h data- entry
operators were entering information from
legal- s ize forms . T he entry s c reen had to
matc h the layout of the form.

T he P age U p and P age D own keys on the keyboard make it eas y


to s c roll up and down through the form, but you c an't c ontrol how
muc h s c rolling will oc c ur. T he odds that pres s ing P age U p or
P age D own will leave the form right where you need it are indeed
s mall.

L uc kily, you c an get this to work properly. A ll you need to do is


add Page Break controls to the form. What are P age Break
c ontrols , you as k? Figure 3 - 2 s hows the Toolbox with the P age
Break c ontrol pointed out. By plac ing page breaks s trategic ally
in the form des ign, you c an get the P age U p and P age D own keys
to s c roll the form right to where you need it.

Figure 3-2. The Page Break control on the


Toolbox
Figure 3 - 3 s hows the form in D es ign mode. A P age Break c ontrol
has been plac ed direc tly above the P ers onal I nformation
s ec tion. T his is a rather unobtrus ive c ontrol. I t s imply appears
as a s tring of dots in D es ign mode, and in V iew mode, you don't
even s ee the c ontrol.

Figure 3-3. Adding Page Break controls


N ow, when you us e the P age D own key you will s c roll the form
direc tly to where the P age Break c ontrol res ides . Figure 3 - 4
s hows how the form eas ily s c rolls to the P ers onal I nformation
s ec tion.

Figure 3-4. Smart scrolling


3.2.1. Smart Navigation

U s ing the P age U p and P age D own keys is an adequate way to


s c roll a form, but we c an do better. I magine this : a form has
s everal s egregated data areas , and you need to ac c es s them in
random order. H aving to pres s P age U p or P age D own s everal
times to get the form to where you need it is a lot of work.

To avoid this problem, you c an plac e a s eries of c ommand


buttons in the form header (or form footer). T he header and
footer are always vis ible bec aus e thes e s ec tions are exempt
from s c rolling. Figure 3 - 5 s hows the form in D es ign mode, with
c ommand buttons in the header that will let us ers s c roll to where
they want.

Figure 3-5. Command buttons to facilitate form


navigation
3.2.2. The Code

T he tric k is for eac h button to s imulate P age U p and P age D own


keys trokes . T he following c ode us es the SendKeys s tatement to
ac c omplis h this . T he four new buttons on the form eac h initiate a
s eries of SendKeys s tatements :

Private Sub cmdWorkInfo_Click()


'
' 4 SendKeys up, 1 SendKeys down
'
navigate_form 4, 1
End Sub

Private Sub cmdPersonalInfo_Click()


'
' 4 SendKeys up, 2 SendKeys down
'
navigate_form 4, 2
End Sub
Private Sub cmdContactDetails_Click( )
'
' 4 SendKeys up, 3 SendKeys down
'
navigate_form 4, 3
End Sub

Private Sub cmdOrders_Click( )


'
' 4 SendKeys up, 4 SendKeys down
'
navigate_form 4, 4
End Sub

Sub navigate_form(u As Integer, d As Integer)


For form_up = 1 To u
SendKeys "{PGUP}"
Next form_up
For form_down = 1 To d
SendKeys "{PGDN}"
Next form_down
End Sub

E ac h Click event s ends the number of P age U p and P age D own


keys trokes that are required to have the form s c roll to the
des ired area. T hes e s imulated keys trokes ac c es s the P age
Break c ontrols plac ed earlier on the form. When a button is
c lic ked, the routine immediately s ends four P age U p keys trokes .
T he number of keys trokes is bas ed on this partic ular example.
T he point is to get to the top of the form. Four P age U p
keys trokes are needed only if the form is c urrently at the bottom
page break, but us ing four regardles s of the form's loc ation
does n't pres ent a problem.

T he c ode then s imulates the c orrec t number of P age D own


keys trokes . T he number differs for eac h buttonfor eac h area
being s c rolled to. I n other words , the Work I nfo button has one
P age D own, the P ers onal I nfo button has two P age D owns , and
s o forth.

With this approac h, any area of a long form is jus t a s ingle c lic k
away, and it is n't nec es s ary for us ers to go through the entire
form.
Hack 20. Help Users Enter Additional
Text

Place the insertion point at the end of the text in a text box so
that additional entries land just where they should.

T his tec hnique makes s o muc h s ens e, and yet it is often


overlooked. H ave you ever notic ed that when you're editing data
in a form, and you tab into a text box, the entire text is s elec ted?
U nfortunately, this default behavior makes the data vulnerable to
ac c idental overwriting. Figure 3 - 6 s hows the addres s text box
fully s elec ted. A s s uming an edit is needed to add additional text
(not to replace the text), the us er mus t move his mous e to the
end of the text and then c lic k to des elec t it.

Figure 3-6. Automatically selected data,


vulnerable to an accidental delete or overwrite
Wouldn't it be nic e if the us er didn't have to c lic k firs t to
des elec t the text? O f c ours e, there is a way to do this . I t takes
jus t a s mattering of c ode.

M any c ontrols , inc luding text boxes , have an Enter event, whic h
is triggered when the c ontrol is c lic ked or tabbed into. T his is
the event in whic h you c an plac e c ode to move the c urs or to the
end of the text, before the us er has a c hanc e to enter any
keys trokes . T he following c ode s nippet is bas ed on the c ontrol
being named CompanyAddress1:

Private Sub CompanyAddress1_Enter()


Dim text_length As Integer
text_length = Len(Me.CompanyAddress1)
Me.CompanyAddress1.SelStart = text_length
End Sub

T he length of the text is determined with the Len func tion, and
then the SelStart property is s et to the length. I t's that s imple.

You c an add a routine s uc h as this to all the text boxes in a form,


if it makes s ens e to do s o. I s data us ually overwritten, or does it
rec eive additional c harac ters ? O nly you know your applic ation,
s o only you c an dec ide where to inc orporate this c ode. I n this
example, s ome additional information has been added to the
addres s , as s hown in Figure 3 - 7 .

Figure 3-7. Information added, but not used to


replace the existing text
T he SelStart property has two related members : SelLength and
SelText. Singly or in c ombination, three text s elec tion properties
give you fine c ontrol over handling text in a text box or c ombo
box.

So far, this hac k has s hown you how to us e SelStart to s et where


the entry will begin. But onc e I had to provide a us er with an
eas y way to revers e las t name/firs t name to firs t name/las t
name for a s et of rec ords . N ames often inc lude initials , middle
names , and s o forth. I f you've ever written a name- pars ing
routine, you know how diffic ult it is to get the routine to handle
all the variations found in names .

T his was a one- s hot deal, s o it didn't make s ens e for me to


c reate a long, drawn- out routine. I ns tead, I us ed the s elec tion
properties , and I left s ome of the work up to the us er. H ere's how
it worked.

I pres ented the c ontac t names that had to be revers ed in a form,


s uc h as that s hown in Figure 3 - 8 .

Figure 3-8. The names that needed to be


reversed
T he us er s imply s elec ted the firs t nameor firs t name and initial,
or firs t name and middle name, and s o onand then pres s ed either
the Tabor the E nter key, as s hown in Figure 3 - 9 .

T hat's all it took! I t worked by implementing the s elec tion


properties on the text box's Exit event. I n this example, the text
box is named Contact. T he routine explained earlier, whic h us es
the Enter event, is als o us ed in this example:

Private Sub Contact_Enter()


'
'remove selection effect
'and place insertion point at end
'
Dim text_length As Integer
text_length = Len(Contact)
Contact.SelStart = text_length
End Sub

Private Sub Contact_Exit(Cancel As Integer)


'
'if there is selected text and the selection
'is less than the full text size then
'if the selection starts past the first position then
'move the selected text to the front
'
Dim new_text As String
If Contact.SelLength > 0 And _
Contact.SelLength < Len(Contact) Then
If Contact.SelStart > 1 Then
new_text = Contact.SelText & " " & _
Left(Contact, Len(Contact) - Contact.S
Contact.Text = Trim(new_text)
End If
End If
End Sub

Figure 3-9. A first name selected


For c onvenienc e, the Enter event makes s ure the text is n't
s elec ted at the beginning. T he us er s elec ts the firs t name, and
middle initial or middle name, if pres ent, and then either tabs out
of the text box or jus t pres s es the E nter key. T his fires the Exit
event, whic h tes ts the text to make s ure s omething has been
s elec ted, it is n't the s ame length as the entire text, and it
does n't s tart at the beginning.

T he routine then revers es the s elec ted and uns elec ted portions
(the firs t name and the las t name) and returns them to the text
box. Figure 3 - 1 0 s hows the res ult.

Figure 3-10. All the names reversed


T he routine us es all three s elec tion properties : SelStart,
SelLength, and SelText. T hes e properties give you all you need to
work with s elec ted portions of text. When you c ompare them to
the equivalent text properties and methods Text, Len, Left,
Right, Mid, and s o onyou c an s ee that they give you quite a bit
of c ontrol when manipulating text.
Hack 21. Let Users Add Custom Items to
Predesigned Lists

A void f orcing choices to existing list items only by adding a


procedure to handle new values.

U s ers often c hoos e items from an exis ting lis t via a c ombo box
on a form. Sometimes , however, a us er might need to enter into
the c ombo box a value that is n't on the lis t. T his c an happen, for
example, when the us er is working with a new c us tomer that he
has not yet appended to a c us tomer table, or when the us er is
c orrec ting an exis ting lis t item that is mis s pelled.

T he Limit To List property c ontrols whether a new value is


allowed entry in a c ombo box. I f this property is s et to No, us ers
c an enter new values into the c ombo box. T his is fine, but with
one c aveat: if the new value is meant to bec ome a permanent
member of the lis t, jus t typing it into the c ombo box does n't add
it to the lis t.

I f it makes s ens e for your applic ation to let us ers permanently


add values to a c ombo box's s ourc e lis t, you need to us e a
different tec hnique. Firs t, s et the Limit To List property to Yes.
(Yes , this means the new item won't be allowed, but read on! )
T he tric k to this hac k is to implement inc lus ion of the new item
by tapping the On Not In List event, whic h works only when the
Limit To List property is s et to Yes.

Figure 3 - 1 1 s hows a form in D es ign mode. T he form has a


c ombo box on it, and the property s heet s hows the properties for
the c ombo box, with the Limit To List property s et to Yes .

When a us er attempts to add a new value to the c ombo box, the


On Not In List event fires . Within this event, a c ode routine
handles adding the new value to the lis t.

3.4.1. The Code

T he c ode is s imple and s traightforward. Two arguments are


provided to the routine: NewData and Response. T he event s tub
c omes predes igned with thes e arguments , s o you don't have to
c reate them:

Private Sub cmbCustomers_NotInList(NewData As String, _


Response As Integer)
Dim ctl As Control
Set ctl = Me.cmbCustomers
Response = acDataErrAdded
ctl.RowSource = ctl.RowSource & ";" & NewData
End Sub

Figure 3-11. The Limit To List property set to


Yes
T he Response argument tells A c c es s to override the behavior of
not allowing a value to be added. T he developer does this by
s etting the Response to the adDataErrAdded c ons tant. T he new data
(s upplied by the NewData argument) is then added to the Row
Source.
3.4.2. Hacking the Hack

So far, this hac k works on the premis e that the Row Source Type is
s et to a Value List. I f the Row Source Type is Table/Query, you
need an append routine to plac e the new value in the underlying
data s tore. I n this c as e, the Not In List event appends the new
value to the s ourc e table.

H ere is an example of how to c ode the routine. T his example


as s umes a s ourc e table named tblShippingM ethods with a field
named Shipping- M ethod:

Private Sub cmbShippingMethods_NotInList(NewData As String


Response As Integer)
Dim new_data As String
Dim conn As ADODB.Connection
Set conn = CurrentProject.Connection
'double up any apostrophes before Insert
new_data = Replace(NewData, "'", "''")
Response = acDataErrAdded
conn.Execute "Insert Into " & _
"tblShippingMethods(ShippingMethod) Values('" & _
new_data & "')"
End Sub
Hack 22. Populate and Sort Lists with
Flair

Use these three clever techniques to populate and sort listbox


controls.

L is ts are integral to form des ign. True, not all forms need a lis t,
but when they're applic able, s elec ting an item from a lis t is muc h
eas ier than typing in the value. T his als o makes it eas ier to
avoid typos .

T his hac k pres ents three ways to populate and s ort lis tbox
c ontrols . I n eac h example, the underlying tables and s truc ture
are key elements . T he examples s how how to s ort alphabetic ally,
but from two s ourc es ; how to s ort bas ed on a key value; how to
s ort on plac ement in the SQ L s tatement; and even how to s ort
by trac king the popularity of the lis t items thems elves ! T he SQ L
Union c laus e is a key fac tor to getting muc h of this to happen.

3.5.1. The Form

Figure 3 - 1 2 s hows a form with three lis ts , aptly named L is t 1 ,


L is t 2 , and L is t 3 .

Figure 3-12. Three list controls on a form


Behind the s c enes , two tables populate the lis t c ontrols :
tblFruits and tblVegetables , s hown in Figure 3 - 1 3 . N ote that
they s hare two c ommon fields : SortN umber and L is tI tem. T his
c ommon s truc ture is put to good us e, as you will s ee s oon.

Figure 3-13. Two tables used to populate the list


controls

3.5.2. Populating a Listbox Alphabetically


from Two Sources
L is t 1 dis plays the values from the two tables , s orted
alphabetic ally as one larger lis t. T he tric k is to have the List
c ontrol us e the rec ords of both tables in its Row Source property.
You do this by c ombining the rec ords of both tables in a Union
query. Figure 3 - 1 4 s hows the form in D es ign mode with the
property s heet s et to L is t 1 .

Figure 3-14. The Row Source property for List 1


T he SQ L s tatement in the Row Source property reads like this :

Select ListItem from tblFruits UNION Select ListItem from


T he Union c laus e allows the values from the two tables to be
c ombined, given that the s truc ture and datatype are the s ame.
I n other words , the L is tI tem field from eac h table is addres s ed
with the SQ L s tatement. Q uerying the s ame number of fields in
eac h Select s tatement with the Union query is a requirement. T he
query c an't run if the number of fields being ac c es s ed from eac h
table differs .

A s a res ult the c ombined rec ords are s orted as if they really
c ome from one s ourc e (whic h tec hnic ally is true via the Union
query). T herefore, the dis tinc tion of fruits and vegetables is
purpos ely los t, and ins tead, as paragus follows apple, broc c oli
follows banana, and s o on.

T his tec hnique is us eful when you need to pres ent items in a lis t
that c ome from more than one s ourc e. A s dis c us s ed in the
following s ec tion, you c an bring together as many s ourc es as
you need with multiple Union c laus es .

3.5.3. Controlling the Sort in a Listbox


Populated from Multiple Sources

I n Figure 3 - 1 2 , L is t 2 s hows the res ult of s orting fruits in a


c ertain order and vegetables in a c ertain order. A dditionally, the
fruits and vegetables aren't s orted with eac h other. T he lis t als o
inc ludes s eparators and values not found in the s ourc e tables :
All, All Fruits, and All Vegetables. H ow did all thes e items get
into the lis t?

A Union query populates the lis tbox. T he two s ourc es tblFruits


and tblVegetables are us ed, but ins tead of letting the lis t mix and
s ort the items alphabetic ally, the SortN umber field c ontrols the
s ort.

A key point here is that the range of values for SortN umber in
the tblFruits table is different from the range of values for
SortN umber in the tblVegetables table. T he Union operation
ac tually does c ombine both s ourc es into one s ort, but the
SortN umber field ranges keep the two lis ts apart in the lis tbox.

Figure 3 - 1 5 s hows the form in D es ign mode with the property


s heet s et to L is t 2 . T he SQ L s tatement that s erves as the Row
Source property is dis played in the Zoom box.

H ere is the SQ L s tatement:

Select "All" as a, -2 as SortNumber from tblFruits


Union Select "---" as a, -1 as SortNumber from tblFruits
Union Select "All Fruits" as a, 0 as SortNumber from tblFruits
Union Select ListItem, SortNumber From tblFruits
Union Select "---" as a, 99 as SortNumber from tblVegetables
Union Select "All Vegetables" as a,
100 as SortNumber from tblVegetables
Union Select ListItem, SortNumber From tblVegetables
Order By SortNumber

Figure 3-15. The Row Source property for List 2


Q uite a bit is going on here. O verall, the SQ L c ombines items
from the s ourc e tables with items provided right within the SQ L .
A ll thes e tie together via the SortN umber field.

T his SQ L s tatement us es the Union c laus e s everal times , to


make s ure that all Select s tatements point to the s ame number
of fields . I n this example, that number is 2 .

T he SQ L s tarts by getting the word All to the top of the lis t. T his
s nippet forc es the word All into the lis t:

Select "All" as a, -2 as SortNumber

T he c ode s nippet does this by giving the word All the lowes t
value of SortNumberin this c as e, - 2 . To be c lear, neither the word
All nor the value - 2 ac tually c omes from an underlying table.
H owever, their plac ement in the SQ L follows the s truc ture of all
the other Select s tatements in the SQ L , whic h allows them to be
c ombined with the other values being ac c es s ed by the SQ L .

T he SQ L us es Union to c ombine values from the tables with


thes e on- the- fly values . A number of thes e values are in the
SQ L :

Select "All" as a, -2 as SortNumber from tblFruits


Select "---" as a, -1 as SortNumber from tblFruits
Select "All Fruits" as a, 0 as SortNumber from tblFruits
Select "---" as a, 99 as SortNumber from tblVegetables
Select "All Vegetables" as a, 100 as SortNumber from tblVegeta

A ll thes e parts of the SQ L forc e the lis t to pres ent a value: All,
All Fruits, All Vegetables, or - - - . N one of thes e values c omes
from the tables . H owever, all of them are paired with a s ort
number, and this is what plac es them in their s equential plac e in
the lis tbox.

C ons ider the s ort numbers as s oc iated with thes e on- the- fly
items , while c ons idering the s ort numbers of the items in the
tables (s ee Figure 3 - 1 3 ). Sort numbers for the vegetables s tart
at 1 0 1 . T herefore, the All Vegetables item has been as s oc iated
with the number 1 0 0 . T his forc es it to appear in the lis t direc tly
above the ac tual vegetables .

Keep in mind that a lis tbox s uc h as this , with s everal pos s ible
items a us er c an s elec t, als o requires a related level of
func tionality to handle the us er's s elec tion. I f a us er s elec ts a
s ingle fruit or vegetable, c hanc es are the applic ation will
c ontinue proc es s ing. H owever, what if a us er s elec ts All Fruits?
Your proc es s ing will need to handle all the values in the tblFruits
table.

A ls o note that you enter the s eparator c harac ters (- - - ) into the
lis t for the s ake of s egregating parts of the lengthy lis t of items .
T his is rather pleas ing for s omeone s c rolling through a long lis t;
however, a us er c an s elec t the s eparators ! T herefore, you need
to ens ure that us er validation and feedbac k are in plac e in c as e
this happens . Typic ally, if a us er s elec ts the s eparator
c harac ters , a mes s age s hould appear alerting him to make
another s elec tion.

3.5.4. Sorting List Items by Popularity

I t's not always eas y to know ahead of time whic h items us ers
will s elec t mos t often from a lis t. You c an us e a Sort N umber
field to arrange lis t items in a way that s eems bes t, but there is
an even better way to do this .
Why not let us er ac tions drive the way the lis t is s orted?
Keeping in mind that it is eas y to s ort a lis t by a numeric al field,
logic dic tates that the values in the numeric al field s hould
reflec t the popularity of the lis t items .

T his is eas y to do by updating a lis t's Sort field eac h time it is


s elec ted. Figure 3 - 1 6 s hows the form in D es ign mode with the
property s heet s et to L is t 3 .

H ere's the Row Source SQ L s tatement for L is t 3 :

SELECT Occurrence, ListItem FROM tblFruits ORDER BY Occurrenc

T his lis tbox us es the tblFruits table exc lus ively. T his table has
the additional O c c urrenc e field, whic h drives the way items are
s orted in the lis tbox. N ote from the Row Source property that
items are lis ted bas ed on the O c c urrenc e field values being in
des c ending order.

Figure 3-16. The Row Source property for List 3


To make s ens e of this , it is nec es s ary to s omehow update the
values in the O c c urrenc e field. T his update oc c urs when you
proc es s the s elec ted lis t valuein whatever way your proc es s ing
works . For the purpos e of this demons tration, a button has been
plac ed on the form. H ere's the Click event for the button:

Private Sub cmdUpdateCount_Click()


'get the current count for this item
Dim selected_item_count As Integer
If Not IsNull(Me.List3) Then
selected_item_count = _
DLookup("Occurrence", "tblFruits", "ListItem='" & Me.Li
'increase the count and update the table
selected_item_count = selected_item_count + 1
DoCmd.SetWarnings False
DoCmd.RunSQL ("Update tblFruits Set Occurrence=" &
selected_item_count & " Where ListItem='" & Me.
Me.List3.Requery
End If
End Sub

I n a nuts hell, the DLookup func tion finds the c urrent value of the
O c c urrenc e field for the s elec ted item and s tores it in the
selected_item_count variable. T he value is inc remented by 1 , and
a SQ L Update s tatement writes the value bac k into the table, for
the given item. Finally, the lis t is refres hed s o that on the form
the lis t will res ort.

A s a res ult, when items in the lis t are s elec ted and proc es s ed,
they float to the top of the lis t. You c an s ee this by c omparing
the plac ement of items in L is t 3 in Figure 3 - 1 2 with the values of
the O c c urrenc e field in the tblFruits table in Figure 3 - 1 3 . For
example, ras pberry is the firs t item in L is t 3 bec aus e it has the
highes t value in the O c c urrenc e field.
Hack 23. Use Custom Controls on Your
Forms

Move past standard A ccess controls, and discover new design


possibilities.

I f you develop enough A c c es s applic ations , you begin to take


the Toolbox for granted. You know all the c ontrols and when to
us e them. But did you ever notic e the Toolbox has a button that
leads to more c ontrols ? Figure 3 - 1 7 s hows where this button is
loc ated.

Figure 3-17. Finding additional controls


C lic king the M ore C ontrols button opens a lis t of new c ontrols
from whic h to s elec t. I don't s ugges t tes ting all of them bec aus e
I think that s ome aren't us eful in A c c es s . H owever, if you s c roll
to the M ic ros oft c ontrols , you might find s ome interes ting ones
to try on a form. L et's s ee how we c an us e a few of thes e c us tom
c ontrols .

3.6.1. Adding a Custom Control to a Form

T he lis t of c ontrols will probably differ between c omputer


s ys tems , but it's a s afe bet that you have the M ic ros oft Forms
c ontrols loaded on your c omputer bec aus e they are ins talled
with M ic ros oft O ffic e.

A s an example, I plac ed a M ic ros oft Forms 2 .0 s pinbutton


c ontrol on a form, as s hown in Figure 3 - 1 8 . I did this by s imply
s elec ting it from the long c ontrol lis t and then drawing on the
form us ing the mous e.

Figure 3-18. A spinbutton control on a form


T he s pinbutton c ontrol has minimum and maximum value
properties that you c an s et. T herefore, you c an us e the s pinner
to c yc le through from 1 1 0 0 , 1 2 8 1 3 3 , or whatever makes s ens e
for your applic ation. You c an ac c es s the s pinner's value from
c ode in the s ame way you do for other c ontrols .

N ext, I put the M ic ros oft D ate and T ime P ic ker C ontrol 6 .0 on
the form, as s hown in Figure 3 - 1 9 . T his nifty c ontrol s tays in a
c ollaps ed s tate until you c lic k it. I t then opens to a s c rollable
c alendar. T his is a great us er enhanc ement; us ers don't have to
enter dates manually when you put s uc h a c ontrol as this on a
form. A nd the good thing about this c ontrol is that it remains
s mall when it's not in us e.

Figure 3-19. Making it easy to select a date


I t's us eful to bec ome familiar with a few of the new c ontrols and
to think of them as part of your c ontrol toolbox. T his opens the
door to new ways to des ign forms , and it provides us ers with an
enhanc ed experienc e.
3.6.2. See Also

"P lay V ideos in A c c es s Forms " [Hack #34]

"U s e a Brows er I ns ide A c c es s " [Hack #97]


Hack 24. Confirm Record Updates Before
Saving

Give users a chance to review their edits bef ore they save a
record.

When you're working on a bound form, as you s c roll through


rec ords , data c hanges are s aved automatic ally. T his behavior is
normal and is often apprec iated rather than ques tioned. H owever,
s ometimes it is prudent to interrupt this proc es s and let a us er
review her work. O nc e the update happens , the original data is
gone, unles s other meas ures , s uc h as bac kups , are in plac e.

O ne thing that works in our favor to c ontrol this is the Before


Update event. By hooking into this event, you c an as k the us er
whether s he wants to c omplete the update. I f the ans wer is no,
you undo the c hanges .

U s ers s hould c ontrol whether they want to be prompted to


c onfirm c hanges bec aus e the prompts c an bec ome annoying. A
us er might want this feature s ometimes but not other times .
Figure 3 - 2 0 s hows a form with a c hec kbox in the upper- right
s ec tion that ac ts as a flag indic ating whether to c onfirm updates .

Figure 3-20. A checkbox to indicate whether to


confirm updates
T he Before Update event fires only when the data c hanges . I n the
event, the c hec kbox value is tes ted, and if the value is true, the
us er is prompted. Figure 3 - 2 1 s hows the prompt.

I f the us er c lic ks Yes , the update proc eeds . I f s he c lic ks N o, an


undo c ommand runs , thereby dropping the c hanges to the data.
H ere is the event and the c ode:

Private Sub Form_BeforeUpdate(Cancel As Integer)


If Me.chkConfirm = True Then
proceed = MsgBox("Do you want to save the changes?", vbYes
"Save Changes")
If proceed = vbNo Then
DoCmd.RunCommand acCmdUndo
End If
End If
End Sub

A key point to this hac k is letting the us er dec ide whether to be


prompted. Being as ked to c onfirm endles s c hanges will quic kly
bec ome a s ourc e of frus tration. T he nic e thing is that us ers c an
dec ide to turn on the feature when updating c ritic al information,
s uc h as names and addres s es , but turn off the feature when
making c hanges to les s important data.

Figure 3-21. Confirming an update


Hack 25. Put a Clock on a Form

Give users the time and date, even f or more than one time zone.

By c ombining a form's On Timer event, its timer interval, and the


s ys tem c loc k, you c an plac e a func tional c loc k on a form.

Figure 3 - 2 2 s hows a form with a c loc k in the header. I ts


plac ement in the header, and not in the details s ec tion, makes
s ens e bec aus e it is unbound and is n't s pec ific to any rec ord in
the detail.

You c an go about your bus ines s moving through rec ords , editing
data, and s o on; the time will jus t keep tic king away undis turbed.

3.8.1. Creating the Clock

T he c loc k is eas y to c reate. Firs t, plac e a label c ontrol on the


form. T hen, s et the form's timer interval to 1 ,0 0 0 (whic h equals
one s ec ond). Finally, plac e a s ingle line of c ode in the form's On
Timer event:

Me.lblClock.Caption = Format(Now( ), "hh:mm:ss AMPM")


Figure 3-22. A form that tells the time

T his as s umes the label c ontrol is named lblClock. T he Now


func tion returns the s ys tem time, and the Format func tion gives
the time a des irable look. T he format is n't nec es s ary, though. I f
you don't us e it, the full date and time is returned. T he format,
as applied here, dis plays jus t the time; hh:mm:ss is a format for
hours (hh), minutes (mm), and s ec onds (ss).
3.8.2. Hacking the Hack

You c an do a lot to boos t the c loc k's appeal and func tionality.
O ne idea is to dis play the time for c ities in different time zones .
Figure 3 - 2 3 s hows a form with two c loc ks . O ne dis plays the time
in N ew York, and the other dis plays the time in C hic ago.

Figure 3-23. Two clocks on a form


C hic ago is one hour behind N ew York. You ac c ount for the one-
hour differenc e by applying the DateAdd func tion. H ere is the
updated On Timer event:

Me.lblClockNewYork.Caption = Format(Now( ), "hh:mm:ss AMPM


Me.lblClockChicago.Caption = Format(DateAdd("h", -1, Now( )),
"hh:mm:ss AMPM")

DateAdd c an add or s ubtrac t time. I n this c as e, a value of - 1


s ubtrac ts one hour.

H ere's another idea: give the us er a way to c hange the format.


You ac c omplis h this by us ing a public variable and the label
c ontrol's DblClick event. When the form is opened, a public
variable, named format_type in this example, is given a value of
1 . E ac h time a us er double- c lic ks the c loc k, the format_type
variable inc rements . When it hits 4 , it is s et bac k to 1 . T he On
Timer event tes ts the format_type variable and applies the
partic ular format. H ere is the full c ode behind the form that takes
c are of this :

Option Compare Database


Public format_type As String

Private Sub Form_Open(Cancel As Integer)


format_type = 1
End Sub

Private Sub Form_Timer( )


Select Case format_type
Case 1
Me.lblClock.Caption = Format(Now( ), "hh:mm:ss AMPM")
Case 2
Me.lblClock.Caption = Format(Now( ), "hh:mm AMPM")
Case Else
Me.lblClock.Caption = Format(Now( ), "mm/dd hh:mm AMPM")
End Select
End Sub

Private Sub lblClock_DblClick(Cancel As Integer)


format_type = format_type + 1
If format_type = 4 Then format_type = 1
End Sub

N ow, a us er c an double- c lic k the c loc k until it dis plays the date
and time in a way that s uits him. O f c ours e, you c an eas ily
inc reas e the number of available formats .
Hack 26. Be Tab-Smart

Override a f orm's tab order so that users get to the entry boxes
they need.

Setting the tab order is probably not the bigges t thing on your
mind when you des ign a form. I t does n't really require muc h
planning or analys is .

Typic ally, you do it at the end, after all the c ontrols are in plac e.
To be hones t, at times I 've s imply forgotten to s et a tab order on
my forms . But, of c ours e, the us er c ommunity lets me know when
that happens .

So, I s tarted thinking that if tab s tops on a form are really


important to us ers , why not go one s tep better and make the tab
order work more intelligently. O n s ome forms only s ome boxes
rec eive entries . A s mart way to tab through a form in this
s ituation is to s ens e whic h entry boxes c an be s kipped.

T his makes s ens e when the c ondition or entry into a text box is
bas ed on s ome unders tandable logic flow. For example, if you're
entering information about a new c us tomer, us ually you fill in
every field. H owever, when you're entering a piec e of information
on an exis ting c us tomer, mos t likely you tab over s everal text
boxes .

Figure 3 - 2 4 s hows a form in whic h a s elec tion has been made


from a c ombo box. T he c ombo box, named Status , has four
c hoic es : E nrolled, Walk- I n, P hone, and Renewal.
I n the Exit event of the c ombo box, a Select Case s tatement s ets
the foc us to different form c ontrols , depending on the s elec tion
made in the c ombo box. For example, a s tatus of Walk- I n is us ed
for unregis tered people who c ome in without an appointment.
T hey need to be s et up in the s ys tem, s o the next entry box to
go to is the Referral Sourc e. By c ontras t, when an E nrolled
pers on c omes in, the next box to enter is C omments . H ere is the
c ode in the Exit event:

Private Sub status_Exit(Cancel As Integer)


Select Case status
Case "Enrolled"
Me.txtComments.SetFocus
Case "Walk-In"
Me.txtReferralSource.SetFocus
Case "Renewal"
Me.txtStudentFirstName.SetFocus
End Select
End Sub

T he gis t of this approac h is that you c an apply rules to the tab


order bas ed on what makes s ens e to the bus ines s . A s another
example, a databas e s ys tem at a c ar dealers hip s hould s kip
right over a s ec tion about what type of new c ar a c us tomer wants
to purc has e if s he is in the market for a us ed c ar. I f the s ec tion
about new c ars c ontains 1 2 fields , the s ales pers on needs to tab
through 1 2 unneeded boxes , unles s a better method is in plac e.
I ns tead, s ome other c ontrol on the form c an ac t as a flag s o that
you c an implement s mart tabbing. U s ing the Exit events of
c ontrols is a great way to s upport this bus ines s workflow.

Figure 3-24. A combo box selection to drive the


tab order
Hack 27. Highlight the Active Control

Provide a visual aid on a busy f orm by emphasizing the control


that has the f ocus.

A s you tab through a form and enter data, it is pos s ible to get
los t. T he more c ontrols on a form, the more likely this is to
oc c ur. T his hac k s hows you how to keep the c urrent entry box
highlighted.

"Be Tab- Smart" [Hack #26] s hows a way in whic h us ers c an tab
around the form in a s ens ible manner. T hat hac k works with a
little c ode put into a c ontrol's Exit event. T his hac k us es the
Exit event, as well as the Enter event. I n this hac k, all c ontrols
that ac c ept entry have c ode in both events .

Figure 3 - 2 5 s hows the entry form with a border around the Firs t
N ame entry box. A s a us er tabs through the form or c lic ks
s pec ific c ontrols , the border will always appear around the ac tive
c ontrol. T he border is dis played on only one c ontrol at a time.

Figure 3-25. A border appearing around the


active control
To get this to work, the c ontrols on the form mus t have their
Special Effect property s et to Flat. You c an us e other s ettings
that allow borders to be dis played, s uc h as Shadow, but Flat
s eems to work bes t. When Shadow is us ed, and when the c ontrol
los es the border, the c ontrol s till retains the s hadow part of the
border, and that does n't look right.

Figure 3 - 2 6 s hows the c ode module behind the form. Two


s ubroutines are at the top: active_control_enterand
active_control_exit. T hes e s ubroutines run when they're c alled
by eac h c ontrol's Enter and Exit events . T he active_
control_enter s ubroutine s ets the ac tive c ontrol, whatever it is at
the time, to have a thic k, red border. T he border s tyle value of 1
indic ates a s olid border. T he border width value of 2 indic ates a
thic k border. Red is then s et as the border c olor.

T he active_control_exit s ubroutine has jus t a s ingle line of c ode;


it s ets the border to trans parent.

Figure 3-26. The code for turning a border on


and off
E ac h time a c ontrol is entered, it means a different c ontrol was
jus t exited. T herefore, thes e two s ubroutines fire in a pair. T he
Exit routine turns off the border for one c ontrol, and the Enter
routine turns it on for another.

U s ing the tec hnique in this hac k ens ures that us ers c learly s ee
whic h c ontrol is ac tive.
4. Presentation

Sec tion 4 .1 . H ac ks 2 8 3 9

H ac k 2 8 . Separate A lphabetic ally Sorted Rec ords into


L etter G roups

H ac k 2 9 . C reate C onditional Subtotals

H ac k 3 0 . U s e C onditional Formatting to P oint O ut


I mportant Res ults

H ac k 3 1 . P rovide a D irec t L ink to a Report

H ac k 3 2 . P rotec t I ntellec tual P roperty

H ac k 3 3 . C reate a Slides how in A c c es s

H ac k 3 4 . P lay V ideos in A c c es s Forms

H ac k 3 5 . V iew Reports E mbedded in Forms

H ac k 3 6 . P ut L ine N umbers on a Report

H ac k 3 7 . Shade A lternating L ines on a Report

H ac k 3 8 . Save P aper by Reduc ing Whites pac e

H ac k 3 9 . I nc lude the D ate, T ime, and P age C ount


4.1. Hacks 2839
A s c omprehens ive as a databas e c an bewith programmatic
func tionality, s ophis tic ated queries , and other bells and
whis tles you s till need to be able to communicate fac ts about the
data it c ontains . T his is where forms and reports c ome into play.
A dec ent databas e front end is c ritic al for interac tion between
the databas e and us mere mortals . A c c es s , of c ours e, s hines in
this area.

T he report des igner in A c c es s is a feature- ric h development


platform. I t inc ludes formatting tools , grouping and s orting
options , a palette full of c ontrols , and the ability to hook into
events and mus ter up s ome c oolnes s with V BA . Forms als o have
events , formatting options , and properties galore. P utting them
all to us e is beyond the s c ope of a s ingle c hapter. So ins tead,
this c hapter highlights s ome exc iting ways to work with forms
and reports .

You've already s een how to us e forms to dis play databas e


rec ords . H ow about us ing a form to play a s lides how or movies ?
"C reate a Slides how in A c c es s " [Hack #33] and "P lay V ideos in
A c c es s Forms " [Hack #34] s how you how.

Firs t impres s ions are us ually the bes t. M ake s ure your reports
are eye- poppers . A number of hac ks in this c hapter explain how
to provide s ophis tic ated grouping and formatting in reports .
"P rovide a D irec t L ink to a Report" [Hack #31] s hows how a
bus y us er c an c lic k a s hortc ut to print a report without fus s ing
around with the databas e. "V iew Reports E mbedded in Forms "
[Hack #35] explains how to inc orporate reports into forms .
Hack 28. Separate Alphabetically Sorted
Records into Letter Groups

Tap the Pref ix Characters property to gain new layout


possibilities.

Sorting alphabetic ally is nothing new; in fac t, it's rather oldone


of the s tandard prac tic es we take for granted. When you've got
dozens or hundreds of printed rec ords , though, it c an be tedious
to flip through report pages looking for a partic ular line item,
even though they're in alphabetic al order.

A neat thing to do is to s egregate the rec ords on a report


alphabetic ally. Figure 4 - 1 s hows a page from a report in whic h
s orted rec ords lis t repeatedly with no s uc h s egregation or break.
T he rec ords are s ortedno ques tion on that s c orebut the layout
makes it c hallenging to flip to the approximate area you need to
find.

Figure 4-1. A report with a repetitive layout


T he report's des ign is s traightforward. T he details s ec tion
c ontains the fields that bec ome the line items . T he report in this
format does n't us e groups , and that is why it is monotonous to
look at. Figure 4 - 2 s hows the des ign of the report.

4.2.1. Segregating by Letter

A way to break up the endles s line- item lis ting is to add a group
to the report. Figure 4 - 3 s hows how the report's des ign has been
altered to inc lude a group.

T he group is bas ed on the C lientL as tN ame field, whic h, of


c ours e, is the field being s orted on. H ere are a few key points
about how this group is being us ed:

Figure 4-2. A report that doesn't use grouping


and sorting
Figure 4-3. A report that uses grouping and
sorting
T he group has a header. A footer is n't required. I n the
Sorting and G rouping dialog box, G roup H eader and
G roup Footer are s et to Yes and N o, res pec tively.

I n the Sorting and G rouping dialog box, the G roup On


property is s et to Prefix Characters, and the Group
Interval property is s et to 1.

I n the group header its elf, an unbound text box has been
ins erted, and its Control Source property is s et to an
expres s ion.

When the report runs , the expres s ion in the unbound text box
forc es the group break to oc c ur on the letters of the alphabet,
ins tead of on eac h oc c urrenc e of a las t name. A s a res ult, all the
A s are together, all the Bs are together, and s o on. You
ac c omplis h this by us ing the Left func tion to return the firs t
letter:

=Left([ClientLastName],1)

Figure 4 - 4 s hows how the report s egregates by letter.

Figure 4-4. Clients broken out by first letter


T he larger font, bold, and underline s ettings make the
dis tinc tions vis ually c lear when thumbing through a report.

4.2.2. Hacking the Hack

N ote that on the report page s hown in Figure 4 - 4 , none of the


c lients ' las t names s tart with the letter J . T he fac t that s ome
rec ords don't exis t c ould be vital news to s omeone. I c an jus t
hear the bos s yelling, "What happened to the J ohns on ac c ount? "
Suc h a reac tion is bas ed on expec ting to s ee s omething that
is n't there. T he flip s ide to this is that mis s ing rec ords might be
identified only by pointing out that no rec ords have met a
c ondition.

I n partic ular, it would be us eful if the report s tated that no


rec ords were found for the letter J . We need a way to s till dis play
the alphabetic letter on the report, but in the c urrent des ign, this
won't ever happen. A ny alphabetic letters that c urrently appear
on the report are there bec aus e rec ords in whic h the las t name
s tarts with the letter J do exis t.

To get all letters to appear on the report, regardles s of whether


rec ords beginning with thos e letters exis t, inc lude s omewhere in
the des ign a lis t of all the letters to be c ompared agains t. T he
approac h us ed here is to relate the c lient table with a table of
the letters , ins tead of bas ing the report on jus t the c lient table.

A table is added to the databas e with jus t one field: L etter. T he


table c ontains 2 6 rec ords , for the letters A through Z. Figure 4 -
5 s hows the table, named tblL etters .
Figure 4-5. A table filled with letters of the
alphabet
I t's not a bad idea to inc lude the digits 0 9 in the table as well,
es pec ially if you're working with the names of c ompanies .

T he report's Record Source property was previous ly s et to the


c lient table (tblC lients ). N ow, though, the report's rec ord s ourc e
will be bas ed on a query. H ere is the SQ L s tatement:

SELECT tblClients.ClientFirstName, tblClients.ClientLastName,


tblClients.ClientAddress1, tblClients.ClientCity, tblLetters.Lette
FROM tblClients RIGHT JOIN tblLetters ON
left(tblClients.ClientLastName,1) = tblLetters.Letter;

A key point about this s tatement is that a RIGHT JOIN is us ed to


relate the tables . T his ens ures that all rec ords from the letters
table (tblL etters ) will be pres ent. I n other words , every letter will
be available to the report, even when no las t names s tart with
that letter.

T he report's des ign als o needs a s light c hange. T he group is no


longer bas ed on the las t name; ins tead, it's bas ed on the L etter
field. A ls o, a new expres s ion is us ed in the unbound text box.
Figure 4 - 6 s hows thes e c hanges .

Figure 4-6. Grouping on the alphabet


T he expres s ion in the text box returns one of two pos s ible
s tatements . When at leas t one rec ord c ontains a las t name
s tarting with a given letter, the letter is dis played. When no
rec ords c ontain a las t name s tarting with the given letter, a
mes s age is dis played that no rec ords were found for that letter.
You ac c omplis h this us ing the IIF and Count func tions :

=IIf(Count([ClientLastName])>0,[Letter],"No records for " & [Lette

A s a res ult, this report has all the alphabetic al letters as group
headers , regardles s of whether any rec ords matc h, as s hown in
Figure 4 - 7 .

Figure 4-7. Reporting that no records exist


You c an adapt this hac k in a number of ways . For example, you
c an hide the details s ec tion, and you c an alter the expres s ion in
the header to print a line only when no rec ords exis t. T his alters
the report to lis t exc eptions only.
Hack 29. Create Conditional Subtotals

Split a grand total into pertinent business summaries using


running sums and expressions.

A c ommon reques t is to c reate two s ets of totals for c omparis on.


T his , by its elf, is reas onable in a report des ign; you c an s et a
group that is bas ed on a field that breaks on different values . A
perfec t example is data bas ed on a year. I f the report inc ludes a
Year field, you c an inc lude s ubtotals in the group footer. T hat is ,
you c an get a s ummary (of whatever other fields ) for eac h year.

But when you throw in the need to report totals on more than one
c ondition, things s tart to get a bit mes s y. You c an c reate two
groups , but you mus t dec ide whic h group nes ts ins ide the other.
T hat dec is ion is n't always c lear- c ut. A dded to this are various
layout options . I f you want to arrange thes e s ubtotals in any
fas hion other than underneath eac h other, you are out of luc kthat
is , unles s you us e running s ums and c alc ulated c ontrols .

Figure 4 - 8 s hows a report that dis plays grand totals for eac h
year and, underneath them, the yearly grand totals s eparated by
eac h s tate's c ontribution.

Figure 4-8. Grand totals and subtotals


O f c ours e, you c an c reate a report s uc h as
the one in Figure 4 - 8 us ing other methods ;
for ins tanc e, you c ould us e a s ubreport
ins tead. T he running s ums method
outlined in this hac k is only one method
available for reporting totals on more than
one c ondition.

T his hac k us es an example of a veterinary prac tic e, whic h has


data about vis its to the prac tic e over two years and c lients who
c ome from five different s tates . T he report's rec ord s ourc e is
bas ed on a Union query that c ombines two identic al Select
queries identic al, that is , exc ept that one us es rec ords for 2 0 0 3
and the other us es rec ords for 2 0 0 4 . T he report's rec ord s ourc e,
therefore, is the following s tatement:

SELECT * FROM qryServiceDates_2003


Union SELECT * FROM qryServiceDates_2004

Figure 4 - 9 s hows the qryServiceDates_2003 query. E ac h c us tomer


has zero or more pets , and eac h pet has zero or more vis its .
Bear in mind that the report reports on vis its only. T he type of
pet is n't relevant, but the data model c alls for the pets table
(tblPets) to be inc luded.

Figure 4-9. Querying information about visits


4.3.1. Using Running Sums

T his report proc es s es hundreds of rec ords , but only the totals
appear bec aus e the detail s ec tion's Visible property has been
s et to false. E ven s o, the details s ec tion plays a vital role in
hous ing a s et of text boxes that are us ed for running s ums .
Figure 4 - 1 0 s hows the report des ign.c reate two s ets of totals for
c omparis on. T his , by

I n addition to ac tual data fields , the detail s ec tion c ontains 1 0


unbound text boxes , all of whic h have the Running Sum property
s et to Over All, as s hown in the property s heet in Figure 4 - 1 0 .

T he 1 0 text boxes handle the 1 0 pos s ible c onditions . T he data


c ompris es two years and five s tates , for a total of 1 0 pos s ible
s ubtotals . E ac h unbound text box has a c alc ulation for its
c ontrol s ourc e. For example, the txtC T 2 0 0 4 text box c ontains
this expres s ion:

=IIf([ClientState]="CT" And Year([DateOfService])=2004,1,0)

T his s tatement gets the running s um to inc rement only when the
s tate is C T and the year is 2 0 0 4 . E ac h text box works in this
way, with eac h inc rementing on s ome variation of the two
c onditions , s tate and year.

Figure 4-10. The Running Sum property set to


Over All
T he names of thes e text boxes are vital bec aus e they are
referenc ed in other c ontrols in the report footer. T he names are
txtC T 2 0 0 3 , txtC T 2 0 0 4 , txtM A 2 0 0 3 , txtM A 2 0 0 4 , and s o on.
A ll in all, five s tates are us ed: C T, M A , N Y, N J , and P A .
T he report footer c ontains two areas , one for the s ummary of
eac h year. T he areas are s eparated vis ually with s ome line
c ontrols . T here is no real s etting to s plit the report footer.

A ll the text boxes in the report footer are unbound, and they
referenc e the text boxes in the detail s ec tion. For example, the
report footer text box that dis plays the total for C T for 2 0 0 3
s imply referenc es the txtC T 2 0 0 3 running s um text box, with
this s tatement:

=[txtCT2003]

T he 1 0 s ummaries in the report footer that dis play a s um bas ed


on year and s tate all work in the s ame way. E ac h referenc es a
s ingle text box from the detail s ec tion. T he two grand totals in
the footer, the ones bas ed on total year, s imply s um the
as s oc iated five text boxes from the detail s ec tion. For example,
the text box that dis plays the grand total for 2 0 0 4 has this
s tatement for its c ontrol s ourc e:

=[txtCT2004]+[txtMA2004]+[txtNY2004]+[txtNJ2004]+[txtPA2004]

By c alc ulating totals in the detail s ec tion and then referenc ing
thos e running s um text boxes , you c an arrange the report's
layout any way you wis h.

4.3.2. Hacking the Hack

T he data model s hown in this hac k (s ee Figure 4 - 9 ) inc ludes a


table with pets . What if the us er wanted to report by year, s tate,
and pet? A s s uming the data inc ludes 1 0 types of pets (c at, dog,
bird, and s o on), you would have 1 0 0 variations of c onditions :
that is , 2 years times 5 s tates times 1 0 pet types . You c ould
c reate s uc h a report us ing the s teps des c ribed in this hac k, but
this would be tedious . A better approac h with s uc h a large
number of c onditions is to bas e the report on a Crosstab query.
T he example in "Summarize C omplex D ata" [Hack #45] us es
the data model from this hac k to s how how s uc h a query works .

4.3.3. See Also

"Summarize C omplex D ata" [Hack #45]

"U s e C onditional Formatting to P oint O ut I mportant


Res ults " [Hack #30]
Hack 30. Use Conditional Formatting to
Point Out Important Results

Not only can you use the built-in conditional f ormatting f eature,
but you also can roll your own with a little VBA !

Why not add a little impac t to important res ults or fac ts about
your data? I ns tead of having a report dis play res ults textually,
us e a bit of formatting bas ed on c onditions in the data to draw
readers ' eyes direc tly to the important news . I f the news is good,
you c an take all the c redit and maybe get a promotion. I f the
news is bad, you c an always us e the "D on't s hoot the
mes s enger" line.

"C reate C onditional Subtotals " [Hack #29] demons trates how to
c reate a report bas ed on data that c overs two years , with eac h
year broken out as its own total. T his is great for c ommon
analys es in whic h you're c omparing res ults from one year to the
next to s ee how muc h the data has c hanged (inc luding whether
the c hange was pos itive or negative).

Some reports , however, als o print a third c olumn indic ating the
perc ent c hange when the two values are c ompared. A lthough
this is n't c overed here, you c an apply the c onditional formatting
explained in this hac k to the perc ent c hange text boxes if you
c hoos e to inc lude them.
4.4.1. Standard Conditional Formatting

A c c es s provides a nic e c onditional formatting utility. With it you


c an eas ily c hange font attributes , foreground c olor, and
bac kground c olor properties when a s pec ified c ondition is met.
Figure 4 - 1 1 s hows the C onditional Formatting dialog box. I n this
example, expres s ions have been entered for the c onditions .
A lternatively, you c an bas e the c onditions on ac tual data values .

Figure 4-11. Font colors that change based on


the condition
U s e the Format C onditional Formatting… menu to dis play
the C onditional Formatting dialog box. T he C onditional
Formatting dialog box manages formatting for one c ontrol at a
time. T herefore, you mus t s elec t a c ontrol before you c an
ac c es s the menu. A ls o, the menu item is dis abled unles s
c onditional formatting c an be applied to the s elec ted c ontrol.

Figure 4 - 1 1 s hows the c onditional formatting that has been s et


up for the txtC T 2 0 0 4 Total text box. I n partic ular, for this c ontrol
the following three formatting options have been s et, to tes t for
the differenc e in perc entage between the 2 0 0 3 and 2 0 0 4
amounts :

Greater than 20%

([txtCT2004]-[txtCT2003])/[txtCT2003]>0.2

Greater than 15% and equal to or les s than 20%

([txtCT2004]-[txtCT2003])/[txtCT2003]<=0.2 And
([txtCT2004]-[txtCT2003])/[txtCT2003]>0.15

Greater than 10% and equal to or les s than 15%

([txtCT2004]-[txtCT2003])/[txtCT2003]<=0.15 And _
([txtCT2004]-[txtCT2003])/[txtCT2003]>0.1

E ac h c ondition provides different formatting bas ed on s elec tions


made in the C onditional Formatting dialog box. T his works fine,
but the three- c ondition limit might require another approac h.
4.4.2. Conditional Formatting the VBA Way

By plac ing c ode into the report's event s tubs , you c an provide
robus t formattingbeyond what the s tandard c onditional
formatting feature allows . T he s tandard formatting has two major
limitations : you c an tes t for three c onditions only, and s ome of
the formatting options aren't available.

T he workaround is to jus t c ode up your own us ing V BA . Figure 4 -


1 2 s hows the report; note that Total V is its for 2 0 0 4 is s et in
italic and has a border around it.

Figure 4-12. Conditional formatting applied


through VBA code
T his formatting was applied bec aus e a c ondition was met, bas ed
on what was tes ted in the c ode. T his c ode has been plac ed in the
report's ReportFooter_Print event:

Private Sub ReportFooter_Print(Cancel As Integer, PrintCount As In


Dim visits_change As Single
visits_change = ([txt2004Total] - [txt2003Total]) / [txt2003Tota
Select Case visits_change
Case Is >0.25
Me.txt2004Total.Properties("ForeColor") = vbBlue
Me.txt2004Total.Properties("Borderstyle") = 1
Me.txt2004Total.Properties("BorderColor") = vbBlack
Me.txt2004Total.Properties("FontItalic") = 1
Case Is <= 0.25, Is > 0.2
Me.txt2004Total.Properties("ForeColor") = vbBlue
Case Is <= 0.2, Is > 0.15
Me.txt2004Total.Properties("ForeColor") = vbGreen
Case Is <= 0.15, Is > 0.1
Me.txt2004Total.Properties("ForeColor") = vbMagenta
Case Is <= 0.1, Is > 0
Me.txt2004Total.Properties("ForeColor") = vbBlack
Case Is <= 0
Me.txt2004Total.Properties("Borderstyle") = 1
Me.txt2004Total.Properties("BorderColor") = vbRed
End Select
End Sub

T he c ode tes ts the perc entage c hange and then us es a Select


Case s tatement to apply different formatting bas ed on the
perc entage c hange. Six c onditions are provided, but you aren't
limited in terms of number of c onditions ; us e whatever number
makes s ens e for your applic ation. A ls o, the type of formatting is
open to whatever you c an c ontrol through V BA , whic h is jus t
about everything.
Hack 31. Provide a Direct Link to a
Report

Provide a desktop shortcut to a report so that users can


completely skip the process of starting up the database.

What c an be eas ier for a manager or a databas e- c hallenged


individual than jus t s kipping the proc es s of opening the
databas e? You c an eas ily provide this func tionality by inc luding
a s hortc ut direc tly to the report. T he s hortc ut goes on the us er's
des ktop.

4.5.1. Creating a Shortcut

To c reate s uc h a s hortc ut, firs t open the databas e, and then


right- c lic k the des ired report. T he c ontext menu inc ludes a
C reate Shortc ut… menu item, as s hown in Figure 4 - 1 3 .

When the us er c lic ks the C reate Shortc ut… menu item, a dialog
box pops up for her to s elec t where to plac e the s hortc ut. T he
us er's P C des ktop will probably be filled in as the default, as
s hown in Figure 4 - 1 4 .

A fter the us er c lic ks the O K button, the des ired s hortc ut is


c reated. C lic king the s hortc ut s tarts up the databas e and opens
the report, but unfortunately, the databas e s tays open.
Figure 4-13. Creating a shortcut to a report
Figure 4-14. Selecting the location for the
shortcut
4.5.2. Printing a Report and Closing the
Database in One Click

A better approac h is to enable the us er to c lic k the s hortc ut,


print the report, and c los e the databas e afterward, all
automatic ally via a s ingle c lic k. Sounds like a mac ro to me!

Figure 4 - 1 5 s hows a s imple mac ro that prints a report and then


c los es the databas e.

Figure 4-15. Using a macro to run the report and


close the database
A ll that is nec es s ary is the ac tion to open the report. I t's
important that View is s et to Print, not to Print Preview or Design.
T his s ends the report direc tly to the printer ins tead of dis playing
it. T he follow- up Quit ac tion c los es the databas e.

N ote that if the report is us ually generated with s elec tions made
on a form, you s hould c reate a s hortc ut to the form ins tead. You
c an c reate s uc h s hortc uts for any databas e objec t.
Hack 32. Protect Intellectual Property

Prevent misuse of conf idential and copyrighted material by


printing watermarks on reports.

I f s omeone is bent on taking your intellec tual property, you have


no fool- proof way to prevent it. H owever, c ommon s ens e dic tates
that we do our bes t to protec t our as s ets . O ften, putting s ome
wording on the page or in the report header or footer s aying the
material is c onfidential s erves this need. H owever, this method
does n't nec es s arily make the mes s age s tic k out like a s ore
thumb.

A n additional meas ure is to put watermarks on reports . A


watermark s its right within the body of a report, page after page.
I t ends up underneath the ac tual text and is s omewhat
trans parent; that way, it does n't obs c ure the text, but it is
evident enough to get the mes s age ac ros s in a big way.

Figure 4 - 1 6 s hows a report in whic h a watermark s its mixed in


with the data. T he word "C onfidential" s tretc hes diagonally from
the lower left to the upper right. T he text appears to s it on top of
the watermark.

Figure 4-16. Using a watermark to get the


message across
4.6.1. Making the Watermark

To c reate a watermark, you need to c reate a graphic that you will


s et to the P ic ture property on a report. You will need a graphic s
program to c reate a dec ent watermark. Several good graphic s
programs are available. I us e an exc ellent, affordable program
c alled P aint Shop P ro by C orel C orp. (http:// www.jas c .c om.)
Whic hever graphic s program you us e, it mus t be able to do the
following:

Work with text as a graphic

You want to be able to s tretc h and orient the data.

Apply trans parency s ettings

T he final graphic s hould be in the ballpark of 7 5 %


trans parent. I t's bes t to determine the ac tual s etting via
trial and error.

Specify the s ize of the graphic

When c reating the graphic , it's important to make it


almos t as large as the paper you are us ing. Whatever
s ize you make the graphic is the s ize it appears on the
report (bas ed on a property s etting des c ribed later in
this hac k).

Save the graphic as a .j pg, .bmp, and s o on

You c an us e any file format that c an be us ed when


tapping the Picture property on the report.

C reating s uc h a graphic is beyond the s c ope of this hac k, but for


your information, the graphic in Figure 4 - 1 6 is 7 0 % trans parent
and was s aved as a .j pg file. T he graphic is about 4 x 7 inc hes .
Figure 4 - 1 7 s hows how the graphic s file appears on its own.

Figure 4-17. The watermark as a graphics file


4.6.2. Using the Watermark

O nc e you s ave the watermark as a file, go into the report's


D es ign mode. I n the property s heet, c lic k the Picture property,
and brows e to s elec t the graphic s file, as s hown in Figure 4 - 1 8 .

Figure 4-18. Setting the report's Picture


property
A few other relevant s ettings work with the Picture property:

Picture Type

You have a c hoic e of E mbedded or L inked. E mbedded is


the c orrec t c hoic e here. L inked will attempt to open the
graphic s eparately, whic h is n't the point of us ing a
watermark.

Picture Size Mode

T he c hoic es are C lip, Stretc h, and Zoom. E ac h treats


how the graphic is plac ed on the report in a different
manner. E xperimenting with thes e s ettings is the bes t
way to unders tand how they work. H owever, as
mentioned earlier, if you s ized the graphic c orrec tly when
you c reated it, us e the C lip s etting here. Following this
approac h helps to avoid having to gues s your way
through the pic ture's plac ement on the report.

P ic ture A lignment

You have five c hoic es : Top L eft, Top Right, C enter,


Bottom L eft, and Bottom Right. M os t people c hoos e
C enter, but you might find a different s etting s erves your
needs .
P ic ture T iling

T he s ettings are Yes and N o. T he N o s etting plac es the


graphic onc e on the page. T he Yes s etting tiles the
graphic , whic h c reates a repeating pattern. Try both of
them to s ee whic h is better for you. T he Yes s etting
makes a bus ier- looking watermark, but perhaps that is
what you want.

P ic ture P ages

T his lets you des ignate on whic h pages the watermark


will appear. T he c hoic es are A ll P ages , Firs t P age, and
N o P ages . I hope you don't c hoos e N o P ages , or you
won't s ee your watermark!

You als o might have to c hange the Back Style property on the
report. You might have to do this bec aus e the watermark
appears under the text, and text boxes c an take up more room
than the ac tual text they dis play. Figure 4 - 1 9 demons trates this
dilemma. I n the report, the rec tangular s hape of the text box
c overs up part of the watermark. You don't ac tually s ee the text
box, but the rec tangular s hape bec omes apparent when it's
c ontras ted with the watermark underneath.

Figure 4-19. Text boxes covering up the


watermark
To avoid this behavior, go into the des ign of the report. For any
text boxes that s it over the watermark, c hange the Back Style
property from Normal to transparent. T his forc es the text boxes to
dis play jus t the text, whic h is exac tly the effec t you want. Figure
4 - 2 0 s hows how the report appears when the text boxes are
trans parent.

Figure 4-20. The watermark, appearing through


the text boxes
Hack 33. Create a Slideshow in Access

Use images and the OnTimer event to create a controllable


visual show.

I nc orporating pic tures into A c c es s is a great thing. I t provides a


way to depic t produc ts , pers onnel, or any other items you want
to s how via pic tures ins tead of via a textual des c ription.
Typic ally you do this by s toring the paths to graphic s files in a
table field. I n this manner, the graphic s and the table data are
c onnec ted. O n a form, when a rec ord is dis played, an image
c ontrol c an be updated with the graphic found at the related
path.

T his hac k does n't imitate s uc h a data- bas ed s c enario. I ns tead,


it works with an unbound form to dis play unbound graphic s . You
c an mix the func tionality des c ribed here with a data- bound
s olution. For example, while a form dis plays databas e rec ords ,
the unbound graphic s func tionality c ould be inc orporated into
the form header or footer.

T his hac k us es the image c ontrol to dis play the graphic s and
s hows how to update the graphic s being dis played on a periodic
bas is , typic ally a few s ec onds . T he hac k als o s hows you how to
let us ers drive the graphic dis play update on demand.

4.7.1. The Graphics


For the example in this hac k, all the graphic s are in the s ame
direc tory. T he direc tory is hardc oded in the c ode, but the c ode
c an eas ily be updated to handle multiple direc tories , pas s ed
paths from a dialog, and s o on. For now, let's s ay that a group of
.j pg files s it in a direc tory, as s hown in Figure 4 - 2 1 .

Figure 4-21. Picture files in a directory


N ote that all the files don't have to be in the .j pg format. You
als o c an us e other graphic s types , s uc h as bitmaps (.bmp) and
T I FF (.tif) files (this is als o s pec ified in the c ode).

4.7.2. The Form Design

Figure 4 - 2 2 s hows the form in D es ign mode. I t c ontains jus t a


few c ontrols : s ome buttons , a c hec kbox, and the image c ontrol.
T he image c ontrol has an initial pic ture, but this is overridden as
the s lides how runs .

Figure 4-22. The design of the slideshow form


A s you c an s ee, the us er c an either run an automated s lides how
or navigate through the graphic s manually. T he N ext and
P revious buttons allow the us er to move forward and bac kward
through the graphic s while not in an automated mode. T hes e
buttons jus t c yc le through a V BA c ollec tion of paths to the
graphic s , and the us er c an us e them to update the image
c ontrol's Picture property.

T he c hec kbox is us ed to s et the mode. I f it's c hec ked, the


s lides how runs . Regardles s of whether the us er lets the graphic s
c hange automatic ally, s he mus t pres s the Start button. T his is a
nic e feature bec aus e it gives the us er c ontrol over when to s tart
the s how. A fter all, s omeone might s till be getting popc orn!

H owever, bec aus e there is a Start button, it makes s ens e to als o


have a Stop button. C lic king the Stop button merely c hanges the
value of a Boolean- type public variable, named stop_show, from
false to true.

4.7.3. The Code

A vital piec e of this applic ation is the us e of the form's OnTimer


event. I n the following c ode, note that the Timer Interval has a
s etting of 2000. T his tells the form to fire its OnTimer event every
two s ec onds . You c an c hange this value to ac c ommodate other
intervals :

Option Compare Database


Public stop_show As Boolean
Public pixpaths As Collection
Public pixnum As Integer
'
Private Sub Form_Open(Cancel As Integer)
'
'set initial properties
'
Me.chkRunContinuous = False
Me.cmdNext.Enabled = False
Me.cmdPrevious.Enabled = False
End Sub
'
Private Sub cmdStart_Click()
'
'read paths of graphics into a collection
'displays the first graphic
'

stop_show = False
If Me.chkRunContinuous = False Then
Me.cmdNext.Enabled = True
Me.cmdPrevious.Enabled = True

End If
'replace with your path!!
pix_path = "C:\Product Photos"
Set pixpaths = New Collection
Set fs = Application.FileSearch
With fs
.LookIn = pix_path
.FileName = "*.jpg"
If .Execute() > 0 Then
For i = 1 To .foundfiles.Count
pixpaths.Add Item:=.foundfiles(i)
Next i
Else
MsgBox "No files found!"
End If
End With
'load first pix
Me.imgPixHolder.Picture = pixpaths(1)
pixnum = 1
End Sub
'
Private Sub cmdNext_Click()
'

'advances to the next graphic


'cycles forward through collection
'

If pixnum = pixpaths.Count Then


pixnum = 1
Else
pixnum = pixnum + 1
End If
Me.imgPixHolder.Picture = pixpaths(pixnum)
End Sub
'
Private Sub cmdPrevious_Click()
'
'displays the previous graphic
'cycles backward through collection
'

If pixnum = 1 Then
pixnum = pixpaths.Count
Else
pixnum = pixnum - 1
End If
Me.imgPixHolder.Picture = pixpaths(pixnum)
End Sub
'
Private Sub cmdStop_Click()
'
'sets global variable to false
'disables Previous and Next buttons
'

stop_show = True
Me.cmdNext.Enabled = False
Me.cmdPrevious.Enabled = False
End Sub
'
Private Sub Form_Timer()
'
'if the mode is to run continuously and the
'stop button has not been clicked, then keep cycling gra
If Me.chkRunContinuous = True _
And stop_show = False Then cmdNext_Click
End Sub

T his c ode module c ontains a handful of routines . T he form's Open


event builds a c ollec tion of paths to the graphic s , found within a
s pec ified direc tory. T he s pec ific direc tory and graphic s file type
are hardc oded. T he FileSearch objec t us es thes e values to find
the graphic s files . You c an expand this to look in more than one
direc tory and/or for more than one file type. Read up on the
FileSearch objec t in the H elp s ys tem or on the I nternet for more
information.

T he c hkRunC ontinuous c hec kbox determines how the


applic ation will run. I f it is unc hec ked, the N ext and P revious
buttons are us ed to navigate through the graphic s . When the
us er c lic ks either button, the index to the c ollec tion is inc reas ed
or dec reas ed, and the Picture property of the image c ontrol is
updated to the partic ular c ollec tion item.

I f the us er c hec ks the c hkRunC ontinuous c hec kbox, the N ext


and P revious buttons are dis abled, and the s lides how runs on its
own. T he time that elaps es between eac h graphic is bas ed on
the Timer Interval. T he s lides how will run c ontinuous ly until the
us er c lic ks the Stop button. When the Stop button is c lic ked, it
s ets the stop_show variable to true. T his c aus es the Timer event
to s kip updating the graphic bec aus e the update oc c urs only
when stop_show is false.

4.7.4. Hacking the Hack

You c an enhanc e this bas ic s lides how in s everal ways . O ne thing


to c ons ider is that the graphic s have no s upporting text. Keeping
an unbound approac h, one way around this problem is to provide
a way to dis play details about a graphic when it is c lic ked. T he
image c ontrol has a Click event, whic h you c an us e to return the
value of the Picture property, as s hown in Figure 4 - 2 3 .

Figure 4-23. Taking advantage of the image


control's Click event
Bec aus e the pic ture being dis played c an provide its name, you
c an us e the image c ontrol's Click event to find and dis play
anything pertaining to the partic ular graphic . For example,
bec aus e the graphic 's filename c an be is olated, you c an gather
additional information about the graphic from a text file or other
s ourc e.

O f c ours e, another approac h is to s imply bind the image c ontrol


to paths s tored in a table. A nother field c an then s upply textual
information about the pic ture in a text box. I n fac t, this is the
more s tandard approac h. I didn't point this out earlier bec aus e it
requires more work to update the paths in table rec ords than jus t
onc e in the c ode. A ls o, if you adapt the c ode to work with a us er-
s upplied path, of c ours e the unbound approac h makes more
s ens e bec aus e there would be no preentered paths .
Hack 34. Play Videos in Access Forms

Deliver your message the multimedia way with the Windows


Media Player.

H ere's a really neat way to s pic e up your A c c es s applic ations :


play movies ! A lthough this might s eem a little too entertaining
for "real" bus ines s us e, c ons ider that movies are one of the bes t
vehic les for delivering information. You c an inc orporate movies
into your databas e des ign in s everal ways . You c an relate movie
c lips to data rec ords , in whic h c as e the as s oc iated movie runs
when a rec ord is dis played. You c an als o have an unrelated
movie play on demand (requiring a button c lic k or s ome other
way to initiate the movie to play).

To play movies you need to inc orporate a c ontrol that c an handle


movie files . A number of thes e are available, mos t notably
Windows M edia P layer, whic h is what this hac k us es .

4.8.1. Putting the Player on the Form

Firs t, you mus t add Windows M edia P layer to the form. Bec aus e
this is n't a s tandard c ontrol, you mus t ac c es s it us ing the M ore
C ontrols button on the toolbox, as s hown in Figure 4 - 2 4 .

C lic king the M ore C ontrols button dis plays a lengthy lis t of
c ontrols and libraries . Sc roll down to find Windows M edia P layer,
as s hown in Figure 4 - 2 5 .

A fter you c lic k the c ontrol in the lis t, draw it on the form. Figure
4 - 2 6 s hows a form in whic h Windows M edia P layer, a lis tbox, and
a c ommand button have been ins erted. I n this c onfiguration, the
lis tbox dis plays a lis t of movies from whic h to s elec t; c lic king
the button plays the s elec ted movie.

I n Figure 4 - 2 6 , the lis tbox is populated with paths to .mpg


movie files . T he lis tbox has two c olumns . T he firs t c olumn is the
bound c olumn, whic h holds the paths to the movie files . I ts width
is s et to zero, s o it is n't dis played to the us er. I ns tead, the
s ec ond c olumn, whic h c ontains friendly names for the movies , is
dis played. When the us er has s elec ted a movie, s he s imply
pres s es the c ommand button to s tart the movie. T his effec tively
is a s imple playlis t. Figure 4 - 2 7 s hows the form in V iew mode
before playing a movie.

Figure 4-24. Looking for more controls


Figure 4-25. Selecting Windows Media Player
4.8.2. Playing a Movie

So, jus t how does a movie play? A c tually, it's quite s imple: the
path to a movie file is handed to Windows M edia P layer's URL
property and the movie s tarts playing automatic ally. T his
example s hows the button's c ode; it takes the path from the
lis tbox and hands it to the player:

Private Sub cmdPlayMovie_Click()


If Not IsNull(Me.listMovies) Then
Me.WMPlayer.URL = Me.listMovies

Figure 4-26. Form design with Windows Media


Player
Figure 4-27. Selecting a movie
Else
MsgBox "First select a movie"
End If
End Sub

Starting, s topping, fas t- forwarding, rewinding, and paus ing are


func tions built into the player. T hes e func tions are available
through the buttons on the player its elf. T his allows the us er to
work the movie in any needed fas hion.

Windows M edia P layer has many events you c an hook into. A


little thought and c reativity will go a long way toward integrating
movies into your applic ations . T his hac k s hows the bas ic way to
implement a movie, but you c an c ode around and work with the
player in myriad ways .

4.8.3. See Also

Windows M edia Support C enter


(http://s upport.mic ros oft.c om/default. as px? s c id=fh;en-
us ;wmp)
Hack 35. View Reports Embedded in
Forms

Preview reports, whether current or historical, directly on the


f orm you are working on.

A c c es s is one of the mos t powerful reporting tools on the


market. Beginning with A c c es s 9 7 , M ic ros oft introduc ed the
ability to c reate s naps hot reports that you c an view with the free
Snaps hot V iewer, available for download from M ic ros oft (s earc h
for A c tiveX Snaps hot V iewer at http://www.
mic ros oft.c om/downloads ).

A c c es s databas e applic ation developers c an us e the A c tiveX


Snaps hot V iewer to c us tomize the look and feel of their
applic ations by dis playing reports embedded in forms .

4.9.1. Creating the Form

T he form is c ompos ed of a c ombo box and the A c tiveX Snaps hot


V iewer. T he c ombo box c ontains a lis t of all the reports in the
databas e. When the form opens , the Load event exec utes the
following c ode to fill the c ombo box with a lis ting of all available
reports :

Private Sub Form_Load( )


Dim obj As AccessObject, dbs As Object
Dim strList As String
Set dbs = Application.CurrentProject
For Each obj In dbs.AllReports
strList = strList & obj.Name & ";"
Next obj
cboReports.RowSourceType = "Value List"
cboReports.RowSource = strList
End Sub

To add the A c tiveX Snaps hot V iewer, s elec t M ore C ontrols from
the toolbox, as s hown in Figure 4 - 2 8 , s c roll down, and s elec t
Snaps hot V iewer C ontrol 1 1 .0 . N ote that depending on your
vers ion of A c c es s , your c ontrol might be earlier than the 1 1 .0
vers ion.

Figure 4-28. Adding the ActiveX Snapshot


Viewer
Size the Snaps hot V iewer C ontrol to an approximate width that
matc hes the s ize of the paper on whic h you will print the report.
T his helps avoid having to s c roll left and right to s ee a report
onc e it is dis played. Figure 4 - 2 9 s hows the form des ign with the
Snaps hot V iewer C ontrol in plac e.

Figure 4-29. Sizing the snapshot viewer


A fter adding the Snaps hot V iewer C ontrol, plac e the following
c ode in the On Change event of the c ombo box. M ake s ure the
name of the Snaps hot V iewer on your form matc hes the name in
the c ode:

Private Sub cboReports_Change()


DoCmd.OutputTo acOutputReport, cboReports,
Application.Curren
SnapshotViewer1.SnapshotPath = _

Application.CurrentProjec
End Sub

I n this example, a temporary s naps hot report is c reated, c alled


temp.s np, and it is plac ed in the direc tory in whic h the databas e
is running. T he temp.s np s naps hot report is then loaded into the
Snaps hot V iewer. T his temp.s np file is replac ed eac h time a new
s elec tion is made. I f you are running from a s hared loc ation with
a multius er databas e, make s ure you s tore the temporary
s naps hot file on the loc al mac hine, not on the network; this
avoids any multius er is s ues .

A s s hown in Figure 4 - 3 0 , the final form dis plays an invoic e


report that was s elec ted from a c ombo box.

Figure 4-30. Displaying a report


T his hac k gives the applic ation one plac e for us ers to s elec t
reports and view them before printing. A ls o note that the
A c tiveX Snaps hot V iewer inc ludes a P rint button next to the
navigation buttons .
4.9.2. Hacking the Hack

A n advantage to s aving reports as s naps hots is they are s tatic ,


as oppos ed to the dynamic reports you get with report objec ts in
A c c es s . You c an s ave thos e reports in a folder and view them as
his toric al reports with the Snaps hot V iewer. You als o c an c hange
the c ode to have a c ombo box that dis plays the report names
from a given direc tory, whic h allows the us er to s elec t his toric al
reports to view with the Snaps hot V iewer.

Steve Huff
Hack 36. Put Line Numbers on a Report

Use the Running Sum property to include an incremental counter


in your report.

Sometimes you might want to inc lude line numbers on a report.


T his is fairly eas y to do. T he tric k is to inc lude an unbound text
box, s et its Running Sum property to Over Group or Over All, and
s et its Control Source to the s tarting value.

Figure 4 - 3 1 s hows a report in D es ign mode. T he text box on the


left is unbound and will dis play an inc remental value when the
report runs . T he property s hows how its c ontrol s ourc e is s et to
=1, the beginning value.

Figure 4-31. A text box to display line numbers


Figure 4 - 3 2 s hows how the report looks when run.

T he value plac ed in the Control Source property not only provides


the s eed value, but als o s erves as the inc rement value. When a
value of 1 is us ed, the c ounting s tarts at 1 and inc rements by 1 .
You c an us e other values as well. For example, a value of 10
might be des irable, in whic h c as e the c ounting s tarts at 1 0 and
inc rements by 1 0 , as s hown in Figure 4 - 3 3 .

Figure 4-32. Displaying a report with line


numbers
Figure 4-33. Lines counting by tens
Hack 37. Shade Alternating Lines on a
Report

Go f or the readability f actor. Use alternating shaded lines to


make a more pleasing presentation.

A quic k way to make reports eas ier to read is to s hade every


other line. A lthough no direc t property or method provides this
feature, you c an ac hieve the look with a little planning. To
ac c omplis h this , us e an unbound text box to keep an
inc remental value. A s line items are proc es s ed, the
inc rementing value toggles between even and odd. You c an then
us e this toggle's values to your advantage.

T he bac kground c olor property of the report's details s ec tion is


c hanged, depending on the value of the inc remental running s um.
When the value is odd, one c olor is applied. When the value is
even, another c olor is applied.

You have to s et a few properties for this to work:

I n the report's details s ec tion, an unbound text box is


inc luded. I ts Control Source property is s et to =1.I ts
Visible property is s et to No. Set its name to
txTRunningSum.
Set the Back Style property of the text boxes and labels
to transparent. T his applies to c ontrols in the details
s ec tion only.

4.11.1. The Code

I n the details s ec tion's Format event, plac e this c ode:

Dim even_odd As Integer


Me.Detail.BackColor = vbWhite
even_odd = Me.txtRunningSum Mod 2
If even_odd = 0 Then
Me.Detail.BackColor = vbYellow
End If

You us e the Mod operator to determine whether the c urrent


running s um value is even or odd. Mod returns the remainder of a
divis ion operation. When an even number is divided by 2 , the
remainder is 0 . T he even_odd variable holds the res ult of the Mod
operation.

4.11.2. The Results

T he routine s tarts out by defaulting the bac kground c olor to


white. I f the even_odd variable is n't 0, the bac kground c olor is
c hanged to yellow.

Figure 4 - 3 4 s hows how the report looks when run.


4.11.3. Hacking the Hack

A c ouple of alternatives are available. I f, for example, you have


to s hade every third line, you c an tes t whether the running s um
is a multiple of 3 . A ny multiple of 3 divided by 3 has no
remainder.

A lternatively, you c an us e the RGB func tion to c ontrol the c olor.


RG B is an ac ronym for red, green, blue. T he func tion works by
blending the three c olors , eac h as a number between 0 and 2 5 5 .
L ook up the RGB func tion in the A c c es s H elp s ys tem; it's a great
func tion to get familiar with. To us e it in this hac k, jus t c hange
the BackColor property, like this :

Figure 4-34. A report with alternate row shading


Me.Detail.BackColor = RGB(200, 200, 200)

You will have to experiment with different s ettings , but here's a


guide you c an follow:

Setting all three RGB argument func tions to 0 c reates


blac k.

Setting all three RGB argument func tions to 255 c reates


white.

A ll other c olors are available by applying varying values to the


arguments .
Hack 38. Save Paper by Reducing
Whitespace

Use the Can Shrink property to condense your reports.

E mpty data fields in a long report c an pos e a problem when it


c omes time to print the report. I magine a lis t of 1 ,0 0 0 c ontac ts ,
and only half have a phone number entered into a phone number
field. When you des igned the report, you inc luded the phone
number field for c ontac ts that have a phone number. H owever,
when you print the report, you s ee 5 0 0 empty s pac es
repres enting the phone number fields of c us tomers without
phone numbers . When other data fields are empty as well, the
s ituation jus t gets wors e.

A ll told, this whites pac e c an ac c ount for 5 0 or more extra pages


in the report, depending on how the report is laid out. Figure 4 -
3 5 s hows a report that s uffers from this problem. Some c ontac t
information is mis s ing, yet room is s till s et as ide for it.

Figure 4-35. A waste of paper


Figure 4 - 3 6 s hows the des ign of the report. T he detail s ec tion
c ontains a group of fields . A s s hown in Figure 4 - 3 5 , s ome of the
fields are empty.

Figure 4-36. Setting the Can Shrink property


O n the property s heet, s et the Can Shrink property to Yes. A pply
this to the fields in the detail s ec tion and to the detail s ec tion
its elf.

When you us e the Can Grow or Can Shrink


properties , you mus t apply the s ettings to
both the bound c ontrols and the detail
s ec tion.

With Can Shrink s et to Yes, any empty data fields on this report
won't take up s pac e. Figure 4 - 3 7 s hows the improved report.

Figure 4-37. A more compact report


I n that figure, you c an s ee s ome c ontac ts are mis s ing a phone
number, and others are mis s ing all the data. I n both c as es , the
amount of empty s pac e s hrinks . A s you c an s ee when c omparing
the report in Figure 4 - 3 7 with the one in Figure 4 - 3 5 , even the
firs t page dis plays more c ontac ts . A s was teful whites pac e is
dropped, the number of pages on the report is reduc ed.

T he Can Grow property provides the oppos ite func tionality as well.
When des igning a report, plac e c ontrols where they make the
mos t s ens e. O c c as ionally, you might have more data than you
c an dis play in the field, given the s ize of the bound c ontrol.
Setting Can Grow to Yes lets the field expand as needed to print all
the data.
Hack 39. Include the Date, Time, and
Page Count

Use common expressions to quickly insert necessary header and


f ooter inf ormation.

I t's always helpful to inc lude a times tamp on a report, indic ating
when it was printed. T his might be the only c lue as to whether
the information is up to date. I nc luding page c ounts is als o
important. H aving dozens of printed pages and not knowing the
order in whic h they go c an be quite frus trating.

A c c es s provides an eas y way to inc lude thes e nec es s ary and


s ometimes overlooked items . T he E xpres s ion Builder c ontains a
lis t of c ommon expres s ions . While des igning a report, the bes t
way to dis play the E xpres s ion Builder is to firs t plac e an
unbound text box in the report header or footer (or wherever
makes s ens e) and then c lic k the ellips es (…) next to the c ontrol
s ourc e for the unbound text box. T his opens up the E xpres s ion
Builder, s hown in Figure 4 - 3 8 .

Figure 4-38. Using the Expression Builder to


insert common expressions
T he available c ommon expres s ions inc lude page numbers , date
and time, and c urrent us er. T he P age N of M s etting is
partic ularly us eful bec aus e it not only s tates the page number
but als o provides a mes s age s uc h as "P age 1 5 of 4 0 ." O nc e
you've s elec ted the expres s ion you want, c lic k the O K button to
ins ert the expres s ion into the unbound text box. When the report
runs , the page numbering (or other c ommon expres s ion) is
inc luded on the report.
5. Queries and SQL
Sec tion 5 .1 . H ac ks 4 0 5 4

H ac k 4 0 . Return a Sample of Rec ords

H ac k 4 1 . C reate Bulletproof I ns ert O perations

H ac k 4 2 . Find U nmatc hed Rec ords on M ultiple Field


Keys

H ac k 4 3 . P lac e a G rand Total in a Q uery

H ac k 4 4 . Sort A ny A rbitrary String of C harac ters

H ac k 4 5 . Summarize C omplex D ata

H ac k 4 6 . G et A ll C ombinations of D ata

H ac k 4 7 . D on't L et N ulls Ruin D ata Summaries

H ac k 4 8 . U s e a C us tom Func tion in a Q uery

H ac k 4 9 . C reate A c c es s Tables with SQ L Server


Sc ripts

H ac k 5 0 . U s e Wildc ards in Q ueries

H ac k 5 1 . G et C leaner O r- Bas ed C riteria

H ac k 5 2 . G et C leaner A nd- Bas ed C riteria

H ac k 5 3 . C reate an O uter J oin

H ac k 5 4 . U s e Regular E xpres s ions in A c c es s Q ueries


5.1. Hacks 4054
You c an ac c omplis h a lot with the A c c es s query grid. You c an
ac hieve even more by working direc tly in the SQ L pane. Q ueries
are c ategorized into two types : Select (pas s ive) queries and
Action queries . You us e Select queries to pull data rec ords out of
tables . You us e Action queries to do s omething with or to the
data. You c an run both types of queries in the query grid, but
only up to a point. For example, to us e a Union query you mus t
us e s traight SQ L s tatements . You als o mus t us e the SQ L
language for c ertain s ophis tic ated Action queries .

I n this c hapter, you'll find all s orts of SQ L ac tivities going on,


both in and out of the grid. For ins tanc e, "Return a Sample of
Rec ords " [Hack #40] s hows how to return a s mall s et of rec ords
us ing s ettings in the grid. M eanwhile, "P lac e a G rand Total in a
Q uery" [Hack #43] is ac hieved with SQ L .

A ll in all, thes e hac ks s how how to ac c omplis h s ome c ool and/or


nec es s ary tas ks you might be hard- pres s ed to figure out how to
do on your own. D ec ent knowledge of the SQ L language pays off
in s pades as you work on your projec ts . T he query grid s hields
you from learning SQ L , but after a while, you probably will need
to do things you c an't do us ing the grid. H opefully, the hac ks in
this c hapter will get the brain gears turning and s erve as a
s pringboard to even more s ophis tic ated databas e work.
Hack 40. Return a Sample of Records

Use the Top predicate to return a portion of your records


without bias.

M os t often, you us e a Select query to return all the rec ords that
matc h c ertain c riteria. U s ually, this query returns a data s et that
is s maller than the table or tables upon whic h the query is built.
T hat is , not all rec ords matc h the c riteria, and the number of
rec ords that do matc h is s maller than the underlying s et of table
data.

Sometimes , you might need only a s ample of rec ords that aren't
bas ed on the c riteria or in whic h the c riteria are irrelevant. T his
is n't the s ame as fine- tuning the c riteria to limit the number of
returned rec ords . For example, s tatis tic al work might require a
s ample from whic h to infer fac ts about the whole data s et.
Regardles s of whether the population data is a table or a filtered
data s et already bas ed on s ome c riteria, the point is that the
next s tep of getting a s ample is c ompleted without any
prec onc eived notion. T his is where the SQ L Top predic ate c omes
in handy.

T he Top predic ate works in two ways :

Returns a number of rec ords


Returns a perc entage of rec ords

5.2.1. Using the Top Predicate

T he Top predic ate allows you to is olate rec ords from the top of a
data s et. I f you want to get rec ords from the bottom, firs t apply a
revers e s ort (i.e., des c ending ins tead of as c ending). E ither way,
you will c ontinuous ly get the s ame s et of rec ords eac h time you
run the query. L ater in this hac k, we'll dis c us s a method for
getting a true random s ample.

Figure 5 - 1 s hows a query in D es ign mode in whic h the query


property s heet is us ed to indic ate the number of rec ords to
return. T he Top Values property has a few values from whic h to
s elec t. You c an us e one of thes e, or you c an enter your own.

Figure 5-1. Selecting a value for the Top


predicate
With 25 as the s elec ted c ount to return, the query returns , no
s urpris e, 2 5 rec ords from the top of the data s et, as s hown in
Figure 5 - 2 .

Figure 5-2. Returning the designated number of


records
I t's interes ting to s ee the SQ L the A c c es s query grid generates .
Switc hing to SQ L view, here is what you s ee:

SELECT TOP 25 Occurrences.Reading


FROM Occurrences;

T he Top predic ate s its jus t after the Select s tatement and
s pec ifies the number of rec ords to return.

To return a perc entage of rec ords , s imply add the word Percent to
the SQ L s tatement, after the number:

SELECT TOP 25 PERCENT Occurrences.Reading


FROM Occurrences;

To indic ate perc ent when us ing the query des igner, add the
perc ent s ign (% ) to the Top Values property, as s hown in Figure
5 -3 .

Figure 5-3. Indicating to return a percentage of


records
5.2.2. Hacking the Hack

T he Top predic ate is great for grabbing a handful of rec ords , but it
will always grab the s ame rec ords . E ven when no s ort is plac ed
on the s ourc e data, the rec ords s till s it in the order in whic h they
were plac ed in the table.

Returning a random s et of rec ords requires us ing the Rnd


func tion. You apply this as a s ort. N ormally, a s ort is n't what you
want to us e to return an unbias ed data s et, but s orting on a
random value makes this a moot point. To make this work, alter
the SQ L s tatement to look like this :

SELECT TOP 25 Occurrences.Reading


FROM Occurrences
ORDER BY RND([Reading]);

E nter the name of the field as the argument for the Rnd func tion.
E ac h time the query runs , a random s elec tion of rec ords is
returned.
Hack 41. Create Bulletproof Insert
Operations

Prevent f ailed append operations so that all the records make it


into the table.

You us e the SQ L Insert s tatement to append rec ords to a table.


A lthough this us ually works great, it is prone to is s ues that c an
make it bomb. T his hac k s hows two things you c an do to validate
data before handing it off to an Insert operation. Before we
dis c us s thes e validation methods , let's c reate a s imple table, as
s hown in Figure 5 - 4 .

Figure 5-4. A table that accepts names and ages


T he table has two fields :

Patient

M eant to take jus t the firs t name; the length is s et to


10.

Age

T he age of the patient.

5.3.1. Handling Excessive Text Length

O ne thing that c an trip up an Insert is trying to s tic k data into a


field when the data is longer than the field length. T his is an
is s ue only for text fields . H ere is an Insert s tatement that works
fine:

"Insert Into Patients (Patient, Age) Values ('Gary', 22)"

T he name Gary fits in the P atient text field. N ow, look at this
s tatement:

"Insert Into Patients (Patient, Age) Values ('Bartholemew'


U h- oh. T he name Bartholemew is 1 1 c harac ters long, but the
P atient field c an ac c ept a maximum of only 1 0 c harac ters . T he
eas ies t way to fix s tatements s uc h as thes e is to trunc ate the
text to 1 0 c harac ters by us ing the Left func tion.

H ere is a s ample c ode routine that appends rec ords from the
N ewP atients table to the P atients table. T he Left func tion s its in
the middle of the Insert s tatement and ens ures that no name is
longer than 1 0 c harac ters :

Dim myDB As ADODB.Connection


Set myDB = CurrentProject.Connection
Dim rsNewPatients As ADODB.Recordset
Set rsNewPatients = New ADODB.Recordset
rsNewPatients.Open ("Select * from NewPatients"), myDB
Do Until rsNewPatients.EOF
myDB.Execute ("Insert Into Patients Values ('" & _
Left(rsNewPatients.Fields("Patient"), 10) & _
"', " & rsNewPatients.Fields("Age") & ")")
rsNewPatients.MoveNext
Loop
rsNewPatients.Close
myDB.Close
Set myDB = Nothing

T he Left func tion c uts the s ize of the name to 1 0 c harac ters .
A nother option, of c ours e, is to inc reas e the s ize of the table
field.
5.3.2. Watching Out for Apostrophes

N othing dis rupts an Insert fas ter than the odd apos trophe or
s ingle quotation mark. I t's reas onable to have thes e in your
data; after all, the name O 'Reilly has one. But in a SQ L Insert,
the s ingle quote qualifies text. T herefore, without a little help,
this Insert operation will fail:

"Insert Into Patients (Patient, Age) Values (Left('O'Reill

T he problem is that as the s tatement is exec uted, the s ingle


quote before the letter O s tarts the text and the s ingle quote
after the letter O ends the text. T his leaves the Reilly part of the
name as unidentifiable.

D oubling up s ingle quotes removes the problem, and the way to


do this is to us e the Replace func tion. Replace replac es eac h
ins tanc e of a s ingle quote with two s ingle quotes . H ere is the
previous c ode routine modified to handle s ingle quotes :

Dim myDB As ADODB.Connection


Set myDB = CurrentProject.Connection
Dim rsNewPatients As ADODB.Recordset
Set rsNewPatients = New ADODB.Recordset
rsNewPatients.Open ("Select * from NewPatients"), myDB
Do Until rsNewPatients.EOF
myDB.Execute ("Insert Into Patients Values ('" & _
Replace(rsNewPatients.Fields("Patient"), "'", "''") & _
"', " & rsNewPatients.Fields("Age") & ")")
rsNewPatients.MoveNext
Loop
rsNewPatients.Close
myDB.Close
Set myDB = Nothing

H ere is how to us e the Replace func tion:

Replace(rsNewPatients.Fields("Patient"), "'", "''")

Replace works by tes ting a s tring of text for one or more


c harac ters . I f the s tring is found, it is replac ed with another
s tring of one or more c harac ters . T he three func tion arguments
are:

T he s tring being s earc hed

T he c harac ters being s earc hed for

T he replac ement s tring

A ll data c oming from the N ewP atients P atient field is tes ted for
the s ingle quote, and if it's found, the quote is replac ed with two
s ingle quotes . T his c reates an ac c eptable SQ L s tatement, and
the ins ert c an proc eed.

5.3.3. Combining the Two Validations


I left the bes t for las t. You need to tes t for both exc es s ive length
and apos trophes . C an you tes t for both s imultaneous ly? You
bet! J us t nes t one func tion ins ide the other with the following
c ode:

myDB.Execute ("Insert Into Patients Values ('" & _


Left(Replace(rsNewPatients.Fields("Patient"), "'", "''"), 1
"', " & rsNewPatients.Fields("Age") & ")")
Hack 42. Find Unmatched Records on
Multiple Field Keys

The Find Unmatched Query Wizard looks f or unmatched records


based on a single related f ield. You can adapt this query to work
on more than one related f ield.

T he eas ies t way to find rec ords in one table that has no related
rec ords in another table is to us e A c c es s 's built- in Find
U nmatc hed Q uery Wizard. Figure 5 - 5 s hows the N ew Q uery
dialog box, whic h ac c es s es the wizard.

Figure 5-5. Starting up the Find Unmatched


Query Wizard
A handful of wizard s c reens walk you through s etting up the
query. You s elec t the two tables and even whic h way the query
s hould work. For example, do you need to know whic h rec ords in
Table A have no related rec ords in Table B? O r do you need to
know whic h rec ords in Table B have no related rec ords in Table
A ? E ither way, the key to making this pos s ible is that the tables
are related in the firs t plac e.

Stric tly s peaking, the tables s elec ted to


be in an unmatc hed query don't have to be
formally related, at leas t in regard to
s etting up a relations hip in the
Relations hips window. I t's jus t that the
fields being matc hed s hould be hous ing
the s ame data; otherwis e, all rec ords are
returned as unmatc hed.

Tables c an be related on s ingle field keys or on multiple field


keys . U nfortunately, the wizard lets you s pec ify only a s ingle
field to relate the tables , as s hown in Figure 5 - 6 .

Figure 5-6. Specifying a single field to be


included for the match
Selec t a s ingle field from eac h table, on the left and right, and
then c lic k the button between the two tables to s et the matc h
the query will us e. T he wizard generates a query that is s aved in
the databas e. T his is c onvenient bec aus e it allows you to reus e
the query without having to rec reate it. You als o c an c hange the
query, whic h I 'll des c ribe next.

5.4.1. Reviewing the Query

T he example us ed here finds whic h c us tomers have no matc hing


rec ords in a s ales table. U s ing the Find U nmatc hed Q uery
Wizard, I c an look for c us tomers bas ed on their las t name alone.
Figure 5 - 7 s hows the query des ign the wizard generated.

T he query us es a LEFT JOIN to return all rec ords of c us tomers


whos e las t name field is Null in the s ales table. T he SQ L looks
like this :

SELECT tblCustomers3.FirstName, tblCustomers3.LastName


FROM tblCustomers3 LEFT JOIN tblSales3 ON
tblCustomers3.LastName = tblSales3.LastName
WHERE (((tblSales3.LastName) Is Null));

Figure 5-7. The unmatched query design


T here is a problem here, though. Two c us tomers might have the
s ame las t name. I n that c as e, as long as one of the c us tomers
has a rec ord in the s ales table, any other c us tomers with the
s ame las t name don't appear in the query's res ults , even if they
s hould.

Figure 5 - 8 illus trates this point. L eft to right ac ros s the s c reen
are the c us tomer table, the s ales table, and the query that looks
for c us tomers that have no s ales . Starting on the left, there are
two c us tomers with the s ame las t name: Kam Winter and M uriel
Winter. I n the s ales table, in the middle, M uriel Winter has a
s ales rec ord. I n the query res ult on the right, Kam Winter is not
lis ted as a c us tomer with no s ales , even though Kam s hould be
there.

Bec aus e the las t name is all that is tes ted, all c us tomers with
the s ame name are s kipped in the query res ults , as long as one
of them has a s ales rec ord. T his is n't ac c eptable.

5.4.2. Changing the Query

A ll you need to do is alter the query s o that both the las t name
and the firs t name are tes ted. We do this in the query des ign, in
either the grid or the SQ L pane. Figure 5 - 9 s hows how the query
is des igned now.

I t's important to make s ure a few things are c hanged c orrec tly:

You need to add a c riterion that looks for Null in First


Name.

You need to add a s ec ond relations hip between the


tables , on the new inc luded field. L ook c los ely at the
differenc es in how the tables in the query are related,
c omparing the des ign in Figure 5 - 7 with the des ign in
Figure 5 - 9 .
Figure 5-8. Reviewing tables and the
unmatched query
Figure 5-9. The unmatched query, now
testing on two fields
You s hould unc hec k the fields that c ome from the
s ec ondary table (the s ales table in this example); that
is , they s hould not appear in the output.

Figure 5 - 1 0 s hows how the query returns Kam Winter as being a


c us tomer with no s ales rec ords . Some other c us tomers appear
in the res ult as well.

Figure 5-10. The correct unmatched records


Hack 43. Place a Grand Total in a Query

Use a Union query to combine raw data records with the data
total.

H ere's a neat way to lis t the rec ords in a table and have the total
appear at the bottom. Firs t, c reate a Select query to return the
table rec ords ; then us e the Union s tatement to c ombine them
with the data total. T he Sum aggregate func tion handles returning
the total. T he as s umption, of c ours e, is that the data is numeric .

You need to enter this type of query in the SQ L pane bec aus e
the query grid does n't s upport c reating or dis playing Union
queries . H ere is a s ample SQ L s tatement that c ombines s ales
rec ords with the s um of the s ales :

SELECT tblSales.Amount
FROM tblSales
UNION ALL SELECT Sum(tblSales.Amount) AS SumOfAmount
FROM tblSales;

Figure 5 - 1 1 s hows the bottom of the returned query rec ords .


Sure enough, a grand total is in the las t rec ord.

Figure 5-11. Including the total with the data


5.5.1. Hacking the Hack

You c an eas ily modify this query to return other aggregate


values , s uc h as a c ount or an average. For example, here is the
SQ L from before, but modified to return the average:

SELECT tblSales.Amount
FROM tblSales
UNION ALL SELECT Avg(tblSales.Amount) AS AvgOfAmount
FROM tblSales;
Hack 44. Sort Any Arbitrary String of
Characters

The A ccess query grid is great f or sorting your data, but you
need to help it sort on characters in the middle of a f ield.

I love the query grid. I t's very helpful for doing all s orts of s orts
(pun intended). But did you ever notic e that s orting on text data
always oc c urs on the whole field, going left to right? T his makes
s ens e bec aus e this is the mos t c ommon s orting requirement.
I magine, though, the problem of having to s ort on, s ay, jus t the
fifth c harac ter, or the las t three c harac ters , or in any other way
that is n't the norm.

T his is an is s ue es pec ially when A c c es s is us ed with data that


c omes from other s ys tems . A c c ounting s ys tems are notorious
for this . T hey often lump together a bunc h of dis parate data into
a fixed- width field. H ere's another c las s ic problem: you are
given a lis t of people's names in one field, s truc tured as firs t
name/las t name, but you need to s ort on jus t the las t name.

5.6.1. Sorting in the Middle

Figure 5 - 1 2 s hows a table filled with s ales rec ords . T he rec ords
follow a s tric t format c ompris ing a two- c harac ter vendor c ode
and a s ix- c harac ter date; the remaining digits are the s ales
amount, and the las t two of thos e digits are the dec imal part of
the amount. T herefore, the firs t Sales D ata rec ord
(C T 1 0 2 3 0 4 4 5 9 5 ) breaks down like this :

Vendor c ode is C T.

T he date is O c tober 2 3 , 2 0 0 4 .

T he amount is $ 4 5 .9 5 .

L et's s ay you need to s ort the rec ords by date. A s s hown in


Figure 5 - 1 2 , in eac h rec ord, the date s tarts in pos ition 3 and
takes up s ix plac es .

H ave you ever worked with data s uc h as this ? You need a rec ord
layout to go with the data; otherwis e, you c an't tell what kind of
data it is . What if you had to gues s whic h c harac ters make up
the date? G arbage in, garbage out, as the s aying goes .

T he bes t way to tac kle a problem s uc h as this is to us e the Mid


func tion. Mid is one of the func tions that let you manipulate
textual data. I t works by is olating a part of a larger text s tring.
You have to tell Mid tH Ree things : the s tring of data, the pos ition
you want to s tart from, and how many c harac ters to inc lude. T he
s yntax looks like this :

Mid(string, starting position, length)


E ven though we are c onc eptually working
with dates in this example, the s tored
information is in text format. T herefore, it's
eas y to manipulate the date with s tandard
s tring func tions .

Figure 5 - 1 3 s hows a query des ign in whic h the Mid func tion is
us ed. T he firs t c olumn is the Sales D ata field its elf, and the
s ec ond c olumn is a c alc ulated field us ing the Mid func tion. Within
the func tion, SalesData is enc los ed in brac kets . T his is the
s tandard way to put a field name in a func tion. Mid's parameters
are s et to is olate s ix c harac ters s tarting at pos ition 3 (the date,
in other words ).

Figure 5-12. A vendor code, date, and amount,


combined in one field
When the query runs , the s ec ond c olumn has jus t the date in it
bec aus e Mid does the job of grabbing the c harac ters from
pos itions 3 through 8 . T he s ec ond c olumn rec eives the s ort
bec aus e, after all, the date is what we need to s ort on. So, where
the Sort row and the s ec ond c olumn meet, s et the c hoic e to s ort
in as c ending order by s elec ting A s c ending from the drop- down
menu.

N ote in Figure 5 - 1 3 that the Show c hec kbox for the c alc ulated
field is unc hec ked. You don't have to ac tually dis play the c olumn
when the query is run. I t is us ed jus t to make the s ort happen,
but it does n't nec es s arily have to appear in the res ults .

Figure 5-13. Using the Mid function to isolate the


date for sorting
Figure 5 - 1 4 s hows the res ult of running the query. N ow the s ales
rec ords are s orted by date. T he firs t returned rec ord
(M R0 1 0 4 0 4 7 0 1 1 ) c ontains 0 1 0 4 0 4 , the equivalent of J anuary
4, 2004.

Figure 5-14. Records sorted by date


5.6.2. Sorting on Mixed Fixed Positions

What if you have to s ort on both the date and the amount? What
if the s ort has to s how the date in as c ending order and the
amount in des c ending order? T his is a c ommon requirement: to
s ee money amounts s orted from high to low. C an you do this ?

But of c ours e! I n this c as e, the tec hnique is to have two


c olumns with expres s ions , one eac h for the date and the amount.
Figure 5 - 1 5 s hows how you do this , with the amount s tarting in
the ninth pos ition. T he length parameter for the Mid func tion that
proc es s es the amount is s et to 5 . U s ually, a length is known, but
not always . I n this example, the amounts among the rec ords
might be four or five digits long, s o s etting the length to 5 works
for all rec ords .

Figure 5-15. A query design for sorting on two


subsections of the field
I n this example, as before, only the ac tual Sales D ata field is
s hown when the query runs . T herefore, the s ec ond and third
c olumns both have unc hec ked Show boxes . T he s ec ond and
third c olumns both us e Mid to work on different s ubs trings within
the s ame full SalesData s tring.

N ow the res ult is s lightly different. Figure 5 - 1 6 dis plays the


returned data. C omparing this res ult to the res ult s hown in
Figure 5 - 1 4 , you c an s ee that rec ords 6 through 8 have been
reordered. T hes e rec ords s hare the s ame date of J anuary 1 6 ,
2 0 0 4 (0 1 1 6 0 4 ), but now the amounts are reordered bas ed on
the query s pec ific ation.

Figure 5-16. Sorting on date and amount, which


returns a different order
5.6.3. Sorting on Characters When Their
Position Is Unknown

O ften, you need to manipulate data imported from external


s ys tems before you c an us e it in your applic ation. T his is a
c ommon is s ue with names . Your databas e table might have
s eparate fields for firs t and las t names . T his of c ours e makes it
a no- brainer to s ort on las t name. But imagine the diffic ulty when
you are given full names in one field. What if the names are in the
order of firs t and then las t name, with a s pac e in the middle, and
you need to s ort on the las t name? T he differenc e here,
c ompared to the previous s ales information example, is that you
c an't know, rec ord by rec ord, in whic h pos ition the las t name
s tarts .

T he tric k to s orting by las t name is to firs t determine the


pos ition of the s pac e. I n this c as e, you us e the InStr func tion
with the Mid func tion. I ns tead of hard- c oding the pos ition of the
s pac e, InStr returns the pos ition of the s pac e.

T he InStr func tion tells you the s tarting pos ition of the firs t
oc c urrenc e of a s ubs tring ins ide a larger s tring. I n this example,
the s tring being s earc hed is the C lient field, and the s ubs tring is
a s pac e. By its elf, InStr looks like this :

InStr([Client]," ")

H ere we us e the InStr func tion to tell the Mid func tion the
pos ition from whic h it s hould s tart c ounting. InStr is embedded
ins ide the Mid func tion. Together, they look like this :

Mid([Client],InStr([Client]," ")+1,10)

N ote that although the InStr func tion returns the pos ition of the
s pac e, we are interes ted in the s tarting pos ition of the las t
name. T his is one pos ition to the right of the s pac e, and for this
reas on, 1 is added after the InStr func tion. T he returned value of
InStr plus the value 1 is us ed as the s tarting pos ition parameter
in the Mid func tion.

Figure 5 - 1 7 s hows how to s et up a query us ing thes e nes ted


func tions . T he value of 10 is arbitrarily us ed here as the length of
the las t name. L as t names vary in length, but us ing 1 0
c harac ters to s ort on all but guarantees the s ort will be in the
right order.

Figure 5-17. Using nested functions in a sort


Figure 5 - 1 8 s hows the res ult of the query. C lients are s orted by
las t name, within a s ingle field that c ontains full firs t and las t
names . N eat!
5.6.4. Hacking the Hack

Sorting on names is n't diffic ult when firs t and las t names are all
you have to work with. But what about middle names , titles , and
s uffixes ? H ow c an you handle thes e? L et's up the ante on this
hac k and inc lude a c us tom func tion in the query.

Figure 5-18. Clients sorted by last name


T he func tion we need will examine the names in the C lient field
to determine the pos ition of the s pac e. H ere's the c atc h: now
there c ould be more than one s pac e. M y name is Ken S.
Bluttman; that's two s pac es one on eac h s ide of the middle
initial. Some names have three, four, or even five s pac es . T he
func tion is meant to s imply figure out the bes t s pac e to us e; it
figures out the pos ition of that s pac e and tells the Mid func tion
where it is .

Firs t, you write the func tion in a V BA c ode module. To do this ,


from the databas e window, go to the M odules tab, and s elec t to
c reate a new module. E nter this c ode:

Function find_space(client_name As String)


Dim name_length As Integer
Dim space_loop As Integer
Dim space_count As Integer
Dim partial_name As String
Dim first_space_position As Integer
'count spaces in full name
space_count = 0
name_length = Len(client_name)
For space_loop = 1 To name_length
If Mid(client_name, space_loop, 1) = " " Then
space_count = space_count + 1
End If
Next space_loop
'parse the full name using assumptions in each Case
Select Case space_count
Case 0
'no spaces found!
'return 1 as the position
find_space = 1
Case 1
'a first name and last name
'split after first space
find_space = InStr(client_name, " ")
Case 2, 3
'assume a first name, Middle name, and last name (2 spaces)
'or a first name, Middle name, last name, and suffix (3 space
'split after second space
find_space = InStr(client_name, " ")
first_space_position = find_space
partial_name = _
Mid(client_name, find_space, name_length - find_space)
find_space = InStr(partial_name, " ") + first_space_position
Case Else
'difficult to make assumption on name structure
'split after first space
find_space = InStr(client_name, " ")
End Select
End Function

I n a nuts hell, the func tion takes a c lient name, c ounts how many
s pac es are in it, and then determines whic h s pac e is bes t. T he
pos ition of that s pac e is us ed in the Mid func tion as before.

I n the query grid, the c all to the func tion, named find_space, is
embedded in the Mid func tion, like this :

Mid([Client],find_space([Client])+1,10)
Figure 5 - 1 9 s hows how to s et up the query.

Figure 5-19. The Mid function, using the


find_space function
When the query runs , eac h c lient name is examined in the
find_space func tion. T he func tion returns the bes t s pac e pos ition,
and the names are s orted. Figure 5 - 2 0 s hows the res ults of
running the query.

Figure 5-20. Sorting by last name when middle


names and suffixes are present
L ooking c los ely, you will s ee that the s ort is n't without problems .
T he way the func tion is written, it as s umes that when there are
two s pac es , the format of the name is firs t name, las t name,
s uffix. T his works for a name s uc h as A lex A vakian I I I . T he
func tion as s umes the las t name s tarts after the firs t s pac e.

U nfortunately, a name s uc h as Tammy J ill A dams does n't end up


with the other las t names beginning with A . T he func tion
as s umes the bes t s pac e is the firs t, and the name is s orted as
though the las t name s tarts with J . Tammy's las t name s tarts
after the s ec ond s pac e. Sorry, Tammy!

Splitting names apart is traditionally a thorny problem. Some


names always c onfound the bes t intentions of a name- pars ing
routine. T hat mus t be why I keep getting c atalogs addres s ed to
M r. Ken.

5.6.5. See Also

"U s e a C us tom Func tion in a Q uery" [Hack #48]


Hack 45. Summarize Complex Data

Take advantage of Crosstab queries to get a view on


multif aceted data.

When you need to aggregate data that has more than a s imple
grouping s truc ture, Crosstabs are the way to go. "C reate
C onditional Subtotals " [Hack #29] s hows you how to us e groups
and c onditional s umming on a report. T hat works as long as the
c onditions don't lead to an overwhelming number of pos s ibilities .

T he example in that hac k us es five s tates and two years on


whic h to c reate s ubtotals . T he data model, though, has another
table in play: a table of pets (the model is a s imulation of a
veterinary prac tic e). T his c reates a large number of
pos s ibilities , s uc h as all c at vis its in N ew York in 2 0 0 3 , or all
dog vis its in P enns ylvania in 2 0 0 4 , and s o on. Figure 5 - 2 1
s hows the updated data model for this hac k.

Figure 5-21. The pets data model


T he data model inc ludes s even types of pets (bird, c at, dog,
ferret, hors e, monkey, and s nake), five s tates (C T, M A , N J , N Y,
and P A ), and two years of data (2 0 0 3 and 2 0 0 4 ). T his makes
7 0 pos s ible c ombinations . T he bes t way to s um up the number
of vis its in whic h all thes e c ombinations of c riteria are mixed and
matc hed is to us e a Crosstab query.

To get s tarted, we mus t put together a Select query to join the


different tables and return the fields needed in the Crosstab. N ote
that the Select query has a c alc ulated field that is olates the year
out of the D ateO fServic e field. Figure 5 - 2 2 s hows the des ign of
the Select query.

Figure 5-22. A Select query on which a Crosstab


will run
5.7.1. Introducing the Crosstab

A c c es s has a C ros s tab Q uery Wizard, whic h walks you through


c reating a Crosstab query. Figure 5 - 2 3 s hows the N ew Q uery
dialog box in whic h a Crosstab query is initiated.

Figure 5-23. Starting up the Crosstab Query


Wizard

I n this example, s elec t the qryStatesPetsDates query in the firs t


s c reen of the wizard, as s hown in Figure 5 - 2 4 .

I n the next s c reen, s elec t two fields as the rows . I n a Crosstab


query, the rows ac t as groups . N ote that at leas t two fields mus t
remain after you s elec t fields for the rows . Selec t the s tate and
pet type fields to be the row headings .
Figure 5-24. Selecting the Select query
I n the next s c reen, s elec t a field to be the c olumn field. Crosstabs
require at leas t one c olumn field. C hoos e Year here, as s hown in
Figure 5 - 2 5 (note that this figure s hows the third s c reen, not the
s ec ond).

Figure 5-25. Selecting Year as the column


heading
I n the las t field s elec tion s c reen, one field remains . Selec t the
type of aggregationin this c as e, C ount, as s hown in Figure 5 - 2 6 ,
bec aus e the purpos e is to c ount vis its . A ls o be s ure to unc hec k
the "Yes , inc lude row s ums " c hec kbox on the left. Keeping this
c hec ked returns a field of s ums bas ed on jus t c ombinations of
s tate and pet type (the row headings ) and that is n't our foc us
here; we're looking for the c ombination of s tate, pet type, and
year.

Figure 5-26. Selecting to return a count


When the query c ompletes , all the c ounts are available. Figure
5 - 2 7 s hows how the query pres ents s ums in all c ombinations of
s tate, pet type, and year.

Figure 5-27. The completed Crosstab query


T here are 3 5 rec ords by virtue of the fac t that s tate and pet type
are row headings , and year is a c olumn heading. T his s till
provides the 7 0 unique c ombinations bec aus e the two years ,
2 0 0 3 and 2 0 0 4 , eac h have their own c olumn.

5.7.2. See Also

"C reate C onditional Subtotals " [Hack #29]


Hack 46. Get All Combinations of Data

Remove the Join clause in a SQL statement to return a


Cartesian product (which returns all possible combinations).

L eaving the Join c laus e out of a SQ L s tatement returns a


number of rec ords equal to the produc t of the number of rec ords
in the tables . Taking two tables , for example, as long as one field
from either table is des ignated for output, the number of returned
rec ords in a Select query of this des ign is the produc t of the
c ounts of the two tables .

Behind the s c enes , the query is matc hing all c ombinations of the
data. I f eac h table has hundreds or thous ands of rec ords , the
returned number of rec ords c an be in the millions . T his c an be
dis as trous that is , unles s returning rec ords in this way is by
des ign. Why would you do this ? I t makes s ens e to do it to
explic itly return all the c ombinations . I f you need s uc h all-
inc lus ive matc hing, you don't have to bother with any V BA c ode;
jus t c reate a query that does it for you. Figure 5 - 2 8 s hows a
table with 1 2 people and another table with eight pos s ible
ac tivities .

Figure 5-28. Two unrelated tables


C reate a Select query with the two tables , and des ignate the
s ingle field from eac h table for output. Figure 5 - 2 9 s hows the
query des ign. N ote that the lac k of a relation line between the
tables is intentional.

Figure 5-29. A Select query of unrelated tables

A little tip- tap on a c alc ulator s hows 9 6 c ombinations of pers on


and ac tivity. Running the query returns the 9 6 rec ords , as s hown
in Figure 5 - 3 0 .

Figure 5-30. Returning the combined records


T he query res ults c an be c opied, exported, and s o on. T his is a
fas t and eas y way to get all c ombinations of data. G oing one
s tep further, a third table is added to the query. T his new table
c ontains parts of the day, in two rec ords : morning and afternoon.
Running the query returns the expec ted 1 9 2 rec ords , whic h is
the produc t of 1 2 x 8 x 2 . Figure 5 - 3 1 s hows the res ult.

Figure 5-31. Returning combinations on three


unrelated tables
A lthough it is n't effic ient to handle data in this unrelated way, at
leas t with regard to databas e work, a s et of c ombinations s uc h
as this makes for us eful reports , c hec klis ts , and s o on.
Hack 47. Don't Let Nulls Ruin Data
Summaries

When nulls are mixed in with valid data, incorrect results can
occur. Here are some guidelines to tame the beast.

When you are dealing with values in A c c es s , you might be


tempted to think that a blank field is s imply a blank field.
H owever, there is a differenc e between a blank field that is filled
in with an empty s tring and a blank field that is null. For example,
when you are looking at number fields , there is a differenc e
between a field with a 0 value and a field with a null value. T his
hac k helps you work with thes e nonvalue values .

T he firs t frus trating thing about nulls is that if you write a line
s uc h as this , every line will s how up as Not Blank, even if you
have null values :

IIF([Amount]=Null,"Blank","Not Blank")

T his oc c urs bec aus e in a Boolean expres s ion, any item


c ompared to Null returns False.

T here is an eas y way to deal with this , us ing a func tion available
in A c c es s c alled ISNULL. T his func tion returns a Boolean and
allows you to perform your tes t. H ere is how to rewrite the
previous example:
IIF(ISNULL([Amount],"Blank","Not Blank")

T hat c linc hes it. N ow, any enc ountered null is c onverted to
Blank.

5.9.1. Nulls in Number Fields

L et's as s ume you have a table with a field c alled A mount. You
are trying to determine the average of that field (as s ume als o
that the average does n't need to be weighted). I f you write a
query that attempts to determine the average value, the SQ L
might look like this :

SELECT Avg(tbl_Amount.Amount) AS AvgOfAmount


FROM tbl_Amount;

T his gives you the average amount of the values in that field.
H owever, if you have nulls for any of the values , the query will
ignore them. So, if your values are 8, null, 8, null, 8, null, the
average is 8. I f your values are 8, 0, 8, 0, 8, 0, the average is
4. D epending on the purpos e of the query, you might want to s ee
4 ins tead of 8.

I f you want to s ubs titute 0 for null, you c an try to do it with the
ISNULL func tion by writing a line s uc h as this :

IIF(ISNULL([Amount]),0,[Amount])
T here is a muc h eas ier way, though. T he NZ func tion available in
A c c es s requires two parameters : one for the value and the other
for the value if it is null. You c an us e this for both number and
s tring func tions . H ere is what the SQ L of the query looks like
us ing the NZ func tion:

SELECT Avg(NZ([Amount],0)) AS AverageofAmount


FROM tbl_Amount;

A s you c an s ee, this is more c ompac t than writing out IIF


s tatements to perform the s ame func tion.

N ext, let's look at an example of a s tring func tion. A s s ume you


live in an area where pine trees are popular, and you have a
s urvey in whic h you input the type of tree only if it is s omething
other than pine; otherwis e, you jus t input the number of trees
(bad des ign, but I 've s een wors e) and leave the tree type field
null.

N ow, as s ume that you have inherited this applic ation, and you
want to us e it in other areas of the c ountry. You want to update
all the null Tree_Type fields with P ine Tree. You c an do s o with
the NZ func tion. H ere is what the SQ L for this query looks like:

UPDATE tbl_TreeTypes SET tbl_TreeTypes.Tree_Type =


nz([Tree_Type],"Pine Tree");

T his will work, but you have to update every rec ord. So, if you
c an't us e tree_Type = Null, you might as k if you c an us e null for
c riteria in a query. You c an, us ing one of two methods . T he
eas ies t way is to us e IS NULL for the c riteria. T he previous query
looks like this us ing IS NULL:

UPDATE tbl_TreeTypes SET tbl_TreeTypes.Tree_Type = "Pine T


WHERE (((tbl_TreeTypes.Tree_Type) Is Null));.

5.9.2. Preventing Nulls

I t might be nec es s ary for you to prevent nulls and zero- length
s trings in your databas e in the firs t plac e. A good example for
this might be a name field or a ZI P c ode field. You c an do this
through either your table des ign or your data entry forms .

5.9.2.1 Table design to prevent nulls and


zero-length strings.

When you des ign your table, you c an s et s everal properties to


help you handle blank fields , as s hown in Figure 5 - 3 2 .

Figure 5-32. Setting field properties to control


nulls and zero-length strings
T he firs t is a property c alled Required. I f you enter Yes for the
Required property, you are telling A c c es s a value mus t be
entered in this field for it to be s aved. T his won't prevent
s omeone filling it with a zero- length s tring. Setting the Allow Zero
Length property to No forc es an entry other than a zero- length
s tring. I f you s ay Yes, and you jus t want to eliminate nulls (tes t
for blank by writing [Field]=""), you c an s et the Default Value
property to "".

I f you s et thes e two properties c orrec tly, you will have a value in
eac h field, and you won't have to deal with nulls in your
applic ation. T he s ame thing applies to number fields : there is a
Required property you c an s et to Yes, and there is als o a Default
Value property. N ormally, the Default Value property is s et to 0 in
a number field. H owever, if you want to ens ure that us ers enter a
value in this field and don't s imply s kip over it, you c an remove
the 0 in the default value field and s et the Required property to
Yes. T his ens ures that the rec ord is n't s aved until the us er puts
a value in the field (0 c an be entered unles s you have a
validation rule in plac e).

5.9.2.2 Form design to prevent nulls and


zero-length strings.

I f you don't have c ontrol over the table des ign, but you want to
ens ure the data entered is ac c urate, you c an do s o through
A c c es s forms . When you c reate a form in A c c es s , s everal
textbox properties are available that c an help you ens ure
meaningful data, as s hown in Figure 5 - 3 3 .

Figure 5-33. Controlling nulls through form


control properties

You c an s et the Default Value property to allow a zero- length


s tring if all you want is avoid a null.

You c an als o write c ode in the Lost Focus event. I t is important to


do this in the Lost Focus event bec aus e the Before Update event
won't fire if the field is jus t tabbed through, and the After Update
event fires after the field has ac tually been c hanged. H ere is
what that c ode might look like for a text box c alled TextBox1 :

Private Sub TextBox1_LostFocus( )


If IsNull(Me.TextBox1.Value) Then
MsgBox "You must enter a value in this field", vbOKOnly, "Im
Me.TextBox2.SetFocus
Me.TextBox1.SetFocus
End If
End Sub

You might be wondering why the Set Focus event is c alled twic e.
You mus t s et the foc us off of the text box and then bac k onto it;
otherwis e, it won't let you s et the foc us to the box. You might
als o be wondering why the c ode does n't us e the Validation Rule
property. T he validation rule run onlys when the field is c hanged,
s o if you s imply s kip a field, it won't run.

T here is a limitation to us ing the Lost Focus event if a us er us es


th mous e and does n't c lic k eac h field. You c an get around this
limitation by s etting the Cycle property on the O ther tab of the
Form P roperties dialog box to Current Record (as s hown in Figure
5 - 3 4 ) and then s etting the Navigation Buttons property to No on
the Format tab of the s ame dialog box (as s hown in Figure 5 - 3 5 ).

Figure 5-34. Setting the Cycle property to


Current Record
Figure 5-35. Setting the Navigation Buttons
property to No
O nc e you have done this , you c an c reate your own buttons to
allow us ers to move to the next rec ord, and you c an put your
validation text in there. I n all c as es , it is muc h eas ier to as s ign
thes e s ettings during table des ign, but many times you don't
have that c ontrol.

Michael Schmalz
Hack 48. Use a Custom Function in a
Query

Write a custom f unction to manipulate multiple data f ormats.

When you need to perform c omplex manipulation of data in a


query, it is often eas ier to write a func tion to perform the
manipulation. You c an avoid us ing c omplex func tions ins ide a
query and always write a us er func tion. H owever, it is bes t to us e
your judgment. I f you have a rather s imple c onc atenation of a
few fields , I s ugges t you write a us er func tion within your query.
But if you need to perform s omething c omplex and it is likely
that you will need to do it in other plac es in the applic ation,
c reating a new func tion will s ave you a lot of time.

5.10.1. Creating a New Function

To c reate a func tion, go to the M odules tab in A c c es s , and


c reate a new module. O nc e you are in the new module (you c an
als o go into D es ign view in an exis ting module), s elec t I ns ert
P roc edure. G ive it a name, s elec t Func tion as the Type,
and s elec t P ublic as the Sc ope. O nc e you have your func tion,
you c an plac e variables between the parenthes es . A fter the
parenthes es , give your func tion a type by typing A s datatype;
this ens ures that your func tion is returned in the datatype that
you expec t.
5.10.2. Manipulating Dates

D ates c ome from different s ys tems in many different formats ,


inc luding Y Y Y Y M M D D , M M /D D /Y Y Y Y, and M M D D Y Y Y Y. T he
problem c omes when you need to have the date in another
format, as happens when you import data from a mainframe or a
fixed- length text file in whic h the date is ac tually imported as
text. T his firs t example as s umes the format being imported is
Y Y Y Y M M D D or Y Y M M D D . I n this func tion, the s tring is brought
in as an argument and the Left, Right, and Mid func tions are
us ed with the CDate func tion to c reate the date:

Public Function GetDate(Dt As Variant) As Date


Dim MM As String
Dim DD As String
Dim YYYY As String
If VBA.InStr(1, Dt, "/") > 0 Then Dt = ""
Select Case VBA.Len(Access.Nz(Dt, ""))
Case 8
YYYY = VBA.Left(Dt, 4)
MM = VBA.Mid(Dt, 5, 2)
DD = VBA.Right(Dt, 2)
GetDate = VBA.CDate(MM & "/" & DD & "/" & YYYY)
Case6
YYYY = VBA.Left(Dt, 2)
MM = VBA.Mid(Dt, 3, 2)
DD = Right(Dt, 2)
GetDate = VBA.CDate(MM & "/" & DD & "/" & YYYY)
Case Else
GetDate = #1/1/1900#
End Select
End Function
N otic e that this func tion pas s es the s tring as a variant; this
allows it to tes t for a null value or trap an ac tual date. I f the
variable is dec lared as a s tring, a null value res ults in an error.
I n this c as e, if a real date, null value, or anything other than
Y Y M M D D or Y Y Y Y M M D D is pas s ed, it returns a date of
1 /1 /1 9 0 0 . You c an s et that date to be s omething els e. T he If
InStr … Then line tes ts to s ee if a s las h (/) is in the Dt variable. I f
it is , the proc edure s ets Dt to an empty s tring.

N ow, let's as s ume you need to take it the other way. A s s ume
you have a date field, and you need to turn it into a fixed- length
s tring. H ere is how the required func tion looks :

Public Function GetDateString(Dt As Date) As String


Dim MM As String
Dim DD As String
Dim YYYY As String

MM = VBA.Right(DatePart("m", Dt) + 100, 2)


DD = VBA.Right(DatePart("d", Dt) + 100, 2)
YYYY = VBA.DatePart("yyyy", Dt)

GetDateString = YYYY & MM & DD

End Function

T his func tion pas s es the variable as a date. I f a null or nondate


is pas s ed to the func tion, it returns #Error. T his res ult s imply
s hows how the func tion reac ts when an inappropriate date is
pas s ed to it.
A ls o notic e that the func tion us es the Right func tion along with
DatePart and then adds 100 to it. T his ens ures that the month and
date return two digits . I f you didn't do this , it might work on your
c omputer if you have dates s et with leading zeros , but it c ould
bomb on another c omputer. You c an als o us e this logic anytime
you need to put leading zeros in for a number. I f you need five
leading zeros , type right(x+100000,5).

Your next ques tion might be, "H ow do I us e thes e func tions ? "
You c an c all thes e func tions from within a query, jus t like you
would any other func tion. You c an als o us e them in forms ,
reports , and s o on. L et's as s ume you have a table c alled
tbl_P ers onalI nformation, and you have a field c alled H ireD ate
that is a date type. I f you need to have a field in a query that
formats the date as Y Y Y Y M M D D , write it in the query's D es ign
view, like this :

TextHireDate: GetDateString([HireDate])

T hat is all you need to do. T he date will be c onverted to a s tring


in the format that you need. I t is important to note that you
c ould do the s ame thing the func tion does right in the query.
H owever, by doing it that way, you have no way of eas ily reus ing
the logic els ewhere in the applic ation.

Michael Schmalz
Hack 49. Create Access Tables with SQL
Server Scripts

SQL Server writes scripts that create tables. With a little


editing, you can put them to work in A ccess.

So muc h attention is given to ups izing from A c c es s to SQ L


Server. T his makes s ens e bec aus e, after all, databas es tend to
grow, not s hrink. H owever, this hac k is n't c onc erned with data; it
has to do with des ign. E very s o often you might need to
duplic ate a SQ L Server s c hema in A c c es s . T his c ould be for the
very purpos e of preparing your A c c es s databas e for SQ L Server.

I f you are familiar with SQ L Server, you already know SQ L Server


E nterpris e M anager c an write SQ L create table s c ripts bas ed on
exis ting tables . I f this is all new to you, c ome along for the ride!

5.11.1. Walking Through Enterprise


Manager

E nterpris e M anager, s hown in Figure 5 - 3 6 , is the utility you us e


to manage SQ L Server.

T he P ets databas e is ac c es s ed in the left pane. T he databas e


c ontains various objec ts . T he tables of the P ets databas e are
lis ted in the right pane. M os t of the tables are s ys tem tables .
T he las t three tables tblA ppointments , tblC lients , and tblP ets are
us er tables . T hat means I c reated them; this is the s ame
paradigm we us e in A c c es s .

To generate a SQ L s c ript, right- c lic k the tblC lients table, and


s elec t A ll Tas ks G enerate SQ L Sc ript…, as s hown in Figure
5 - 3 7 . A fter you s elec t a des tination for the s c ript, a file is
c reated.

A text file is written with SQ L Servers pec ific SQ L s tatements .


Figure 5 - 3 8 s hows the generated s c ript opened in N otepad.

Figure 5-36. Exploring Enterprise Manager


Figure 5-37. Preparing to generate a SQL script
A s is , this s c ript won't work if it's run ins ide an A c c es s query.
T he pertinent part is in the middle, s tarting with the Create Table
s tatement. Create Table is rec ognizable SQ L in A c c es s . E ven s o,
the field types aren't c orrec t in A c c es s , s o we s till have to c lean
this up. Knowing what to do requires a little SQ L knowledge, but
if you haven't learned any yet, it's not a bad thing to get to know.

Figure 5-38. The generated script


P lenty of books are available on the
s ubjec t of SQ L . See the end of this hac k
for a s hort lis t.

O nc e you've c leaned up the SQ L , and it's ready for A c c es s , you


need to c all up a Data Definition query. Figure 5 - 3 9 s hows where
to ac c es s this s pec ial type of query in A c c es s .

Figure 5-39. Creating a Data Definition query


O nc e you s elec t the query type, you are left in a pane in whic h
SQ L is entered. Figure 5 - 4 0 s hows the pane with the edited SQ L
s c ript. N ow it is ready to run in A c c es s .

Sure enough, when this query runs , it c reates a new tblC lients
table. So, es s entially, not only is it pos s ible to rec reate a SQ L
s c hema in A c c es s , but you als o c an edit a SQ L Servergenerated
s c ript to get the job done.

Figure 5-40. A SQL script, ready to run in Access

5.11.2. See Also


SQL Pocket Guide (O 'Reilly)

SQL in a Nuts hell (O 'Reilly)


Hack 50. Use Wildcards in Queries

The Like operator comes in handy when you don't quite


remember how to spell a data item.

When you c an remember only a partial amount of information, a


great way to s earc h through your databas e rec ords is to us e the
SQ L Like operator. C ombining Like with wildc ards makes for
s ome powerful queries .

For example, imagine you have a databas e table filled with


c us tomer rec ords . You need to look up s omeone whos e las t
name s tarts with D e, and that's all you c an rec all about her
name.

Figure 5 - 4 1 s hows a query that us es the as teris k (* ) wildc ard to


find all c us tomers whos e las t name s tarts with D e. T he as teris k
is a plac eholder for any number of c harac ters . T herefore, running
this query returns all c us tomers with D e as the firs t two
c harac ters in their las t name.

L et's s ay ins tead that you remember the las t name s tarts with D
and is four c harac ters long. I n this c as e, the ques tion mark (?)
wildc ard c omes in handy. You us e the ques tion mark as a
plac eholder to repres ent a s ingle c harac ter. Figure 5 - 4 2 s hows
three ques tion marks being us ed to make up for three s pac es
after the letter D .

Figure 5 - 4 3 s hows the res ult of running the query. A ll c us tomers


with four- c harac ter las t names that s tart with D are returned.
U s ing wildc ards lets you really get to your data in c reative ways .
You might rec all the firs t and las t letter of a name, or even jus t
that a name is four c harac ters long, but you don't remember any
of the ac tual c harac ters ! I n s uc h as c as e, us ing a c riterion of
Like "????" returns all c us tomers with las t names that are four
c harac ters long, as s hown in Figure 5 - 4 4 .

Figure 5-41. Finding customers with an asterisk


wildcard
Figure 5-42. Finding customers using question
marks
Figure 5-43. Returning records based on the
wildcards

Figure 5-44. Returning all customers with a last


name four characters long
Hack 51. Get Cleaner Or-Based Criteria

A void using multiple rows in the query grid by using the In


operator.

T he A c c es s query grid is des igned for eas y query as s embly, and


it does a great job. Without a doubt, the grid has been an
es s ential learning tool that helps us unders tand and us e queries .

You c an us e the grid to c reate Or- bas ed c riteria in two ways .


Figure 5 - 4 5 s hows a typic al way to s et up a query. I n this c as e,
the query returns rec ords in whic h the s tate is any of s ix
pos s ible values . A s you c an s ee, however, if a few more s tates
were to be inc luded, it would bec ome nec es s ary to s tart s c rolling
vertic ally to work on the lis t of s tates .

Figure 5-45. Creating Or-based criteria


Figure 5 - 4 6 s hows an alternate way to s et up the Or c riteria. T he
s pec ified s tates are put on one row, with Or s tatements
throughout. H owever, this des ign als o s uffers from bec oming
unwieldy if more s tates are added. E ac h additional s tate being
added als o requires another Or operator, s o the expres s ion c an
bec ome quite long.

Figure 5-46. A long criteria statement


T he In operator is the s olution to this dilemma. T he In operator
is perfec t for es tablis hing Or- bas ed c riteria. Whereas in Figure
5 - 4 6 the inc lus ion of eac h s tate requires another Or operator,
only one In operator is nec es s ary, as s hown in Figure 5 - 4 7 .

Figure 5-47. Using the In operator


U s ing the In operator makes it eas y to add more s tates to the
c riteria. J us t make s ure you s eparate eac h s tate abbreviation
with a c omma.
Hack 52. Get Cleaner And-Based Criteria

Remove the need f or multiple A nd statements by combining the


In and Not operators.

Sometimes , c riteria are s et up to filter out c ertain rec ords


ins tead of inc luding them. T his revers al of logic makes s ens e in
s ituations in whic h you want to return mos t of the rec ords , but
not all of them. "G et C leaner O r- Bas ed C riteria" [Hack #51]
s hows how to us e the In operator to better manage Or bas ed
c riteria. When you s et up c riteria to be exc luded, however, us e
the And operator. For example, you might as k, "G ive me all
s tates , exc ept C alifornia and N ew M exic o."

Figure 5 - 4 8 s hows a query des ign that exc ludes s ix s tates from
the query res ults . A s new s tates are added to this lis t, an
additional And operator is required. E ventually, this method of
writing multiple Andoperators bec omes tires ome and you end up
having to s c roll to read through it all.

Figure 5-48. Using multiple And operators to


filter out records
T he In operator might c ome to mind as a way to reduc e the long
c riteria s tatement. H owever, the point is to not inc lude the
c riteria. T he s olution is to us e both the In and the Not operators .
Not is a logic al operator: it revers es a c ondition. I nc luding it with
an In operator res ults in a lis t of items not to inc lude, whic h
works perfec tly for this type of query.

Figure 5 - 4 9 s hows the improved query, in whic h the multiple And


s tatements are removed.

T he query returns the s ame res ults , with a les s - c luttered SQ L


s tatement.

Figure 5-49. Using Not and In together


Hack 53. Create an Outer Join

A ccess doesn't support making an outer join; here's a


workaround.

T he s tandard join between two tables returns rec ords that matc h
bas ed on the field or fields being s elec ted as keys . T his is c alled
an inner join. For example, a s tatement s uc h as "give me all
c us tomers and their s ales rec ords " us ually is interpreted to
mean return all the s ales rec ords and the c us tomers to whom
thos e rec ords belong.

Sometimes , though, a left or right join is reques ted. For example,


"give me all our c us tomers and any s ales they might have had"
is really a reques t for a left join. I n other words , return all the
rec ords from the left table (the c us tomers ) and any s ales
rec ords that go with them.

Figure 5 - 5 0 s hows how rec ords returned from a left join query
look. I n this example, there are more c us tomers than purc has e
date rec ords . Some of the c us tomers have no purc has es and
therefore have no data in the c olumn on the right.

A right join returns all rec ords from the table on the right and
only thos e rec ords from the table on the left that matc h on the
key. T he three types of joins inner, left, and rightare eas y to s et
up. T he inner one is the default when two tables are related.

You c an s et the join type in either the Relations hips window or in


a query des ign by double- c lic king direc tly on the line between
the two tables . Figure 5 - 5 1 s hows the J oin P roperties dialog box
that appears when the line is double- c lic ked. T he dialog
c ontains options for the three join types .

Figure 5-50. Returned records from a left join


query
Figure 5-51. Setting join properties
A s you c an s ee, there is no option to c reate an outer join, whic h
would return all the rec ords that matc h, plus the rec ords that
don't matc h from both tables . T he tric k to doing this is to s imply
as s emble the three types of available join queries into one
query. T his final query us es the Union operator to as s emble the
res ults from the other three queries .

A Union query works only with s traight SQ L s tatements . You c an


enter the SQ L direc tly into a new query or, to make it eas ier,
c opy the generated SQ L from the three join types and pas te it
into a new query. A ll you need to do is s tart the s ec ond and third
mini- SQ L Select s tatements with the Union operator in a new
query, like this :

SELECT tblCustomer2.LastName, tblSales2.PurchaseDate


FROM tblCustomer2 INNER JOIN tblSales2 ON
tblCustomer2.CustomerID = tblSales2.Customer_ID
Union
SELECT tblCustomer2.LastName, tblSales2.PurchaseDate
FROM tblCustomer2 LEFT JOIN tblSales2 On
tblCustomer2.CustomerID = tblSales2.Customer_ID
Union
SELECT tblCustomer2.LastName, tblSales2.PurchaseDate
FROM tblCustomer2 RIGHT JOIN tblSales2 ON
tblCustomer2.CustomerID = tblSales2.Customer_ID;

Figure 5 - 5 2 s hows the res ult of running the SQ L .

Figure 5-52. The results of an outer join


Both c olumns (eac h c omes from a different table) have
blanks where there was no matc hing rec ord in the other tableand
the rec ords that matc h are there as well.
Hack 54. Use Regular Expressions in
Access Queries

Sometimes wildcards aren't enough. With a little hacking, you


can use regular expressions in your queries.

A lthough A c c es s allows for s ome powerful s tring matc hing (s ee


"Wildc ard c harac ters and the L ike operator" in the A c c es s H elp
s ys tem), s ometimes you require an even more powerful s olution.
M ic ros oft added the ability to us e regular expres s ions bac k in
Vers ion 5 .0 of its Windows Sc ripting E ngine, bringing it up to par
with J avaSc ript. You c an us e this power ins ide an A c c es s query
as well.

A lthough the advanc ed details of regular expres s ions are


beyond the s c ope of this hac k, this example will get you s tarted
if you are new to the s ubjec t. I f you need more information, I
rec ommend the book Mas tering Regular Expres s ions (O 'Reilly).

I n many c as es it's pos s ible to work around the lac k of built- in


regular expres s ions us ing A c c es s 's wildc ard c harac ters and
multiple c alls to different s tring func tions , s uc h as Left, Mid,
Right, Len, and s o on. H owever, onc e you s ee what you c an do
with a s ingle c us tom func tion c all, you c an imagine the advanc ed
pos s ibilities and time s avings .

5.16.1. Creating the Custom Function


T he firs t thing we need to do is c reate a func tion that c an be
c alled from our A c c es s query that ties into the M ic ros oft
Sc ripting Runtime library.

T his hac k as s umes the mac hine you are


running has the lates t vers ion of
M ic ros oft's Sc ripting E ngine ins talled. I f
you are uns ure, vis it
http://www.mic ros oft.c om/s c ripting.

T he following c ode us es the CreateObject func tion s o that you


don't have to c hec k the Referenceeac h time the c ode is plac ed in
a new databas e:

Public Function RegExp(strString As String, _


strRegExp As String, Optional bolIgnoreCase As Boolean =
Boolean
Dim re As Object
Set re = CreateObject("vbscript.RegExp")
re.Pattern = strRegExp
re.IgnoreCase = bolIgnoreCase
If re.Test(strString) Then
RegExp = True
Else
RegExp = False
End If
End Function
T he func tion has two required parameters : the s tring being
matc hed agains t and the s tring that c ontains the regular
expres s ion. T he third, optional parameter tells the func tion
whether to matc h the regular expres s ion while ignoring the c as e;
the default won't ignore the c as e.

5.16.2. Creating an Example Query

A s an example, let's look at verifying part numbers by finding


thos e that don't matc h a given c riterion. M any times , you might
rec eive data from multiple people and platforms that needs to be
c leaned before going into a mas ter databas e. L et's s ay that part
numbers for a fac tory have the following c riteria:

T hey mus t s tart with a c apital P N or a c apital P.

T he next two pos itions mus t be numeric .

T he next pos ition mus t be a c apital letter (A Z).

T he next three to four pos itions mus t be numeric .

T he next five to s ix pos itions mus t be c apital letters


(A Z).
E xamples of part numbers that meet the c riteria inc lude
P N 1 2 W1 2 3 A BC D E and P 1 2 W1 2 3 A BC D E . E xamples that don't
meet the c riteria inc lude P N 1 2 W1 3 A BC D E (only two digits after
the W) and 1 2 W1 2 3 A BC D E (does n't s tart with P N or P ).

G iven the s et of c riteria for the part number, here's the regular
expres s ion:

"^(PN|P)[0-9][0-9][A-Z][0-9]{3,4}[A-Z]{5,6}$"

A s mentioned earlier, thes e regular expres s ions c an bec ome


quite overwhelming until you get us ed to them. I f you aren't
familiar with them, I s trongly rec ommend additional reading to
learn the full power of thes e expres s ions . To better unders tand
it, let's break down this expres s ion:

Tells the expres s ion to s tart at the beginning of the


s tring

(PN|P)

Says to matc h the c harac ters P N or P

[0-9][0-9]

Tells the expres s ion to matc h two digits , both in the


range 0 through 9
[A-Z]

Says to matc h a s ingle c harac ter A through Z

[0-9]{3,4}

Says to matc h a s ingle digit 0 through 9 at leas t three


times and a maximum of four times

[A-Z]{5,6}

Says to matc h a s ingle c harac ter A through Z at leas t


five times and a maximum of s ix times

Figure 5 - 5 3 s hows the layout for a query to find part numbers


that don't matc h our c riteria.

Figure 5-53. Calling the RegExp function from a


query
Running the query in Figure 5 - 5 3 returns the part numbers that
do not matc h our given c riteria s o that you c an review them
before plac ing them into a mas ter databas e. A lthough you c an
do this without tapping into the power of regular expres s ions , it
requires a muc h more involved s olution.

5.16.3. Hacking the Hack


A s you dis c over the power of regular expres s ions , you will find
them to be very robus t for all kinds of text proc es s ing. A nother
handy tric k is to us e them to verify text input on a form. To do
s o, c all the c us tom RegExp func tion from the BeforeUpdate event of
the text box. I f it returns false, s et the Cancel parameter variable
to true, whic h c lears the input on the text box.

You c an even add an advanc ed feature to your applic ation, whic h


allows the us er to do s earc hes bas ed on her own regular
expres s ions !

Steve Huff
6. Multiuser Issues
Sec tion 6 .1 . H ac ks 5 5 5 8

H ac k 5 5 . Tes t for D uplic ation

H ac k 5 6 . D is tribute a Split D atabas e with P redefined


Table L inks

H ac k 5 7 . Build a T ime- O ut Feature

H ac k 5 8 . I mplement U nique U s ernames


6.1. Hacks 5558
You c an deploy A c c es s databas es as s tandalone applic ations as
well as in s hared s ys tems . A lthough working with a s hared
databas e provides many benefits in terms of effic ienc y, is s ues
c an c rop up with regard to us ers ' ac tivities getting in the way of
eac h other's data. T his c hapter provides a few workarounds for
integrating A c c es s in a multius er environment while ens uring
data does n't get trampled. "Build a T ime- O ut Feature" [Hack
#57] c atc hes and c ompletes idle rec ord edits , thereby allowing
others to make c hanges . "Tes t for D uplic ation" [Hack #55]
s hows a way to validate data before us ers duplic ate eac h other's
entries . T he c hapter als o c overs a dis tribution method [Hack
#56] that makes it eas y to get a s plit databas e from your
development mac hine to your c lients , with the table links
already matc hing the network.
Hack 55. Test for Duplication

Bef ore you insert multiple entries into master tables in a busy
data-entry environment, you'll need a custom validation process
to avoid duplicated data.

J us t bec aus e a databas e is deployed on the s erver does n't mean


the entire applic ation mus t be in that s erver c opy. A c ommon
approac h is to put the data in the s erver databas e and dis tribute
the forms to the loc al c lient c omputers , ins ide another A c c es s
file. T his is a typic al A c c es s vers ion of a c lient/s erver
applic ation.

Bec aus e the c lient ins tallations are A c c es s databas es , us ing


tables in the c lient databas es opens up pos s ibilities . O ne us eful
tec hnique is to have new data entries go into loc al tables firs t,
and later to bulk- ins ert them into the mas ter table or tables on
the s erver.

T he heart of this tec hnique is that entry operators have loc al


tables that mirror the s erver tables . T heir forms are bound to the
loc al tables and all entry is done loc ally. A t the end of the day, or
at s c heduled times throughout the day, a proc es s runs that
takes the data out of the loc al tables and moves it to the s erver
tables .

H ere is where the advantage lies . T he ins ert proc es s gathers the
input from all the entry operators ' c lient tables and tes ts for
duplic ation before the ac tual ins ert into the s erver tables .
N ormally, s uc h an interim validation would be overkill bec aus e
data us ually is validated upon entry. H owever, the point here
is n't really to validate data in terms of c orrec t c ontent, but
rather, to s ee if duplic ate rec ords were entered during input.

T his is c ertainly a pos s ibility in a bus y c us tomer s ervic e, s ales ,


or telemarketing operation. For example, in an environment
where phone c alls c ome in, it is pos s ible that J ane plac es an
order for s omething and, an hour later, her hus band J oe plac es an
order for the s ame item. I f different operators handled the
hus band and wife, no one would be the wis er that this is a
duplic ate order. E ven the two c reated rec ords might not be
ac tual duplic ates bec aus e the firs t name is different in eac h
rec ord. But if a c us tom- des igned validation proc es s is us ed,
thes e two rec ords c an be flagged as duplic ates bec aus e at leas t
the addres s is the s ame in both rec ords .

You als o c an tes t for near duplic ation on the s erver, s o this begs
the ques tion: why bother with the s eparate table- entry
approac h? T he ans wer is performanc e. I f all entry goes s traight
to the s erver tables , and the c us tom duplic ation proc es s runs on
the larger tables , there c ould be s ome is s ues with s peed.

A nother is s ue to c ons ider is how far bac k in time to look for


duplic ates . With the loc al approac h, the tes t c ertainly is done at
leas t at the end of the day, if not during s c heduled times
throughout the day, all for that day's proc es s ing. I nvolving older
rec ords in the s erver tables is n't nec es s ary. A n order plac ed
twic e in one day is probably a duplic ate. A n order that res embles
one plac ed las t week or las t month is probably a repeat order.

Andrea Mos s
Hack 56. Distribute a Split Database with
Predefined Table Links

If you f ollow this interesting distribution game plan, users will


not have to link their local database f iles to the data tables on
the system.

T he tec hnique known as databas e s plitting, whic h involves a


tables - only bac k- end A c c es s file on a network s hare, c opies of
front- end A c c es s files (with forms , reports , and s o on) on eac h
us er's C:\ drive, and the us e of linked tables , has been around for
quite s ome time. T he benefits of s uc h c lient/s erver databas e
c onfigurations are widely known and doc umented.

H owever, it c an be a c hallenge to deal with s plit databas es


during periods of frequent updates , es pec ially during the
development phas e. For s ome us ers , their O ffic e ins tallation
does n't even inc lude the L inked Table M anager, s o they c an get
prompted for the O ffic e ins tallation C D when they attempt to
refres h and c hange links . O ther us ers might s imply be
unc omfortable or unfamiliar with how linked tables work.
Frequent relinking, es pec ially for us ers who were us ed to jus t
s haring an M D B from one loc ation, c an be problematic .

When frequent rounds of revis ions are being s ubmitted to end


us ers during the initial prototyping s tage of development, it
makes s ens e to keep the projec t in only one file. But onc e the
data model is s igned off, it's time to s plit the databas e. You c an
do this in two ways :
M ake a c opy of the databas e s o that you have two
identic al c opies . I n one c opy, delete the tables . I n the
other c opy, delete everything but the tables .

U s e the D atabas e Splitter utility (Tools D atabas e


U tilities ). T his automatic ally c reates an A c c es s file with
jus t the tables and, at the s ame time, removes the
tables from the databas e running the utility.

T hen the bac k- end databas e (the one with the tables ) goes on a
network s hare, while the front- end databas e is dis tributed to
us ers and is run from their P C s . T he problem is that the front-
end databas e mus t be linked to the tables in the bac k- end
databas e. Simulating thes e links on your development P C before
dis tributing the front end is the point here. I f you c an s et the
links s o that they are the s ame as thos e in the produc tion
environment, us ers will not have to deal with es tablis hing the
links thems elves from their P C s .

I n other words , you c an dis tribute the front end prelinked. A ll of


this is bas ed on the as s umption that a drive- mapping s tandard
is in plac e and that all us ers will have an identic al map path.

6.3.1. Copying the Network Drive to Your


Development Machine

T he SUBST D O S c ommand is all you need to c opy the network


drive to your development mac hine. O n your development
mac hine, you c an us e SUBST to c reate a map that matc hes the
one us ers need.
Firs t, c reate a direc tory on your c omputer that matc hes the
folder on the s hare where the bac k- end databas e will go. I f the
network path inc ludes s ubdirec tories , c reate a path that
matc hes that path s truc ture on your development mac hine.

T he s yntax for us ing SUBST requires the new drive letter and the
path it is s et to, like this :

SUBST <New Virtual Drive Letter:> <Path to map to that let

For example, if you have a s ubfolder named XYZ_Corp in your


C:\Clients folder, and you want to map that folder to an S:\ drive,
c lic k Start/Run; type command (Windows 9 8 , M e) or CMD (Windows
N T, 2 0 0 0 , XP ); c lic k O K; and enter this at the c ommand line:

SUBST S: C:\Clients\XYZ_Corp

Figure 6 - 1 s hows how you do this in the C ommand P rompt box.

Figure 6-1. Using SUBST


I f us ers are ac c es s ing a s ubfolder under the S: drive, c reate
matc hing folder names under the folder that was s ubs tituted to
the S: drive. P lac e the bac k- end databas e in the appropriate
folder.

N ow, when you are in the front- end databas e file and are linking
the bac k- end tables , brows e to your new S: drive to find the
bac k- end databas e, thereby keeping the links the s ame as what
the us ers need. When you s end your us ers an update, they
s hould not have to relink anything. T his new drive letter will even
s how up when you open M y C omputer. T he new virtual drive
letter will las t until the next time you res tart. I f you dec ide you
no longer need a virtual drive, you c an get rid of it with the /d
s witc h:

SUBST S: /d

I t's important to note that this removes the virtual mapping; it


does n't delete the folder or its files . A ls o, you c an't us e SUBST if
you already have a drive us ing that letter, s o if your keyc hain
flas h drive is us ing G:\, you have to s afely remove it before
SUBSTing a folder to us e the G: drive.

O f c ours e, this tec hnique works only in extremely s table


environments , where all us ers have the s ame drive letter
mapped to the given s hare. A lthough S: might work for your
primary c ontac t, other us ers of the applic ation might have a
different letter mapped to that loc ation, or they might not have
any letter mapped. T hey c ould be ac c es s ing the folder through
its U nivers al N aming C onvention (U N C ) name
(\\ServerName\ShareName). I f this is the c as e, you c an emulate
this on your P C as well as long as you are willing to rename your
P C to matc h the s erver's name (you c an always c hange it bac k
later).

6.3.2. Using UNC Instead

I f you want to us e U N C ins tead, you need to rename your


c omputer. Firs t, you need to know the s erver name at the c lient
s ite and the full path of folders and s ubfolders to the s hare that
will hold your bac k- end datafile. To rename your c omputer to
matc h the s erver, bring up your P C 's Sys tem P roperties by right-
c lic king M y C omputer and c lic king P roperties , or by double-
c lic king the Sys tem ic on in the C ontrol P anel to open the
Sys tem P roperties dialog box. Selec t the C omputer N ame tab,
and then c lic k the C hange button. When you s ee the C omputer
N ame C hanges dialog box, s hown in Figure 6 - 2 , type the des ired
name for the c omputer. I t will require a reboot to take effec t. O f
c ours e, this as s umes you will be c reating a name c onflic t on the
network. T he as s umption is that your development mac hine is n't
on the produc tion network. I f it is , you c an dis c onnec t your
c omputer during this proc es s .
Figure 6-2. Changing the name of the computer
So, if XY Z C orp.'s s erver, c alled ServerName, has a DeptShare
folder and a s ubfolder c alled DataFolder that will hold your
datafile, c hange your c omputer's name to ServerName. T hen,
c reate a folder named DeptShare off the root of your C: drive, and
c reate a s ubfolder c alled DataFolder ins ide the DeptShare folder.

O nc e the folder s truc ture is in plac e, brows e to the DeptShare


folder, rightc lic k in an empty area of the folder, and then c lic k
P roperties . Selec t the Sharing tab, and make s elec tions to s hare
the folder, as s hown in Figure 6 - 3 .

Figure 6-3. Sharing a folder


N ow go to M y N etwork P lac es , and c lic k A dd a N etwork P lac e.
C lic k N ext on the wizard's firs t s c reen, and the wizard will as k
you where to c reate the new network plac e; s elec t "C hoos e
another network loc ation." C lic k N ext, and in the I nternet or
N etwork A ddres s box, type \\ServerName\DeptShare. C lic k N ext;
Windows will as k what to c all the s hare. I f the name is n't already
in the box, type DeptShare for ServerN ame. C lic k O K, and then
c lic k Finis h.

Finally, bac k in your A c c es s applic ation file, delete all the linked
tables . T his time, when relinking the tables , make s ure to go
through M y N etwork P lac es /E ntire N etwork in the link dialog to
brows e to the datafile, or type \\servername\deptshare into the
dialog to brows e to the datafile. T his c aus es A c c es s to c reate
the links to us e the U N C naming c onvention. I f you us e the M y
C omputer s hortc ut to your s hare, A c c es s rec ognizes that it is
loc al and us es the C: drive path to c reate the link. To ens ure that
your link is us ing U N C , type this in the debug window:

?Currentdb.TableDefs("<your table name>").Connect

M ake s ure to put the name of one of the linked tables in the c ode
line where you s ee the <your table name> prompt. T he res pons e
s hould look like this :

;DATABASE=\\Servername\DeptShare\DataFolder\Project_dat.md
N ote that you will s ee the name of your databas e; you won't s ee
Project_ dat.mdb. I f you get the following res pons e, you need to
try again, making s ure you go through the entire network,
workgroup, c omputer name, and s hare name when brows ing to
your datafile:

;DATABASE=C:\DeptShare\DataFolder\Project_dat.mdb

O nc e this is c orrec t, end us ers at the c lient s ite s houldn't need


to relink, regardles s of whic h drive letter (if any) they have
mapped to the network loc ation.

Steve Conklin
Hack 57. Build a Time-Out Feature

Make sure your data is saved and available to others. Lock the
records when they're not being updated.

T he phone rings , or you are late to a meeting, or any number of


other dis trac tions pop up. I t happens to all of us . U nfortunately,
you s ometimes forget to c los e out of the file open on your P C .

I n a multius er databas e, this c an be a real nuis anc e. D epending


on the rec ord- loc king s c heme being us ed, if a rec ord is left in
the middle of an edit, other workers might not be able to make
c hanges to that rec ord. Figure 6 - 4 s hows the dreadful mes s age
a us er c an get when attempting to make a c hange to a rec ord
s omeone els e has left open.

Figure 6-4. A record that has been left in an


edited state
A lthough the mes s age in Figure 6 - 4 gives the s ec ond us er the
options he needs , it is better to not even s ee this mes s age, if it
c an be avoided. A produc tive meas ure for this s ituation is to
c los e a form in whic h no ac tivity is s ens ed after a period of time.
I n other words , if the firs t us er has not c ompleted any c hanges
to the rec ord within a s pec ified time, the form s hould jus t c los e.
C los ing the form ends the rec ord- editing proc es s , and the
c hanges are s aved automatic ally. T he alternative, to drop the
c hanges , is dis c us s ed at the end of this hac k.

6.4.1. It's About Time

Forms have an intrins ic timer c ontrol and Timer event. I f you're


familiar with V is ual Bas ic , you know how to ac tually plac e a
timer c ontrol on a form. I n A c c es s , the c ontrol is effec tively
already on the form, although you don't s ee it. I ns tead, you us e
the property s heet to s et the Interval property and to indic ate
what oc c urs in the On Timer event.

To dis play the property s heet, open the form in D es ign mode,
and pres s F4 on the keyboard. I f nec es s ary, make s ure the
property s heet is dis playing properties about the form its elf, not
about one of the c ontrols or s ec tions . Selec t Form from the drop-
down box at the top of the property s heet.

Figure 6 - 5 s hows the property s heet s et to dis play the


properties for the form. T he On Timer and Timer Interval
properties are found on both the E vent tab and the A ll tab.

You c an dis play the property s heet in a few


ways . You c an pres s F4 , or you c an pres s
A lt- E nter. You c an als o us e the V iew
P roperties menu, or jus t c lic k the
P roperties button on the Form D es ign
toolbar.

T he Interval property ac c epts values between 0 and


2,147,483,647 millis ec onds . A s etting of 1000 equals one s ec ond.
T he 10000 s etting s hown in Figure 6 - 5 is the equivalent of 1 0
s ec onds . By the way, the larges t s etting of 2,147,483,647 equals
almos t 2 5 days . Yes , you c an s c hedule an A c c es s event every
2 5 days !

T he On Timer event property links to either a mac ro or a c ode


proc edure. I n this example, a c ode proc edure was written. I 'll
explain the c ode s oon, but firs t, let's examine the form's des ign.

6.4.2. In Good Form

Figure 6 - 6 illus trates the form des ign, field lis t, and property
s heet. N otic e the text box c ontrol in the form header that is n't
bound to a field. T he property s heet is s et to dis play the
properties of the unbound box, txtT ime, and its Visible property
is s et to No. I n other words , when the form is in V iew mode, the
txtT ime text box won't be s een.

Figure 6-5. Setting the Timer Interval and On


Timer event
Figure 6-6. An unbound text box to hold a time
reference
T he txtT ime text box is n't meant to be us ed for entry. I ns tead, it
holds a s naps hot of the c omputer's c loc k time, at the moment
the form is ac tivated. To make this happen, you need to enter a
little c ode in the form's Activate event.

To get to the event c ode s tub, s elec t [E vent P roc edure] from the
drop- down menu to the right of O n A c tivate in the property s heet
and then c lic k the ellips es (…) button, as s hown in Figure 6 - 7 .
T his brings you to the form's c ode module, right at the s tart of
the Activate event. H ow's that for c onvenienc e?

Figure 6-7. Getting to an event code stub from


the property sheet
H ere is the c ode to enter in the Activate event:

Private Sub Form_Activate( )


Me.txtTime = Now
End Sub

T he On Timer event c ontains the DateDiff func tion, s et to tes t for


the differenc e in s ec onds between the es tablis hed form
ac tivation time and the c urrent time. From the property s heet,
s elec t [E vent P roc edure] in the drop- down menu to the right of
O n T imer. C lic k the ellips es button and enter this c ode:

Private Sub Form_Timer( )


If DateDiff("s", Me.txtTime, Now) > 5 Then
DoCmd.Close
End If
End Sub

T he firs t parameter of the DateDiff func tion indic ates whic h


interval to tes t; in this c as e, s is for s ec onds . T he func tion tes ts
if more than five s ec onds have laps ed between the time s tored in
the txtT ime text box and now. Bear in mind that there are two
values to c oordinate here: the timer interval and how many
s ec onds to tes t for.

T his example is s et up to tes t every 1 0 s ec onds if there is a


differenc e of five s ec onds , but you c an c hange thes e numbers .
For example, it might be eas ier on the us er if the timer interval is
3 0 s ec onds . T here is a balanc e of what makes s ens e here. I f
us ers are likely to edit the s ame rec ords often, make the interval
s horter.
T he Now() func tion returns the s ys tem
time. E very c omputer keeps an internal
c loc k running. When timing events , it's
nec es s ary to s tart with a bas eline time.
T he Now() func tion takes a s naps hot of the
time, whic h is then c ompared to a later
time (effec tively another s naps hot, but
later in time). Subtrac ting the firs t
s naps hot from the s ec ond s naps hot equals
the elaps ed time. I nc identally, the
c omputer c loc k is als o us ed in programs
that allow you to enter "today's date."
Sometimes , the c loc k needs to be res et.

I f we s topped here, the form would c los e 1 0 s ec onds after being


opened. T hat is , upon the firs t run of the On Timer event (whic h
oc c urs 1 0 s ec onds after the form is opened) a differenc e greater
than five s ec onds is found, and the DoCmd.Close line runs , c los ing
the form. But our goal is to c los e the form only when there is no
ac tivity, not jus t for the hec k of it.

T he key to making this hac k work is to add c ode to eac h c hange


event for the various text entry boxes on the form. T he form in
this example has text boxes for editing employee name,
department, title, and s o on. T he Change event for eac h text box
rec eives a s ingle line of c ode to update the txtT ime text box with
the c urrent time. I n other words , every time a c hange is made in
an entry text box the txtT ime text box (remember, this one is
invis ible) is res et to Now, like this :
Private Sub Department_Change( )
Me.txtTime = Now
End Sub

T he Change event fires eac h time a c harac ter is entered or


bac ks pac ed out of the text box. T herefore, as a us er types in
one of the entry text boxes , the txtT ime text box is c ons tantly
updated with the c urrent time. T hen, when the timer event fires ,
the DateDiff func tion returns a differenc e of les s than five
s ec onds , and the form s tays open. O nly when the form is left idle
does a differenc e greater than five s ec onds oc c ur, thereby
c los ing the form.

Figure 6 - 8 s hows how the form's c ode module s hould look after
thes e routines have been entered. I t's O K if your event routines
aren't in the s ame order.

Figure 6-8. The code that handles an inactive


form
6.4.3. Hacking the Hack

You c an implement this hac k in many different ways . So far, all


we know is how to res et the bas eline time eac h time a c harac ter
is entered with the keyboard. A ls o, the only ac tion after a period
of inac tivity has been to c los e the form. H ere are s ome other
ideas .

6.4.3.1 Reset the time when the mouse is


moved.

I n addition to c apturing keyboard entries as a way to res et the


time held in the invis ible text box, it makes s ens e to do this
whenever the mous e is moved as well. Some people are quic k on
the mous e, and jus t giving the mous e a pus h keeps the form
open. I n fac t, I often do this to keep my s c reens aver from
s tarting up.

A c c es s forms c an als o us e the MouseMove event. I ns ert c ode into


the MouseMove event in the s ame manner explained earlier. T he
purpos e of the c ode is the s ame, to res et the invis ible text box
to Now .

Private Sub Title_MouseMove(Button As Integer, _


Shift As Integer, X As Single, Y As Single)
Me.txtTime = Now
End Sub

A s long as the mous e is moved at leas t onc e every 1 0 s ec onds ,


the form will s tay open.

6.4.3.2 Let the user decide the timer


interval.

E ac h us er has his own way of working, not to mention his own


s peed of working. So, ins tead of hardc oding the timer's interval
value, why not let the us er dec ide what is bes t? To do this , you
have to build a way to let him s elec t the interval into the form (or
s omewhere els e, s uc h as in a preferenc es area). Figure 6 - 9
s hows how the form has been modified by adding a c ombo box.
T he c ombo box lets the us er s elec t from a lis t of pos s ible
values .

Figure 6-9. Letting the user decide how long to


wait before closing the form
T he c ode is updated as well. T he c ombo box has been named
c mbSec onds . I ts Row Source Type is s et to Value List, and the Row
Source is s et to the c hoic es 10, 20, 30, 40, 50 , and 60 . When the
us er s elec ts a value from the c ombo box, the c ombo box's Change
event fires to update the invis ible text box to the c urrent time.
A ls o, the form's Activate event now takes c are of es tablis hing a
default time to wait2 0 s ec onds in this c as e, as s hown in Figure
6 -1 0 .

I t's nec es s ary to have a default value to us e until or unles s the


us er s elec ts an interval. Finally, the number of elaps ed s ec onds
that are tes ted for is now always one fewer than the interval
s elec ted in the c ombo box. Figure 6 - 1 0 s hows the updated c ode
module.

Figure 6-10. Setting the Interval property with


the combo box Change event
6.4.3.3 Save the record but leave the
form open.

J us t bec aus e a rec ord is dis played in a form does n't nec es s arily
mean it is being edited. T he Dirty property is true if edits have
been made or false if no data has c hanged. You c an c hange the
c ode in the form's Timer event to tes t the Dirty property. I f it is
true , the rec ord is s aved, and a mes s age is pres ented, as s hown
in Figure 6 - 1 1 . I f Dirty is false, and no edit is oc c urring, nothing
happens . E ither way the form s tays open.

T he new c ode for the Timer event us es a pair of nes ted If


s tatements . Firs t, if the elaps ed time is greater than the
predetermined interval of the tes t, the s ec ond If s tatement
c omes into play. T he s ec ond If tes ts the Dirty property. I f true ,
the rec ord is s aved, and the mes s age is dis played:

Figure 6-11. The saved-edits message


Private Sub Form_Timer( )
If DateDiff("s", Me.txtTime, Now) > Me.cmbSeconds - 1 Then
If Me.Dirty Then
DoCmd.RunCommand acCmdSaveRecord
MsgBox "Edits have been saved!"
End If
End If
End Sub

I f you s tarted an edit and didn't c omplete it, the c ode c ompletes
it for you. I f an edit is initiated, no harm is done.

6.4.3.4 Close the form without saving the


record.

So far, eac h approac h in the hac k has been bas ed on s aving the
rec ord that is in the middle of an edit. T his is a judgment c all
bec aus e even if the pers on walked away from his work you don't
know for s ure whether to s ave his entry. To be on the s afe s ide,
the work is s aved.

O f c ours e, the argument exis ts to not s ave the edits . I t's eas y
to drop the edits with an Undo ac tion. H ere is a s nippet of
modified c ode that goes into the Timer event:

Private Sub Form_Timer( )


If DateDiff("s", Me.txtTime, Now) > 10 Then
If Me.Dirty Then
DoCmd.RunCommand acCmdUndo
DoCmd.Close
End If
End If
End Sub

E s s entially, if the rec ord is n't in its pris tine s tate (c onfirmed by
the Dirty property), the c ode runs an Undo c ommand and c los es
the form without s aving the rec ord. T his is jus t one way to
handle dropping a half- c ompleted edit.

A nother enhanc ement is to s ave the values out of the form and
into a temporary table or even a text file, and to leave a
mes s age box alerting the us er that his entry was dropped but
that he c an find his uns aved efforts at the plac e where you s aved
them.
Hack 58. Implement Unique Usernames

Even when A ccess Security isn't active, you can implement


unique usernames when all users are A dmin.

A c c es s Sec urity is great in many multius er s ituations bec aus e


you c an as s ign rights to groups and individuals . H owever,
s ometimes this is jus t a lot of overhead. I n a s mall group of
us ers who all us e the s ame objec ts , you don't get muc h added
value by implementing s ec urity.

T he downs ide of not us ing s ec urity is that all us ers are given the
name A dmin. You c an c onfirm this in an uns ec ured databas e by
going to the I mmediate window (C trl- G in the V B E ditor) and
typing the following:

?CurrentUser

T he CurrentUser property c ontains the logged- in name of the


us er. When s ec urity is on, eac h us er has a s pec ific name. When
s ec urity is off, all us ers are A dmin.

A n eas y way to us e s pec ific names in an uns ec ured databas e is


to firs t have us ers enter their names and then have the entered
names available throughout the s es s ion. T his tec hnique makes
s ens e only in a c onfiguration in whic h eac h us er works on a loc al
databas e with the forms . T he data tables remain on in a bac k-
end databas e on the s erver.

When a us er s tarts up her c lient- bas ed front end, s he is as ked to


enter her name. T his is jus t an eas y affair handled by an input
box. A loop keeps tes ting for her entry, and when s he is done,
the entry is handed off to the Tag property of the main form. T his
works bes t if the main form opens automatic ally when the us er
s tarts up the databas e. T his c ode goes into the main form's Open
event:

Private Sub Form_Open(Cancel As Integer)


Dim user_name As String
user_name = ""
Do Until user_name <> ""
user_name = InputBox("Please enter your name", "Enter Na
Loop
Me.Tag = user_name
End Sub

T hroughout the s es s ion, the pers on's name is always available


via this s imple referenc e bac k to the main form and its tag,
as s uming that the main form is named frmM ain. C hange the form
name to matc h yours :

Forms!frmMain.Tag

Bec aus e the applic ation is c onfigured in the way that eac h us er
is us ing a loc al vers ion of the main form, the referenc e to the Tag
property always returns the us er's unique name.

T hat's all it takes . By ac c es s ing the entered us ername in this


way, you c an us e the name in reports , populate field text boxes
with it, us e it in queries ; in other words , us e the name wherever
you need it in your applic ation.

Andrea Mos s
7. External Programs and
Data
Sec tion 7 .1 . H ac ks 5 9 7 1

H ac k 5 9 . I mport N onc ontiguous Ranges of D ata from


E xc el

H ac k 6 0 . U s e E xc el to Reorient A c c es s D ata

H ac k 6 1 . U s e E xc el Func tions I ns ide A c c es s

H ac k 6 2 . U s e Word to C ompare D ata in Two A c c es s


Tables

H ac k 6 3 . I mport Varied XM L D ata into A c c es s

H ac k 6 4 . E xport XM L D ata Sanely

H ac k 6 5 . Break T hrough V BA 's Trans formation Barrier

H ac k 6 6 . L everage SQ L Server P ower by C alling Stored


P roc edures

H ac k 6 7 . M anage Word D oc uments from A c c es s

H ac k 6 8 . U s e A c c es s as a Front E nd to M ySQ L

H ac k 6 9 . Send A c c es s D ata T hrough O utlook


A utomatic ally

H ac k 7 0 . C reate A c c es s Tables from O uts ide A c c es s

H ac k 7 1 . Write V BA with the M ac ro Rec order in Word


and E xc el
7.1. Hacks 5971
A c c es s is n't an is land of an applic ation, not by a long s hot. I t
integrates eas ily with many programs . O bvious ly, it s hares
c harac teris tic s with the other O ffic e produc ts , and it is relatively
eas y to inc lude Word, E xc el, O utlook, and P owerP oint files in
your A c c es s s olutions . Several hac ks in this c hapter do jus t
that. For example, "I mport N onc ontiguous Ranges of D ata from
E xc el" [Hack #59] and "U s e E xc el Func tions I ns ide A c c es s "
[Hack #61] involve integration with E xc el. "M anage Word
D oc uments from A c c es s " [Hack #67] s hows you how to us e
Word's objec t model to c reate a programmatic s olution, and
"U s e Word to C ompare D ata in Two A c c es s Tables " [Hack #62]
s hows you a neat way to us e Word independently to tes t your
data.

T his c hapter als o inc ludes hac ks on us ing XM L data, integrating


with M ySQ L , and us ing SQ L Server s tored proc edures . A ll in all,
the c hapter offers quite a bit, and us ing the tec hniques
pres ented here will c ertainly gain you an edge in your
development efforts .
Hack 59. Import Noncontiguous Ranges
of Data from Excel

A standard import lets you get only one data range at a time.
Here are a couple of workarounds to get you more.

When importing data from an E xc el workbook into A c c es s , you


c an s elec t to import a works heet or a range. You c an s elec t a
range only when the workbook inc ludes es tablis hed named
ranges . Figure 7 - 1 s hows the firs t s c reen of the I mport
Spreads heet Wizard. T his wizard appears after you s elec t File
G et E xternal D ata and s elec t to import from an E xc el file.

Whether you're importing a works heet or a range, the problem is


that you c an s elec t only one item in the lis t. U s ually, s ingle
works heets are imported bec aus e a wealth of data c an s it on a
s ingle works heet. Ranges are a different s tory. You might need to
import more than one range. I t's tedious to run the I mport
Spreads heet Wizard over and over again.

Figure 7-1. Importing data from Excel


7.2.1. Using Macros for Multiple Imports

A n eas y way around the one- range- at- a- time import is to c reate
a mac ro that us es multiple transferSpreadsheet ac tions . E ac h
oc c urrenc e of this ac tion imports a s ingle range, but you c an
c reate a s equenc e of them in a s ingle mac ro. You s hould
c ons ider whether the ranges are to be imported as new tables , or
whether the ranges are to be ac c umulated into a s ingle table.

7.2.1.1 Import Excel data into separate


tables

Figure 7 - 2 s hows a mac ro that imports five ranges into five


tables . E ac h import puts data into a s eparate A c c es s table.
E ac h table name is s pec ified in the Table Name argument of eac h
TRansferSpreadsheet ac tion. T he firs t five ac tions of the mac ro
delete the exis ting tables jus t before the imports . T he imports
plac e the E xc el data into tables with the s ame name as the
tables being deleted

H ere is a potential problem: if you don't delete the tables firs t,


the data is appended to the tables bec aus e they already exis t.
M os t likely you don't want to do this . D eleting the A c c es s tables
firs t guarantees that the tables are rec reated with jus t the newly
imported data.
Figure 7-2. A macro that creates separate
Access tables
You s et the ac tual transferSpreadsheet ac tions s uc h that eac h
addres s es a different range in the E xc el data. You s et this in the
Range argument, s hown in Figure 7 - 2 ; it's an ac c eptable way to
gather data from different E xc el ranges .

7.2.1.2 Import Excel data into a single


table

I f you want to c ombine the data from different E xc el ranges into


one A c c es s table, the Table Name argument of eac h
transferSpreadsheet ac tion s hould be identic al. You s till mus t
empty the des tination table firs t. I n this mac ro, you do s o with
the RunSQL ac tion, whic h runs a s imple Delete operation:

Delete * From Inventory_All

P rior to this , turn off warnings s o that the proc es s is n't


interrupted with a c onfirmation mes s age.

A fter the Delete operation, the transferSpreadsheet ac tions fill the


nowempty I nventory_A ll table. A ll the data is appended to the
table.

Figure 7 - 3 s hows how this mac ro is s truc tured.

Figure 7-3. A macro that populates one Access


table
7.2.2. Importing Noncontiguous Data from
Excel Without Using Ranges

M ac ros are handy but are limited in power. A s s hown in the


previous s ec tion, you c an import ranges eas ily enough. You c an
even import areas of a workbook by addres s . I n other words , you
c an enter A1:D15 to import part of an E xc el works heet. T hat's
about it, though. A mac ro c an't do anything muc h more
s ophis tic ated than that. T his is where s ome V BA c omes in
handy.

Figure 7 - 4 s hows an E xc el works heet. T he data c ons is ts of


produc t amounts broken out by years and quarters .

Figure 7-4. Excel data to be imported


To import, s ay, jus t the s ec ond- quarter figures for eac h year
requires a proc es s that tes ts eac h row to s ee if the quarter is Q2.
H ere is a c ode routine that does jus t that:

Sub get_excel()
Dim test_quarter As Integer
Dim conn As ADODB.Connection
Set conn = CurrentProject.Connection
Dim xl As Object
Set xl = GetObject("C:\Inventory\Inventory.xls")
'first delete existing records
conn.Execute "Delete * From Inventory_All"
With xl
With .Worksheets("Data")
For test_quarter = 2 To 25 'known row numbers on work
If .Cells(test_quarter, 2) = "Q2" Then
ssql = "Insert Into Inventory_All Values(
ssql = ssql & .Cells(test_quarter, 1) & "
ssql = ssql & "'" & .Cells(test_quarter,
ssql = ssql & .Cells(test_quarter, 3) & ", "
ssql = ssql & .Cells(test_quarter, 4) & "
ssql = ssql & .Cells(test_quarter, 5) & "
conn.Execute ssql
End If
Next test_quarter
End With
End With
xl.Close
Set xl = Nothing
MsgBox "done"
End Sub
T his c ode us es automation to c reate an E xc el objec t and s ets
the workbook to the objec t. I t then c yc les through the works heet
rows . A tes t s ees if the value in c olumn 2 is Q2. When this is
true, all five c olumns of the row are ins erted into the
I nventory_A ll A c c es s table.

O f c ours e, you c an alter the c ode to tes t on other c onditions .


A ls o, you don't have to hardc ode the Q 2 tes t. Figure 7 - 5 s hows
the A c c es s table populated with jus t the s ec ond- quarter
rec ords .

Figure 7-5. Populating the table with portions of


the Excel workbook
A little c ode c an go a long way. Setting the referenc e to the
E xc el workbook is a s imple proc es s with the GetObject func tion.
O nc e the routine is c onnec ted to a workbook, you c an do many
things with a little knowledge of E xc el's programmatic model.
Hack 60. Use Excel to Reorient Access
Data

Use Excel's Paste Special Transpose f eature to turn data on its


ear.

H ere's an eas y way to c hange c olumns to rows (or rows to


c olumns ; I gues s it all depends on how you look at it). Figure 7 -
6 s hows a table filled with s ome data. T he table c ontains 8 fields
and 1 0 0 rows of data.

Figure 7-6. Eight columns of data in a table


P erhaps your us er wants to view the data s o that eac h pers on's
rec ord is dis played vertic ally, whic h is n't an unc ommon reques t
with E xc el us ers . T his hac k s hows how to do jus t that: put this
data in E xc el, but turn it s ideways .

Firs t, s elec t all the data in the A c c es s table; then, pop over to
an open E xc el workbook. Find an empty works heet, and pas te
the data. N ote that for this example, I have purpos ely pas ted the
data in row 1 2 . You will s ee why in a moment. Figure 7 - 7 s hows
how the data landed in E xc el.

Figure 7-7. Access data pasted in Excel


U pon being pas ted, the data is in a s elec ted s tate. T hat's great!
J us t leave it as is , but if you los e the s elec tion, jus t s elec t it
again. T he next s tep is to c opy the data, by either s elec ting E dit
C opy or pres s ing C trl- C . C opying the data is a nec es s ary
s tep. I t might s eem that the data is already on the c lipboard. I t
is , but not in the way we need; therefore, the extra c opy from
within E xc el is nec es s ary.

N ow that the data is c opied in an E xc el format, c lic k in c ell A 1 .


T his removes the s elec ted s tate from the data, but that's okay
at this point. I n fac t for the next s tep, the data mus t be
des elec ted, and a s ingle c ell mus t be ac tive.

U s e the E dit P as te Spec ial menu to open the P as te Spec ial


dialog box, as s hown in Figure 7 - 8 .

Figure 7-8. The Paste Special dialog box


T here are few things to note in Figure 7 - 8 . A s already noted, the
data is des elec ted. C ell A 1 is the ac tive c ell. T he P as te Spec ial
dialog will pas te the c opied data in the next operation, but the
c ritic al point is that the Trans pos e box is c hec ked. T his
c hec kbox is near the bottom of the dialog box.

C lic king the O K button c ompletes the proc es s . Figure 7 - 9


s hows how the data s its at the top of the works heet. E arlier, I
made the firs t pas te in row 1 2 to give enough room for the
s ec ond pas te. We already knew there were eight fields of data,
and now they oc c upy eight works heet rows . A lthough not vis ible
in Figure 7 - 9 , the data goes 1 0 0 c olumns to the right.

T he data in the firs t pas te is no longer needed, s o you c an delete


it. A few formatting c hanges will make the data pres entable and
ready for work. Figure 7 - 1 0 s hows how the data looks after a
fac elift and how analys is is already being run on the data.

Figure 7-9. The transposed data


Figure 7-10. Working with the transposed data
Hack 61. Use Excel Functions Inside
Access

Expose powerf ul f unctions available in Excel to your A ccess


application.

E xc el has many powerful built- in func tions for s uc h things as


financ ial and s tatis tic al analys is . I f you want to do the s ame
type of analys is in A c c es s , you c an do one of the following three
things : purc has e an off- the- s helf c ode s olution, write your own
c ode for analys is , or us e automation to tap into E xc el's
func tions from ins ide A c c es s . T his hac k s hows you how to tap
into E xc el via automation and us e s preads heet func tions , s aving
you time and money over the other options .

T his hac k involves A c c es s working hand in hand with E xc el, s o


you need to make s ure E xc el is ins talled on the mac hine on
whic h your databas e will be running. T his is a s afe as s umption in
mos t c orporate environments .

7.4.1. A Simple Excel Function

E xc el's FV (future value) func tion c alc ulates the value of an


inves tment at s ome time in the future bas ed on periodic ,
c ons tant payments and on a c ons tant interes t rate. T he
following V BA func tion takes the s ame parameters as E xc el's FV
works heet func tion and returns the s ame res ult as if you were
us ing the future value func tion right in E xc el:

Public Function FV(dblRate As Double, intNper As Integer, _


dblPmt As Double, dblPv As Double, _
intType As Integer) As Double
Dim xl As Object
Set xl = CreateObject("Excel.Application")
FV = xl.WorksheetFunction.FV(dblRate, intNper, dblPmt, dblPv,
Set xl = Nothing
End Function

T he WorksheetFunction property of E xc el's Application objec t is


key to c alling E xc el func tions from c ode, whether in A c c es s or
even direc tly in E xc el's V BA environment. With this property,
nearly every E xc el works heet func tion is available to build into a
s olution.

Figure 7 - 1 1 s hows a form that takes input from a us er and c alls


the FV func tion from the C alc ulate Future Value button.

Figure 7-11. Calling the FV function from a form


C lic king the C alc ulate Future Value button exec utes the
following c ode:

Private Sub cmdFV_Click( )


Dim dblFV As Double
dblFV = FV(txtRate / 12, txtNper, txtPmt, dblPv, frmType)
MsgBox "FV = " & dblFV, vbInformation, "Future Value"
End Sub

T he cmdFV_Click event c alls the FV func tion and dis plays the
mes s age box s hown in Figure 7 - 1 2 . You c an modify the c ode to
write the s olution bac k to a table or to dis play it els ewhere on
the form objec t as needed.

Figure 7-12. Message box displayed from the


cmdFV_Click event

7.4.2. An Excel Function with an Array


Parameter

T he example of c alc ulating a future value required five


parameters to be pas s ed into E xc el, and with the magic of
automation, we got the res ult bac k. H owever, what would happen
if one of thos e parameters were an array, as many are in E xc el?

I f an E xc el func tion requires an array or table array, you c an


pas s it an array or a multidimens ional array c reated in A c c es s
and get bac k the needed res ult. L et's look at the c ode you'd us e
to c all E xc el's perc entile works heet func tion, whic h returns the
kth perc entile of values that you s pec ify from a given array of
values :

Public Function Percentile(strTbl As String, strFld As String, k A


As Double
Dim rst As ADODB.Recordset
Dim dblData() As Double
Dim xl As Object
Dim x As Integer
Set xl = CreateObject("Excel.Application")
Set rst = New ADODB.Recordset
rst.Open "Select * from " & strTbl, CurrentProject.Connection,
adOpenStatic
ReDim dblData(rst.RecordCount - 1)
For x = 0 To (rst.RecordCount - 1)
dblData(x) = rst(strFld)
rst.MoveNext
Next x
Percentile = xl.WorksheetFunction.Percentile(dblData, k)
rst.Close
Set rst = Nothing
Set xl = Nothing
End Function
With this func tion, we pas s the table name and field name to be
read into the A c c es s array, whic h in return is pas s ed into
E xc el's perc entile func tion along with the kth perc entile value
that we are looking for in the array of values . I t's worth noting
that you c an pas s the func tion a query name ins tead of a table,
depending on the applic ation's requirements .

Figure 7 - 1 3 s hows a form that dis plays a s ubform that is bound


to the tblD ata table and dis playing the SampleD ata field in
datas heet mode.

Figure 7-13. Calling the percentile function from


a form
T his s ample c alc ulates the 3 0 th perc entile from the lis t 1, 2, 3,
4, 5, 14, 13, 13, 16, 15, 16, 156 when the us er c lic ks the
C alc ulate P erc entile button. C lic king the C alc ulate P erc entile
button exec utes the following c ode:

Private Sub cmdPercentile_Click()


Dim dblPercentile As Double
dblPercentile = Percentile("tblData", "SampleData", txtK)
MsgBox "Percentile = " & dblPercentile, vbInformation, "Percen
End Sub

T his c ode produc es the mes s age box in Figure 7 - 1 4 .

Figure 7-14. The message box displayed from


the cmdPercentile_Click event
A s noted previous ly with the FV func tion, you c an write this
return value bac k to a table or dis play it on the form. You c an
als o c all the FV func tion or Percentile func tion from a query or
us e it on a report.

7.4.3. Other Excel Spreadsheet Functions

You c an c all more than 1 0 0 func tions us ing the WorksheetFunction


method of the E xc el objec t via automation. Keep in mind that
s ome are redundant with built- in A c c es s func tions , s uc h as
E xc el's ISNUMBER and A c c es s 's ISNUMERICM, and others , s uc h as
ISERR and ISNA, aren't of muc h us e unles s you are doing s ome
other advanc ed s preads heet automation.

You als o have to c ons ider whether the overhead of automation is


ac c eptable in your applic ation. I t might not be as effic ient as a
well- written c us tom func tion. H owever, it c an be a huge
times aver if you don't have time to write your own c us tom
func tions s uc h as the Percentile func tion.

Steve Huff
Hack 62. Use Word to Compare Data in
Two Access Tables

Look f or discrepancies the easy way, using Word's Document


Compare utility.

Sometimes , you have to c ompare data in two A c c es s tables .


U s ually you do this when you have one table that derives from
two different c opies of the databas e. T he data might differ
between the tables ; for example, s ome data has been updated in
one table, and now you need to unc over the dis c repanc ies .

You c an do this in a c ouple of ways . You c an us e s ome queries ,


but if there are many fields , query des ign c ould be diffic ult.
A nother option is to write c ode to read through both tables and
identify the differenc es . T his works but it als o takes a bit of time
to get the c ode working c orrec tly.

H ere's a great alternative: Word has a built- in feature that


c ompares two doc uments and highlights the differenc es .

T he firs t thing you need to do is export the A c c es s tables as


text files . Word then us es thes e to run a c omparis on. Figure 7 -
1 5 s hows the two tables already s aved as text. A s you c an s ee,
they appear identic al.

Figure 7-15. Two tables saved as text files


I n Word, open one of the text files . T hen, us e the Tools
C ompare and M erge D oc uments menu item to brows e to the
s ec ond text file. A s s hown in Figure 7 - 1 6 , you have options for
how to c ompare and merge the doc uments . I always c hoos e
"M erge into new doc ument." T hat way, I know the original files
aren't altered.

A new doc ument is c reated, but you immediately run into a


problem. Word's s pellc hec ker and grammar c hec ker will flag
nearly everything as inc orrec t bec aus e the export from A c c es s
c reates rec ords with no s pac e breaks . T his is c orrec t for the
data, but not as far as Word is c onc erned. So, the next thing to
do is turn off the s pellc hec ker and grammar c hec ker in Word's
O ptions dialog, as s hown in Figure 7 - 1 7 . By the way, the firs t
opened text file didn't flag any errors bec aus e it was s till a text
file. T he new merged doc ument, on the other hand, is a proper
Word doc ument.

Figure 7-16. Setting up the document


comparison
Figure 7-17. Turning off spellchecker and
grammar checker in Word
O nc e you c an s ee the doc ument for what it is , you c an s ee
plac es where the data does n't matc h bec aus e the data is
formatted with s trikethroughs , as s hown in Figure 7 - 1 8 .

Sc rolling through this data is a breeze. You c an quic kly s ee


where the data is different and dec ide what to do about it.

Andrea Mos s

Figure 7-18. Identifying unmatched data


Hack 63. Import Varied XML Data into
Access

A ccess is pretty good at importing simple XML data, but


sometimes you want to import data that isn't precisely the way
A ccess expects it to be.

A c c es s lets you import data from XM L files into its tables . For
example, let's c ons ider a databas e c ontaining a table that
defines a lis t of books . Figure 7 - 1 9 s hows the D es ign view for
this table. I t inc ludes s ix fields of three different types .

To begin, the table c ontains a few books , as s hown in Figure 7 -


20.

T he eas ies t way to s ee the XM L format A c c es s expec ts to


rec eive when it imports data to this table is to export s ome of
the data, whic h you c an do by s elec ting a table in the databas e
and then s elec ting E xport... from the File menu. I n this c as e, the
XM L format we'll need to let A c c es s import automatic ally looks
like the data that was jus t exported as XM L . I n other words ,
exporting rec ords into XM L s hows the XM L node s truc ture any
XM L data being imported bac k in s hould have. E xample 7 - 1
s hows the exported data.

Figure 7-19. A simple table to which we'll import


data
Figure 7-20. Test data in the books table

Example 7-1. New data for import

<?xml version="1.0" encoding="UTF-8"?>


<dataroot>
<books>
<ISBN>0596002637</ISBN>
<Title>Practical RDF</Title>
<Tagline>Solving Problems with the Resource Description Framework<
<Short_x0020_Description>The Resource Description Framework (RDF)
for describing and interchanging metadata on the Web.</Short_x0020
<Long_x0020_Description>The Resource Description Framework (RDF) i
for describing and interchanging metadata on the Web - anything fr
catalogs and worldwide directories to bioinformatics, Mozilla inte
structures, and knowledge bases for artificial intelligence projec
x0020_Description>
<PriceUS>39.95</PriceUS>
</books>
<books>
<ISBN>0596003838</ISBN>
<Title>Content Syndication with RSS</Title>
<Tagline>Sharing Headlines and Information Using XML</Tagline>
<Short_x0020_Description>RSS is sprouting all over the Web, connec
and providing news feeds.</Short_x0020_Description>
<Long_x0020_Description>RSS is sprouting all over the Web, connect
providing news feeds. Originally developed by Netscape in 1999, RS
stand for RDF Site Summary, Rich Site Summary, or Really Simple Sy
an XML-based format that allows Web developers to create a data fe
supplies headlines, links, and article summaries from a web site</
Description>
<PriceUS>29.95</PriceUS>
</books>
<books>
<ISBN>0596002912</ISBN>
<Title>XPath and XPointer</Title>
<Tagline>Locating Content in XML Documents</Tagline>
<Short_x0020_Description>Referring to specific information inside
can be like looking for a needle in a haystack: how do you differe
information you need from everything else?</Short_x0020_Descriptio
<Long_x0020_Description>Referring to specific information inside a
can be like looking for a needle in a haystack: how do you differe
information you need from everything else? XPath and XPointer are
related tools that play a key role in XML processing by allowing d
find these needles and manipulate embedded information.</Long_x002
<PriceUS>24.95</PriceUS>
</books>
</dataroot>

T he s truc ture begins with the dataroot element, though A c c es s


does n't ac tually c are what that c ontainer element's name is . T he
books element tells A c c es s this information goes into the books
table, and the ISBN, Title, Tagline, and other elements ins ide
eac h books element go to fields in the books table. T he only tric k
is in the Short Description and Long Description fields , whic h,
bec aus e XM L won't ac c ept s pac es in tag names , A c c es s prefers
to s ee as Short_x0020_Description and Long_x0020_Description.
A c c es s does n't c are what order the fields c ome in, but it will
rec ognize them only if they're c hild elements , not attributes .

To get s tarted, s elec t G et E xternal D ata from the File menu, and
then s elec t I mport. T he dialog box s hown in Figure 7 - 2 1 will
appear.

You might need to s elec t XM L from the "Files of type" drop- down
menu at the bottom bec aus e the dialog initially defaults to
A c c es s formats . Selec t a file, and c lic k I mport. T he I mport XM L
dialog box s hown in Figure 7 - 2 2 will appear.

You c an c lic k the plus s ign to the left of the books if you want to
ins pec t their s truc ture. I f you jus t c lic k O K, A c c es s c reates a
new table c alled books 1 (or whatever number avoids a c onflic t)
to import the XM L into A c c es s without c onflic ting with the prior
XM L table.

Figure 7-21. Initial Import dialog box


Figure 7-22. Import dialog box showing
structure of XML documents

T hat might be perfec tly fine bec aus e it gives you a c hanc e to
c ompare the new data with the old before merging the two.
A c c es s provides two more options , however: one that lets you
jus t c reate a new table bas ed on the s truc ture of the XM L file,
and another that lets you append the data in the XM L file to an
exis ting table. I n this c as e, we know the new books are different
from the old books , s o c lic k O ptions , and s elec t A ppend D ata to
E xis ting Table(s ), as s hown in Figure 7 - 2 3 .

I f you c lic k O K now, the extra books will be added to the exis ting
books table, as s hown in Figure 7 - 2 4 .

A c c es s refus es to import XM L data, whic h c aus es a c onflic t with


exis ting key relations hips . For example, if you import that s ame
doc ument again in the s ame way, you'll be rewarded with the
I mportE rrors table s hown in Figure 7 - 2 5 .

Figure 7-23. Import dialog box showing more


complex structure of XML documents and append
options
Figure 7-24. The results of importing a
document and appending its data

Figure 7-25. The results of importing a


document and appending its data when the data
is already there
U s ing the Trans form… button s hown in Figure 7 - 2 3 , you c an als o
perform c onvers ions , whic h make it eas ier to import data that
does n't arrive in a form that meets A c c es s 's expec tations . For
example, s uppos e information about a new book arrived in the
form s hown in E xample 7 - 2 .

Example 7-2. ch0812.xml, an attribute-bas ed XML document for


import

<update>
<books ISBN="0596003277" Title="Learning XSLT" Tagline="A Hands-On
Introduction to XSLT and XPath" Short_x0020_Description="A gentle
introduction to the complex intricacies of XSLT" Long_x0020
_Description="A gentle introduction to the complex intricacies of
XSLT and XPath, walking through the spec from simple work to
complex." PriceUS="34.95" />
</update>
I n E xample 7 - 2 , all data is s tored in attributes , and A c c es s
won't even look at attributes during an import. To get this
information into A c c es s , you need to us e a trans formation, s uc h
as the generic one s hown in E xample 7 - 3 , whic h c onverts all
attributes to c hild elements .

Example 7-3. ch0813.xs l, a s tyles heet for trans forming attributes


into elements

<?xml version="1.0" encoding="UTF-8"?>


<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XS
<!--Derived from recipe 6.1 of Sal Mangano's XSLT Cookbook-->

<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="ye

<xsl:template match="@*">
<xsl:element name="{local-name(.)}" namespace="{namespace-uri(..
<xsl:value-of select="."/>
</xsl:element>
</xsl:template>

<xsl:template match="node( )">


<xsl:copy>
<xsl:apply-templates select="@* | node( )"/>
</xsl:copy>
</xsl:template>

</xsl:stylesheet>

When applied to E xample 7 - 2 , the s tyles heet in E xample 7 - 3


produc es the res ult s hown in E xample 7 - 4 , whic h A c c es s c an
import eas ily.

A gain, A c c es s does n't c are what the root


element's name is ; update is an
appropriate des c ription for human
c ons umption.

Example 7-4. An "elementized" vers ion of the data in Example 7-2

<?xml version="1.0" encoding="UTF-8"?>


<update>
<books>
<ISBN>0596003277</ISBN>
<Title>Learning XSLT</Title>
<Tagline>A Hands-On Introduction to XSLT and XPath</Tagline>
<Short_x0020_Description>A gentle introduction to the complex intr
XSLT</Short_x0020_Description>
<Long_x0020_Description>A gentle introduction to the complex intri
and XPath, walking through the spec from simple work to complex.</
Description>
<PriceUS>34.95</PriceUS>
</books>
</update>

I f you tell A c c es s to import ch0812.xml, the file s hown in


E xample 7 - 2 , you won't have muc h to c hoos e from in the I mport
XM L dialog box, as s hown in Figure 7 - 2 6 .

Figure 7-26. Access's initial reaction to the


document that stores data in attributes

I f you c hoos e O ptions Trans form…, you c anadd the


s tyles heet, muc h as you did for the export trans formation. A dd
the s tyles heet to the lis t of trans formations , and s elec t c h0 8 1 3 ,
as s hown in Figure 7 - 2 7 .

When you c lic k O K, A c c es s applies the trans formation to the


doc ument, modifying the dis play of c omponents you s ee and
produc ing the res ult in Figure 7 - 2 8 .

I n this c as e, the table already exis ts , s o be s ure to s elec t


A ppend D ata to E xis ting Table(s ). When you c lic k O K, the data
from E xample 7 - 1 is added to the books table, as s hown in
Figure 7 - 2 9 .

Trans formations are a powerful tool in pretty muc h any area of


XM L development. U s ing a bit of XSLTadmittedly, a bit
c hallenging to learnyou c an c onvert the s truc tures you have into
the s truc tures A c c es s expec ts .

Figure 7-27. Selecting a stylesheet for


transformation
Figure 7-28. A transformed document ready for
import
7.6.1. See Also

"E xport XM L D ata Sanely" [Hack #64]

"Break T hrough V BA 's Trans formation Barrier" [Hack


#65]

Simon St. Laurent

Figure 7-29. The result of importing a


transformed document
Hack 64. Export XML Data Sanely

Working around the thorny issue of exporting related data to


XML.

E xporting a s ingle table to XM L produc es s ome eas ily reus able


data. E xporting multiple tables to XM L , however, might not
produc e data that other applic ations c an us e; it all depends on
how you s truc tured your tables and relations hips . You c an s olve
this problem in two ways : res truc ture your data or us e a query to
export data that's been unnormalized.

For our initial example, we'll s tart with a databas e c ontaining a


table that defines a lis t of books . Figure 7 - 3 0 s hows the D es ign
view for that table. I t inc ludes s ix fields of three different types .

Figure 7-30. A simple table for export


For the initial tes ts , this table c ontains jus t a little bit of
information. E xporting mature tables with thous ands of rec ords
c an quic kly produc e large XM L files definitely us eful in real life
but diffic ult for initial analys is . Figure 7 - 3 1 s hows a partial view
of the c ontent in the tes t table.

Figure 7-31. Test data in the books table

E xporting this table to XM L involves a few s teps , mos t of whic h


will be familiar to developers who have exported information from
A c c es s databas es before. T he proc es s s tarts by s elec ting the
books table in the databas e, then s elec ting E xport… from the File
menu. T he dialog box s hown in Figure 7 - 3 2 will appear, and you'll
need to s elec t XM L (* .xml) from the "Save as type" drop- down
box.

Figure 7-32. Selecting the destination for the


export
When you perform the export, A c c es s might ac tually c reate
more files than jus t the XM L file, but they'll all appear in the
s ame direc tory together with the XM L . O nc e you c lic k the E xport
button, a s mall dialog box with bas ic options , s hown in Figure 7 -
3 3 , appears .

Figure 7-33. Basic export options

For now, we'll ac c ept the defaults and jus t c lic k O K. T his res ults
in two files : books .xml and books .xs d. T he books .xml file c ontains
the information from the table, and books .xs d c ontains an XM L
Sc hema des c ription of that c ontent, annotated with a bit of
information s pec ific to A c c es s and its J et databas e engine.

T he books .xml file, s hown in E xample 7 - 5 , reflec ts the s truc ture


and c ontent of the original table c los ely.

Example 7-5. A s imple table export

<?xml version="1.0" encoding="UTF-8"?>


<dataroot xmlns:od="urn:schemas-microsoft-com:officedata" xmlns:xs
generated="2003-03-26T13:49:17">
<books>
<ISBN>0596005385</ISBN>
<Title>Office 2003 XML Essentials</Title>
<Tagline>Integrating Office with the World</Tagline>
<Short_x0020_Description>Microsoft has added enormous XML function
Excel, and Access, as well as a new application, Microsoft InfoPat
gets readers started in using those features.</Short_x0020_Descrip
<Long_x0020_Description>Microsoft has added enormous XML functiona
Excel, and Access, as well as a new application, Microsoft InfoPat
gets readers started in using those features.</Long_x0020_Descript
<PriceUS>34.95</PriceUS>
</books>
<books>
<ISBN>0596002920</ISBN>
<Title>XML in a Nutshell, 2nd Edition </Title>
<Tagline>A Desktop Quick Reference</Tagline>
<Short_x0020_Description>This authoritative new edition of XML in
provides developers with a complete guide to the rapidly evolving
Short_x0020_Description>
<Long_x0020_Description>This authoritative new edition of XML in a
provides developers with a complete guide to the rapidly evolving
Serious users of XML will find topics on just about everything the
including fundamental syntax rules, details of DTD and XML Schema
transformations, and APIs used for processing XML documents. Simpl
the only references of its kind among XML books.</Long_x0020_Descr
<PriceUS>39.95</PriceUS>
</books>
<books>
<ISBN>0596002378</ISBN>
<Title>SAX2</Title>
<Tagline>Processing XML Efficiently with Java</Tagline>
<Short_x0020_Description>This concise book gives you the informati
effectively use the Simple API for XML, the dominant API for effic
processing with Java.</Short_x0020_Description>
<Long_x0020_Description>This concise book gives you the informatio
effectively use the Simple API for XML, the dominant API for effic
processing with Java.</Long_x0020_Description>
<PriceUS>29.95</PriceUS>
</books>
</dataroot>

T his doc ument's root element, dataroot, is the only piec e of this
doc ument s pec ific to A c c es s :

<dataroot xmlns:od="urn:schemas-microsoft-com:officedata" xmlns:xs


xsd" generated="2003-03-26T13:49:17">

I t makes a names pac e dec laration for the od prefix, whic h is n't
ac tually us ed in this doc ument, and it inc ludes a pointer to the
XM L Sc hema des c ribing this doc ument's s truc ture. Bec aus e the
element names us ed here aren't in any names pac e, the
doc ument us es the xsi:noNamespaceSchemaLocation attribute to
identify the s c hema that s hould be us ed for all the elements in
this doc ument that have no names pac e. I t als o inc ludes one
s mall bit of metadata in the generated attribute, that identifies
the time and date when this XM L doc ument was c reated.

T he dataroot element c ontains three c hild books elements , eac h


indic ating a row in the books table. T heir c ontents map fairly
s imply to the names and values of the table c olumns :

<books>
<ISBN>0596002920</ISBN>
<Title>XML in a Nutshell, 2nd Edition</Title>
<Tagline>A Desktop Quick Reference</Tagline>
<Short_x0020_Description>This authoritative new edition of XML in
provides developers with a complete guide to the rapidly evolving
</Short_x0020_Description>
<Long_x0020_Description>This authoritative new edition of XML in a
provides developers with a complete guide to the rapidly evolving
Serious users of XML will find topics on just about everything the
including fundamental syntax rules, details of DTD and XML Schema
XSLT transformations, and APIs used for processing XML documents.
put, this is the only references of its kind among XML books.</Lon
Description>
<PriceUS>39.95</PriceUS>
</books>

T he only s ignific ant variation here involves the c olumn names ,


whic h inc lude s pac es . I ns tead of Short Description, now we have
Short_x0020_Description, following a c onvention M ic ros oft
developed for repres enting s pac es in XM L element names .

XM L forbids s pac es in element names


bec aus e they make it diffic ult to s eparate
the element name from the attributes , s o
A c c es s us es _x0020_, the U nic ode hex
number, for the s pac e.

E xporting individual tables is us eful, but s ometimes you might


want to export multiple tables and pres erve the relations hips
between them. A c c es s allows you to export a s et of tables ,
though it works mos t eas ily when only two tables are involved.

7.7.1. Exporting from Tables in a One-to-


Many Relationship

For our firs t example, we'll add a table that c ontains information
about (very fic tional) promotions for various books . Figure 7 - 3 4
s hows what this table looks like.

Figure 7-34. The promotions table


T he promotions table links to the books table through its
BookI D field, as s hown in Figure 7 - 3 5 .

Figure 7-35. Relationship between the books


and promotions tables
E xporting this pair of tables takes a few more s teps bec aus e
A c c es s lets you c hoos e how the export works . T he c hoic e of
whic h table is the bas e table makes a big differenc e in the export
res ults , s o the following examples will export it both ways .

We'll s tart by exporting the books table again, but this time, we'll
s elec t M ore O ptions from the dialog box s hown in Figure 7 - 3 6 .
Figure 7-36. Basic export options

C lic king M ore O ptions brings up a larger dialog with a lot more
c hoic es , as s hown in Figure 7 - 3 7 .

I n this c as e, all the information we need is on the firs t (D ata)


tab. C hec king the P romotions box and c lic king the O K button
tells A c c es s to export both the books table and the linked
rec ords of the promotions tablein this c as e, all of them. E xample
7 - 6 s hows an abbreviated vers ion of the export, with the new
c ontent from the promotions table in bold.

Figure 7-37. The full version of the Export XML


dialog box
Example 7-6. Exported linked tables
<?xml version="1.0" encoding="UTF-8"?>
<dataroot xmlns:od="urn:schemas-microsoft-com:officedata" xmlns:xs
generated="2003-03-31T16:37:01">
<books>
<ISBN>0596005385</ISBN>
<Title>Office 2003 XML Essentials</Title>
<Tagline>Integrating Office with the World</Tagline>
<Short_x0020_Description>...</Short_x0020_Description>
<Long_x0020_Description>...</Long_x0020_Description>
<PriceUS>34.95</PriceUS>

<promotions>
<PromotionID>1</PromotionID>
<BookID>0596005385</BookID>
<Name>Palm civet bonus</Name>
<Venue>Anywhere interested</Venue>
<Description>A stuffed-animal palm civet,
lovingly screen-printed to match the cover,
with every copy of the book.</Description>
<Cost>10000</Cost>
</promotions>
<promotions>
<PromotionID>3</PromotionID>
<BookID>0596005385</BookID>
<Name>Key chains</Name>
<Venue>Conferences</Venue>
<Description>keychains adorned with lovely palm civets
and the title of the book.</Description>
<Cost>1000</Cost>
</promotions>

</books>
<books>
<ISBN>0596002920</ISBN>
<Title>XML in a Nutshell, 2nd Edition</Title>
<Tagline>A Desk top Quick Reference</Tagline>
<Short_x0020_Description>...</Short_x0020_Description>
<Long_x0020_Description>...</Long_x0020_Description>
<PriceUS>39.95</PriceUS>
</books>
<books>
<ISBN>0596002378</ISBN>
<Title>SAX2</Title>
<Tagline>Processing XML Efficiently with Java</Tagline>
<Short_x0020_Description>...</Short_x0020_Description>
<Long_x0020_Description>...</Long_x0020_Description>
<PriceUS>29.95</PriceUS>

<promotions>
<PromotionID>2</PromotionID>
<BookID>0596002378</BookID>
<Name>Free filters</Name>
<Venue>Online/Safari</Venue>
<Description>Bonus SAX filters, open source-licensed,
for developers who visit the SAX2 book site.</Description>
<Cost>0</Cost>
</promotions>

</books>
</dataroot>

T he general pattern here is muc h like the original export of the


books table, exc ept that zero or more promotions elements whos e
BookID holds the s ame value as the c ontaining books element's
ISBN elementnow appear ins ide eac h books element. T his works
the s ame way that zero or more books elements appeared ins ide
the dataroot element. A ll the table c olumns are lis ted ins ide
eac h promotions element, making it eas y to rec ons truc t the
information in the promotions table or to treat the information as
a c omplete s et of information about eac h book. T here's no need
to rec ons truc t the original tables and c alc ulate primary
key/foreign key links .

A s s oon as you s tep beyond the one- to- many relations hip,
however, this kind of s imple c ontainment will fail you.

7.7.2. Exporting from Tables in a Many-to-


Many Relationship

A many- to- many relations hip, implemented with an intermediary


table, as s hown in Figure 7 - 3 8 , produc es XM L that mos t likely
will be us eful only if s omeone reimports it into A c c es s and works
with it there.

A c c es s lets you travers e this relations hip in an XM L export, as


s hown in Figure 7 - 3 9 . T his time, the export us es a [Lookup Data]
element to indic ate that s imply nes ting the data in the XM L
doc ument s truc tures is n't going to work. O ne- to- many
relations hips are repres ented us ing c ontainment, and many- to-
one relations hips are repres ented as s eparate piec es . I n this
c as e, the many- to- many relations hip inc ludes both of thos e
c hoic es .

Figure 7-38. Related tables with a many-to-


many relationship, expressed as two one-to
many relationships
Figure 7-39. Exporting related tables with a
many-to-many relationship
[Lookup Data] provides a warning that reas s embling s ome of
thes e relations hips is going to require extra lookup work on the
part of the c ons uming applic ation.

I f you reimport this data into A c c es s , it'll


do that work, s o this might not be a
problem.

E xample 7 - 7 s hows the res ults of this export.

Example 7-7. A many-to-many export combining containment and


lookup

<?xml version="1.0" encoding="UTF-8"?>


<dataroot xmlns:od="urn:schemas-microsoft-com:officedata" xmlns:xs
generated="2003-04-01T21:01:50">
<books>
<ISBN>0596005385</ISBN>
<Title>Office 2003 XML Essentials</Title>
<Tagline>Integrating Office with the World</Tagline>
<Short_x0020_Description>...</Short_x0020_Description>
<Long_x0020_Description>...</Long_x0020_Description>
<PriceUS>34.95</PriceUS>
<authorBookLink>
<bookISBN>0596005385</bookISBN>
<authorID>1</authorID>
</authorBookLink>
</books>
<books>
<ISBN>0596002920</ISBN>
<Title>XML in a Nutshell, 2nd Edition</Title>
<Tagline>A Desktop Quick Reference</Tagline>
<Short_x0020_Description>...</Short_x0020_Description>
<Long_x0020_Description>...</Long_x0020_Description>
<PriceUS>39.95</PriceUS>
<authorBookLink>
<bookISBN>0596002920</bookISBN>
<authorID>3</authorID>
</authorBookLink>
<authorBookLink>
<bookISBN>0596002920</bookISBN>
<authorID>4</authorID>
</authorBookLink>
</books>
<books>
<ISBN>0596002378</ISBN>
<Title>SAX2</Title>
<Tagline>Processing XML Efficiently with Java</Tagline>
<Short_x0020_Description>...</Short_x0020_Description>
<Long_x0020_Description>...</Long_x0020_Description>
<PriceUS>29.95</PriceUS>
<authorBookLink>
<bookISBN>0596002378</bookISBN>
<authorID>2</authorID>
</authorBookLink>
</books>
<authors>
<AuthorID>1</AuthorID>
<GivenName>Simon</GivenName>
<FamilyName>St.Laurent</FamilyName>
<FullName>Simon St.Laurent</FullName>
</authors>
<authors>
<AuthorID>2</AuthorID>
<GivenName>David</GivenName>
<FamilyName>Brownell</FamilyName>
<FullName>David Brownell</FullName>
</authors>
<authors>
<AuthorID>3</AuthorID>
<GivenName>Elliotte</GivenName>
<FamilyName>Harold</FamilyName>
<FullName>Elliotte Rusty Harold</FullName>
</authors>
<authors>
<AuthorID>4</AuthorID>
<GivenName>Scott</GivenName>
<FamilyName>Means</FamilyName>
<FullName>W. Scott Means</FullName>
</authors>
</dataroot>

N ow eac h books element c ontains one or more authorBookLink


elements , eac h holding an authorID element. T he value of that
authorID element maps to an authorID element ins ide an authors
element. I f the data is going bac k into A c c es s , this is fine, but if
it's going to another applic ationE xc el, perhaps , or an XSLT
trans formation into H T M L for a brows erthis is n't muc h fun.

T his might feel like a c as e in whic h it would make s ens e to s tore


repetitive (nonnormalized) data in the tables , but fortunately,
there's a better option: exporting a query ins tead of a table.

7.7.3. Using a Query to Tame the Export


By thems elves , queries don't provide nes ted views , but they
c ertainly make it eas ier to pres ent s ome kinds of
informationnotably, many- to- many relations hips . T he mec hanic s
of exporting queries are muc h like thos e of exporting s ingle
tables , and the res ults are s imilar.

A c c es s s upports SQ L queries , obvious ly,


bec aus e that's at the heart of its
func tionality. A c c es s does n't, however,
s upport other s tandards for querying, s uc h
as XQ uery.

To demons trate, let's export a SQ L query named books ByA uthor,


whic h us es the books , authors , and authorBookL ink tables to
c reate a lis t of books s orted by author. T he SQ L for the query
expres s es the relations hips an XM L proc es s or working with the
linked table export would otherwis e have to deal with:

SELECT authors.GivenName, authors.FamilyName, books.ISBN, books.Ti


FROM books INNER JOIN (authors INNER JOIN authorBookLink ON author
= authorBookLink.authorID) ON books.ISBN = authorBookLink.bookISBN
ORDER BY authors.FamilyName;

T he interfac e for exporting a query is the s ame as the interfac e


for a table, exc ept there is no option for exporting linked
information. When you export a query, all the information you
want to export mus t be in that query. E xporting the query
produc es the res ult s hown in E xample 7 - 8 .

Example 7-8. An exported query

<?xml version="1.0" encoding="UTF-8"?>


<dataroot xmlns:od="urn:schemas-microsoft-com:officedata" xmlns:xs
xsd" generated="2003-04-02T14:47:59">
<booksByAuthor>
<GivenName>David</GivenName>
<FamilyName>Brownell</FamilyName>
<ISBN>0596002378</ISBN>
<Title>SAX2</Title>
</booksByAuthor>
<booksByAuthor>
<GivenName>Elliotte</GivenName>
<FamilyName>Harold</FamilyName>
<ISBN>0596002920</ISBN>
<Title>XML in a Nutshell, 2nd Edition</Title>
</booksByAuthor>
<booksByAuthor>
<GivenName>Scott</GivenName>
<FamilyName>Means</FamilyName>
<ISBN>0596002920</ISBN>
<Title>XML in a Nutshell, 2nd Edition</Title>
</booksByAuthor>
<booksByAuthor>
<GivenName>Simon</GivenName>
<FamilyName>St.Laurent</FamilyName>
<ISBN>0596005385</ISBN>
<Title>Office 2003 XML Essentials</Title>
</booksByAuthor>
</dataroot>
J us t as in a tabular repres entation of the query, information
repeats notably, the I SBN and title of XML in a Nuts hell, whic h has
two authors . I f you're s ending data to an applic ation that lac ks
A c c es s 's apprec iation for relations between tables , this
approac h will probably work muc h more eas ily.

7.7.4. See Also

"I mport Varied XM L D ata into A c c es s " [Hack #63]

"Break T hrough V BA 's Trans formation Barrier" [Hack


#65]

Simon St. Laurent


Hack 65. Break Through VBA's
Transformation Barrier

Strange but true: A ccess supports XSLT transf ormation on input


when you use the GUI, but not when you automate the process
with VBA . The same goes f or output. Fortunately, you can work
around this by calling the MSXML parser directly.

T he examples in "I mport Varied XM L D ata into A c c es s " [Hack


#63] give s ome ideas for how to get information into your
A c c es s tables even if the data arrives in a format other than the
s imple element- only form A c c es s expec ts . H owever, if s uc h data
arrives on a regular bas is , you probably don't want to be c lic king
through forms every time you need to import more data.

U nfortunately, c onverting thes e s teps to an automated V BA


proc es s is a c hallenge bec aus e the ImportXML func tion does n't
provide a plac e for any trans formations . A s it turns out, neither
does the ExportXML func tion.

T he s yntax of the ImportXML func tion looks like this :

Application.ImportXML (DataSource, ImportOptions)

I t takes only a data s ourc e, the name and path of the XM L file to
import, and an options c ons tant acAppendData, acStructureAndData
(the default), or acStructureOnly. T here is no option for an XSLT
trans formation. Similarly, the ExportXML func tion looks like this :

Application.ExportXML (ObjectType, DataSource, DataTarget,

T he PresentationTarget argument does have s omething to do with


trans formation, but it's only for output. I t identifies where
A c c es s will put a s tyles heet for turning the XM L into H T M L
bas ed on its own expec tations , not yours .

You c an get around thes e problems in two ways . Firs t, you c an


write s ome c us tom c ode. T he import vers ion will ins tantiate an
XM L pars er (probably M SXM L ), read the c ontent from the
doc ument however you deem appropriate, and then us e A D O ,
D A O , or SQ L Update queries to put the data in the databas e. T he
export vers ion will read data from the databas e and write it to an
M SXM L D O M tree as nec es s ary.

T his might be appropriate if you have c omplic ated c as es , but it's


a lot of c ode for what's mos t likely a s imple problem, and you
c an't tes t how it works (or reus e that work) outs ide of A c c es s .

A more likely approac h, if you c an s tand working with XSLT, is to


add a s tep before the import or after the export that performs an
extra trans formation. Bec aus e A c c es s does n't let you pas s
objec ts to the import or get objec ts from the export, you need to
work with temporary files to produc e the res ults you want.
C onveniently, you c an us e the s ame func tion for both c as es .

A s imple vers ion of this func tion looks like this :

Private Sub Transform(sourceFile, stylesheetFile, resultFile)

Dim source As New MSXML2.DOMDocument30


Dim stylesheet As New MSXML2.DOMDocument30
Dim result As New MSXML2.DOMDocument30

' Load data.


source.async = False
source.Load sourceFile

' Load style sheet.


stylesheet.async = False
stylesheet.Load stylesheetFile

If (source.parseError.errorCode <> 0) Then


MsgBox ("Error loading source document: " & source.parseError.r
Else
If (stylesheet.parseError.errorCode <> 0) Then
MsgBox ("Error loading stylesheet document: " & _
stylesheet.parseError.reason)
Else
' Do the transform.
source.transformNodeToObject stylesheet, result
result.Save resultFile
End If
End If

End Sub

T he transform func tion takes three arguments : the path of a


s ourc e file holding the original XM L , the path of a s tyles heet file
holding the XSLT that will be us ed to trans form it, and the path to
whic h the res ulting doc ument s hould be s aved. Typic ally, you'll
want to c all transform before us ing A c c es s 's native ImportXML
func tion or after you've us ed the ExportXML func tion.

For example, you might import XM L files to a table direc tly with
this c all:

Application.ImportXML "http://simonstl.com/ora/updateBook.

But if that XM L file s tored the data as attributes , and you wanted
to apply a trans formation to that data before you imported it into
A c c es s , you might do this ins tead:

Transform "http://simonstl.com/ora/updateBook.xml", _
"C:\xslt\attsToElem.xsl", _
"C:\temp\tempImport.xml"
Application.ImportXML "C:\temp\tempImport.xml", acAppendData

Similarly, you c an apply a trans formation after you exported


data, turning it into H T M L :

Application.ExportXML acExportTable, "books", "C:\temp\tempExport.


Transform "C:\temp\tempExport.xml", _
"C:\xslt\booksToHTML.xsl", _
"C:\export\exportedBooks.html"

Writing XM L doc uments out to files and then repars ing them is n't
effic ient by any means , but it patc hes a gap left by the A c c es s
A P I for importing and exporting XM L . U nles s you're dealing with
huge volumes of data, or doing this proc es s ing c ons tantly, us ers
of your databas es aren't likely to notic e a big differenc e. I mport
and export are us ually pretty s low operations anyway.
7.8.1. See Also

"I mport Varied XM L D ata into A c c es s " [Hack #63]

"E xport XM L D ata Sanely" [Hack #64]

Simon St. Laurent


Hack 66. Leverage SQL Server Power by
Calling Stored Procedures

Get a leg up on perf ormance when using SQL Server data.

D evelopers c reating A c c es s applic ations that are front ends to


SQ L Server databas es have two c hoic es for their applic ation
type. T he M ic ros oft- rec ommended c hoic e is to us e an A c c es s
data projec t (A D P ), whic h is direc tly tied to the SQ L Server
databas e. T his native- mode O L E D B c onnec tion res ults in a
lighter- weight, better- performing front end that c an direc tly us e
SQ L views , s tored proc edures , and us er- defined func tions . I t
als o lets developers des ign objec ts direc tly on the s erver (no
need to us e E nterpris e M anager).

D es pite thes e advantages , many s ituations forc e developers to


us e O D BC linked tables in a traditional A c c es s M D B file. N ot
the leas t of thes e is the ability to c reate loc al tables in the M D B
(in an A D P, even the Switc hboard I tems table mus t be on the
s erver) and the ability to c onnec t to other data s ourc es (s uc h as
other A c c es s databas es , E xc el s preads heets , text files , and s o
on). J us t bec aus e you c hoos e to us e an M D B as a front end
does n't mean you have to give up the s erver- s ide proc es s ing
power of SQ L Server s tored proc edures .

7.9.1. Hooking Up with ODBC


When an A c c es s M D B is us ing O D BC links to SQ L Server, all
data proc es s ing is done on the c lient s idethat is , within A c c es s
on the works tation. I f a lis tbox on a form gets filtered by a
c ombo box s elec tion, all the rec ords are returned over the
network to A c c es s and A c c es s applies the filter. A lternatively,
the us e of s tored proc edures c an inc reas e performanc e in your
A c c es s M D Bs by s hifting the filtering to the s erver. Stored
proc edures are powerful bec aus e they c ombine the data- joining
c apabilities of A c c es s queries or SQ L views with the ability of
V BA proc edures to ac c ept parameters and to loop and proc es s
data.

T- SQ L , M ic ros oft SQ L Server's vers ion of the SQ L language, is


s omewhat different from the J et (A c c es s 's ) flavor of SQ L . I t is
als o muc h different from V BA . H owever, if you c an c reate A c c es s
queries and write V BA func tions , you c an learn to write SQ L
s tored proc edures . I t is n't diffic ult to bec ome good enough in T-
SQ L to inc reas e the performanc e of your applic ations . Whether
you ins tall M SD E (the lite vers ion of SQ L Server that s hips with
M ic ros oft O ffic e) or SQ L Server its elf, you c an look at the s tored
proc edures within the N orthwind databas e to get s tarted.

T he A D O library is one way to exec ute s tored proc edures in


A c c es s . You do this in V BA by exec uting a Command objec t whos e
c ommand text is the s tored proc edure name. Firs t it is
nec es s ary to open a Connection objec t on the SQ L Server
databas e. T he c ode in E xample 7 - 9 exec utes the
CustOrdersOrders s tored proc edure that s hips with N orthwind,
s ending in the muc h- abus ed customerid ALFKI to fill an A D O
rec ords et with all the orders belonging to A lfreds Futterkis te.

Example 7-9. Running a s tored procedure

Dim cn As ADODB.Connection
Dim sp As ADODB.Command
Dim rs As ADODB.Recordset
Set cn = New ADODB.Connection
cn.ConnectionString = CurrentDb.TableDefs("dbo_customers").Connect
cn.Open
Set sp = New ADODB.Command
sp.ActiveConnection = cnSQL
sp.CommandType = adCmdStoredProc
sp.CommandText = "CustOrdersOrders"
sp.Parameters.Refresh
sp.Parameters("@customerid") = "ALFKI"
Set rs = sp.Execute

A c c es s , however, c an't us e A D O rec ords ets in c ertain


s ituations . A lthough A c c es s us es A D O more and more with
every new vers ion releas e, A c c es s 2 0 0 3 s till has deep ties to
D A O , s o muc h s o that M ic ros oft put bac k a default referenc e to
D A O in V BA , after not inc luding it in A c c es s 2 0 0 2 (XP ). A data-
entry form bound to a linked table will have an underlying
rec ords et that is n't A D O , but rather, is D A O . C ontrols s uc h as
c ombo boxes or lis tboxes , on unbound or D A O - bound forms ,
require their rec ords ets to be D A O as well.

7.9.2. Creating a Pass-Through Query

A c c es s c an tap into s tored proc edure power and get a D A O


rec ords et filled with data via a s tored proc edure us ing an
underutilized feature known as a Pass-Through query. C reating a
Pass-Through query is relatively s traightforward, and the res ults
returned are in a D A O rec ords et, appropriate for us e in any
A c c es s objec t or c ontrol that c an us e a query as its data
s ourc e.
To c reate a Pass-Through query, s elec t Q ueries in the D atabas e
window, and c lic k N ew. C lic k D es ign V iew, and then c lic k O K.
C lic k C los e on the Table lis t to go direc tly into D es ign view. O n
the Q uery menu, c lic k SQ L- Spec ific , and then c lic k P as s -
T hrough, as s hown in Figure 7 - 4 0 .

Figure 7-40. Creating a Pass-Through query


T he query des igner will s witc h to SQ L view and allow only SQ L
s tatements to be entered. E nter CustOrdersOrders 'ALFKI' in the
SQ L view of the query des igner. C lic k Save, and name the query
qry_C us tO rders O rders _pt.

A t this point, A c c es s does n't know where to pas s this query. O n


firs t exec ution, you are prompted for the data s ourc e c onnec tion
to us e: c C hoos e the s ame data s ourc e you us ed to link your
SQ L tables . A fter c hoos ing the appropriate data s ourc e, A c c es s
s ends the SQ L s tring c ontained in the query to the s erver, and
SQ L runs the s tored proc edure and returns the res ults to
A c c es s , as s hown in Figure 7 - 4 1 .

Figure 7-41. Data returned from SQL Server via


a stored procedure
Steve Conklin
Hack 67. Manage Word Documents from
Access

Tap into the Word object library to copy A ccess data directly
into a Word document.

A s is the c as e with all M ic ros oft O ffic e produc ts , Word has a


s ignific ant number of expos ed objec ts and methods to work with,
and bec oming familiar with a dec ent number of thes e is a
c hallenge worth undertaking.

T his hac k c reates a proc edure that plac es data from A c c es s


into a table in a Word doc ument. T he c onc epts here als o apply to
other Word manipulations . P erhaps this will be your s pringboard
into a new avenue of O ffic e development.

7.10.1. Hooking into Word

I n an A c c es s c ode module, we're going to plac e a routine to


work with an exis ting Word doc ument. To make this a little eas ier,
we'll s et a referenc e to Word's objec t library. We'll do this ins ide
the A c c es s V B E ditor, us ing the Tools Referenc es menu
and the Referenc es dialog box, as s hown in Figure 7 - 4 2 . N ote
that your vers ion number of the Word library might differ, s o us e
whatever you have.
Figure 7-42. Setting a reference to the Word
object library
7.10.2. The Code

T he next thing to do is enter the c ode. T his mus t go into an


A c c es s c ode module:

Sub Access_to_Word()
Dim conn As ADODB.Connection
Set conn = CurrentProject.Connection
Dim recset As ADODB.Recordset
Set recset = New ADODB.Recordset
Dim row_num As Integer
Dim col_num As Integer
Dim word_doc As Object
'Assumes Word doc is in same path - change name and path as needed
Set word_doc = GetObject(Application.CurrentProject.Path & "\Custo

'get data from Access table


recset.Open "Select * From Customers Where State='OR'", _
conn, adOpenKeyset, adLockOptimistic
'get the record count - used to create Word Table
recset.MoveLast
recset.MoveFirst

With word_doc
'navigate to Word bookmark and create table
'the number of table rows matches the recordset row count
'the number of table columns matches the number of recordset field
.Bookmarks("Customers").Select
.Tables.Add Range:=Selection.Range, _
NumRows:=recset.RecordCount, NumColumns:=recset.Fields.Co
For row_num = 1 To recset.RecordCount
For col_num = 1 To recset.Fields.Count
.Tables(.Tables.Count).Cell(row_num, col_num).
Select Selection.TypeText recset.Fields(col_num - 1)
Next col_num
'next database record
recset.MoveNext
Next row_num
End With
recset.Close
Set recset = Nothing
Set word_doc = Nothing
MsgBox "done"
End Sub

H ere are s ome highlights of this c ode:

T he A c c es s data is gathered into a rec ords et.

T he GetObject func tion is referenc ed to the exis ting Word


doc ument. N ote that this example as s umes the
databas e and the doc ument are in the s ame direc tory.
A ls o, the name of the doc ument is hardc oded, but you
c an c hange this as nec es s ary.

T he doc ument has a prees tablis hed bookmark named


C us tomers . T his is us ed as a guide to where to c reate
the table.
A Word table is c reated, and its row and c olumn
dimens ions matc h thos e of the rec ords et. T his ens ures
the new Word table is exac tly the c orrec t s ize to hous e
the data.

T he Word table is populated c ell by c ell by looping


through the rec ords et. A n outer loop c yc les through the
rec ords et rows , and in eac h row an inner loop c yc les
through eac h field.

7.10.3. The Data Has Landed Intact

A fter running this c ode, the doc ument has a table with the data,
as s hown in Figure 7 - 4 3 . N ote that there is no c onnec tion bac k
to A c c es s ; the data is jus t es s entially part of the Word
doc ument.

N ote that this s implis tic example as s umes a number of things :


the bookmark exis ts , there is no exis ting table, and the A c c es s
table is n't too large in terms of rows and fields to make the Word
table too dens ely pac ked.

N onetheles s , this hac k s erves as a brief introduc tion to tapping


into Word objec ts . Bec aus e the referenc e has been s et to the
library, you c an now us e the O bjec t Brows er in A c c es s to review
Word's objec ts .

Figure 7-43. The Access data in a Word table


Hack 68. Use Access as a Front End to
MySQL

MySQL is a widely used open source database program that


of ten runs on Linux web servers, and A ccess makes a great
f ront end f or data entry and reporting.

M ySQ L is a wildly s uc c es s ful open s ourc e SQ L databas e that


runs on mos t L inux- bas ed web s ervers . I t's the perfec t databas e
to s tore information for us e in databas e- driven web s ites
bec aus e you c an us e the P H P programming language to read the
data from M ySQ L and dis play it on web pages . I n fac t, the
c ombination of L inux, A pac he (an open s ourc e web s erver that
runs on L inux), M ySQ L , and P H P is s o popular that it's known by
its initials : L A M P.

H owever, M ySQ L does n't hold a c andle to A c c es s when it c omes


to forms and reports . M ySQ L has no built- in form editor or report
writer. I ns tead, you type c ommands at a c ommand line or write
programs (us ually as part of P H P - bas ed web pages ) to enter,
edit, and dis play information.

But who wants to c reate programs that dis play data entry forms
for eac h table? T his is where A c c es s c omes in. A c c es s c an add
a friendly fac e to your M ySQ L databas e. I n the s ame way an
A c c es s databas e c an link to tables in another A c c es s databas e,
you c an link to tables in a M ySQ L databas e on your web s erver
over a L A N or the I nternet. O nc e the tables are linked, you (or
your us ers ) c an us e A c c es s forms to enter or edit the M ySQ L
data and A c c es s reports to dis play it.

7.11.1. Installing the MySQL Tools

M ySQ L does have a Windows - bas ed utility you c an us e for


c reating and editing the s truc ture of the tables in your M ySQ L
databas es . T he older vers ion of the program is c alled M ySQ L
C ontrol C enter, and it has been replac ed by M ySQ L Q uery
Brows er. You c an download either program from the M ySQ L web
s ite (http://dev.mys ql.c om/downloads /) for free. T he manual for
M ySQ L Q uery Brows er is available online at
http://dev.mys ql.c om/doc / query brows er/en/, or you c an
download it as a P D F or Windows H elp file.

When you s tart M ySQ L Q uery Brows er, you s pec ify the s erver on
whic h M ySQ L runs , your M ySQ L us ername, and your pas s word.
O nc e c onnec ted, you s ee a lis t of the tables for whic h you have
ac c es s permis s ion, and you c an view or edit the data or
s truc ture of the tables , as s hown in Figure 7 - 4 4 .

Figure 7-44. MySQL Query Browser


M ySQ L Q uery Brows er is us eful, but it's not the tool to give to
your databas e us ers . For example, you c an't c reate forms with
data validation, c ombo boxes , or s ubforms , and you c an't c reate
formatted reports . For this , you need A c c es s .

For A c c es s to c onnec t to a M ySQ L databas e, you need to ins tall


the M ySQ L C onnec tor/O D BC driver (als o c alled the M ySQ L
O D BC or M yO D BC driver). T his driver lets A c c es s
c ommunic ate with M ySQ L via O pen D ataBas e C onnec tivity
(O D BC ). You c an download the M ySQ L O D BC driver from
(http://dev.mys ql.c om/downloads /) for free. A fter you ins tall it, a
new option appears when you link to external tables from an
A c c es s databas e.

You don't need to ins tall M ySQ L Q uery Brows er on every


c omputer on whic h your A c c es s databas e will run; you need it
only if you plan to us e it to look at or c hange the s truc ture of the
tables in your M ySQ L databas e. But every c omputer on whic h
your A c c es s databas e runs needs the M ySQ L O D BC driver
ins talled bec aus e A c c es s us es the driver every time you open a
linked M ySQ L table.

I f you plan to c onnec t to a M ySQ L databas e over the I nternet,


your reques ts will probably need to pas s through one or more
firewalls . T he M ySQ L O D BC driver c ommunic ates over port
3 3 0 6 , s o this port mus t be open on all the firewalls between your
c omputer and the M ySQ L s erver. You c an s pec ify a different port
when you c reate the link from A c c es s to M ySQ L , in c as e your
M ySQ L s erver is c onfigured to us e a nons tandard port number.

7.11.2. Linking to MySQL Tables


O nc e you've got the M ySQ L O D BC driver ins talled, linking
A c c es s to M ySQ L tables requires two s teps : making s ure the
tables c ontain the right fields and making the link. For the
A c c es s /M ySQ L link to work right when editing data into the
tables , eac h table to whic h you link needs to have the following
two fields (the names of the fields don't matter):

AutoNumber

I n M ySQ L , this is an INT (integer) field of s ize 11 with the


UNSIGNED and AUTO INC (auto inc rement) options s elec ted.
T his field mus t be the primary key for the table.

Date

T his field is updated automatic ally any time the rec ord
is edited. I n M ySQ L , this is a TIMESTAMP field.

M os t tables have thes e two fields anyway; good databas e des ign
s ugges ts us ing an A utoN umber field as the primary key for mos t
tables . H owever, if your M ySQ L tables don't have thes e fields ,
you need to us e M ySQ L Q uery Brows er or s ome other tool to
add them.

C reating the links in A c c es s is a s nap. C hoos e File G et


E xternal D ata L ink Tables to dis play the L ink dialog box.
Set the file type to O D BC D atabas es , and you s ee the Selec t
D ata Sourc e dialog box, whic h lis ts O D BC databas es you've
us ed before, in the form of D ata Sourc e N ame (D SN ) files that
c ontain the c onnec tion information for the databas e. I f you are
opening a table in a databas e you've us ed before, c hoos e the
D SN file for the databas e, c lic k O K, and c hoos e the tables to
link.
I f you are linking to a M ySQ L databas e for the firs t time, c lic k
the N ew button in the Selec t D ata Sourc e dialog box, c hoos e
M ySQ L O D BC D river from the driver lis t (it's near the end), c lic k
N ext, s pec ify a name for the D SN file you are c reating to s tore
the c onnec tion information, and c lic k Finis h. You'll s ee the
M ySQ L O D BC D river D SN C onfiguration dialog box, s hown in
Figure 7 - 4 5 .

Figure 7-45. Specifying connection information


for a MySQL database
Fill in the hos tname of the M ySQ L s erver, the name of the
databas e that c ontains the tables to whic h you want to link, and
your M ySQ L us ername and pas s word. I f your M ySQ L s erver
does n't c ommunic ate over port 3 3 0 6 (the default), enter the
port number, too. I f you want to make s ure your c onnec tion
information is c orrec t, c lic k Tes t D ata Sourc e, and A c c es s will
try to c onnec t to the M ySQ L databas e and tell you whether it
s uc c eeded.

When you c lic k O K, A c c es s dis plays the L ink Tables dialog box
(the s ame dialog box you us e when linking to tables in other
A c c es s databas es ). H owever, in addition to the lis t of tables , you
c an s elec t the Save P as s word c hec kbox. T his option is
mis named bec aus e A c c es s s tores the M ySQ L pas s word no
matter what; this c hec kbox ac tually c ontrols whether it s tores
the M ySQ L us ername. I f you don't s elec t this option, you have
to enter the M ySQ L us ername eac h time your A c c es s databas e
makes its initial c onnec tion to the M ySQ L databas e.

I f you have any s ec urity c onc erns about


the information in the table, don't c hec k
the Save P as s word c hec kbox when you
c reate a link to a M ySQ L table. I f you s ave
both the M ySQ L us ername and pas s word
in the A c c es s databas e, anyone who c an
open the A c c es s databas e c an make
c hanges to the information in your M ySQ L
databas e.

L inked tables from M ySQ L databas es appear on the Tables lis t


in the A c c es s D atabas e window with a blue- green globe ic on
rather than the us ual box ic on. You c an't c hange the s truc ture of
linked tables , and you c an't c reate relations hips that enforc e
referential integrity between tables , but otherwis e, you c an us e
the data jus t as if it were in your A c c es s databas e. I f you
c hange the s truc ture of a table in the M ySQ L table, be s ure to
relink it by c hoos ing Tools D atabas e U tilities L inked
Table M anager.

7.11.3. Hacking the Hack

When you s pec ify the information about a M ySQ L databas e,


A c c es s c reates a D SN file and s tores it in the C:\Program
Files \Common Files \ODBC\Data Sources folder (as s uming Windows
is ins talled on your C: drive). Strangely, A c c es s als o s tores the
information in the databas e (M D B) file, s o it does n't read this
D SN file again after it c reates it. I f you s et up an A c c es s
databas e with M ySQ L links and then take the databas e to
another mac hine, all you need is the M ySQ L O D BC driver
ins talled. You don't need to bring along the D SN file, too.

A c c es s s tores the M ySQ L c onnec tion information as a


c onnec tion s tring that looks like this (the line breaks after eac h
s emic olon are inc luded for readability only):

ODBC;
DRIVER={MySQL ODBC 3.51 Driver};
DESC=;
DATABASE=financial;
SERVER=data.gurus.com;
UID=odbc-margy;
PASSWORD=ziasti;
PORT=;
OPTION=;
STMT=;
TABLE=Categories

To s ee the c onnec tion s tring for a linked table, s elec t the table
in the D atabas e window, c lic k D es ign, c lic k Yes when A c c es s
points out that the table s truc ture will be read- only, right- c lic k
anywhere in the D es ign window, and c hoos e P roperties from the
menu that appears . A s you c an s ee, both the us ername and the
pas s word (if you have c hos en to s ave the pas s word) appear in
plain texts o muc h for s ec urity. You c an't edit the c onnec tion
s tring bec aus e the table s truc ture c an't be edited.

I f you open a D SN file with N otepad or another text editor, you


s ee the s ame c onnec tion s tring, but without the s emic olons . You
c an edit the D SN file, but it won't affec t exis ting linked tables ; it
affec ts only tables that you link us ing the D SN file in the future.

7.11.4. See Also

M ySQ L doc umentation at http://dev.mys ql.c om/doc /

M ySQ L C onnec tor/O D BC driver doc umentation at


http://dev.mys ql.
c om/doc /mys ql/en/O D BC _C onnec tor.html

P H P doc umentation at http://www.php.net/doc s .php


Margaret Levine Young
Hack 69. Send Access Data Through
Outlook Automatically

Implement bulk emailing of your data by tapping into Outlook


objects.

T he purpos e of mos t databas es is to s tore and report


information. O ften, it is nec es s ary to s end the reports that are
generated by a databas e to multiple us ers . T his does n't have to
be a manual proc es s . By automating M ic ros oft O utlook from
A c c es s V BA , it is pos s ible to automatic ally generate reports
and s end them via email.

T he firs t item you need to determine is whether you are going to


s end emails only through your addres s book. I f you dec ide to do
that, you don't need to adjus t any of the default s ettings in
O utlook. I f, however, you want to s end to any addres s through
your applic ation, you need to make a c hange in O utlook.

By default, O utlook automatic ally c hec ks the email addres s es


when you s end an email. When you are doing this in an
automated fas hion, you will have errors to deal with if an email
addres s does n't exis t in your addres s book. To s hut off this
feature in O utlook, go to the Tools O ptions dialog.

O n the O ptions dialog, s hown in Figure 7 - 4 6 , c lic k the E - mail


O ptions button in the P referenc es tab, and then c lic k the
A dvanc ed E - mail O ptions button s hown in Figure 7 - 4 7 .
T his ac tion brings up a dialog box with three s ec tions : "Save
mes s ages ," "When new items arrive in my I nbox," and "When
s ending a mes s age," as s hown in Figure 7 - 4 8 .

T he "When s ending a mes s age" s ec tion c ontains a c hec kbox for


"A utomatic name c hec king," as s hown in Figure 7 - 4 8 . C hec k
the box if you want O utlook to c hec k addres s es , and unc hec k it
if you want to s imply s end the mes s ages without c hec king.

N ow that you have determined how you want O utlook to handle


addres s es , you are ready to build email func tionality into your
applic ation. A lthough you will eventually want to have reports
bas ed on parameterized queries that go to different us ers , this
example s hows how to s end individual reports to multiple
rec ipients .

Figure 7-46. Outlook's Options dialog


I t s hould be noted that to deal with the inc reas ing number of
problems with virus es , O utlook prompts the us er to allow ac c es s
to the addres s book and to s end the mes s ages . A lthough this
prevents you from s ending email unattended, it is c ertainly muc h
eas ier than doing everything manually every time. I n older
vers ions of O utlook, you c an s end multiple emails unattended.

To ac c omplis h the email tas k, c reate a table c alled tbl_E mail


with two text fields : E mail_A ddres s (5 0 c harac ters ) and
Report_N ame (2 5 c harac ters ). You c an make the fields larger if
it is warranted. I f you us e automatic name c hec king, you jus t
need to put in the dis play name of the people you want to s end
the mes s ages to in the E mail_A ddres s field. I f you aren't us ing
automatic name c hec king, you need to enter the full email
addres s . P ut in two or three rec ords for your tes t.

I n a normal applic ation environment, you would want this to be


driven from a form; however, this example s imply s ends all the
emails through a proc edure.

Figure 7-47. The Advanced E-mail Options


dialog
To c reate the proc edure, go to the M odules tab in A c c es s , and
c lic k N ew. O nc e you are in a blank module, go to I ns ert
P roc edure, make s ure the radio boxes for Sub and P ublic are
s elec ted, and fill in SendO utlookE mail in the N ame text box.
T his c reates the s hell for your proc edure.

N ow you need to c reate a referenc e to M ic ros oft O utlook. D o


this by going to Tools Referenc es and c hec king the box for
the vers ion of O utlook that you have. N ow you c an referenc e the
O utlook objec t model. I f you us e a vers ion of A c c es s other than
A c c es s 2 0 0 3 , you might need to c hec k the box for M ic ros oft
D ata A c c es s O bjec ts (mine is M ic ros oft D A O 3 .6 O bjec t
L ibrary). N ow you are ready to begin c oding.

7.12.1. The Code

T he c ode is s hown in E xample 7 - 1 0 .

Figure 7-48. Changing how Outlook handles


names and email addresses
Example 7-10.Acces s VBA code to s end email

Public Sub SendOutlookEmail()


Dim db As DAO.Database
Dim ReportRs As DAO.Recordset
Dim EmailRS As DAO.Recordset

Dim olApp As Outlook.Application


Dim olMail As Outlook.MailItem

Dim EmailColl As Collection


Dim varEmail As Variant
Dim FileName As String

' Outlook only allows one instance to be open at a time,


' so you can call it with New and it will use the instance
' that you already have open. I suggest having Outlook open
' already so you are not prompted for user name or password.
Set olApp = New Outlook.Application
Set db = CurrentDb
Set ReportRs = db.OpenRecordset( _
"Select Report_Name from tbl_Email Group by Report_Name")

ReportRs.MoveFirst

While Not ReportRs.EOF


Set EmailColl = New Collection
Set EmailRS = db.OpenRecordset( _
"Select Email_Address from tbl_Email Where Report_Name = " & "
ReportRs.Fields(0).Value & """" & ";")
EmailRS.MoveFirst
While Not EmailRS.EOF
EmailColl.Add EmailRS.Fields(0).Value
EmailRS.MoveNext
Wend

EmailRS.Close
Set EmailRS = Nothing

Set olMail = olApp.CreateItem(olMailItem)


olMail.subject = "Monthly Report"
For Each varEmail In EmailColl
olMail.Recipients.Add varEmail
Next
olMail.Body = "Your Monthly Report is attached"
FileName = "C:\Reports\" & ReportRs.Fields(0).Value & ".rtf"
DoCmd.OutputTo acReport, ReportRs.Fields(0).Value, _
acFormatRTF, FileName
' If you had multiple attachments, you could add them one at a t
olMail.Attachments.Add FileName, olByValue, 1, "Monthly Report"
olMail.Send

Set olMail = Nothing


Set EmailColl = Nothing

ReportRs.MoveNext
Wend

ReportRs.Close
Set ReportRs = Nothing

Set olApp = Nothing


' You can close Outlook with olApp.Quit - but since I suggested
' that you keep it open I am not closing it here

Set db = Nothing
End Sub

When you run the c ode, you will quic kly bec ome annoyed at the
number of prompts you rec eive. A s s tated earlier, this is muc h
better than doing it manually, but there has to be a better way.

T he c ode requires s everal variables for the


O utlook objec ts and data objec ts ; s ee
E xample 7 - 1 0 for thes e items . T his
example als o takes advantage of the
Collection objec t; however, you c an s kip
that s tep and jus t us e the rec ords et. T he
main reas on the c ode us es the Collection
objec t is that, in my produc tion- automated
email applic ations , I pas s Collections to
the email proc edure for the report names
and the email addres s es . T his lets me us e
that s ame proc edure in other M ic ros oft
O ffic e applic ations s uc h as E xc el or Word,
where I might not be us ing rec ords ets . T he
proc edure s aves the reports in a direc tory
c alled C:\Reports ; if this direc tory does n't
exis t on your s ys tem, you c an c reate the
direc tory, or you c an plac e the reports in a
different direc tory.
7.12.2. An Easier Way

N ow that you are familiar with the items to s end emails through
O utlook, here is an eas ier way to handle it. M os t likely this will
be helpful only for large jobs bec aus e it requires two- s teps .

T he adjus ted A c c es s proc edure in E xample 7 - 1 1 c hanges the


original c ode from E xample 7 - 1 0 to s ave the email ins truc tions
in an ADO.Records et XM L file. O utlook then proc es s es this file.
You will need to c reate a referenc e to A D O in both the O utlook
and A c c es s V BA environments .

Example 7-11. Creating an XML file from an ADO records et

Public Sub CreateOutlookXML()


Dim db As DAO.Database
Dim ReportRs As DAO.Recordset
Dim EmailRS As DAO.Recordset

Dim saveRS As ADODB.Recordset


Set saveRS = New ADODB.Recordset

saveRS.Fields.Append "Email_Address", adVarChar, 50, adFldFixed


saveRS.Fields.Append "File_Name", adVarChar, 50, adFldFixed
saveRS.Open
Dim FileName As String

Set db = CurrentDb
Set ReportRs = db.OpenRecordset( _
"Select Report_Name from tbl_Email Group by Report_Name")

ReportRs.MoveFirst
While Not ReportRs.EOF
FileName = "C:\Reports\" & ReportRs.Fields(0).Value & ".rtf"
Set EmailRS = db.OpenRecordset( _
"Select Email_Address from tbl_Email Where Report_Name = " & """
ReportRs.Fields(0).Value & """" & ";")
EmailRS.MoveFirst
While Not EmailRS.EOF
saveRS.AddNew
saveRS.Fields(0).Value = EmailRS.Fields(0).Value
saveRS.Fields(1).Value = FileName
saveRS.Update
EmailRS.MoveNext
Wend

EmailRS.Close
Set EmailRS = Nothing

DoCmd.OutputTo acReport, ReportRs.Fields(0).Value, _


acFormatRTF, FileName

ReportRs.MoveNext
Wend

saveRS.Save "C:\Reports\EmailFile.xml", adPersistXML


saveRS.Close
Set saveRS = Nothing
ReportRs.Close
Set ReportRs = Nothing

Set db = Nothing
End Sub

T his proc edure takes advantage of a dis c onnec ted A D O


rec ords et. With A D O , you c an c reate a rec ords et on- the- fly
without c onnec ting to a databas e. I n addition, you might als o
notic e that this proc edure c reates all the files O utlook will s end
later. I f you want to, you c an have a s tep that runs at the
beginning of the proc es s to c reate the XM L file with no rec ords
and then have multiple proc edures run that c ontinue to add to
the XM L file to be proc es s ed by O utlook at a partic ular time.

7.12.3. Macros in Outlook

N ext, you need to c reate the O utlook proc edure. To make this
work, you need to add a mac ro to your O utlook environment. I n
O utlook, s elec t Tools M ac ros V is ual Bas ic E ditor,
and c lic k the ThisOutlookSession objec t in the P rojec t E xplorer.
O nc e there, enter the c ode in E xample 7 - 1 2 .

Example 7-12. Proces s ing the ADO records et in Outlook

Public Sub EmailTest()


Dim mi As MailItem
Dim varitm As Variant
Dim adors As ADODB.Recordset
Set adors = New ADODB.Recordset
adors.Open "C:\Reports\EmailFile.xml"
adors.MoveFirst
While Not adors.EOF
Set mi = Application.CreateItem(olMailItem)
mi.Recipients.Add adors.Fields(0).Value
mi.Subject = "Monthly Report"
mi.Body = "Your monthly report is attached."
mi.Attachments.Add adors.Fields(1).Value, olByValue, 1, "Monthly R
mi.Send
Set mi = Nothing
adors.MoveNext
Wend
adors.Close
Set adors = Nothing
End Sub

T his s ends all your emails without prompting you eac h time.
A lthough it c reates a two s tep proc es s , you will apprec iate not
having to c lic k through eac h mes s age. T his is partic ularly us eful
if you have a s ignific ant number of emails to s end. I f nec es s ary,
you c an s tore additional fields for Subject and Body in the
rec ords et and have thos e als o bec ome dynamic .

T he one downs ide of this proc edure is that it s ends an individual


email for eac h rec ord. You c an update it to go through the
rec ords et and determine if emails c an be grouped; however, this
is unlikely to be nec es s ary. I n addition, you c an als o c reate
multiple XM L files for eac h email to be s ent and have the
proc edure c yc le through all the XM L files and then move them
when it is c ompleted (I implemented s uc h a proc edure for a
c lient onc e).

You will need to s ave this proc edure us ing the Save ic on from
the V is ual Bas ic E nvironment if you want to us e it again. A ls o,
depending on your s ec urity s ettings , you might be prompted to
enable this mac ro eac h time you open O utlook and attempt to
us e it.

U s ing either approac h will c ertainly help you tac kle your A c c es s
projec ts and help automate s ending emails . I f you need to s end
jus t a mes s age to us ers , you c an us e the firs t proc edure and
eliminate the lines related to attac hments . I n either c as e, the
power of us ing V BA in M ic ros oft O ffic e applic ations s hould be
evident.

Michael Schmalz
Hack 70. Create Access Tables from
Outside Access

You don't have to be in A ccess to use A ccess.

H ere's the s c enario: you have an E xc el s olution that needs to


populate an A c c es s table. T he table will be a new table in the
databas e. You might think the table needs to exis t before you
c an populate it via A D O or s ome other means , s o you manually
c reate the table and then go bac k to E xc el and run the routine
that populates the table.

A c tually, you don't have to go to A c c es s and c reate the table.


J us t c reate it direc tly from c ode while in E xc el. I t's a s imple
matter, really; you jus t need to work with the A D O X library.

I n E xc el, s et a referenc e to A D O X by us ing the Tools


Referenc es menu and s etting the referenc es in the Referenc es
dialog box, as s hown in Figure 7 - 4 9 .

Figure 7-49. Setting a reference to ADOX


7.13.1. The Code

I t's now jus t a matter of whipping up a little c ode that us es the


A D O X programmatic model. I n an E xc el c ode module, enter this
c ode:

Dim cat As New ADOX.Catalog


Dim tbl As New ADOX.Table
Dim db_file_path As String
'change path and database!
db_file_path = ActiveWorkbook.Path & "\abc.mdb"
'connect to the Access database
cat.ActiveConnection = "Provider=Microsoft.Jet.OLEDB.4.0;" & _
"Data Source=" & db_file_path

'Create a table
With tbl
.Name = "Prospects"
' First, fields are appended to the table object
' Then the table object is appended to the Tables collection
.Columns.Append "Name", adVarWChar
.Columns.Append "Company", adVarWChar
.Columns.Append "Phone", adVarWChar
.Columns.Append "Address", adVarWChar
End With

cat.Tables.Append tbl

Set cat = Nothing


MsgBox "Table created!"
J us t be s ure to c hange the hardc oded databas e name and path.
T his c ode c reates the P ros pec ts table in the A c c es s databas e.
T he table will have four fields . T he Append method of the Columns
property takes the name of the field and the field type as a
c ons tant. T he c ons tants for the field types differ from what you
s ee in A c c es s , although they s erve the s ame purpos e. To s ee all
the c ons tants , us e the O bjec t Brows er and filter it to jus t s how
the A D O X library, as s hown in Figure 7 - 5 0 .

Figure 7-50. Displaying ADOX constants


7.13.2. Hacking the Hack

T his hac k us es A D O X, whic h is an available external library. You


jus t as eas ily c an run the c ode in this hac k from Word,
P owerP oint, O utlook, and other programs . T he point is that you
c an c reate and manipulate A c c es s databas e tables even when
A c c es s is n't running.
Hack 71. Write VBA with the Macro
Recorder in Word and Excel

Take advantage of autogenerated code to speed up your coding


ef f orts.

L et's s ay you need to work with Word or E xc el from within


A c c es s . A nd let's s ay the projec t involves writing V BA for Word
or E xc el that will be us ed from A c c es s via automation. Well, you
don't have to c limb as s teep a learning c urve as you might think.
T hat's bec aus e both Word and E xc el c an generate V BA c ode
automatic ally.

To make this work, you firs t turn on the M ac ro Rec order, perform
c ertain ac tions in the applic ation, and then s top the rec order.
J us t s elec t Tools M ac ro Rec ord N ew M ac ro to s tart
the rec order, as s hown in Figure 7 - 5 1 .

Figure 7-51. Starting to record an Excel macro


With the rec order running, you c an perform a few ac tions of
entering data and c reating a c hart. A fter s topping the rec order
(while it is rec ording, a toolbar with a Stop button is vis ible), the
c ode is in an E xc el c ode module. Figure 7 - 5 2 s hows an example
of the c ode E xc el generates .

U s ually, you'll need to work with the generated c ode. I t will have
hardc oded c ell referenc es that might not make s ens e for your
applic ation.

Figure 7-52. Excel autogenerated VBA code


H owever, us ing the M ac ro Rec order, you c an generate mos t of
what you need and then jus t edit it to make it work right. C ode
s uc h as this that works in E xc el will run fairly well from A c c es s
when a referenc e is s et to the E xc el library (everything s aid here
als o applies to Word). You will need to make s ome c hanges , but
this is s till a big times aver.

Kirk Lamb
8. Programming
Sec tion 8 .1 . H ac ks 7 2 9 1

H ac k 7 2 . Store I nitial C ontrol Selec tions for L ater


Rec all

H ac k 7 3 . Write C ode Fas ter by Turning O ff Syntax-


C hec king

H ac k 7 4 . Subs titute D omain A ggregate Func tions for


SQ L A ggregate Func tions

H ac k 7 5 . Shrink Your C ode with Subroutines

H ac k 7 6 . Shrink Your C ode with O ptional A rguments

H ac k 7 7 . P rotec t P rogramming C ode from C urious


U s ers

H ac k 7 8 . Build a Sec ret D eveloper Bac kdoor into Your


A pplic ations

H ac k 7 9 . H elp U s ers D rill D own to a Rec ord

H ac k 8 0 . P revent U s ers from D is abling Your Startup


O ptions

H ac k 8 1 . I nform U s ers of a L ong P roc es s

H ac k 8 2 . A llow U s ers to C hoos e a Bac k- E nd D atabas e

H ac k 8 3 . O verride the T imeout I nterval

H ac k 8 4 . Save Values from U nbound C ontrols for L ater


Rec all
H ac k 8 5 . Sort Rec ords Randomly

H ac k 8 6 . Bulk- U pdate C ontrols on a Form

H ac k 8 7 . P rovide C omplete XM L C ontrol to A ny Vers ion


of A c c es s

H ac k 8 8 . U s e C us tom E numerations

H ac k 8 9 . C onvert Text to the D es ired C as e

H ac k 9 0 . C reate a C ode L ibrary

H ac k 9 1 . A utomatic ally C hec k for D atabas e Table


U pdates
8.1. Hacks 7291
V BA , A D O , D A O , SQ L , XM L : developers s ubs is t in a world of
ac ronyms . T he great thing about all thes e tec hnologies is how
well they work together in our c us tom s olutions . You might be
familiar with jus t a handful of thes e tec hnologies , but when you
finis h reading this c hapter, you will walk away with a bit more
knowledge than when you s tarted.

I n this c hapter, you'll find hac ks that optimize c ode by reduc ing
the number of lines and reduc ing repetitive routines . "Subs titute
D omain A ggregate Func tions for SQ L A ggregate Func tions "
[Hack #74] s hows you how to reduc e c ode by avoiding SQ L
func tions . O ther hac ks s how you how to protec t your c ode and
keep us ers out of the des ign elements . O ne of thes e, "P rotec t
P rogramming C ode from C urious U s ers " [Hack #77], s hows you
how to apply pas s word protec tion to your c ode.

T he c hapter als o c ontains hac ks that enhanc e the us er


experienc e. For example, "H elp U s ers D rill D own to a Rec ord"
[Hack #79] helps us ers quic kly find a des ired rec ord from a
large number of rec ords . A nd "Save Values from U nbound
C ontrols for L ater Rec all" [Hack #84] gives us ers a way to
rec reate form s elec tions from earlier s es s ions .
Hack 72. Store Initial Control Selections
for Later Recall

The Tag property is a great place to store data about controls.


Here's how to put it to good use.

C ombo boxes and lis tboxes are great for pres enting us ers with
c hoic es . A s the us er makes s elec tions in a c ombo box or
lis tbox, the value is available to us e in c ode s imply by referring
to the c ontrol's Value property:

If IsNull(cmbLevel) Then
MsgBox "No Value Selected"
Else
MsgBox cmbLevel.Value
End If

You c an even refer only to the c ontrol its elf and leave the Value
property off, like this :

If IsNull(cmbLevel) Then
MsgBox "No Value Selected"
Else
MsgBox cmbLevel
End If
N ote that both c ode s nippets begin with a tes t for a null. T he
lis tbox or c ombo box might initially be null, s o it is good prac tic e
to inc lude a way to avoid bombing out if this is the c as e.

A s us ers c lic k away and make s elec tions , a lis tbox or c ombo
box's value c hanges to reflec t the us er's las t s elec tion. But what
if you have to rec all an earlier s elec tion or perhaps the firs t
s elec tion the us er made? A us er might have forgotten what he
firs t s elec ted and wants to return to that value. You c an build
into your applic ation a way to do this , but it will be for naught
unles s you s tored the initial value.

O f c ours e, you c an keep the initial value s tored in a table, but


that's extra work. I ns tead, this hac k s hows you how to s tore a
c ontrol's initial value right in the c ontrol its elf! Both the lis tbox
and the c ombo- box c ontrols have a Tag property. T his property
has no purpos e other than to ac t like a little c omment area for
A c c es s objec ts .

I n c as e you are wondering about the


OldValue property, here is the s kinny on
that. OldValue s tores the unedited value
while the value is being edited. T his is like
a before- and- after pic ture of an edit, but it
works only for the mos t rec ent c hange. You
c an't us e OldValue to rec all a value from
s everal c hanges bac k.
Figure 8 - 1 s hows a form with a c ombo box, a lis tbox, and a
button. Both the c ombo box (c mbL evel) and the lis tbox
(lis tRegion) have their Row Sourc e Type s et to Value L is t. T he
c ombo box has four Row Sourc e items : Beginner, I ntermediate,
A dvanc ed, and E xpert. T he lis tbox has five Row Sourc e items :
E as t, Wes t, N orth, South, and C entral. O riginal s elec ted values
for thes e c ontrols are s tored in the Tag property.

T hes e c ontrols are unbound. When the form opens , they are
blank. I n the form's Open event, the Tag property of eac h c ontrol
is s et to a zero- length s tring. H ere is the c omplete c ode:

Figure 8-1. A form with a combo box, listbox,


and button
Private Sub Form_Open(Cancel As Integer)
cmbLevel.Tag = ""
listRegion.Tag = ""
End Sub

Private Sub cmbLevel_AfterUpdate( )


If cmbLevel.Tag = "" Then
cmbLevel.Tag = cmbLevel
End If
End Sub

Private Sub listRegion_AfterUpdate( )


If listRegion.Tag = "" Then
listRegion.Tag = listRegion
End If
End Sub

Private Sub cmdReset_Click( )


cmbLevel = cmbLevel.Tag
listRegion = listRegion.Tag
End Sub

T he firs t time a s elec tion is made in either c mbL evel or


lis tRegion, the Tag property is tes ted to s ee if it is a zero- length
s tring. I f it returns true, the Tag property updates to the c ontrol's
c urrent value. T his ac tivity oc c urs in the AfterUpdate event
bec aus e the new lates t value is required. T he lates t value is the
c urrent one after the update.

T he tes t for a tag with a zero- length s tring is s uc c es s ful only the
firs t time it is run. E very time after that, a new s elec tion is made
in a c ontrol; therefore, the tes t fails bec aus e the Tag property
has a valuethe original s elec ted value.
T he Res et button, when c lic ked, s imply s ets both the c ombo box
and the lis tbox to the original s elec tions by s etting them to their
own Tag properties .

T his hac k s hows you only how to s tore the initial s elec tions .
What you ac tually do with them is a func tion of your applic ation.
T he great thing about this hac k is that it is eas ily portable.
Bec aus e no tables are involved, you c an c opy the c ode into
other c ontrols ' event s tubs and jus t c hange the name of the
c ontrol referenc e in the c ode.
Hack 73. Write Code Faster by Turning
Off Syntax-Checking

Make sure A ccess doesn't pester you with warning messages


while you're writing code.

I f you're like me, s ometimes your fingers c an't keep up with your
brain. O ften, when I am writing c ode, I am s everal lines ahead in
my thoughts , and I 'm typing as fas t as I c an to keep up. T he las t
thing I want is to los e my train of thought, but if I make a few
typing errors or leave out a keyword or variable, A c c es s pops up
a s c olding mes s age.

T he error mes s age in Figure 8 - 2 is a perfec t example. I am in


the mids t of c oding a Select Case s tatement. I don't know what
the expres s ion is yet, but I do have in mind what the ac tual
c as es are, and I want to get them down fas t. But no, A c c es s
hangs me up on the Select Case line.

I know I left s omething out! I did this on purpos e, and I will


return and enter it in a little while! T his interruption is jus t
annoying.

So, I have learned to turn off thes e annoying mes s ages as I


enter lengthy c hunks of c ode. Figure 8 - 3 s hows the O ptions
dialog. I n the V B editor, us e Tools O ptions to dis play the
dialog box.

O n the E ditor tab of the dialog, unc hec k the A uto Syntax C hec k
c hec kbox. D oing s o is the key to fas t c oding. When you turn off
the s yntax c hec k, you c an type without interruption. E rrors will
s till be flagged by their c olor c oding (lines with errors will c hange
c olor), but no more pes ky mes s age boxes will appear that you
have to c lear.

Figure 8-2. A pesky warning message


Figure 8-3. Turning off Auto Syntax Check
O f c ours e, it's a good idea to turn s yntax- c hec king bac k on
when you finis h entering c ode!
Hack 74. Substitute Domain Aggregate
Functions for SQL Aggregate Functions

Reduce the amount of code you enter and still get the same
results.

Within V BA c ode, it is a c ommon prac tic e to tap into the A D O


objec ts and us e s ome SQ L to query data in the databas e.
Bec aus e SQ L is the de fac to s tandard for querying data,
following this route is unders tandable. H owever, s ometimes you
don't need to query data in this way.

For example, if you need to proc es s individual rec ords , us ing


A D O and SQ L makes s ens e. A rec ords et is c reated that is
typic ally s c rolled through us ing the MoveNext method within a Do
Until loop or s imilar c ons truc t.

O n the other hand, A D O and SQ L are s ometimes us ed jus t to


get an aggregate value from a s et of rec ords . I n this s ituation,
the individual rec ords are of no c onc ern. I ns tead, you're looking
for a s ummary, s uc h as a s um, a c ount, or an average.

8.4.1. The Code

E xample 8 - 1 s hows a routine that us es A D O and SQ L to return


the s um of s ome invoic e amounts .
Example 8-1. Us ing ADO and SQL to return a s um

Sub get_SQL_Sum( )
Dim conn As ADODB.Connection
Set conn = CurrentProject.Connection
Dim rs As New ADODB.Recordset
rs.Open "Select Sum(Amount) As SumOfAmount From Invoices" & _
" Where InvoiceDate=#12/10/04#", _
conn, adOpenKeyset, adLockOptimistic
MsgBox rs.Fields("SumOfAmount")
rs.Close
Set rs = Nothing
Set conn = Nothing
End Sub

T he SQ L s tatement inc ludes the SQ L aggregate Sum func tion.


A ls o, the s um of the amounts is from a s et of rec ords filtered to
a s ingle invoic e date of 1 2 /1 0 /0 4 . T he c ode in E xample 8 - 1
requires c reating A D O objec ts and then des troying them
afterward (by s etting them to Nothing).

You c an boil down all this to a s ingle line us ing a domain


aggregate function.

8.4.2. Boiling Down the Code

D omain aggregate func tions provide the s ame res ults as SQ L


aggregate func tions . H owever, whereas you need to s omehow
embed SQ L aggregate func tions into a SQ L s tatement, you c an
c ode domain aggregates independently.
E xample 8 - 2 s hows how a s hort routine us ing the DSum domain
aggregate func tion replac es the c ode in E xample 8 - 1 .

Example 8-2. Us ing DSum to return the s um

Sub get_Domain_Sum( )
Dim amount As Single
amount = DSum("[Amount]", "Invoices", "[InvoiceDate] = #12/10/04
MsgBox amount
End Sub

O ther than dimens ioning the amount variable and us ing a


mes s age box to dis play the res ult, the c ode requires jus t one
s tatement:

amount = DSum("[Amount]", "Invoices", "[InvoiceDate] = #12

T he arguments handed to DSum are the field to s um, the domain (a


table or Select query), and any filtering. T he third argument
works in the s ame manner as the SQ L Where c laus e.

You c an even enter c omplex c riteria for the third argument. For
example, this line of c ode returns the s um of amount when the
invoic e date is 1 2 /1 0 /0 4 , the c us tomer is A nders on, and the
loc ation is either C hic ago or D allas :

amount = DSum("[Amount]", "Invoices", "[InvoiceDate] = #12/10/0


Customer]='Anderson' And ([Location]='Chicago' or [Location]='D
8.4.3. Domain Aggregate Functions

T here are s everal domain aggregate func tions :

DAvg

Returns the average of the values in the field in the firs t


argument.

DCount

Returns the c ount of rec ords .

DLookup

Returns the value of the firs t field in the firs t rec ord that
matc hes bas ed on the c riteria in the third argument.

DFirst and DLast

Returns the value of the field in the firs t argument from


the firs t or las t rec ord.

DMin and DMax

Returns the minimum or maximum value of the field in


the firs t argument from among the rec ords .
DStDev and DStDevP

Returns the s tandard deviation of the values in the field


in the firs t argument.You us e DStDev with a s ample from a
population. You us e DStDevP with the full population.

DSum

Returns the s um of the values in the field in the firs t


argument.

DVar and DVarP

Returns the varianc e among the values in the field in the


firs t argument.You us e DVar with a s ample from a
population. You us e DVarP with the full population.

A ll the domain aggregate func tions work with the s ame three
arguments : the field being evaluated, the domain, and the
c riteria. L ook up thes e func tions in the A c c es s H elp s ys tem if
you want more information. I ntegrating them into your
proc edures is a great way to retrieve quic k s ummaries of data
with jus t s ingle lines of c ode.
Hack 75. Shrink Your Code with
Subroutines

Say goodbye to long and dif f icult-to-maintain code by placing


repetitive processing into subroutines.

A ll applic ations live and grow. Func tionality begets func tionality.
A s us ers s tart banging away at your firs t delivered applic ation,
they s c ream for more features . A s you add thes e features , the
amount of c ode c an grow. O ften, routines get c opied, and then a
c ouple of literals , variable names , or c riteria get c hanged. You
end up with c ode that has a number of s imilar routines .

E xample 8 - 3 s hows three identic al routines , with the exc eption


that eac h addres s es a different s tate.

Example 8-3. Multiple nearly identical routines

Sub get_NY_records( )
'
'get New York customers
'
Dim conn As ADODB.Connection
Set conn = CurrentProject.Connection
Dim recset As ADODB.Recordset
Set recset = New ADODB.Recordset
recset.Open "Select * From Customers Where State='NY'", conn
Do Until recset.EOF
''Process records here
recset.MoveNext
Loop
recset.Close
Set recset = Nothing
End Sub

Sub get_CT_records( )
'
'get Connecticut customers
'
Dim conn As ADODB.Connection
Set conn = CurrentProject.Connection
Dim recset As ADODB.Recordset
Set recset = New ADODB.Recordset
recset.Open "Select * From Customers Where State='CT'", conn
Do Until recset.EOF
''Process records here
recset.MoveNext
Loop
recset.Close
Set recset = Nothing
End Sub

Sub get_MA_records( )
'
'get Massachusetts customers
'
Dim conn As ADODB.Connection
Set conn = CurrentProject.Connection
Dim recset As ADODB.Recordset
Set recset = New ADODB.Recordset
recset.Open "Select * From Customers Where State='MA'", conn
Do Until recset.EOF
''Process records here
recset.MoveNext
Loop
recset.Close
Set recset = Nothing
End Sub

T he c ode in E xample 8 - 3 is frightfully redundant. You c an


optimize c ode s uc h as this by c reating a s ubroutine that takes
an argument. You plac e the c ode that is identic al in all the
routines into the s ubroutine and then us e the argument to pas s
the partic ular individual value to the s ubroutine to run the
proc es s .

I n E xample 8 - 3 the only differentiating item in the c ode is the


s tate, s uc h as in this s tatement, whic h s elec ts M as s ac hus etts
(MA) rec ords :

recset.Open "Select * From Customers Where State='MA'", conn

E xample 8 - 4 s hows how to c hange the c ode by us ing a


s ubroutine. N ow the repetitive c ode is plac ed in a s eparate
s ubroutine named get_state_records. T he s ubroutine takes a
s tring argument, named s tate.

Example 8-4. The repetitive code placed into a s ubroutine

Sub get_NY_records( )
'get New York customers
get_state_records "NY"
End Sub

Sub get_CT_records( )
'get Connecticut customers
get_state_records "CT"
End Sub

Sub get_MA_records( )
'get Massachusetts customers
get_state_records "MA"
End Sub

Sub get_state_records(state As String)


Dim conn As ADODB.Connection
Set conn = CurrentProject.Connection
Dim recset As ADODB.Recordset
Set recset = New ADODB.Recordset
recset.Open "Select * From Customers Where State='" & state & "'
Do Until recset.EOF
''Process records here
recset.MoveNext
Loop
recset.Close
Set recset = Nothing
End Sub

N ow, eac h individual routine, s uc h as get_MA_records, s imply


c alls the generic s ubroutine and pas s es the s tate initials as the
argument. T his is done in a s ingle line of c ode:

get_state_records "MA"

T he generic get_state_records s ubroutine takes the pas s ed


argument and us es it in the SQ L s tatement that opens the
rec ords et:

recset.Open "Select * From Customers Where State='" & stat

You c an eas ily s ee that the c ode in E xample 8 - 4 is s horter than


the c ode in E xample 8 - 3 .

Years ago, programmers would boas t about how many thous ands
of lines of c ode they had written. I s uppos e now the fas hion is for
programmers to talk about how many lines of c ode they avoided
writing!
Hack 76. Shrink Your Code with Optional
Arguments

Put subroutines to even more general use by accepting dif f erent


numbers of arguments.

"Shrink Your C ode with Subroutines " [Hack #75] s hows you how
to reduc e c ode by us ing a generic s ubroutine. T his hac k takes
that c onc ept a s tep further. Subroutines c an take optional
arguments . C alling routines are required only to s upply
arguments that aren't optional. T he optional ones are, well,
optional.

E xample 8 - 5 s hows a handful of routines and the s ubroutine


they c all.

Example 8-5. A s et of routines that call a s ubroutine

Sub get_NY_records( )
'get New York customers
get_state_records "NY"
End Sub

Sub get_CT_records( )
'get Connecticut customers
get_state_records "CT"
End Sub

Sub get_MA_records( )
'get Massachusetts customers
get_state_records "MA", "Boston"
End Sub

Sub get_state_records(state As String, Optional city As String)


Dim conn As ADODB.Connection
Set conn = CurrentProject.Connection
Dim recset As ADODB.Recordset
Set recset = New ADODB.Recordset
If state = "MA" Then
recset.Open "Select * From Customers Where State='" & state _
& "' And City='" & city & "'", conn
Else
recset.Open "Select * From Customers Where State='" & state &
End If
Do Until recset.EOF
''Process records here
recset.MoveNext
Loop
recset.Close
Set recset = Nothing
End Sub

T he s ubroutine takes two arguments : state, whic h is required,


and city, whic h is optional. T he Optional keyword is plac ed in
front of the argument name:

Sub get_state_records(state As String, Optional city As String)


N o keyword is available for s pec ifying that
an argument is required. A ll arguments are
required unles s you s pec ific ally s et them
as optional with the Optional keyword. N ote
als o that optional arguments mus t c ome
after all the required arguments . You c an
have multiple optional arguments , but you
mus t prec ede eac h one with the Optional
keyword.

L et's as s ume the requirements have c hanged; now, any NY or CT


rec ords c an be us ed, but for M as s ac hus etts (MA), we need only
rec ords for whic h the c ity is Bos ton.

O ne way to ac c ommodate this new requirement is to us e


s ubroutines : one that ac c epts a s ingle s tate argument and one
that ac c epts two arguments , for the s tate and the c ity. T he
func tionality would work, but there would be more c ode.

H owever, by inc orporating the c ity as an optional argument, you


c an us e the s ingle s ubroutine for all the s tates . M as s ac hus etts
rec ords are ac c es s ed by the two arguments being pas s ed:

get_state_records "MA", "Boston"

N ew York and C onnec tic ut rec ords are ac c es s ed with the s ingle
required s tate argument:

get_state_records "CT"
We've modified the s ubroutine to handle two types of queries ,
one with a s ingle c riterion and one that us es both c riteria:

If state = "MA" Then


recset.Open "Select * From Customers Where State='" & s
Else
recset.Open "Select * From Customers Where Stat
End If

A nd that's it! By jus t altering the exis ting s ubroutine a bit, we


don't need to c reate a s ec ond s ubroutine.
Hack 77. Protect Programming Code
from Curious Users

Your code is valuable. A dd password protection to your modules.

I think you'll agree that developed c ode is valuable, in terms of


the time and expens e it took to c reate it as well as what would
be involved to rec reate it. So, like any valuable as s et, a little
protec tion goes a long way. O f c ours e, I would be remis s if I
didn't mention that the firs t thing you s hould do is have a bac kup
s ys tem in plac e. But even if you do have a bac k- up s ys tem, if
the c ode in a produc tion s ys tem gets trampled s omehow, it s till
c os ts the c ompany to take down the s ys tem to fix the c ode.

L uc kily, there is an eas y way to protec t your c ode. T his hac k


prevents all but thos e who know the pas s word from s eeing your
c ode. To add this protec tion, you mus t ac c es s the Tools
P roperties menu in the V B editor, as s hown in Figure 8 - 4 . I n this
example, the menu item is C us tomers P roperties bec aus e my
c ode projec t is named C us tomers . Your menu item will reflec t
the name of your c ode projec t.

I nitially, the name of the c ode projec t is


s et to the name of the databas e. You c an
c hange this in the G eneral tab on the
P rojec t P roperties dialog box (Tools
P roperties ).

Figure 8-4. Accessing the project properties


O n the P rotec tion tab in the P rojec t P roperties dialog box,
s elec t the option to protec t the projec t and es tablis h a
pas s word. E nter a pas s word, and c lic k O K, as s hown in Figure 8 -
5 . D on't forget that pas s words are c as e- s ens itive.

Figure 8-5. Protecting the project


You won't s ee any c hange until you c los e the databas e and then
reopen it. When you go to the V B editor, the lis t in the P rojec t
window will be c ollaps ed to the top level of the projec t its elf. I f
you try to open the lis t (by c lic king the plus s ign), a box in whic h
to enter the pas s word will pop up, as s hown in Figure 8 - 6 .

Figure 8-6. Requiring a password to see the


code
T his is as far as you c an get without knowing the pas s word. T he
protec tion ac tually helps in two ways : not only is the c ode
protec ted from tampering, but als o it is invis ible. T herefore, your
c ode is protec ted in terms of intellec tual rights as well. I f you
developed s ome exc ellent routines that you hold dear, you c an
relax knowing that others c an't even view them, muc h les s c opy
them.
Hack 78. Build a Secret Developer
Backdoor into Your Applications

Keep users out of your design while letting yourself in the easy
way.

Some us ers are a little too c urious for their own good. L eft to
their own devic es , they will explore your databas e's des ign. A
few approac hes exis t to c ombat this . You c an turn on s ec urity,
c onvert the databas e to a read- only .mde vers ion, or jus t remove
the ability to get to the des ign elements .

T his las t option, although not as robus t as us ing A c c es s


s ec urity, is a c ommon way to keep us ers on the up and up.
Setting the following options in Startup O ptions , s hown in Figure
8 - 7 , helps make it hard for us ers to get into the databas e
des ign:

Set the opening form.

U nc hec k the D is play D atabas e Window c hec kbox.

U nc hec k the A llow Built- in Toolbars c hec kbox.

U nc hec k the A llow Toolbar/M enu C hanges c hec kbox.


Figure 8-7. Changing startup options

I n the form des ign, you c an implement the following additional


s ettings :

Set the Shortcut Menu property to No.


Set A llow D es ign C hanges to D es ign V iew O nly.

A s s ign a c us tom menu bar that does n't provide a way to


ac c es s des ign elements .

T his will keep mos t us ers foc us ed on their work. U nfortunately, it


might als o mean you jus t made it more diffic ult for you to get
into the databas e des ign. T his hac k builds a hidden s hortc ut
that only you, the developer, know about. T his s hortc ut takes
you direc tly to the databas e's des ign elements .

Figure 8 - 8 s hows a form in D es ign mode. A c ommand button is


s trategic ally plac ed where you wouldn't think to c lic k. You c an
s ee it in the upper- lef c orner of the form. I t does n't look like a
regular button, although it is . T he transparent property has been
s et to Yes, whic h makes it look flat. A ny c aption dis appears as
well when the button is trans parent. T herefore, it really appears
as jus t the outline of a rec tangle.

Figure 8-8. A form with a transparent button


T he button has c ode ins ide its double- c lic k event. A double-
c lic k event is never really us ed for buttons bec aus e everyone
expec ts that a s ingle button c lic k will perform an ac tion. When
the form is in V iew mode, the button is invis ible. I ts Visible
property is s et to true, but the trans parenc y overrides that.

N ote that the mous e pointer does n't c hange when the us er
pas s es it over the invis ible button. T he button is enabled,
though. A us er c ould ac c identally c lic k it without knowing it.
T herefore, us ing the double- c lic k event ins tead of the s ingle-
c lic k event s erves as a s afeguard. T his approac h is n't foolproof,
but it does keep s tray c lic ks from running any c ode.

8.8.1. The Code

When you double- c lic k the trans parent button, the following c ode
runs to as k for a pas s word:

Dim pw As String
pw = InputBox$("Enter Password", "Password")
If pw = "Access Rocks!" Then
DoCmd.SelectObject acTable, , True
Else
MsgBox "Incorrect Password"
End If

I f the pas s word entered is c orrec t, the databas e window is


dis played.

Figure 8 - 9 s hows the form in V iew mode after the button has
been double- c lic ked. T he button its elf is n't vis ible; you need to
know where it is to double- c lic k it.
Figure 8-9. Entering a password to open the
database window
Hack 79. Help Users Drill Down to a
Record

Facilitate browsing through a lengthy customer database by


f irst grouping customers.

Sometimes us ers need to s pend a lot of time finding a s pec ific


c us tomer from a long lis t of c us tomer rec ords . For example,
loading c us tomer names into a c ombo box or a lis tbox requires
your us ers to do a lot of s c rolling.

H ere's a tec hnique that removes s ome of this drudgery. T he firs t


s tep is to provide a way to lis t s maller groups of c us tomers .
Figure 8 - 1 0 s hows a form in whic h four buttons (on the left,
ins ide the Brows e C us tomers frame) lead to c us tomers whos e
las t names s tart with A F, G L , M R, and SZ.

Figure 8-10. Browsing customers within


alphabetic groupings
Segregating the c us tomers into four groups is s ubjec tive. You
c an apply this tec hnique to even s ingle letters ; in other words ,
you c an have 2 6 buttons . T he more c us tomers there are, the
s maller the groupings s hould be. Keep in mind that the point is
to pres ent s maller lis ts of c us tomers for us ers to brows e
through. I f you have thous ands of c us tomers , a grouping s uc h as
A F s till res ults in a rather lengthy lis t.

C lic king any of the four buttons opens a form that is filtered to
c us tomers whos e las t name falls into the indic ated grouping. For
example, the c ode in the Click event for the A F button looks like
this :

Private Sub cmdAF_Click( )


DoCmd.OpenForm "frmBrowseCustomers", acNormal, , _
"CustomerLastName Like'A*' or CustomerLastName Like 'B*' or " &
"CustomerLastName Like'C*' or CustomerLastName Like 'D*' or " &
"CustomerLastName Like'E*' or CustomerLastName Like 'F*'"
End Sub

T he OpenForm method opens the frmBrows eC us tomers form


filtered to the appropriate c us tomers . Figure 8 - 1 1 s hows the
form opened with the A F c us tomers .

Figure 8-11. A filtered set of customers on a


form
T he form in Figure 8 - 1 1 really s erves jus t as a brows e feature.
U s ers s till don't s ee the full c us tomer rec ord. To s ee the full
rec ord, the us er mus t double- c lic k any rec ord s hown on the
brows e form, whic h opens the full c us tomer rec ord. T he form that
dis plays c us tomer details is filtered to the partic ular rec ord
s elec ted in the form s hown in Figure 8 - 1 1 .

To make this happen, the key is nec es s ary. T he key, an


A utoN umber type named CustomerID in this example, is on the
brows e form, but it is hidden. Figure 8 - 1 2 s hows the des ign of
the brows e form. I n D es ign mode, you c an s ee the key field.

A ll the fields in the brows e form have c ode in their double- c lic k
events . T he c ode c alls a s ubroutine named open_customer, whic h
opens the c us tomer detail form. T his time, the OpenForm method
us es the key to open the detail form. Figure 8 - 1 3 s hows the
c ode behind the brows e form.

D ouble- c lic king a rec ord in the brows e form pulls up the
c us tomer's details . Figure 8 - 1 4 s hows the c us tomer detail form.

To s ummarize, when working with a large number of c us tomers


(or with other types of data, for that matter), us ers c an get to the
des ired detail rec ords pretty quic kly by grouping the rec ords
into s maller brows eable s ets .T hen, from the interim brows e form,
they c an view the full details . U s ing this tec hnique makes it
eas y to get to a detail rec ord with jus t a few c lic ks , without
having to s c roll through a long lis t.

Figure 8-12. The design of the browse form


Figure 8-13. The code behind the browse form
Figure 8-14. A form with the customer detail
Hack 80. Prevent Users from Disabling
Your Startup Options

Stop users f rom being able to hold down the Shif t key to get to
the database window.

A fter s pending all that time developing your databas e


applic ation and then s etting the s tartup options , the las t thing
you want is for s omeone to be able to s imply hold down the Shift
key during s tartup and then mes s around with your applic ation.
T his hac k explores two different ways to prevent this : dis abling
the Shift key c ode for A c c es s databas es (M D Bs ) and dis abling
the Shift key c ode for A c c es s D ata P rojec ts (A D P s ).

8.10.1. Access MDB

With M D B files , the hac k works by adding a property c alled


AllowBypassKey to the databas e objec t. Setting the property to
False dis ables the Shift key, and c hanging it bac k to TRue enables
it again. You need to dec ide on an event to trigger the value
c hange. I t c ould be when a s pec ific us er logs into the databas e
or, as in this c ode example, when a file named AllowByPas s .txt is
in the s ame direc tory as the databas e:

Public Function DetermineByPass( )


If Len(Dir(CurrentProject.Path & "\AllowByPass.txt")) = 0
ChangeProperty "AllowBypassKey", DB_BOOLEAN, False, Tr
Else
ChangeProperty "AllowBypassKey", DB_BOOLEAN, True, Tru
End If
End Function
Public Function ChangeProperty(strPrpName As String,_
varPrpTy
varPrpVa
bolDDL a

Dim prp As Variant


Const conPrpNotFoundError = 3270
On Error GoTo ChangePrp_Err
CurrentDb.Properties(strPrpName) = varPrpValue
ChangeProperty = True
ChangePrp_Bye:
Exit Function
ChangePrp_Err:
If Err = conPrpNotFoundError Then 'Property not found
Set prp = CurrentDb.CreateProperty(strPrpName, _

CurrentDb.Properties.Append prp
Resume Next
Else
'Unknown Error
MsgBox Err.Description
ChangeProperty = False
Resume ChangePrp_Bye
End If
End Function

A fter the c ode c hec ks for the AllowByPas s .txt file, it c alls the
ChangeProperty func tion and s ets it to False if the file is n't found
or to TRue if it is found.

Firs t the ChangeProperty func tion attempts to s et the value


pas s ed to it in varPrpValue to the strPrpName property, whic h in
this c as e is AllowBypassKey. A n error number (3270) oc c urs the
firs t time this func tion is c alled with a new property bec aus e that
property has not yet been appended to the data- bas e's
properties c ollec tion. T he func tion traps for this error number
and c reates the property with the error- handling routine.

T his example us es CurrentDB s o that you don't need to manually


s et a referenc e to the D A O library for A c c es s 2 0 0 0 and 2 0 0 2
(A c c es s 2 0 0 3 has the referenc e s et by default). I t is worth
noting that the CurrentDB method es tablis hes a hidden referenc e
to the M ic ros oft D A O 3 .6 objec t library when us ed in a 2 0 0 0 or
2 0 0 2 M D B file.

T he las t parameter to the ChangeProperty func tion (bolDDL) s ets


the DDL parameter of the AllowBypassKey property. T he DDL
parameter determines if the property c an be altered via
automation. By s etting this parameter to true, you prevent
s omeone with V BA c oding experienc e from res etting the
parameter by automation.

O nc e the c ode is in plac e in an A c c es s module, you need to


make s ure it gets run when the databas e s tarts up. T he bes t way
to do this is to us e the AutoExec mac ro. T he AutoExec mac ro is
nothing more than a regular mac ro with a s pec ial name. A c c es s
automatic ally runs it bas ed on its name. You s hould note that
the AutoExec mac ro gets exec uted after any form is opened from
the Startup properties . T his is s omething to c ons ider if you are
us ing the Startup properties and you plac e any events in the
mac ro other than a c all to the DetermineByPass func tion.

O nc e you've s et up the AutoExec mac ro to c all your func tion and


the s tartup form is in plac e (c alled from either the AutoExec
mac ro or the Startup properties ), open the databas e without
holding down the Shift key. T his allows the AllowBypassKey
property to be added to the databas e for the firs t time.

I f you open the A c c es s databas e with the AllowByPas s .txt file in


the s ame direc tory as the A c c es s M D B, it s ets the
AllowBypassKey property to true but does n't allow the bypas s the
firs t time the databas e is opened. T he A c c es s databas e mus t be
opened a s ec ond time while holding down the Shift key to bypas s
the Startup properties , inc luding the AutoExec mac ro, bec aus e
now the AllowBypassKey internal property is s et to TRue. Removing
the AllowByPas s .txt file from the databas e's direc tory res ets the
property to False the next time the databas e is s tarted up
without holding down the Shift key, thereby preventing the next
us er from bypas s ing the Startup properties and the AutoExec
mac ro.

I f you us e A c c es s 2 0 0 3 , and you don't have your mac ro


s ec urity s etting s et to L ow, you need to hold down the Shift key
when c lic king the O pen c ommand button of the A c c es s Sec urity
Warning s c reen s hown in Figure 8 - 1 5 .

Figure 8-15. Access 2003 security warning


8.10.2. Access ADP

Bec aus e mos t A D P s us e A D O only, the developers at M ic ros oft


provided a way to dis able the Shift key without referenc ing a
D A O library direc tly or indirec tly. T his method of adding the
AllowBypassKey is muc h s impler, but unfortunately, it works only
with A D P projec ts . T he following s ample c ode works identic ally
to the c ode for an A c c es s M D B, without the need for a func tion
s uc h as ChangeProperty:

Public Function DetermineByPass( )


If Len(Dir(CurrentProject.Path & "\AllowByPass.txt")) = 0 Then
CurrentProject.Properties.Add "AllowBypassKey", False
Else
CurrentProject.Properties.Add "AllowBypassKey", True
End If
End Function

A lthough this method for s etting the AllowBypassKey property is


muc h s horter and arguably eas ier to us e with an A D P, it does
have a drawbac k. You have no way of s etting the DDL parameter
to prevent s omeone from c hanging your AllowBypassKey property
with automation.

8.10.3. Be Careful

T his tec hnique of dis abling the Shift key is powerful; make s ure
you don't loc k yours elf out of your databas e. You might want to
c ons ider c hanging the DDL parameter in the s ample c ode for the
A c c es s M D B to False s o that you c an us e remote automation to
res et it if you need to. I f you do loc k yours elf out, you c an
always c reate a new databas e and import all the objec ts over to
the new databas e, then res et your Startup properties and
replac e the databas e you got loc ked out of with the new c opy
you jus t made.

Steve Huff
Hack 81. Inform Users of a Long Process

While your code is conquering a long looping process, users


might think their system has crashed, unless you provide some
visual clue that a process is running.

When a us er c lic ks a button to run a proc es s , and that proc es s


takes a while to c omplete, the us er won't know if the proc es s is
s till running or if the s ys tem has c ras hed. J us t imagine it: you
c lic k a button on a form and … nothing. A minute or two later, s till
nothing. M aybe even 5 or 1 0 minutes later, the s ys tem is s till
unres pons ive.

T his is nerve- wrac king for the us er s itting in front of his


c omputer. H e has to weigh whether he s hould try the break key
or let the s ys tem c ontinue to look like it is hung up. O n the other
hand, if he s tops a proc es s that was running s moothly after all,
he will jus t have to s tart it all over again. U gh!

D on't leave your us ers in this predic ament. You know that
1 0 0 ,0 0 0 rec ords are being proc es s ed and it takes a while. But
your us ers might not know this and get frus trated waiting.

T his hac k takes advantage of the SysCmd method. With SysCmd,


you c an write mes s ages in the s tatus bar during C P U - intens ive
proc es s ing, even with s c reen refres h turned off. Figure 8 - 1 6
s hows a form (a rather s imple one, I admit). T he button has been
c lic ked, and the proc es s is c hugging away. N otic e the s tatus bar
in the lower- left c orner of the s c reen; a c ontinuous ly updated
mes s age is being generated there.
Figure 8-16. Providing a feedback message in
the status bar

T he mes s age in the s tatus bar s ays , "P roc es s ing 2 7 4 1 of


8 5 0 0 ." T hinking like a programmer, you probably realize that to
have the total number of rec ords being proc es s ed in the
mes s age means a rec ord c ount property is being us ed.

8.11.1. The Code

H ere is the c ode behind the button:

Private Sub cmdProcessSales_Click()


Dim conn As ADODB.Connection
Set conn = CurrentProject.Connection
Dim recset As New ADODB.Recordset
Dim total_records As Long
Dim record_num As Long
record_num = 0
recset.Open "Select * From SalesRecords", conn, adOpenKeyset,
adLockOptimistic
total_records = recset.RecordCount
Do Until recset.EOF
recset.Fields("NetSales") = recset.Fields("Sales") - recset.
Fields("Costs")
record_num = record_num + 1
feedback_msg = "Processing " & record_num & " of " & total_rec
SysCmd acSysCmdSetStatus, feedback_msg
recset.MoveNext
Loop
recset.Close
Set recset = Nothing
SysCmd acSysCmdClearStatus
End Sub

T he c ode c reates a rec ords et and loops through it. P rior to the
looping, the RecordCount property populates the total_records
variable. D uring the looping, another variable, record_num, is
inc remented. T hes e two variables are us ed to c reate the
mes s age:

feedback_msg = "Processing " & record_num & " of " & total

T hen the mes s age is us ed in the SysCmd method:

SysCmd acSysCmdSetStatus, feedback_msg


Finally, at the end of the proc es s ing, the c ode c lears the s tatus
bar by giving SysCmd a c lear s tatus flag:

SysCmd acSysCmdClearStatus

8.11.2. Hacking the Hack

P roviding a feedbac k mes s age of the type des c ribed here is


helpful in gauging the length of a proc es s . I f the feedbac k
c ons is ted of jus t the number of rec ords proc es s ed without
indic ating the total number to be proc es s ed, you s till would not
know how long the proc es s will take to c omplete. For example, a
s imple mes s age of "P roc es s ing 2 4 7 1 " does n't let you know if
you are halfway done, are nearly done, or have hardly even
begun.

O f c ours e, the mes s age format of "P roc es s ing X of XX" works
only in a loop. O ther long proc es s es might not be bas ed on a
loop. A c omplex query c an take time, es pec ially when it needs to
work on many rec ords . I t is n't pos s ible to break into a query in
the s ame way, s o the thing to do is to put the time that the
proc es s s tarted in the s tatus bar.

T he Now func tion returns the time from the s ys tem c loc k. By
dis playing that in the s tatus bar, you're at leas t telling us ers
when the proc es s s tarted s o that they c an c ompare the s tart
time to the c loc k time in the s ys tem tray at the right of the
Windows tas kbar.
Hack 82. Allow Users to Choose a Back-
End Database

Store ODBC connection strings in a table so they are ready to go


when needed.

C ertain s ys tem applic ations provide more than one databas e


you c an interac t with. A s long as the s truc ture of the various
databas es and/or their tables is bas ed on a c ommon s c hema, it
is pos s ible to s wap the bac k ends in and out. A n eas y way to do
this is to provide the available c onnec tions in a lis t in whic h
us ers s elec t the databas e to us e.

Figure 8 - 1 7 s hows a table that s imply c ontains O D BC


c onnec tion s trings .

O n the form in whic h us ers s elec t a c onnec tion, they jus t s ee


the friendly names , provided in the table's s ec ond c olumn (the
firs t c olumn s erves as a way to s ort the lis t). Figure 8 - 1 8 s hows
the form and the c ombo box from whic h a c onnec tion is s elec ted.

Figure 8-17. A table filled with ODBC connection


strings
Figure 8-18. Selecting a database connection
T he value of the c ombo box is us ed in c ode to s et the
Connectionproperty for an A D O c onnec tion objec t, like this :

Dim conn As ADODB.Connection


Set conn = New ADODB.Connection
conn.ConnectionString = Me.cmbConnections
conn.Open

I n this manner, us ers c an eas ily c hange the databas e they are
working with at will.
Hack 83. Override the Timeout Interval

In a busy network environment, a little patience is sometimes


necessary.

When us ing A c c es s to c onnec t to an external databas e,


performanc e depends on a number of fac tors . O ne of thes e is the
networks pec ific ally in regard to bandwidth and traffic . Bandwidth
might be c ons tant, but add in the dynamic of network traffic , and
you often don't know what to expec t.

To be on the s afe s ide of having your proc es s ing c omplete, even


if it takes a while longer, you c an turn off the A D O CommandTimeout
property. T he default is for a timeout to oc c ur after 3 0 s ec onds .
T his means that if the s erver has not c ommunic ated bac k to
your applic ation in 3 0 s ec onds , your applic ation as s umes a
res pons e is n't c oming. You might then s ee a timeout mes s age
s uc h as the one s hown in Figure 8 - 1 9 .

Figure 8-19. A timeout expired error message


Setting the timeout interval to 0 turns off this property. Your
applic ation will then wait indefinitely for a res pons e. A s s uming
an A D O c onnec tion objec t named conn is being us ed, this is how
you apply the s etting:

conn.CommandTimeout = 0

T he drawbac k to this approac h is that you really might wait


forever. By the very nature of turning off the timeout, you have no
s pec ific period of reas onable time to expec t for the proc es s ing
to c omplete. I f s omeone pulled the plug on the s erver, you might
not know it. I f it makes s ens e, though, you c an experiment with
different s ettings . N ote that the value us ed is in s ec onds , s o a
value of 300 s ets the timeout interval to five minutes . Knowing
the wait is no more than five minutes for s uc c es s or failure might
make more s ens e than waiting indefinitely.
Hack 84. Save Values from Unbound
Controls for Later Recall

Give users a way to automatically recreate the way a f orm was


set up so that they don't have to reenter inf ormation

E very time a form is c los ed, the values in unbound c ontrols are
los t (this is n't always s tric tly true, but it generally is ).

I magine a form that is filled with many unbound c ontrols . A us er


makes s everal s elec tions and expec ts to need to reus e the
s ame s elec tions another time. Saving the values in the unbound
c ontrols , and making them identifiable and rec allable, c an be a
big times aver. L et's c all this a s cheme.

Saving the values from unbound c ontrols does n't make them
bound to anything. T he values are s aved to a table but only by
c reating c ode to do s o. Figure 8 - 2 0 s hows a form with three
unbound lis tboxes .

Figure 8-20. A form in which schemes of control


values are saved
A s elec tion has been made in eac h lis tbox, a s c heme name has
been entered, and the A dd/U pdate button has been c lic ked. T his
has c reated a s c heme that s tores the values from the lis tboxes .

8.14.1. The Code


T he c ode behind the A dd/U pdate button looks like this :

Dim conn As ADODB.Connection


Set conn = CurrentProject.Connection
Dim rs As ADODB.Recordset
Set rs = New ADODB.Recordset

ssql = "Insert into tblSchemes Values("


ssql = ssql & "'" & Me.txtSchemeName & "', "
ssql = ssql & "'" & Me.listOffices & "', "
ssql = ssql & "'" & Me.listItems & "', "
ssql = ssql & "'" & Me.listDeliveryMethod & "')"

'delete scheme first


delete_ssql = "Delete * From tblSchemes Where Scheme='" & schemena
conn.Execute (delete_ssql)

'now insert scheme


conn.Execute (ssql)

conn.Close
Set conn = Nothing

Me.lstSchemes.Requery
Exit Sub
err_end:
MsgBox Err.Description

N ote that s ome of the c ode has been removed. C hec king for
nulls and s uc h is n't s hown here, to keep the foc us on the point of
the hac k.
8.14.2. Running the Code

T he values in the lis tboxes , along with the s upplied name of the
s c heme, are ins erted as a rec ord into the tblSc hemes table
s hown in Figure 8 - 2 1 .

Figure 8-21. The control values stored in a table

A s more and more s c hemes are s aved, the lis tbox of s c hemes
(on the left) fills up. From this lis t, a s c heme c an be reentered on
the form. Figure 8 - 2 2 s hows the numerous s c hemes now
available.

Figure 8-22. Many schemes to choose from


T he L oad and D elete buttons work with the table rec ords and the
form. L oad populates the unbound c ontrols with values s tored in
the table for whic hever s c heme is s elec ted in the s c heme
lis tbox. D elete s imply deletes the appropriate rec ord from the
table.
8.14.3. Hacking the Hack

T he final thing to c ons ider is what to do when unbound lis tboxes


and c ombo boxes are s et to multis elec t. T his allows more than
one item to be s elec ted. I n this s ituation, you're s toring
relational data. For example, one s c heme c an have more than
one s elec tion in a lis tbox. To handle this , you literally c reate a
s et of related tables . O ne holds the general s c heme information,
and the other holds a rec ord for every value in the lis tbox. T he
tables relate on a keythe s c heme name.

Figure 8 - 2 3 s hows how this works .

Figure 8-23. Saving schemes that have multiple


selections per control
T he s ec ondary table s tores the s c heme name, the name of the
c ontrol, the value of the c ontrol, the item's pos ition in the lis t,
and whether the item is s elec ted. E very lis t item is s tored, along
with its pos ition and its s elec ted s tate. When a s c heme is
reloaded, the various parameters are needed to rec reate the lis t
and to reflec t whic h items were s elec ted.
Hack 85. Sort Records Randomly

Get a unique sort of records whenever you need one.

Rec ords in a table are always in s ome kind of order. A primary


key or other index might have been applied. E ven when all
indexes are removed, the rec ords are in the order in whic h the
table rec eived them.

A hac k is available for getting a true random s ort of the rec ords .
L iterally s ort them on random values ! To get this to work, you
add an extra field to the table. You then populate the field with
randomly generated values . L et's look at s ome c ode:

Sub random_sort_field()
Dim conn As ADODB.Connection
Set conn = CurrentProject.Connection
Dim ssql As String
Dim recset As New ADODB.Recordset
Dim tbl As String
tbl = "tblCustomers" ' the table name could be passed as an argu

ssql = "Alter Table " & tbl & " Add Column RandomSort Long"
'may already have field so trap error
On Error Resume Next
conn.Execute ssql

Randomize
recset.Open "select * From " & tbl, conn, adOpenDynamic, adLockO
Do Until recset.EOF
recset.Fields("RandomSort") = Int(Rnd() * 50000)
recset.MoveNext
Loop
recset.Close
Set recset = noting
conn.Close
MsgBox "done"
End Sub

T he tabletblC us tomers in this examplerec eives a new field


named RandomSort. H owever, the field might already be there
from the las t time this c ode was run, s o an On Error s tatement
prec edes the operation:

ssql = "Alter Table " & tbl & " Add Column RandomSort Long"
'may already have field so trap error
On Error Resume Next
conn.Execute ssql

T he c ode then c yc les through the table, and the RandomSort


field is populated with random values us ing the RND func tion:

recset.Fields("RandomSort") = Int(Rnd() * 50000)

N ow, the tblC us tomers table c an be s orted on the RandomSort


field, as s hown in Figure 8 - 2 4 .

E ac h time the routine runs , the values in the RandomSort field


c hange, thereby providing a new s ort.

Figure 8-24. Randomly sorted records


Hack 86. Bulk-Update Controls on a
Form

Tap the Controls property to make f ast property changes.

When working with a form's des ign, typic ally you s et properties
for c ontrols with the property s heet. You c an s elec t s ingle
c ontrols one by one and s et properties , or you c an s elec t a
handful of c ontrols and update them together. When you us e the
latter method, the number of properties lis ted on the property
s heet s hrinks to jus t thos e that are c ommon to all the s elec ted
c ontrols .

T his works fine for the mos t partthat is , s etting properties to


multiple c ontrols is n't diffic ult. But what if you had to s ee what a
property is before dec iding whether to c hange it? I n this
s ituation, the property s heet won't help you. When multiple
c ontrols are s elec ted, the property s heet c an't dis play individual
c ontrol properties . I t dis plays a property's value if all the
s elec ted c ontrols have the s ame value for the property, but that
is n't always the c as e.

8.16.1. Accessing Control Properties in


Code

L uc kily, A c c es s has a Controls property that belongs to the form


objec t, and a Properties property for the c ontrols thems elves .
T his makes it pos s ible to read and write c ontrol properties . To
s tart, E xample 8 - 6 s hows a routine that reads c ontrol
properties .

Example 8-6. Reading control properties

Sub list_control_properties()
Dim form_name As String
Dim ctl As Control
Dim list_type As Integer
Dim prop_num As Integer

form_name = "frm1" ' change as needed!


DoCmd.OpenForm (form_name), acDesign

list_type = 2 ' change to use with Select Case below

Select Case list_type


Case 1
' list names and values of properties for single control
With Forms(form_name).Controls(1)
For prop_num = 0 To .Properties.Count - 1
Debug.Print .Name & ": " & _
.Properties(prop_num).Name & ": " & _
.Properties(prop_num).Value
Next
End With
Case 2
' list value of entered property for all controls on form
With Forms(form_name)
For Each ctl In Forms(frm_name).Controls
On Error Resume Next
Debug.Print ctl.Name & ": Caption=" & _
ctl.Properties("Caption")
Next
End With
End SelectEnd Sub

T he routine in E xample 8 - 6 has two variations of reading c ontrol


properties , whic h you c an c hoos e by s etting the list_type
variable to 1 or 2.A Select Case s tatement us es this number to
run one or the other c ode s nippet. When the list_type variable is
s et to 1, the properties of a s ingle c ontrol are read. I n partic ular,
eac h property's name and value are written to the I mmediate
window. T he s ingle c ontrol is referenc ed in E xample 8 - 6 s imply
as .Controls(1). You c an enter the name of an ac tual c ontrol
ins tead by putting the name in quotes .

Figure 8 - 2 5 s hows the res ults of returning all the property


names and values for a s ingle c ontrol.

When the list_type variable is s et to 2 , all the c ontrols on the


form are tapped. A s ingle property that is entered into this line
indic ates whic h property to return:

Debug.Print ctl.Name & ": Caption=" & ctl.Properties("Caption")

Figure 8-25. Returning a list of control


properties
A ls o note the On Error Resume Next s tatement before this line.
T his line is inc luded bec aus e not all properties exis t for all
c ontrols . I n this example, the Caption property is being
ac c es s ed, but s ome c ontrols , s uc h as text boxes , don't have
c aptions . T he error trap keeps the proc edure going, and only
c ontrols that have the indic ated property make it into the
I mmediate window, as s hown in Figure 8 - 2 6 .

N ote that the routine in Figure 8 - 2 6 addres s es a form in the


c ode. T he form mus t be open for the c ode to work, pres umably in
D es ign mode bec aus e the point of this hac k is to make bulk
des ign c hanges . T he DoCmd s tatement takes c are of opening the
form in D es ign mode; jus t provide the name of your form. I n the
following line, replac e frm1 with the name of your form:

form_name = "frm1" ' change as needed!

8.16.2. Changing Properties the Easy Way

E xample 8 - 7 s hows a routine that c hanges the ForeColor


property to red for all text boxes on the form. To work with a
s ingle type of c ontrol, the c ode tes ts the ControlType property. I f
the c ontrol type matc hes the enumeration value, the property is
updated.

Figure 8-26. Returning the Caption property for


controls that have one
Example 8-7. Working with a s ingle control type

Sub update_controls()
Dim ctl As Control
Dim form_name As String
form_name = "frm1"
DoCmd.OpenForm (form_name), acDesign
With Forms(form_name)
For Each ctl In Forms(frm_name).Controls
If ctl.ControlType = acTextBox Then
ctl.Properties("ForeColor") = vbRed
End If
Next
End With
End Sub

You c an c hoos e to c hange a property for all the c ontrols on a


form or for partic ular c ontrol types . I f you're working with the full
s et of c ontrols , it's a good idea to us e an On Error Resume Next
s tatement s o that the proc es s won't bomb when a c ontrol
does n't have the partic ular property.

T he c ode in E xample 8 - 7 addres s es text boxes only. Figure 8 -


2 7 s hows how to us e the O bjec t Brows er to find the members of
the acControlType enumeration.

Figure 8-27. Reviewing control-type constants


Hack 87. Provide Complete XML Control
to Any Version of Access

Use the MSXML Parser to make XML native to your applications.

Support for XM L has been growing through s uc c es s ive A c c es s


releas es , but c omplete XM L c ontrol s till is n't available. For
ins tanc e, A c c es s 2 0 0 3 s upports importing and exporting XM L ,
but even s o, the level of func tionality is limited. For example,
you c an't import attributes (a type of XM L data).

Referenc ing an external XM L pars er not only improves the XM L


s upport, but als o provides the s ame level of s upport to any
vers ion of A c c es s . T his is pos s ible bec aus e the pars er is an
independent piec e of tec hnology. A s long as you c an referenc e
it, you c an us e it to its fulles t.

8.17.1. Referencing the Parser

I n the A c c es s V B editor, us e the Tools Referenc es menu to


open the Referenc es dialog box, s hown in Figure 8 - 2 8 .

Sc roll through the lis t, and find M ic ros oft XM L . T he referenc e will
inc lude a vers ion number; any vers ion will do. I f you are c urious
how the pars er vers ions differ, vis it M ic ros oft's web s ite
(http://www.mic ros oft.c om).
Figure 8-28. Adding a reference to the XML
parser
I f you don't find the M ic ros oft XM L
referenc e on your c omputer, download the
M SXM L pars er from M ic ros oft
(http://www.mic ros oft.c om/xml).

With the referenc e s et, you c an work with XM L in many


s ophis tic ated ways .T his hac k is n't the plac e to learn how to us e
the pars er (s ee the end of the hac k for s ome res ourc es ).
I ns tead, we'll preview us ing the pars er to load XM L data and
ins ert it into an A c c es s table. A long the way, we'll ac c omplis h a
c ouple of tric ks : filtering XM L data and loading attributes .

A n XM L file filled with employee information has been prepared,


as s hown in Figure 8 - 2 9 .

8.17.2. The Code

I n an A c c es s c ode module, the following c ode has been entered.


T his c ode us es objec ts available from the pars er referenc e:

Sub read_xml()
On Error GoTo err_end
Dim conn As New ADODB.Connection
Set conn = CurrentProject.Connection
Dim xmlobj As DOMDocument
Dim xml_list As IXMLDOMNodeList
Dim xml_node As IXMLDOMNode
Set xmlobj = New DOMDocument
xmlobj.async = False
xmlobj.Load "C:\Employees.xml"
Set xml_list = xmlobj.selectNodes _
("Employees/Department/Employee")
For Each xml_node In xml_list
ssql = "Insert Into tblEmployees Values (" & _
xml_node.childNodes(0).Text & ", '" & _
xml_node.childNodes(1).Text & "', '" & _
xml_node.parentNode.Attributes(0).Text & "')"
conn.Execute ssql
Next
MsgBox "done"
err_end:
MsgBox Err.Description
End Sub

Figure 8-29. The Employees.xml file


T he XM L file is loaded into the xmlobj objec t variable:

xmlobj.Load "C:\Employees.xml"

Typic al XM L objec ts are nodes and node lis ts . A lis t is a


c ollec tion of nodes . T he ac tual nodes are the employee
elements , whic h are c hildren of the department nodes :

Set xml_list = xmlobj.selectNodes _


("Employees/Department/Employee")

E mployee nodes have two c hildren: EmployeeID and Name. T hes e


c hild elements and the parent department element are the bas is
from whic h a SQ L Insert s tatement is c reated.

ssql = "Insert Into tblEmployees Values (" & _


xml_node.childNodes(0).Text & ", '" & _
xml_node.childNodes(1).Text & "', '" & _
xml_node.parentNode.Attributes(0).Text &

A fter the routine runs , the tblE mployees table is populated with
the XM L data, as s hown in Figure 8 - 3 0 .
Figure 8-30. The XML data now in Access
So, in jus t a s hort routine, we've ac c omplis hed two things that
are typic ally taken for granted as being impos s ible. O ne is that
now, only A c c es s 2 0 0 3 c an work with XM L in a robus t way, and
the other is that attributes c an't be imported. T he routine in this
hac k will work with any vers ion of A c c es s that referenc es the
pars er and c learly has no problem putting an attribute's value
into an A c c es s table.

8.17.3. See Also

"U s e A c c es s as an XM L D atabas e"[Hack #95]

XML Hacks (O 'Reilly)

Office 2003 XML(O 'Reilly)


Hack 88. Use Custom Enumerations

Use f amiliar names, instead of memorizing equivalent numbers,


to avoid errors and speed up coding.

H aving a lis t of properties and methods appear while typing


really helps when you're c oding. For example, entering
Application in a c ode module and then entering a period opens up
a lis t of methods and properties that belong to the Application
objec t.

You c an us e this s ame helpful fac ility to provide you with


c ons tants that are partic ular to your projec t or bus ines s . Take,
for example, an applic ation that has to take the department c ode
into ac c ount for s ome s pec ific proc es s ing. E ac h department has
a unique c ode number, but the numbers thems elves are
meaningles s . T his makes the c ode numbers diffic ult to
remember.

T hat is where a s et of enumerated variables c omes in handy. N ot


only c an you give names to the numeric al c ode numbers , but the
names bec ome available in a lis t while typing.

Figure 8 - 3 1 s hows a V BA c ode module. I n the dec laration


s ec tion, I us ed the Enum s tatement to c reate the variables . I
gave a name to the bloc k (DepartmentCodes in this example) and
us ed the End Enum s tatement to end the bloc k.

Figure 8-31. Using Enum for more efficient


coding
Within the Enum bloc k, I as s igned eac h department's c ode
number to its name. N ow, when c oding within a proc edure, all I
have to do is type DepartmentName and enter a period. T he
enumerated variables appear, and I c an s elec t the one I need.
By s elec ting one, I 'm really s elec ting the department c ode
number, but I no longer have to memorize the numbers . T his
reduc es errors bec aus e hones tly, up until I us ed this tec hnique I
c ould not remember if Advertising was 200 or 600. T hankfully, by
us ing Enum, I 'm able to let go of remembering s uc h things and
c an c onc entrate on the more important as pec ts of my projec ts .
Hack 89. Convert Text to the Desired
Case

Have any text string be returned in uppercase, lowercase, or


proper case.

O ne of the oc c as ional requirements thrown at a developer is the


ability to c hange the c as e of the text. T his is n't a really diffic ult
problem. A ll you have to do is us e the UCase or LCase func tions ,
whic h return a text s tring as all c apital letters or all lowerc as e
letters , res pec tively.

H owever, no func tion is available for returning proper cas e (a.k.a.


mixed cas e or s entence cas e): text in whic h eac h word s tarts with
an upperc as e letter, with the res t of the word in lowerc as e.

M ic ros oft Word has the ability to return proper c as e, but A c c es s


does n't. While you're waiting for the two development teams at
M ic ros oft to get together on this , here is a func tion that returns
all three c as e types : upper, lower, and proper. T he func tion takes
two arguments : the text to be c onverted and the type of
treatment to apply.

8.19.1. The Code

When c onverting to upper- or lowerc as e, the func tion s imply


us es the res pec tive built- in UCase or LCase func tion. Why reinvent
the wheel?

To c onvert text to proper c as e requires a looping proc es s . I f you


think about it, all you need to do is apply UCase or LCase to eac h
c harac ter in the text. T he tric k is to know whic h letters get whic h
treatment.

Function change_case(txt As String, case_type As String) As String


Select Case case_type
Case "Upper"
change_case = UCase(txt)
Case "Lower"
change_case = LCase(txt)
Case "Proper"
'create proper case
Dim space_flag As Boolean
space_flag = False
'first letter is alway uppercase
change_case = UCase(Left(txt, 1))
For test_case = 2 To Len(txt)
If Mid(txt, test_case, 1) = " " Then
space_flag = True
change_case = change_case & LCase(Mid(txt, test_case, 1))
Else
If space_flag = True Then
change_case = change_case & UCase(Mid(txt, test_case, 1)
space_flag = False
Else
change_case = change_case & LCase(Mid(txt, test_case, 1)
End If
End If
Next test_case
End Select
End Function
To s tart, the firs t letter of the s tring bec omes upperc as e. T hat
one is a given. T hen, a loop c yc les through the res t of the text
s tring. A c harac ter c an be a s pac e. When a s pac e is
enc ountered, a flag is s et to true. When a nons pac e c harac ter is
enc ountered, one of two things c an happen:

I f s pace_flag is true

T he c harac ter c omes direc tly after a s pac e, s o c hange


the c harac ter to upperc as e, and s et space_flag to false.

I f s pace_flag is fals e

T he c harac ter followed another nons pac e c harac ter.


T herefore, the c harac ter being evaluated is n't the firs t
letter of a word, s o c hange it to lowerc as e.

N ote that you don't need to tes t whether a c harac ter is upper- or
lowerc as e while it is being evaluated. I f it follows a s pac e, it
ends up as upperc as e, regardles s of the c as e in whic h it was
typed. T he s ame approac h holds true for c harac ters that don't
follow a s pac e: they are s et to lowerc as e regardles s .

8.19.2. Running the Code

T he change_case func tion needs two arguments . You c an s pec ify


them from field c ontrols , from c ode, or even from within the
I mmediate window. Figure 8 - 3 2 s hows how the func tion is c alled
from a form.

A text box c ontains the s tring of text, a lis tbox offers the three
c as e types , and a c ommand button c alls the func tion with this
little s nippet of c ode:

Private Sub Command2_Click()


Dim z As String
z = change_case(Me.Text1, Me.List1)
MsgBox z
End Sub

Figure 8-32. Returning text in a selected case


Hack 90. Create a Code Library

Make your f avorite custom f unctions available in all your


databases.

A s a developer, I find I often c reate and us e the s ame func tions


on a number of different projec ts . Too many times I have had to
hunt down s ome c ode I wanted to reus e. T his has taken a good
amount of time bec aus e not only do I have to rec all where I us ed
the c ode before, but I als o need to remember whic h c omputer it
is on!

T he s olution to this c haos is to put all the c ode in one databas e


and us e the databas e as a c ode library. A good way to do this is
to c lean up the c ode and as s emble it in one or more c ode
modules , in a s ingle databas e. M ake all the c ode routines be
func tions s o that they return values . T hen, jus t s ave the
databas e and c los e it. Finally, c hange the extens ion of the
databas e to.mda.

Figure 8 - 3 3 s hows a module filled with func tions . T his module is


in the A c c es s file that has the extens ion c hanged to .mda.

With a c hanged extens ion, now the file is rec ognized as an add-
in, as s hown in Figure 8 - 3 4 .

From a regular A c c es s databas e, the c ode library is referenc ed.


I n the V B editor, us e Tools Referenc es to open the
Referenc es dialog box. From the dialog, brows e to the loc ation of
the s aved c ode library. I t might be nec es s ary to c hange "Files of
type" in the A dd Referenc e dialog to A dd- ins , as s hown in Figure
8 -3 5 .

Figure 8-33. A module of code functions


Figure 8-34. Recognizing the file as an add-in
N ow, the func tions in the c ode library are available in the regular
databas e. Figure 8 - 3 6 s hows how they are lis ted in the O bjec t
Brows er.

O ver time, as you c reate more and more routines , the value of
keeping them in one plac e bec omes a real times aver. You never
know when you will need to us e an algorithm you c reated years
ago.

Figure 8-35. Referencing the add-in


Figure 8-36. Viewing the custom functions in the
Object Browser
Hack 91. Automatically Check for
Database Table Updates

Pull updated objects f rom a master database when your


database opens.

O ne of the is s ues you fac e with a dis tributed applic ation is how
to propagate updated tables to the us er c ommunity. T his
happens when you mus t proc es s new data or when lookup lis ts
have new values that have table data as the s ourc e.
Redis tributing the entire databas e is one way to go, although
that dis rupts the workflow. I n that s c enario, us ers mus t s top
what they are doing, get the new file, and s ave it s omewhere.
E ven when that proc es s is automated, you c an't be s ure
s omeone is c urrently us ing the databas e you are about to
overwrite.

I ns tead, here's a great way to have us ers ' applic ations update
thems elves . T he update oc c urs when a us er s tarts up her loc al
c opy of the databas e. A c ode routine c hec ks the databas e's
tables agains t thos e in a mas ter databas e on the network. When
a table in the mas ter databas e is found to be newer, it is c opied
into the us er's databas e.

8.21.1. Running Code at Startup


E ac h us er's loc ally ins talled databas e c ontains a table named
tblTableVers ions that has two fields : one c ontains the names of
the tables in the databas e, and the other has the las t modified
date of eac h table. When the databas e is opened, a c ode routine
opens the mas ter databas e and c ompares the modified date of
the tables in the mas ter databas e with the rec ords in the
tblTableVers ions table.

You might wonder why we don't jus t c ompare the modified dates
of the tables thems elves in the loc al databas e. Why is a table
kept with the modified dates ? T he reas on is a s afeguard: us ers
might alter the tables loc ally, thereby c hanging the las t modified
date on loc al tables . T he point is to c hec k if the mas ter
databas e c ontains updated tables that have not been us ed yet.
T he dates in the loc al tblTableVers ions table are the modified
dates of tables in the mas ter databas efrom the las t time any
partic ular table was c opied.

Figure 8 - 3 7 s hows the tblTableVers ions table. Two tables have


dates that are in D ec ember 2 0 0 4 .

Figure 8-37. Keeping track of the last modified


date
8.21.2. The Code

When the loc al databas e opens , the AutoExec mac ro c alls the
following get_ updates func tion, whic h therefore runs upon
s tartup:

Function get_updates()
On Error GoTo err_end
Dim update_db As String
update_db = "G:\UpdateDB.mdb"

Dim cat As New ADOX.Catalog


Dim tbl As New ADOX.Table
Dim conn As ADODB.Connection
Set conn = CurrentProject.Connection
Dim local_tbl As String
Dim current_object_date As Date
' Open the catalog
cat.ActiveConnection = "Provider=Microsoft.Jet.OLEDB.4.0;" & _
"Data Source=" & update_db
For Each tbl In cat.Tables
If Left(tbl.Name, 4) <> "MSys" Then
current_object_date = _
DLookup("[ModifiedDate]", "tblTableVersions", _
"[TableName] = '" & tbl.Name & "'")
If tbl.DateModified > current_object_date Then
DoCmd.DeleteObject acTable, tbl.Name
DoCmd.TransferDatabase acImport, "Microsoft Access", _
update_db, acTable, tbl.Name, tbl.Name
'store new date
conn.Execute ("Update tblTableVersions Set ModifiedDate=#"
tbl.DateModified & "# Where TableName='" & tbl.Name & "
End If
End If
Next
Set cat = Nothing
Set conn = Nothing
MsgBox "done"
Exit Function
err_end:
MsgBox Err.Description
End Function

8.21.3. Running the Code


A mixture of V BA , A D O X, and A D O works together to c hec k for
updates . A n A D O X c atalog objec t is c reated and is s et to the
mas ter databas eG:\ UpdateDB.mdb in this example. A ll the
tables in the mas ter databas e are examined; however, s ys tem
tables aren't inc luded. A ll s ys tem tables s tart with M Sys [Hack
#15].

T he DateModified property of eac h table in the mas ter databas e


is c hec ked agains t the loc ally s tored date for the s ame named
tables . When it finds that a mas ter databas e table has a newer
modified date, DoCmd deletes the table in the loc al databas e and
imports the new table from the mas ter databas e. T hen, a SQ L
Update s tatement updates the date for the table in the tblTable-
N ames table.

A fter the routine c ompletes , the us er c an go about her bus ines s


as us ual. T he applic ation works as always bec aus e the table
names have not c hanged.

T he only c aveat with this hac k is that it is us eful to update


tables that don't s hare in a relations hip. Related tables need to
have the relations hip broken before you delete and then
rees tablis h them. T herefore, this hac k is perfec t for tables that
c ontain the s ourc e for lookup lis ts or that don't partic ipate in a
relations hip. You c an develop additional c ode to tes t for
relations hips and handle them appropriately.
9. Third-Party Applications
Sec tion 9 .1 . H ac ks 9 2 9 5

H ac k 9 2 . D oc ument Your D atabas e with Total A c c es s


A nalyzer

H ac k 9 3 . Build an A pplic ation Shell with E Z A pplic ation


G enerator

H ac k 9 4 . L oad Your D atabas e with Tes t D ata

H ac k 9 5 . U s e A c c es s as an XM L D atabas e
9.1. Hacks 9295
M uc h of the foc us in A c c es s books involves how to manage
building an applic ationworking with tables , queries , forms , and
reports and applying s olutions with mac ros and c ode. H owever,
onc e you've c ompleted all that work, you might need a tool to
doc ument your databas e [Hack #92]. Similarly, when you c reate
an applic ation, you might not be c ons idering all the
management, func tionality, and utilities that c an go into your
databas e. Fortunately, a produc t is available that fles hes this
out for you [Hack #93].

O f c ours e, data is the point of a databas e. H ow often have you


des igned a databas e and then had to find s ome data to tes t it
with? You c an enter data manually, or you c an us e a produc t that
c reates data for you [Hack #94].

I n addition to all this , this c hapter als o s hows you how to build
an applic ation without any tables . "U s e A c c es s as an XM L
D atabas e" [Hack #95] s hows how to run an XM L databas e from
A c c es s , reading from and writing to an external XM L file.
Hack 92. Document Your Database with
Total Access Analyzer

Get the f ull nuts-and-bolts skinny on your database.

E ven a s imple A c c es s databas e has a lot in it. J us t take a table


of data, a form, a few c ontrols , and a report, and the number of
properties is in the hundreds . A large databas e has an
unimaginable number of items in it.

N ow, imagine a utility that lets you drill down anywhere in your
databas e and unc over nuggets of information you probably didn't
even know about. E nter the Total A c c es s A nalyzer by FM S, I nc .
(http://www.fms inc .c om). T his outs tanding produc t tells you
everything about your databas e. I t leaves nothing out.

9.2.1. Running the Analyzer

A fter you ins tall the A nalyzer, it is available as an add- in.


Regardles s of whic h databas e you have open, jus t go to the
Tools A dd- I ns menu to find the A nalyzer. T he A nalyzer runs
from a main form, s hown in Figure 9 - 1 .

Figure 9-1. Total Access Analyzer


To get s tarted, c lic k the D oc ument button. T his runs the
D oc umentation Wizard, whic h walks you through s elec ting whic h
items to doc ument. Figure 9 - 2 s hows the firs t s c reen of the
D oc umentation Wizard.

A fter making a s elec tion, c lic k the N ext button to bring up the
wizard's s ec ond s c reen, s hown in Figure 9 - 3 .

O n this s c reen, you c an s elec t to doc ument relations hips ,


doc ument s ec urity, and generate field c ros s referenc es that
s how you where fields are us ed throughout the databas e, among
other things . G enerating field c ros s referenc es is a great feature
bec aus e although it's good to get details about a field, it's even
better to know where the field's data is pres ented (on forms and
reports ).

T he doc umentation proc es s works by writing the res ults to an


external file. I n the third wizard s c reen, s hown in Figure 9 - 4 , you
c an s elec t where this external file goes (or jus t ac c ept the
default loc ation) and then s c hedule the doc umentation.

Figure 9-2. Documentation Wizard, step 1


Figure 9-3. Documentation Wizard, step 2
T he doc umentation proc es s begins when you c lic k the Finis h
button. I t c an take a moment or two, depending on the s ize of the
databas e.
9.2.2. Viewing the Documentation

To view the doc umentation, c lic k the E xplore button bac k on the
main form, s hown in Figure 9 - 1 . T he D oc umentation E xplorer
opens , as s hown in

Figure 9-4. Documentation Wizard, step 3

Figure 9 - 5 . I n the E xplorer, you s elec t objec ts in the left


explorer pane, and the details are s hown in the right pane.

Figure 9-5. Exploring the documentation


A t this point you have a wealth of information to s ift through. You
c an look up everything about your databas e and its objec ts ; the
E xplorer's layout makes this eas y. For example, as s hown in
Figure 9 - 6 , you c an s ee all the proc edures in a module and their
dec larations all next to eac h other and eas y to c ompare. J us t try
doing that in a c ode module.

9.2.3. Errors and Suggestions

N ot only does Total A c c es s A nalyzer doc ument the databas e,


but it als o goes further to identify problems and offer
s ugges tions . Figure 9 - 7 s hows

Figure 9-6. Exploring a module


where a potential problem has been flagged. I tend to forget to
s et the O ption E xplic it s etting in my modules . Well, I gues s I
c an't get away with that anymore!
Figure 9-7. Exploring an error

Figure 9 - 8 s hows a lis t of s ugges tions the A nalyzer has


as s embled. Suc h s ugges tions are really us eful for making an
applic ation the bes t it c an be.

Figure 9-8. Exploring suggestions


Total A c c es s A nalyzer is a great produc t not only for lis ting all
the objec ts , properties , methods , and attributes about a
databas e, but als o for finding out what you c an do to improve
them. O ther options from the main form inc lude s earc hing for
partic ular items in the doc umentation and printing the
doc umentation.
Hack 93. Build an Application Shell with
EZ Application Generator

Let your f ingers do the walking through the process of creating


an A ccess application.

Why go through the drudgery of putting together a databas e from


s c ratc h c reating a s plas h s c reen, integrating s ec urity and help,
c reating a report generator, and morewhen a great produc t is
available that c an do it for you? E Z A pplic ation G enerator by
D atabas e C reations , I nc . (http://www. databas ec reations .c om)
c reates a framework for an applic ation. You s till c reate your data
tables , forms , and other databas e objec ts , but in the end, you
will have a c omplete applic ation that has it all, from A to Z.

T he E Z A pplic ation G enerator Wizard runs through nearly a


dozen s c reens that c over all the bas es , from adding applic ation
information, logos , and other graphic s , right through to s etting
up s ec urity, error trapping, and advanc ed s earc h features .

To get s tarted, c reate a new databas e. You c an c reate your


tables , forms , reports , and other databas e objec ts now, or
anytime after the applic ation s hell is c ompleted. T he E Z
A pplic ation G enerator s tarts when you s elec t it from the A dd-
I ns lis t.

T he firs t s c reen, s hown in Figure 9 - 9 , ac c epts the applic ation


title and other general information.
Figure 9-9. Entering general application
information
I n the next s c reen, s hown in Figure 9 - 1 0 , you s elec t the images
to us e for the s plas h s c reen, the logo, and the ic on.

I n the next s c reen, s hown in Figure 9 - 1 1 , you s elec t a theme for


the s witc hboard. Boy, is this eas y! To think of all thos e years I
did this by hand!

Several more s c reens follow, in whic h you c an s et up titles , tips


of the day, legal agreements , and other us eful applic ation items .
A long the way, s ec urity and error handling are initiated.

T he s c reen in Figure 9 - 1 2 lets you s elec t to inc lude a c alendar,


c loc k, and c alc ulator, as well as s c heduling options .

When the E Z A pplic ation G enerator Wizard finis hes , the


databas e is populated with the objec ts needed to run your
applic ation. You c an ac c es s and run all tas ks , inc luding your own
databas e objec ts , from a s witc hboard. A ll the utilities you
s elec ted in the E Z A pplic ation G enerator Wizard are available
from the s witc hboard as well, as s hown in Figure 9 - 1 3 .

Figure 9-10. Selecting graphics


Figure 9-11. Selecting a switchboard theme
D atabas e C reations offers other developer- friendly produc ts as
well. V is it the c ompany's web s ite for more information.

Figure 9-12. Selecting utilities


Figure 9-13. Running the application in a
switchboard
Hack 94. Load Your Database with Test
Data

Use predesigned test data that matches your tables and f ields.

Building a databas e applic ation is one thing, but tes ting it is


another. Sometimes you c an get your hands on arc hived
produc tion data and us e it to tes t an applic ation before it is
releas ed. O ther times , though, you have to c reate your own tes t
data.

A lthough putting a few rec ords into your tables to make s ure
everything works ac c ordingly c an reas s ure you that your
databas e applic ation works , loading it with hundreds or
thous ands of rec ords lets you really s ee how your applic ation will
perform when it's releas ed.

Rec ords 2 G o (http://www.rec ords 2 go.c om) is a data- generation


produc t that lets you define s c hemas and then produc es data to
your s pec ific ations . T he c reated data is s aved into an external
text or XM L file, whic h you c an import into A c c es s . You c an
make any number of rec ords us ing this tool; I have us ed
Rec ords 2 G o to make rec ords numbering in the hundreds of
thous ands .

Figure 9 - 1 4 s hows how Rec ords 2 G o works . You have a handful


of s ettings to input, inc luding how many rec ords to make, what
the c reated file is named, and where it will be s aved. T he form's
main foc us is the grid in whic h you define a table layout.
Figure 9-14. Creating an XML data set
You c an c reate two types of files : text and XM L . Figure 9 - 1 4
s hows an XM L s c hema being s et up. Whether you're c reating a
text file or an XM L file, eac h row in the grid repres ents a field. O n
eac h row, you enter the field name, field type, and other
parameters . You c an us e s tandard field types s uc h as numbers ,
dates , and textor you c an us e field types from a s et of predefined
data. T he predefined data offers name and addres s field type
information.

You c an us e wildc ards to c ontrol how data is c reated. For


example, when you s elec t the Text type, you c an enter a phras e,
whic h will be c reated as is , or you c an us e s pec ific wildc ards to
repres ent random numbers or c harac ters . I n the s pec ific ation in
Figure 9 - 1 4 , pound s igns (#) indic ate random number c reation.

Figure 9 - 1 5 s hows the XM L data generated from the


s pec ific ation s hown in Figure 9 - 1 4 . T he predefined Full Name
attribute c reated jus t that: XM L attributes of names . T he
E mployeeI D field is filled with random numbers , and the hire
dates fall within the range in the s pec ific ation.

Figure 9-15. The created XML data


Rec ords 2 G o is great for produc ing large s ets of data. Figure 9 -
1 6 s hows a s pec ific ation for making 1 5 ,0 0 0 text rec ords . T he
tas k was c ompleted in 3 3 s ec onds .

Figure 9-16. 15,000 records in 33 seconds


Figure 9 - 1 7 s hows the c reated text file. O ne of the options is
whether to inc lude a header row. O ther options inc lude s elec ting
the delimiter and the text qualifier.

Figure 9-17. Test data ready to be imported into


Access
A great fac et of the produc t is that you c an s ave and load
s pec ific ations . I n other words , when you c reate a data
s pec ific ation in the grid, you c an s ave it s o that you c an us e it
whenever you need it. You c an move rows around in the grid, add
rows , delete rows , and s o on. You even c an us e jus t s ome of the
rows and leave others outwithout having to delete them.
Hack 95. Use Access as an XML
Database

Have A ccess work as a f ront end to your XML data.

A few hac ks throughout this book (s ee the lis t at the end of this
hac k) explore XM L us age with A c c es s . T his hac k pres ents the
c rowning ac hievement: a c omplete XM L databas e. To c larify, this
hac k s hows you how A c c es s c an read from and write to XM L files
and have the data appear on a form for viewing and editing. T he
form has the requis ite databas e func tionality: brows e, add a
rec ord, update a rec ord, and delete a rec ord.

T he power behind making this work is to inc orporate the M SXM L


pars er from M ic ros oft. V is it http://www.mic ros oft.c om/xml and
s ee "P rovide C omplete XM L C ontrol to A ny Vers ion of A c c es s "
[Hack #87] for an introduc tion to getting and us ing the pars er.
T he pars er is the key to getting A c c es s to do more than s imple
XM L imports and exports .

Figure 9 - 1 8 s hows the form us ed in the applic ation. T he


dis played rec ord is from an XM L file. T he form has P revious and
N ext buttons for navigating through rec ords , as well as U pdate
and D elete buttons . T he N ew button is for entering new rec ords ,
and the Save button is us ed for s aving new rec ords to the file.

Figure 9-18. Displaying data from an XML file


T he data is c ompletely external, but it does n't c ome from a
table. T his applic ation c ontains no tables , whether linked or
c onnec ted with A D O or O D BC . I n fac t, this applic ation c ontains
nothing exc ept this one form.
9.5.1. The Code

T he following c ode behind the form takes c are of all data


management:

Option Compare Database


Public xmlobj As DOMDocument
Public xml_list As IXMLDOMNodeList
Public record_num As Integer
Public file_name As String

Private Sub cmdDelete_Click()


Dim xml_node As IXMLDOMElement
Set xml_node = xmlobj.documentElement.childNodes(record_num)
xmlobj.documentElement.removeChild xml_node
xmlobj.Save file_name
reload_file
End Sub

Private Sub cmdNew_Click()


Me.txtEmployeeID = ""
Me.txtEmployeeName = ""
Me.txtHireDate = ""
Me.txtRecordNum = ""
End Sub

Private Sub cmdNext_Click()


If record_num < xml_list.length - 1 Then
record_num = record_num + 1
Else
record_num = 0
End If
load_record
End Sub
Private Sub cmdPrevious_Click()
If record_num > 0 Then
record_num = record_num - 1
Else
record_num = xml_list.length - 1
End If
load_record
End Sub

Private Sub cmdSave_Click()


Dim xml_node As IXMLDOMElement
If Me.txtEmployeeID = "" Or Me.txtEmployeeName = "" Or _
Me.txtHireDate = "" Then
MsgBox "Must fill in all three fields"
Exit Sub
End If
Set xml_node = xmlobj.createElement("Employee")
xml_node.setAttribute "EmployeeID", Me.txtEmployeeID
xml_node.setAttribute "EmployeeName", Me.txtEmployeeName
xml_node.setAttribute "HireDate", Me.txtHireDate
xmlobj.documentElement.appendChild xml_node
xmlobj.Save file_name
reload_file
End Sub

Private Sub cmdUpdate_Click()


xmlobj.documentElement.childNodes(record_num) _
.Attributes(0).nodeValue = Me.txtEmployeeID
xmlobj.documentElement.childNodes(record_num) _

.Attributes(1).nodeValue = Me.txtEmployeeName
xmlobj.documentElement.childNodes(record_num) _
.Attributes(2).nodeValue = Me.txtHireDate
xmlobj.Save file_name
reload_file

End Sub

Private Sub Form_Open(Cancel As Integer)


file_name = "C:\EmployeeData.xml"
Set xmlobj = New DOMDocument
xmlobj.async = False
xmlobj.Load file_name
Set xml_list = xmlobj.selectNodes _
("Employees/Employee")
'load first record
record_num = 0
load_record
End Sub

Sub load_record()
Me.txtEmployeeID = _
xml_list.Item(record_num).Attributes(0).nodeValue
Me.txtEmployeeName = _
xml_list.Item(record_num).Attributes(1).nodeValue
Me.txtHireDate = _
xml_list.Item(record_num).Attributes(2).nodeValue
Me.txtRecordNum = record_num + 1
End Sub

Sub reload_file()
xmlobj.Load file_name
Set xml_list = xmlobj.selectNodes _
("Employees/Employee")
'load first record
record_num = 0
load_record
End Sub
9.5.2. Loading the XML File

When the form opens , a public XM L variable (xmlobj) is s et to the


loaded XM L file, whic h res ides in memory. A lis t of nodes
(xml_list) holds the E mployee rec ords , and the firs t rec ord is
dis played in the form:

Private Sub Form_Open(Cancel As Integer)


file_name = "C:\EmployeeData.xml"
Set xmlobj = New DOMDocument
xmlobj.async = False
xmlobj.Load file_name
Set xml_list = xmlobj.selectNodes _
("Employees/Employee")

'load first record


record_num = 0
load_record
End Sub

9.5.3. Browsing Records

I n XM L lingo, the length property is the s ame as the count


property in V B. When the N ext or P revious buttons are c lic ked, a
public variable, record_ num, is c ompared with the number of XM L
rec ords . I f the record_num variable hits the total c ount as a res ult
of c lic king N ext, it res ets to 0 . I f the record_num variable hits 0
as a res ult of c lic king P revious , it res ets to the number of
rec ords . C lic king N ext or P revious c ompletes with a c all to the
load_record routine:

Private Sub cmdNext_Click()


If record_num < xml_list.length - 1 Then
record_num = record_num + 1
Else
record_num = 0
End If
load_record
End Sub

Private Sub cmdPrevious_Click()


If record_num > 0 Then
record_num = record_num - 1
Else
record_num = xml_list.length - 1
End If
load_record
End Sub

T he load_record routine s imply fills the c ontrols on the form with


the data from the XM L rec ord that is pos itioned at the record_num
number:

Sub load_record()
Me.txtEmployeeID = _
xml_list.Item(record_num).Attributes(0).nodeValue
Me.txtEmployeeName = _
xml_list.Item(record_num).Attributes(1).nodeValue
Me.txtHireDate = _
xml_list.Item(record_num).Attributes(2).nodeValue
Me.txtRecordNum = record_num + 1
End Sub

9.5.4. Updating a Record

When data is c hanged while on the form, the U pdate button mus t
be c lic ked to s ave the c hanges bac k to the original file. T he
proc es s here is to update the node (the employee rec ord) in the
file with the form values . T he Employee node is a c hild of
documentElement Employees. T he values aren't s aved until the Save
method runs on xmlobj. A fter that, the file is reloaded, and this
las t s tep res ets the form bac k to the firs t rec ord (an alternative
is to leave the form dis playing the updated rec ord):

Private Sub cmdUpdate_Click()


xmlobj.documentElement.childNodes(record_num) _
.Attributes(0).nodeValue = Me.txtEmployeeID
xmlobj.documentElement.childNodes(record_num) _
.Attributes(1).nodeValue = Me.txtEmployeeName
xmlobj.documentElement.childNodes(record_num) _
.Attributes(2).nodeValue = Me.txtHireDate
xmlobj.Save file_name
reload_file

End Sub
9.5.5. Deleting a Record

To delete a rec ord s et a node variable (xml_node) to the employee


rec ord. T hen, the removeChild method of its parent deletes it:

Private Sub cmdDelete_Click()


Dim xml_node As IXMLDOMElement
Set xml_node = xmlobj.documentElement.childNodes(record_num)
xmlobj.documentElement.removeChild xml_node
xmlobj.Save file_name
reload_file
End Sub

A s with other file c hanges , the Save method is nec es s ary.

9.5.6. Adding a New Record

T he N ew and Save buttons work together to add a rec ord to the


XM L file. T he N ew button s imply c lears the form, and new
employee information c an be entered. T he Save button runs the
c ode that s aves a new rec ord.

A fter validating that all text boxes c ontain data, a new element
is c reated. A ttributes are s et to the form values , and the
element, along with its attributes , are s aved us ing the
appendChild method. T he Save method follows , and the file is
reloaded (now it c ontains the new rec ord):

Private Sub cmdSave_Click()


Dim xml_node As IXMLDOMElement
If Me.txtEmployeeID = "" Or Me.txtEmployeeName = "" Or _
Me.txtHireDate = "" Then
MsgBox "Must fill in all three fields"
Exit Sub
End If
Set xml_node = xmlobj.createElement("Employee")
xml_node.setAttribute "EmployeeID", Me.txtEmployeeID

xml_node.setAttribute "EmployeeName", Me.txtEmployeeName


xml_node.setAttribute "HireDate", Me.txtHireDate
xmlobj.documentElement.appendChild xml_node
xmlobj.Save file_name
reload_file
End Sub

9.5.7. See Also

"I mport Varied XM L D ata into A c c es s " [Hack #63]

"E xport XM L D ata Sanely" [Hack #64]

"Break T hrough V BA 's Trans formation Barrier" [Hack


#65]

"P rovide C omplete XM L C ontrol to A ny Vers ion of


A c c es s " [Hack #87]
10. The Internet

Sec tion 1 0 .1 . H ac ks 9 6 1 0 0

H ac k 9 6 . E xport a Report as H T M L

H ac k 9 7 . U s e a Brows er I ns ide A c c es s

H ac k 9 8 . P ull the H T M L Sourc e C ode from a Web Site

H ac k 9 9 . D ownload Files U s ing the Web Brows er


C ontrol

H ac k 1 0 0 . U s e a Smart Tag to O pen a Web P age


10.1. Hacks 96100
Web tec hnologies and the I nternet are ingrained in s o muc h of
what we do that extending the Web to work with our databas e
applic ations makes perfec t s ens e. A lthough at firs t A c c es s
might not s eem like a web- s avvy produc t, with a few hac ks
A c c es s c an work well with the Web.

T his c hapter begins by s howing how to s ave a report as H T M L


[Hack #96]. T his lets you pos t your data to a web s ite. Better
yet, how about bringing the I nternet into your databas e? "U s e a
Brows er I ns ide A c c es s " [Hack #97] s hows you how to us e a web
brows er right within your A c c es s forms . N eed to get data from a
web s ite? J us t FT P it over [Hack #99].
Hack 96. Export a Report as HTML

Preview your reports on web pages to reach a larger audience.

P reviewing s ummarized data is a key bus ines s need, and


A c c es s handles it quite well. To reac h a large audienc e, you c an
print, email, s ave to Word, and s o on. A nother option is to s ave a
report to a web page.

When you open an A c c es s report, one of the s elec tions on the


File menu is E xport. A handful of output types are available, and
one of them is H T M L . Figure 1 0 - 1 s hows a report being exported
as H T M L .

Saving the report as an H T M L file opens up a number of


pos s ibilities . T he likely ac tion is to view the report in a web
brows er. Figure 1 0 - 2 s hows the report previewed in I nternet
E xplorer. T he H T M L file is no longer a part of the databas e, s o
you mus t open it in the brows er us ing the File O pen menu.

You c an upload the H T M L file to a web s erver and make it


available public ly, or you c an upload it to a c ontrolled intranet
group. O pening the file in N otepad or another plain- text editor
reveals the ac tual H T M L c ode, as s hown in Figure 1 0 - 3 .

Figure 10-1. Selecting to export a report as


HTML
Figure 10-2. Viewing the exported data in a web
browser
N ote the highlighted c ontents of the H T M L title tag. Referring
bac k to Figure 1 0 - 2 , you c an s ee that the H T M L file is named
Sightings ByState.html. But the title in Figure 1 0 - 3 jus t s ays
Report1 . Report1 was the name of the report in A c c es s , s o
that's the title us ed in the I nternet E xplorer titlebar in Figure
1 0 - 2 . We'll have to c hange the title in N otepad and dis play the
file again in the brows er.

Figure 10-3. Viewing the source HTML


Figure 1 0 - 4 s hows a s lightly edited vers ion of the H T M L report.
T he title has been c hanged, and a few formatting c hanges are in
plac e.

Figure 10-4. An updated view


T hough the H T M L c ode was edited direc tly in N otepad, you c an
open the H T M L file with any H T M L editor, s uc h as D reamweaver
or FrontP age.
Hack 97. Use a Browser Inside Access

Place the Microsof t web browser on a f orm to coordinate data


and the Web.

A c c es s tables have a hyperlink field type, whic h s tores a U RL


that, when c lic ked, opens your c omputer's brows er and dis plays
the s ite. T his is us eful, but to view the databas e information and
the web s ite together is a c hallenge. You might have to reduc e
the s ize of both A c c es s and the brows er s o that they fit together
on your c omputer s c reen.

T his hac k puts the brows er right on the A c c es s form. To view the
web s ite at the s tored U RL , you jus t load the brows er with the
U RL addres s , and the web s ite appears on the form.

Figure 1 0 - 5 s hows a form in D es ign mode. T he M ic ros oft Web


Brows er c ontrol (one of the items in the lis t of M ore C ontrols
that is available from the button on the toolbox) has been plac ed
on the form.

Figure 10-5. Selecting the Microsoft Web


Browser control
Figure 1 0 - 6 s hows the form in V iew mode.
T he brows er c ontrol dis plays the web s ite lis ted in the c urrent
rec ord. T his oc c urs when you c lic k the D is play Web Site button,
whic h has this line of c ode in its Click event:

Me.WebBrowser1.Navigate URL:=Me.website

T he web brows er c ontrol has the Navigate method, and it


navigates to the s upplied U RL . I n this example, the U RL c omes
from the webs ite field on the form, from the c urrent rec ord. O f
c ours e, you c an feed a U RL to the web brows er c ontrol in other
ways . T his is jus t one example.

Figure 10-6. Viewing a web site on the form


J us t like any c ontrol, you c an plac e multiple web brows er
c ontrols on a form. Figure 1 0 - 7 s hows s uc h an arrangement.

Figure 10-7. Multiple browsers on a form


T his might s eem exc es s ive, but ac tually, you might have a good
reas on for us ing multiple brows ers . For example, an applic ation
that integrates with c urrent events c an us e this . Financ ial
applic ations c ould keep tabs on different s ec urities markets .
T he brows er c ontrols c ould even be dis playing the output of
webc ams .
Hack 98. Pull the HTML Source Code
from a Web Site

Integrate web data into your application.

"U s e a Brows er I ns ide A c c es s " [Hack #97] s hows you how to


us e the M ic ros oft Web Brows er c ontrol to dis play a web page.
T his hac k takes that func tionality a s tep further and s hows how
to get to the s ourc e c ode. Being able to ac c es s the s ourc e c ode
makes it pos s ible to extrac t data from a web s ite.

Figure 1 0 - 8 s hows a web s ite being dis played in the brows er


c ontrol, and a mes s age box dis plays the s ite's H T M L .

Figure 10-8. Reading the HTML source from a


web site
T he M ic ros oft Web Brows er c ontrol has an extens ive
programmatic model. V is it
http://ms dn.mic ros oft.c om/library/default.as p?
url=/works hop/brows er/prog_brows er_node_entry.as p
for more information.

T he H T M L is returned with this line of c ode:

MsgBox Me.WebBrowser1.Document.documentElement.innerhtml

T he programmatic model for the web brows er c ontrol follows the


doc ument objec t model (D O M ). A s the brows er dis plays a web
s ite, documentElement and its c hild nodes bec ome available. I n
this example, the full H T M L is ac c es s ed with the innerhtml
property. Bec aus e the H T M L is ac c es s ible, you c an pas s it to
any routine you want. For example, you c an have a routine that
looks for H T M L tables from whic h to pull data or that s earc hes
through the H T M L for keywords , and s o on.
Hack 99. Download Files Using the Web
Browser Control

FTP f iles without ever leaving your database.

"U s e a Brows er I ns ide A c c es s " [Hack #97] and "P ull the H T M L
Sourc e C ode from a Web Site" [Hack #98] s how you how to
brows e the Web from an A c c es s form and how to retrieve the
s ourc e H T M L c ode. T his hac k s hows you how to us e the Web
Brows er c ontrol to pull files from an FT P s ite.

T he File Trans fer P rotoc ol (FT P ) is c ommonly us ed to move files


to and from a web s erver. O ften, the s ite is pas s word- protec ted,
s o to try out this hac k, you need the rights to ac c es s an FT P
s ite.

10.5.1. Placing the Web Browser Control


on a Form

A s dis c us s ed in "U s e a Brows er I ns ide A c c es s " [Hack #97],


you c an plac e the M ic ros oft Web Brows er c ontrol on a form. T he
Navigate method takes a U RL addres s to navigate to. I n this
hac k, an FT P U RL is s upplied ins tead of an H T T P U RL .

U s ing a line of c ode s uc h as the following, the Web Brows er


c ontrol opens a login dialog (s hown in Figure 1 0 - 9 ):
Me.WebBrowser0.Navigate URL:="ftp.logicstory.com"

Figure 10-9. Entering a username and password


for the FTP site
A fter the login is c omplete, the Web Brows er c ontrol dis plays the
s ite's c ontents . You c an view, c opy, delete, and rename objec ts
depending on your us er permis s ions , as s hown in Figure 1 0 - 1 0 .

Figure 10-10. Viewing contents of the web site


When you s elec t a file to c opy, the C opy I tems dialog dis plays
the files ys tem on the loc al c omputer. A direc tory is s elec ted
where the c opied file will be pas ted, as s hown in Figure 1 0 - 1 1 .

Figure 10-11. Selecting the local directory to


save the copied file
10.5.2. Uploading Files

A s noted earlier, when s elec ting a file to download, the "C opy
this item" link opens the C opy I tems dialog box. A lthough no
equivalent link is available for initiating an upload, you c an
upload files to the FT P s ite. A ll you have to do is s elec t the file
on the loc al mac hine, and then c lic k onc e in the Web Brows er
c ontrol and us e the pas te keyboard s hortc ut (C trl- V ). Figure 1 0 -
1 2 s hows the progres s of a c opy operation, c onfirming that a file
is being c opied to the s ite.

Figure 10-12. Copying a file to the remote


server
Hack 100. Use a Smart Tag to Open a
Web Page

Quickly review web inf ormation when you need to see it.

D evelopers have mixed feelings about s mart- tag tec hnology.


Some c reate s ophis tic ated s mart- tag s olutions , and others s hun
their us e. I f you're open to us ing s mart- tag tec hnology, here is a
s imple way to us e a s mart tag to open a web page.

Firs t, a s mart tag is as s oc iated with a table field. Figure 1 0 - 1 3


s hows a s mart tag being as s oc iated with the C os t field in the
tblServic es table. A fter the s mart tag is s elec ted, the field's
s mart tag is available to c lic k.

Figure 10-13. Selecting smart tags


Figure 1 0 - 1 4 s hows the table open in D atas heet view. I n eac h
row, the C os t field dis plays a s mall purple triangle in the lower-
right c orner. H overing your mous e pointer over the triangle
opens the link to the s mart tag.

Figure 10-14. Accessing the smart tag from the


Cost field

C lic king the s mart tag initiates the ac tion as s oc iated with the
s mart tag. I n this example, the ac tion is to open a web page to
dis play s ome additional bus ines s information.

Figure 1 0 - 1 5 s hows the web page that opens when the s mart tag
is c lic ked.

Figure 10-15. A web site accessed from a smart


tag
I n mos t databas e applic ations , us ers don't work direc tly with
tables ; ins tead, they do all their work through forms . Figure 1 0 -
1 6 s hows that the s mart tag in the underlying table is available
on a form as well. From here, too, c lic king the s mart tag opens
the web page.

Figure 10-16. Smart tags at work on a form

To s ummarize, a s mart tag c an bring information from the Web to


the A c c es s applic ation. T he mec hanic s of c reating s mart tags
are beyond our s c ope here, but if you vis it
(http://www.mic ros oft.c om and s earc h for s mart tags , you will
turn up many artic les on their development and us e.
Index

[SYMBOL] [A ] [B] [C] [D] [E] [F] [G] [H] [I] [J]
[K] [L] [M] [N] [O] [P] [Q] [R] [S] [T] [U] [V ] [W]
[X] [Z]

.bmp files
.jpg files
.tif files
Index

[SYMBOL] [A] [B] [C] [D] [E] [F] [G] [H] [I] [J]
[K] [L] [M] [N] [O] [P] [Q] [R] [S] [T] [U] [V ] [W]
[X] [Z]

Action queries
actions
Admin user
ADO (ActiveX Data Objects)
ADO library

ADOX library
datatypes

references

table creation
alignment
And-based criteria
apostrophes
Append query

appending
across databases

apostrophes and

failed operations

Paste Append

records
log table

records to log table

applications
personalization

third-party
MSXML parser

Total Access Analyzer


arguments
audit logs
AutoExec macro

preferences and
AutoKeys macro

AutoNumber field
custom values
seeding
Index

[SYMBOL] [A ] [B] [C] [D] [E] [F] [G] [H] [I] [J]
[K] [L] [M] [N] [O] [P] [Q] [R] [S] [T] [U] [V ] [W]
[X] [Z]

Back Style property


back-end databases
backdoor building
Before Update event
Before_Update event

browsers
file dow nload
brow sers, Web
bulk updates
by letter
Index

[SYMBOL] [A ] [B] [C] [D] [E] [F] [G] [H] [I] [J]
[K] [L] [M] [N] [O] [P] [Q] [R] [S] [T] [U] [V ] [W]
[X] [Z]

Can Shrink property


Cartesian products

cascading
updates
relationships and
cascading updates
case conversion
characters 2nd 3rd 4th 5th 6th
cleanup, databases
clocks

code
arguments and

passw ord protection


subroutines and
code library creation
coding
command-line
Compact on Close option
compacting databases
comparing tables
compatibility
conditional
conditional formatting

highlighting and
Conditional Formatting dialog box
conditional macro actions
conditional subtotals 2nd 3rd 4th 5th
constants

controls
custom

information about

listboxes
populating

sorting

Tag property and


unbound
copying betw een tables
CreateObject function
creates

creating
tables
outside Access

w ith SQL Server scripts


Crosstab queries
Crosstab Query Wizard
CurrentUser property
custom functions
Index

[SYMBOL] [A ] [B] [C] [D] [E] [F] [G] [H] [I] [J]
[K] [L] [M] [N] [O] [P] [Q] [R] [S] [T] [U] [V ] [W]
[X] [Z]

data changes
cascading deletes
Data Definition query
data entry
data sources
data summaries
data transfer
Database Splitter utility
database splitting

database window
groups 2nd

objects

plain
databases
appending across

compacting

size

size limits
datatypes
DateDiff function

dates
functions and

header

deleting
shortcuts
descriptions
desktop shortcuts to databases

dialog boxes
Conditional Formatting

Edit Relationships

New Query

Sorting and Grouping


DLookup function
Document Compare utility (Word)

domain aggregate functions


aggregate functions and
dow nloading files
Index

[SYMBOL] [A ] [B] [C] [D] [E] [F] [G] [H] [I] [J]
[K] [L] [M] [N] [O] [P] [Q] [R] [S] [T] [U] [V ] [W]
[X] [Z]

Edit Relationships dialog box


embedded reports
Enter event

entering
text
additional

entering text
additional

tab order
Enterprise Manager
enumerations

events
Before Update

Enter

forms

MouseMove

Excel
functions 2nd 3rd 4th

FV function

importing from 2nd 3rd 4th 5th

macro recorder

Paste Special Transpose feature 2nd

w orksheet imports

Excel data
importing
into separate tables
Execute method
Export Text Wizard

exporting
to XML 2nd 3rd 4th 5th 6th 7th 8th 9th 10th 11th 12th
expressions
subtotals
EZ Application Generator
Index

[SYMBOL] [A ] [B] [C] [D] [E] [F] [G] [H] [I] [J]
[K] [L] [M] [N] [O] [P] [Q] [R] [S] [T] [U] [V ] [W]
[X] [Z]

Favorites group

fields
AutoNumber
custom values

default values

Number

number fields

Text
Find Unmatched Query w izard
focus
formatting

forms
custom controls

events

keyboard shortcuts

lists

personalization

Window s Media Player


frmKeyboardShortcuts form
function keys

functions
CreateObject

creating

custom

dates

DLookup

domain aggregate

Excel 2nd 3rd 4th 5th

ISNULL

Len
Mid
FV function (Excel)
Index

[SYMBOL] [A ] [B] [C] [D] [E] [F] [G] [H] [I] [J]
[K] [L] [M] [N] [O] [P] [Q] [R] [S] [T] [U] [V ] [W]
[X] [Z]

graphics
groups

adding

database w indow

Favorites

macros
Index

[SYMBOL] [A ] [B] [C] [D] [E] [F] [G] [H] [I] [J]
[K] [L] [M] [N] [O] [P] [Q] [R] [S] [T] [U] [V ] [W]
[X] [Z]

hiding objects

HTML
source code
Index

[SYMBOL] [A ] [B] [C] [D] [E] [F] [G] [H] [I] [J]
[K] [L] [M] [N] [O] [P] [Q] [R] [S] [T] [U] [V ] [W]
[X] [Z]

images

importing
Excel
noncontiguous data

separate tables

single table

XML data 2nd 3rd 4th 5th 6th 7th 8th 9th
In operator 2nd
inner joins
Insert operation (SQL)
Insert statement

inserting
apostrophes and
insertion point
intellectual property
Interval property
ISNULL function
Index

[SYMBOL] [A ] [B] [C] [D] [E] [F] [G] [H] [I] [J]
[K] [L] [M] [N] [O] [P] [Q] [R] [S] [T] [U] [V ] [W]
[X] [Z]

Join clause

joins
left

right
junction tables
Index

[SYMBOL] [A ] [B] [C] [D] [E] [F] [G] [H] [I] [J]
[K] [L] [M] [N] [O] [P] [Q] [R] [S] [T] [U] [V ] [W]
[X] [Z]

keyboard shortcuts

data entry
keystrokes
Index

[SYMBOL] [A ] [B] [C] [D] [E] [F] [G] [H] [I] [J]
[K] [L] [M] [N] [O] [P] [Q] [R] [S] [T] [U] [V ]
[W] [X] [Z]

left join
Len function
libraries creating code libraries
Like operator
Limit To List property
line numbers in reports
lines in reports

listboxes
populating

Union query and


lists 2nd

items

Limit To List property


popularity
locking records
log table
low ercase text
Index

[SYMBOL] [A ] [B] [C] [D] [E] [F] [G] [H] [I] [J]
[K] [L] [M ] [N] [O] [P] [Q] [R] [S] [T] [U] [V ]
[W] [X] [Z]

macro recorder (Word

macros
AutoExec
personalization and

AutoKeys

conditional actions

groups

Outlook
main form

many-to-many relationships
exporting to XML and
method
Mid function

MouseMove
OnTimer
clock and
MouseMove event
movie playing
MSXML parser

XML and

XSLT transformation and

multiple users
record locking

time-out feature

mouse and

usernames

MySQL
Access as front end 2nd 3rd 4th

linking to tables

tools installation
Index

[SYMBOL] [A ] [B] [C] [D] [E] [F] [G] [H] [I] [J]
[K] [L] [M] [N] [O] [P] [Q] [R] [S] [T] [U] [V ] [W]
[X] [Z]

naming conventions 2nd


navigation
navigation form
netw ork drive
New Query dialog box
New Data argument
Not operator
null values

number fields
defaults

nulls in
Index

[SYMBOL] [A ] [B] [C] [D] [E] [F] [G] [H] [I] [J]
[K] [L] [M] [N] [O] [P] [Q] [R] [S] [T] [U] [V ] [W]
[X] [Z]

object-use log

objects
descriptions

groups

multiple groups

shortcuts

unused
ODBC

On Timer event
clocks and

slideshow s and
On Timer property
OnTimer
MouseMove
slideshow s

open

Timer
open event

operators
In

Like

Not
Options dialog box
Or-based criteria
outer joins

Outlook
macros

sending Access data


Index

[SYMBOL] [A ] [B] [C] [D] [E] [F] [G] [H] [I] [J]
[K] [L] [M] [N] [O] [P] [Q] [R] [S] [T] [U] [V ]
[W] [X] [Z]

Page Break controls


page count
Page Dow n key
Page Up key
pages
pass-through queries
passw ords in code
Paste
Paste Special Transpose feature (Excel)

personalization
preferences
Picture property
plain database w indow

populating
sorting
multiple sources and
populating lists 2nd 3rd 4th

alphabetically

Union query and

preferences
applying
Prefix Characters property
printing reports and closing
processes, length

properties
CurrentUser

hidden objects and

Limit To List

On Timer

Picture
Index

[SYMBOL] [A ] [B] [C] [D] [E] [F] [G] [H] [I] [J]
[K] [L] [M] [N] [O] [P] [Q] [R] [S] [T] [U] [V ] [W]
[X] [Z]

queries
Action

Crosstab

Data Definition

functions

pass-through

regular expressions in

Select

Union queries

XML exports and

query grid
And-based criteria

Or-based criteria

sorting on any character 2nd 3rd 4th 5th 6th 7th 8th 9th 10th
Index

[SYMBOL] [A ] [B] [C] [D] [E] [F] [G] [H] [I] [J]
[K] [L] [M] [N] [O] [P] [Q] [R] [S] [T] [U] [V ] [W]
[X] [Z]

randomly sorting records


read-only command-line sw itch

records
Admin user

grouping

locking

random

separate sorted
alphabetically

sorting
separate alphabetically
unmatched

updates
Records2Go
references
regular expressions in queries

relationships
cascading updates

many-to-many
junction tables
remember

reports
embedded

line numbers

lines

w hitespace
Response argument
RIGHT JOIN
right join
Rnd function
Row Source
Row Source property
row s
RunCode action
Running Sum
Running Sum property
running sums
Index

[SYMBOL] [A ] [B] [C] [D] [E] [F] [G] [H] [I] [J]
[K] [L] [M] [N] [O] [P] [Q] [R] [S] [T] [U] [V ] [W]
[X] [Z]

sampling records
scripts

scrolling
Page Dow n key

security
backdoor building

hiding data

user edits

usernames
Select queries
SelStart

Tag, control details and


SelStart property
sending Access data through Outlook 2nd 3rd 4th 5th
SendKeys statement
separate sorted records alphabetically 2nd 3rd
shaded lines in reports
shortcut keys

shortcuts
desktop to databases

keyboard shortcuts

objects
simple
slideshow s
smart tags
Snapshot View er
Snapshot View er Control
sorting 2nd 3rd

any character 2nd

Excel's Past Special Transpose feature 2nd 3rd


Sorting and Grouping dialog box
source code

split data
w orking w ith
splitting databases distributing split

SQL Server
aggregate functions

Enterprise Manager

ODBC and

scripts

stored procedures
startup
storage, preferences
stored procedures (SQL)
subroutines
subtotals 2nd

sums
subtotals
syntax-checking
system tables 2nd 3rd 4th
Index

[SYMBOL] [A ] [B] [C] [D] [E] [F] [G] [H] [I] [J]
[K] [L] [M] [N] [O] [P] [Q] [R] [S] [T] [U] [V ]
[W] [X] [Z]

tab controls
tab order

tables
copying betw een

creating
speed

MySQL

number of 2nd 3rd 4th

updates
Tag property

testing
third-party applications for 2nd
text
case conversion

length
Text fields
the

third-party
applications
EZ Application Generator

third-party applications
EZ Application Generator

MSXML parser

softw are testing

Total Access Analyzer


tiling
time
time-out feature

mouse and

user-determined
timeouts, overriding intervals
Timer event
timers

to XML
exporting
queries and
Top predicate
Total Access Analyzer
tracking object use

triggers
form events and
Index

[SYMBOL] [A ] [B] [C] [D] [E] [F] [G] [H] [I] [J]
[K] [L] [M] [N] [O] [P] [Q] [R] [S] [T] [U] [V ] [W]
[X] [Z]

unbound controls
UNC (Universal Naming Convention)

renaming computer

Union query
listbox population
unmatched records
unused objects

updates
controls

records

tables
uppercase text
usernames
users
editing data

process length
USys
Index

[SYMBOL] [A ] [B] [C] [D] [E] [F] [G] [H] [I] [J]
[K] [L] [M] [N] [O] [P] [Q] [R] [S] [T] [U] [V ] [W]
[X] [Z]

values
custom

VBA
conditional formatting and
versions
video

viewing
Snapshot View er Control and
Index

[SYMBOL] [A ] [B] [C] [D] [E] [F] [G] [H] [I] [J]
[K] [L] [M] [N] [O] [P] [Q] [R] [S] [T] [U] [V ] [W]
[X] [Z]

w atermarks
Web brow sers

file dow nload


Web pages
w hitespace in reports
w ildcards in queries
Window s Media Player

wizards
Export Text

Find Unmatched Query

Word
documents

table comparison and


w orks
WorksheetFunction property (Excel)
w orksheets (Excel)
Index

[SYMBOL] [A ] [B] [C] [D] [E] [F] [G] [H] [I] [J]
[K] [L] [M] [N] [O] [P] [Q] [R] [S] [T] [U] [V ] [W]
[X] [Z]

XML
exporting to 2nd 3rd 4th 5th 6th 7th 8th

importing data 2nd 3rd

MSXML parser and


XSLT transformation
Index

[SYMBOL] [A ] [B] [C] [D] [E] [F] [G] [H] [I] [J]
[K] [L] [M] [N] [O] [P] [Q] [R] [S] [T] [U] [V ] [W]
[X] [Z]

zero-length strings

You might also like