Access Hacks - by Ken Bluttman
Access Hacks - by Ken Bluttman
By Ken Bluttman
...............................................
Publisher: O'Reilly
Pub Date: April 2005
ISBN: 0-596-00924-0
Pages: 352
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
Index
Credits
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 .
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 .
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 2 , Tables
C hapter 8 ,Programming
Plain text
I talics
Constant width
Color
You s hould pay s pec ial attention to notes s et apart from the text
with the following ic ons :
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
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)
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://hac ks .oreilly.c om
1. Core Access
Sec tion 1 .1 . H ac ks 1 1 2
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.
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 .
Forms
Spec ify the opening form, how it's dis played, and what
func tionality it inc ludes
D ata s ourc es
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
C trl- A lt-
I ns ert the default value for the field.
s pac ebar
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…
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.
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.
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.
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.
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.
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
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.
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:
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 !
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:
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
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 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 .
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.
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 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.
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.
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.
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 .
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 .
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!
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.
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.
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.
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
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.
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.
?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.
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.
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.
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.
2.5.1. An Alternative
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!
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).
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.
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):
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.
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.
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
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.
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
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.
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:
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.
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.
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.
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.
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.
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 .
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.
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:
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 .
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.
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 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.
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
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.
Give users a chance to review their edits bef ore they save a
record.
Give users the time and date, even f or more than one time zone.
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.
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.
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 .
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 .
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.
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
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
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.
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)
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 .
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.
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
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.
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]
=[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.
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
([txtCT2004]-[txtCT2003])/[txtCT2003]>0.2
([txtCT2004]-[txtCT2003])/[txtCT2003]<=0.2 And
([txtCT2004]-[txtCT2003])/[txtCT2003]>0.15
([txtCT2004]-[txtCT2003])/[txtCT2003]<=0.15 And _
([txtCT2004]-[txtCT2003])/[txtCT2003]>0.1
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.
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 .
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
Picture Type
P ic ture A lignment
P ic ture P ages
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.
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.
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()
'
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
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.
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:
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.
Application.CurrentProjec
End Sub
Steve Huff
Hack 36. Put Line Numbers on a Report
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.
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
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.
H ac k 4 6 . G et A ll C ombinations of D ata
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 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.
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:
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 .
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.
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
Patient
Age
T he name Gary fits in the P atient text field. N ow, look at this
s tatement:
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 :
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:
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.
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 - 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.
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:
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;
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.
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 .
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 .
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 ).
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 .
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 ?
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.
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.
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.
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 .
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 .
When nulls are mixed in with valid data, incorrect results can
occur. Here are some guidelines to tame the beast.
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 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.
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 :
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:
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:
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:
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 .
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).
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 .
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.
Michael Schmalz
Hack 48. Use a Custom Function in a
Query
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 :
End Function
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])
Michael Schmalz
Hack 49. Create Access Tables with SQL
Server Scripts
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.
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 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.
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.
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.
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}$"
(PN|P)
[0-9][0-9]
[0-9]{3,4}
[A-Z]{5,6}
Steve Huff
6. Multiuser Issues
Sec tion 6 .1 . H ac ks 5 5 5 8
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.
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.
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.
Andrea Mos s
Hack 56. Distribute a Split Database with
Predefined Table Links
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 .
T he s yntax for us ing SUBST requires the new drive letter and the
path it is s et to, like this :
SUBST S: C:\Clients\XYZ_Corp
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
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:
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
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.
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 - 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.
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 - 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.
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.
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.
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:
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
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
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.
Andrea Mos s
7. External Programs and
Data
Sec tion 7 .1 . H ac ks 5 9 7 1
H ac k 6 0 . U s e E xc el to Reorient A c c es s D ata
H ac k 6 8 . U s e A c c es s as a Front E nd to M ySQ L
A standard import lets you get only one data range at a time.
Here are a couple of workarounds to get you more.
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.
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.
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.
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.
Steve Huff
Hack 62. Use Word to Compare Data in
Two Access Tables
Andrea Mos s
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 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.
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 .
<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 .
<xsl:template match="@*">
<xsl:element name="{local-name(.)}" namespace="{namespace-uri(..
<xsl:value-of select="."/>
</xsl:element>
</xsl:template>
</xsl:stylesheet>
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 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 :
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.
<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>
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.
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 .
<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>
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.
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 :
End Sub
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
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
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
Tap into the Word object library to copy A ccess data directly
into a Word document.
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
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
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.
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.
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 .
AutoNumber
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.
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.
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.
ReportRs.MoveFirst
EmailRS.Close
Set EmailRS = Nothing
ReportRs.MoveNext
Wend
ReportRs.Close
Set ReportRs = Nothing
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.
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 .
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
ReportRs.MoveNext
Wend
Set db = Nothing
End Sub
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 .
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 .
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
'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
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 .
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.
Kirk Lamb
8. Programming
Sec tion 8 .1 . H ac ks 7 2 9 1
H ac k 8 8 . U s e C us tom E numerations
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.
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.
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:
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
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.
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.
Reduce the amount of code you enter and still get the same
results.
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
Sub get_Domain_Sum( )
Dim amount As Single
amount = DSum("[Amount]", "Invoices", "[InvoiceDate] = #12/10/04
MsgBox amount
End Sub
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 :
DAvg
DCount
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.
DSum
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
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 .
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
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
get_state_records "MA"
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
"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.
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
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:
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 .
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.
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
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
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 :
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.
Stop users f rom being able to hold down the Shif t key to get to
the database window.
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.
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
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 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
SysCmd acSysCmdClearStatus
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
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
conn.CommandTimeout = 0
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 ).
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 .
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 .
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.
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
ssql = "Alter Table " & tbl & " Add Column RandomSort Long"
'may already have field so trap error
On Error Resume Next
conn.Execute ssql
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 .
Sub list_control_properties()
Dim form_name As String
Dim ctl As Control
Dim list_type As Integer
Dim prop_num As Integer
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
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).
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
xmlobj.Load "C:\Employees.xml"
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.
I f s pace_flag is true
I f s pace_flag is fals 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 .
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:
With a c hanged extens ion, now the file is rec ognized as an add-
in, as s hown in Figure 8 - 3 4 .
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.
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.
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.
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"
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].
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
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.
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 .
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
Use predesigned test data that matches your tables and f ields.
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.
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.
.Attributes(1).nodeValue = Me.txtEmployeeName
xmlobj.documentElement.childNodes(record_num) _
.Attributes(2).nodeValue = Me.txtHireDate
xmlobj.Save file_name
reload_file
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
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
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):
End Sub
9.5.5. Deleting a Record
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):
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
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.
Me.WebBrowser1.Navigate URL:=Me.website
MsgBox Me.WebBrowser1.Document.documentElement.innerhtml
"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.
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.
Quickly review web inf ormation when you need to see it.
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.
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
applications
personalization
third-party
MSXML parser
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]
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]
cascading
updates
relationships and
cascading updates
case conversion
characters 2nd 3rd 4th 5th 6th
cleanup, databases
clocks
code
arguments and
highlighting and
Conditional Formatting dialog box
conditional macro actions
conditional subtotals 2nd 3rd 4th 5th
constants
controls
custom
information about
listboxes
populating
sorting
creating
tables
outside Access
[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
[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]
entering
text
additional
entering text
additional
tab order
Enterprise Manager
enumerations
events
Before Update
Enter
forms
MouseMove
Excel
functions 2nd 3rd 4th
FV function
macro recorder
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
functions
CreateObject
creating
custom
dates
DLookup
domain aggregate
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
items
[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]
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
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]
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
[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]
personalization
preferences
Picture property
plain database w indow
populating
sorting
multiple sources and
populating lists 2nd 3rd 4th
alphabetically
preferences
applying
Prefix Characters property
printing reports and closing
processes, length
properties
CurrentUser
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
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]
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
shortcuts
desktop to databases
keyboard shortcuts
objects
simple
slideshow s
smart tags
Snapshot View er
Snapshot View er Control
sorting 2nd 3rd
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
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
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
wizards
Export Text
Word
documents
[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
[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
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 .
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 .
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 2 , Tables
C hapter 8 ,Programming
Plain text
I talics
Constant width
Color
You s hould pay s pec ial attention to notes s et apart from the text
with the following ic ons :
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
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)
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://hac ks .oreilly.c om
1. Core Access
Sec tion 1 .1 . H ac ks 1 1 2
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.
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 .
Forms
Spec ify the opening form, how it's dis played, and what
func tionality it inc ludes
D ata s ourc es
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
C trl- A lt-
I ns ert the default value for the field.
s pac ebar
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…
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.
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.
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.
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.
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.
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
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.
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:
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 !
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:
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
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 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 .
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.
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 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.
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.
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.
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 .
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 .
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!
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.
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.
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.
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
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.
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.
?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.
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.
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.
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.
2.5.1. An Alternative
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!
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).
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.
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):
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.
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.
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
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.
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
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.
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:
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.
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.
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.
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.
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.
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 .
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.
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:
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 .
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.
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 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.
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
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.
Give users a chance to review their edits bef ore they save a
record.
Give users the time and date, even f or more than one time zone.
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.
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.
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 .
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 .
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.
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
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
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.
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)
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 .
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.
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
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.
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]
=[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.
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
([txtCT2004]-[txtCT2003])/[txtCT2003]>0.2
([txtCT2004]-[txtCT2003])/[txtCT2003]<=0.2 And
([txtCT2004]-[txtCT2003])/[txtCT2003]>0.15
([txtCT2004]-[txtCT2003])/[txtCT2003]<=0.15 And _
([txtCT2004]-[txtCT2003])/[txtCT2003]>0.1
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.
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 .
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
Picture Type
P ic ture A lignment
P ic ture P ages
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.
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.
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()
'
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
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.
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:
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.
Application.CurrentProjec
End Sub
Steve Huff
Hack 36. Put Line Numbers on a Report
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.
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
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.
H ac k 4 6 . G et A ll C ombinations of D ata
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 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.
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:
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 .
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.
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
Patient
Age
T he name Gary fits in the P atient text field. N ow, look at this
s tatement:
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 :
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:
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.
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 - 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.
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:
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;
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.
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 .
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 .
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 ).
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 .
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 ?
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.
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.
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.
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 .
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 .
When nulls are mixed in with valid data, incorrect results can
occur. Here are some guidelines to tame the beast.
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 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.
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 :
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:
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:
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:
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 .
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).
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 .
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.
Michael Schmalz
Hack 48. Use a Custom Function in a
Query
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 :
End Function
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])
Michael Schmalz
Hack 49. Create Access Tables with SQL
Server Scripts
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.
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 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.
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.
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.
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}$"
(PN|P)
[0-9][0-9]
[0-9]{3,4}
[A-Z]{5,6}
Steve Huff
6. Multiuser Issues
Sec tion 6 .1 . H ac ks 5 5 5 8
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.
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.
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.
Andrea Mos s
Hack 56. Distribute a Split Database with
Predefined Table Links
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 .
T he s yntax for us ing SUBST requires the new drive letter and the
path it is s et to, like this :
SUBST S: C:\Clients\XYZ_Corp
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
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:
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
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.
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 - 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.
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 - 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.
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.
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.
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:
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
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
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.
Andrea Mos s
7. External Programs and
Data
Sec tion 7 .1 . H ac ks 5 9 7 1
H ac k 6 0 . U s e E xc el to Reorient A c c es s D ata
H ac k 6 8 . U s e A c c es s as a Front E nd to M ySQ L
A standard import lets you get only one data range at a time.
Here are a couple of workarounds to get you more.
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.
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.
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.
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.
Steve Huff
Hack 62. Use Word to Compare Data in
Two Access Tables
Andrea Mos s
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 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.
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 .
<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 .
<xsl:template match="@*">
<xsl:element name="{local-name(.)}" namespace="{namespace-uri(..
<xsl:value-of select="."/>
</xsl:element>
</xsl:template>
</xsl:stylesheet>
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 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 :
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.
<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>
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.
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 .
<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>
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.
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 :
End Sub
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
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
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
Tap into the Word object library to copy A ccess data directly
into a Word document.
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
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
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.
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.
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 .
AutoNumber
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.
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.
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.
ReportRs.MoveFirst
EmailRS.Close
Set EmailRS = Nothing
ReportRs.MoveNext
Wend
ReportRs.Close
Set ReportRs = Nothing
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.
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 .
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
ReportRs.MoveNext
Wend
Set db = Nothing
End Sub
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 .
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 .
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
'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
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 .
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.
Kirk Lamb
8. Programming
Sec tion 8 .1 . H ac ks 7 2 9 1
H ac k 8 8 . U s e C us tom E numerations
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.
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.
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:
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
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.
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.
Reduce the amount of code you enter and still get the same
results.
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
Sub get_Domain_Sum( )
Dim amount As Single
amount = DSum("[Amount]", "Invoices", "[InvoiceDate] = #12/10/04
MsgBox amount
End Sub
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 :
DAvg
DCount
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.
DSum
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
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 .
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
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
get_state_records "MA"
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
"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.
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
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:
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 .
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.
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
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
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 :
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.
Stop users f rom being able to hold down the Shif t key to get to
the database window.
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.
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
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 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
SysCmd acSysCmdClearStatus
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
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
conn.CommandTimeout = 0
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 ).
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 .
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 .
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.
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
ssql = "Alter Table " & tbl & " Add Column RandomSort Long"
'may already have field so trap error
On Error Resume Next
conn.Execute ssql
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 .
Sub list_control_properties()
Dim form_name As String
Dim ctl As Control
Dim list_type As Integer
Dim prop_num As Integer
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
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).
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
xmlobj.Load "C:\Employees.xml"
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.
I f s pace_flag is true
I f s pace_flag is fals 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 .
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:
With a c hanged extens ion, now the file is rec ognized as an add-
in, as s hown in Figure 8 - 3 4 .
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.
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.
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.
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"
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].
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
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.
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 .
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
Use predesigned test data that matches your tables and f ields.
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.
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.
.Attributes(1).nodeValue = Me.txtEmployeeName
xmlobj.documentElement.childNodes(record_num) _
.Attributes(2).nodeValue = Me.txtHireDate
xmlobj.Save file_name
reload_file
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
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
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):
End Sub
9.5.5. Deleting a Record
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):
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
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.
Me.WebBrowser1.Navigate URL:=Me.website
MsgBox Me.WebBrowser1.Document.documentElement.innerhtml
"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.
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.
Quickly review web inf ormation when you need to see it.
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.
[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
applications
personalization
third-party
MSXML parser
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]
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]
cascading
updates
relationships and
cascading updates
case conversion
characters 2nd 3rd 4th 5th 6th
cleanup, databases
clocks
code
arguments and
highlighting and
Conditional Formatting dialog box
conditional macro actions
conditional subtotals 2nd 3rd 4th 5th
constants
controls
custom
information about
listboxes
populating
sorting
creating
tables
outside Access
[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
[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]
entering
text
additional
entering text
additional
tab order
Enterprise Manager
enumerations
events
Before Update
Enter
forms
MouseMove
Excel
functions 2nd 3rd 4th
FV function
macro recorder
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
functions
CreateObject
creating
custom
dates
DLookup
domain aggregate
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
items
[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]
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
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]
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
[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]
personalization
preferences
Picture property
plain database w indow
populating
sorting
multiple sources and
populating lists 2nd 3rd 4th
alphabetically
preferences
applying
Prefix Characters property
printing reports and closing
processes, length
properties
CurrentUser
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
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]
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
shortcuts
desktop to databases
keyboard shortcuts
objects
simple
slideshow s
smart tags
Snapshot View er
Snapshot View er Control
sorting 2nd 3rd
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
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
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
wizards
Export Text
Word
documents
[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
[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