<?xml version='1.0' encoding='UTF-8'?><?xml-stylesheet href="http://www.blogger.com/styles/atom.css" type="text/css"?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:georss='http://www.georss.org/georss' xmlns:gd='http://schemas.google.com/g/2005' xmlns:thr='http://purl.org/syndication/thread/1.0'><id>tag:blogger.com,1999:blog-8134759511126815909</id><updated>2012-01-29T13:45:48.571-05:00</updated><category term='Opinion'/><category term='Hookom'/><category term='Data Warehousing in Access'/><category term='New Samples'/><category term='Tejpal'/><category term='Featured Samples'/><category term='Access 101'/><category term='Access 2010'/><category term='Hibbs'/><category term='Quick Tips'/><category term='Problems With Repeated Columns'/><category term='* Index to Access 101 *'/><category term='Tips and Tricks'/><category term='Web Databases'/><category term='Domain Functions'/><title type='text'>Roger's Access Blog</title><subtitle type='html'>Thoughts, opinions, samples, tips, and tricks about Microsoft Access</subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://rogersaccessblog.blogspot.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8134759511126815909/posts/default?max-results=100'/><link rel='alternate' type='text/html' href='http://rogersaccessblog.blogspot.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><link rel='next' type='application/atom+xml' href='http://www.blogger.com/feeds/8134759511126815909/posts/default?start-index=101&amp;max-results=100'/><author><name>Roger's Access Blog</name><uri>http://www.blogger.com/profile/12265719129831415014</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_JYohVQdgpwU/Scu5h2VvtBI/AAAAAAAAAQc/pr3JDoLaqnQ/S220/avi_Roger_1.JPG'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>198</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>100</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-8134759511126815909.post-764831620904628568</id><published>2012-01-25T06:41:00.001-05:00</published><updated>2012-01-25T06:41:09.934-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Access 2010'/><category scheme='http://www.blogger.com/atom/ns#' term='Access 101'/><title type='text'>Now() Function in Default Value Stores the Wrong Time</title><content type='html'>&lt;p&gt;In a previous post (&lt;a href="http://rogersaccessblog.blogspot.com/2012/01/date-stuffusing-functions.html" target="_blank"&gt;&lt;u&gt;Date Stuff – Using the functions&lt;/u&gt;&lt;/a&gt;), I discussed, among other things, using built in Access functions (specifically Date() and Now()) in the default value of a field in a table.&lt;/p&gt; &lt;p&gt;The Date() function is generally safe to use, but the Now() function is problematic. Now() in the default value of your field WILL store the wrong time. If your concern is to store an accurate time of when the record was saved, this is a problem.&lt;/p&gt; &lt;p&gt;Let me illustrate.&amp;nbsp; Suppose I have a simple table like so:&lt;/p&gt; &lt;p&gt;&lt;a href="http://lh3.ggpht.com/-K8qAtoaH2c4/Tx_qRfMCR2I/AAAAAAAAA48/v_Z8kw-O2ZI/s1600-h/image3.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://lh6.ggpht.com/-1MhCm6-fSdw/Tx_qRrxsFpI/AAAAAAAAA5E/vtKqcAvv4N0/image_thumb1.png?imgmax=800" width="350" height="238"&gt;&lt;/a&gt;&lt;/p&gt; &lt;p&gt;Now, I'm going to add a field called CreatedDate, and use the Now() function in the Default Value of the field.&lt;/p&gt; &lt;p&gt;&lt;a href="http://lh6.ggpht.com/-Xr1iSyFiWZM/Tx_qR2cjSrI/AAAAAAAAA5M/tKw82-0TfX8/s1600-h/image4.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://lh4.ggpht.com/-JqXAjkvoPQY/Tx_qSSyvPfI/AAAAAAAAA5U/qsePZKxSdEA/image_thumb1%25255B1%25255D.png?imgmax=800" width="470" height="239"&gt;&lt;/a&gt;&lt;/p&gt; &lt;p&gt;The New Record, that is, the one at the bottom with the asterisk at the far left, has not yet been created.&amp;nbsp; The date and time displayed is the time that I opened the table (or form, if the form was based on that table).&amp;nbsp; &lt;/p&gt; &lt;p&gt;However, suppose I take a few minutes before I actually enter anything into the new record.&lt;/p&gt; &lt;p&gt;&lt;a href="http://lh4.ggpht.com/-trpG7hqwCyM/Tx_qSuguBEI/AAAAAAAAA5c/4ATovIAw53M/s1600-h/image8.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://lh5.ggpht.com/-jx1RzTeSZKM/Tx_qS-nyi0I/AAAAAAAAA5k/hin_f_cCZDU/image_thumb3.png?imgmax=800" width="536" height="341"&gt;&lt;/a&gt;&lt;/p&gt; &lt;p&gt;Once I begin typing in the record, it is created (but not yet saved), but notice that the time that I opened the table is still displayed, even though the actual time (as shown in the task bar) is 6 minutes later.&lt;/p&gt; &lt;p&gt;By the time I get around to actually saving the record (by moving to the new New Record) another 5 minutes has passed.&lt;/p&gt; &lt;p&gt;&lt;a href="http://lh6.ggpht.com/-Eg0J6ypoZG0/Tx_qTGi6YjI/AAAAAAAAA5s/YN7FEKoGiVo/s1600-h/image12.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://lh3.ggpht.com/-lTN6AXqGyZw/Tx_qTR2uPjI/AAAAAAAAA50/qxVxlpFp2YQ/image_thumb5.png?imgmax=800" width="549" height="347"&gt;&lt;/a&gt;&lt;/p&gt; &lt;p&gt;So the record was saved at 6:29, but the time stored in the field is 6:18.&amp;nbsp; Not only that, but the next New Record displays the time that the previous record was created.&lt;/p&gt; &lt;p&gt;&lt;font size="3"&gt;&lt;strong&gt;Solutions&lt;/strong&gt;&lt;/font&gt;&lt;/p&gt; &lt;p&gt;So what do you do to get an accurate time in the CreatedDate field?&amp;nbsp; There are two solutions depending on which version of Access you're using: Access 2007 (and previous) or Access 2010.&lt;/p&gt; &lt;p&gt;&lt;font size="3"&gt;&lt;strong&gt;Access 2007 and Previous&lt;/strong&gt;&lt;/font&gt;&lt;/p&gt; &lt;p&gt;In Access 2007 and before, it cannot be done at the table level.&amp;nbsp; It must be done in a form.&amp;nbsp; So in Design View of your form, create a code procedure in the BeforeUpdate Event of the form:&lt;/p&gt; &lt;p&gt;&lt;a href="http://lh4.ggpht.com/-otRDcW_13ig/Tx_qTuiamTI/AAAAAAAAA58/xm4fIJmi2o0/s1600-h/image17.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://lh5.ggpht.com/-bE76-v4X1WI/Tx_qULuZ-hI/AAAAAAAAA6E/NGrXl5IKcZY/image_thumb8.png?imgmax=800" width="453" height="552"&gt;&lt;/a&gt;&lt;/p&gt; &lt;p&gt;&lt;font face="Courier New"&gt;Private Sub Form_BeforeUpdate(Cancel As Integer)&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; If IsNull(CreatedDate) Then&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; CreatedDate = Now()&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; End If&lt;br&gt;End Sub&lt;/font&gt;&lt;/p&gt; &lt;p&gt;&lt;font size="3"&gt;&lt;strong&gt;Access 2010 &lt;/strong&gt;&lt;/font&gt;&lt;/p&gt; &lt;p&gt;With the introduction of Access 2010, we how have Data Macros (essentially triggers) that work at the table level.&lt;/p&gt; &lt;p&gt;&lt;a href="http://lh6.ggpht.com/-mXB1EHHwyaQ/Tx_qUeodLmI/AAAAAAAAA6M/bZzPs5Ew-e8/s1600-h/image25.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://lh3.ggpht.com/-5rF11MRJkgk/Tx_qUnrR_XI/AAAAAAAAA6U/04QpB3wtUsA/image_thumb12.png?imgmax=800" width="536" height="367"&gt;&lt;/a&gt;&lt;/p&gt; &lt;p&gt;On the Tables tab, select the Before Change Event and create the following data macro:&lt;/p&gt; &lt;p&gt;&lt;a href="http://lh5.ggpht.com/-_M1h9tW4jro/Tx_qU2BaSVI/AAAAAAAAA6c/OS2eQa5E3Ds/s1600-h/image29.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://lh4.ggpht.com/-N4OFD5LwWZM/Tx_qVSW77pI/AAAAAAAAA6k/DOw6CBC73VE/image_thumb14.png?imgmax=800" width="417" height="247"&gt;&lt;/a&gt;&lt;/p&gt; &lt;p&gt;Now, you will save the actual time the record was saved.&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8134759511126815909-764831620904628568?l=rogersaccessblog.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://rogersaccessblog.blogspot.com/feeds/764831620904628568/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8134759511126815909&amp;postID=764831620904628568' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8134759511126815909/posts/default/764831620904628568'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8134759511126815909/posts/default/764831620904628568'/><link rel='alternate' type='text/html' href='http://rogersaccessblog.blogspot.com/2012/01/now-function-in-default-value-stores.html' title='Now() Function in Default Value Stores the Wrong Time'/><author><name>Roger's Access Blog</name><uri>http://www.blogger.com/profile/12265719129831415014</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_JYohVQdgpwU/Scu5h2VvtBI/AAAAAAAAAQc/pr3JDoLaqnQ/S220/avi_Roger_1.JPG'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://lh6.ggpht.com/-1MhCm6-fSdw/Tx_qRrxsFpI/AAAAAAAAA5E/vtKqcAvv4N0/s72-c/image_thumb1.png?imgmax=800' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8134759511126815909.post-3229516603313381788</id><published>2012-01-24T09:08:00.000-05:00</published><updated>2012-01-24T09:08:28.963-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='New Samples'/><category scheme='http://www.blogger.com/atom/ns#' term='Tejpal'/><title type='text'>New Sample: Report_TrainControlChart</title><content type='html'>by AD Tejpal&lt;br /&gt;&lt;br /&gt;This sample db demonstrates time distance charting of trains on a double line route. Time axis, covering 24 hours is depicted horizontally, while various railway stations are displayed along the vertical axis. Vertical lines across chart area identify half hour time intervals. For convenient legibility, time lines at two hour intervals have darker shade. &lt;br /&gt;&lt;br /&gt;Positioning of stations is in proportion to their respective distance from terminal station as compared to overall length of route. Major stations have adequate loop lines, while the minor ones have no facility for overtaking. &lt;br /&gt;&lt;br /&gt;Each pair of trains is assigned a distinct color (in table T_Trains). T1/T2 (Red) is a slow train, stopping at all stations. T3/T4 (Blue) is faster and stops only at major stations en-route. T5/T6 (Green) is a super fast pair, stopping at only at some of the major stations en-route. Halts of slower trains are devised in such a manner as to permit un-impeded overtake by faster ones. Two local trains (L1 and L2) performing multiple U trips are also demonstrated. Start and end points of each train have been made prominent by placement of a marker (a small circle - looks like a big dot) in a color matching that of the train in question. &lt;br /&gt;&lt;br /&gt;Train control chart can be viewed in three alternative styles as follows, depending upon user's choice via option group:&lt;br /&gt;&lt;br /&gt;(a) Whole route at a glance.&lt;br /&gt;&lt;br /&gt;(b) Zoomed view of first half of route.&lt;br /&gt;&lt;br /&gt;(c) Zoomed view of second half of route. &lt;br /&gt;&lt;br /&gt;Note:&lt;br /&gt;&lt;br /&gt;(a) If more stations get added (in table T_Stations), proportionate inter-station spacing in the chart will get adjusted automatically so as to suit the available chart height.&lt;br /&gt;&lt;br /&gt;(b) Chart height is set as 8100 twips via report level constant mTotChartHt. This is found to work fine in Access 2003 on Win XP (Paper size: A4, Default printer: HP Laserjet M1005). If, depending upon local set up, it is found that the report tends to spill over beyond one page, the value of this constant can be adjusted downwards suitably. For complete chart to be depicted, it is important that report (R_TrainControlChart) remains confined to a single page. &lt;br /&gt;&lt;br /&gt;Version: Access 2000 file format.&amp;nbsp;&amp;nbsp; References: DAO 3.6&lt;br /&gt;&lt;br /&gt;You can find the sample here: &lt;a href="http://www.rogersaccesslibrary.com/forum/topic581_post599.html"&gt;http://www.rogersaccesslibrary.com/forum/topic581_post599.html&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8134759511126815909-3229516603313381788?l=rogersaccessblog.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://rogersaccessblog.blogspot.com/feeds/3229516603313381788/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8134759511126815909&amp;postID=3229516603313381788' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8134759511126815909/posts/default/3229516603313381788'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8134759511126815909/posts/default/3229516603313381788'/><link rel='alternate' type='text/html' href='http://rogersaccessblog.blogspot.com/2012/01/new-sample-reporttraincontrolchart.html' title='New Sample: Report_TrainControlChart'/><author><name>Roger's Access Blog</name><uri>http://www.blogger.com/profile/12265719129831415014</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_JYohVQdgpwU/Scu5h2VvtBI/AAAAAAAAAQc/pr3JDoLaqnQ/S220/avi_Roger_1.JPG'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8134759511126815909.post-2647543447903198579</id><published>2012-01-20T07:30:00.001-05:00</published><updated>2012-01-23T06:33:15.386-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Access 101'/><title type='text'>Date Stuff – Useful Examples</title><content type='html'>&lt;p&gt;This post is the third in a 3-part series on Date functions in Access. If you have not read the first two posts, it might be worth while to do so.&lt;/p&gt; &lt;ol&gt; &lt;li&gt;&lt;a href="http://rogersaccessblog.blogspot.com/2012/01/date-stufffunctions.html" target="_blank"&gt;&lt;u&gt;Date Stuff – Functions&lt;/u&gt;&lt;/a&gt;  &lt;li&gt;&lt;a href="http://rogersaccessblog.blogspot.com/2012/01/date-stuffusing-functions.html" target="_blank"&gt;&lt;u&gt;Date Stuff – Using the functions&lt;/u&gt;&lt;/a&gt;  &lt;li&gt;&lt;strong&gt;Date Stuff – Useful Examples&lt;/strong&gt; &lt;strong&gt;(this post)&lt;/strong&gt; &lt;/li&gt;&lt;/ol&gt; &lt;p&gt;To see working examples, please see the following sample: &lt;a href="http://www.rogersaccesslibrary.com/forum/topic356.html" target="_blank"&gt;&lt;u&gt;DateStuff.mdb&lt;/u&gt;&lt;/a&gt; on my website: &lt;a href="http://www.RogersAccessLibrary.com"&gt;www.RogersAccessLibrary.com&lt;/a&gt;.&lt;/p&gt; &lt;p&gt;&lt;font size="3"&gt;&lt;strong&gt;Useful Examples&lt;/strong&gt;&lt;/font&gt;&lt;/p&gt; &lt;p&gt;As with most things in Access, there are multiple ways to do most each of the things below, so I'll list at least a couple of methods.&amp;nbsp; There may be more.&lt;/p&gt; &lt;p&gt;&lt;font size="3"&gt;First Day of the Current Month&lt;/font&gt;&lt;/p&gt; &lt;ul&gt; &lt;li&gt;&lt;font face="Courier New"&gt;DateSerial(DatePart("yyyy", Date()), DatePart("m", Date()),1)&lt;/font&gt;  &lt;li&gt;&lt;font face="Courier New"&gt;DateAdd("d",1-DatePart("d",Date()),Date())&lt;/font&gt;  &lt;li&gt;&lt;font face="Courier New"&gt;CDate(CStr(DatePart("m",Date())) &amp;amp; "/1")&lt;/font&gt;&lt;/li&gt;&lt;/ul&gt; &lt;p&gt;Used in a UDF (User Defined Function). Any of the above formulas can be substituted:&lt;/p&gt; &lt;p&gt;&lt;font face="Courier New"&gt;Function FirstofMonth(vdate As Date)&lt;/font&gt;&lt;font face="Courier New"&gt;&lt;br&gt;FirstofMonth = &lt;font face="Courier New"&gt;DateAdd("d",1-DatePart("d",Date()),Date())&lt;/font&gt; &lt;br&gt;End Function&lt;/font&gt;&lt;/p&gt; &lt;p&gt;&lt;font size="3"&gt;Last Day of the Current Month&lt;/font&gt;&lt;/p&gt; &lt;ul&gt; &lt;li&gt;&lt;font face="Courier New"&gt;DateSerial(DatePart("yyyy",Date()),DatePart("m",Date())+1,1)-1&lt;/font&gt;  &lt;li&gt;&lt;font face="Courier New"&gt;DateAdd("m",1,CDate(CStr(DatePart("m",Date())) &amp;amp; "/1"))-1&lt;/font&gt;&lt;/li&gt;&lt;/ul&gt; &lt;p&gt;Used in a UDF (User Defined Function). Any of the above formulas can be substituted:&lt;/p&gt; &lt;p&gt;&lt;font face="Courier New"&gt;Function LastofMonth(vdate As Date)&lt;br&gt;LastofMonth =&lt;font face="Courier New"&gt;DateAdd("m",1,CDate(CStr(DatePart("m",Date())) &amp;amp; "/1"))-1&lt;br&gt;&lt;/font&gt;End Function&lt;/font&gt;&lt;/p&gt; &lt;p&gt;&lt;font size="3"&gt;First Day of the Previous Month&lt;/font&gt;&lt;/p&gt; &lt;ul&gt; &lt;li&gt;&lt;font face="Courier New"&gt;DateSerial(DatePart("yyyy",Date()),DatePart("m",Date())-1,1)&lt;/font&gt;  &lt;li&gt;&lt;font face="Courier New"&gt;DateAdd("m",-1,CDate(CStr(DatePart("m",Date())) &amp;amp; "/1"))&lt;/font&gt;&lt;/li&gt;&lt;/ul&gt; &lt;p&gt;Used in a UDF (User Defined Function). Any of the above formulas can be substituted:&lt;/p&gt;&lt;font face="Courier New"&gt;Function FirstofPrevMonth(vdate As Date)&lt;br&gt;FirstofPrevMonth=DateAdd("d",1-DatePart("d",Date()),Date()) – 1 &lt;br&gt;End Function&lt;/font&gt;  &lt;p&gt;&lt;font size="3"&gt;Last Day of the Previous Month&lt;/font&gt;&lt;/p&gt; &lt;ul&gt; &lt;li&gt;&lt;font face="Courier New"&gt;DateSerial(DatePart("yyyy", Date()), DatePart("m",Date()),1)-1&lt;/font&gt;  &lt;li&gt;&lt;font face="Courier New"&gt;DateAdd("d",1-DatePart("d",Date()),Date()) - 1&lt;/font&gt;  &lt;li&gt;&lt;font face="Courier New"&gt;CDate(CStr(DatePart("m",Date())) &amp;amp; "/1") – 1&lt;/font&gt;&lt;/li&gt;&lt;/ul&gt; &lt;p&gt;Used in a UDF (User Defined Function). Any of the above formulas can be substituted:&lt;/p&gt; &lt;p&gt;&lt;font face="Courier New"&gt;Function LastofPrevMonth(vdate As Date)&lt;br&gt;LastofPrevMonth=DateAdd("d",1-DatePart("d",Date()),Date())–1 &lt;br&gt;End Function&lt;/font&gt;&lt;/p&gt; &lt;p&gt;&lt;font size="3"&gt;First Day of the Next Month&lt;/font&gt;&lt;/p&gt; &lt;ul&gt; &lt;li&gt;&lt;font face="Courier New"&gt;DateSerial(DatePart("yyyy",Date()),DatePart("m",Date())+1,1)&lt;/font&gt;  &lt;li&gt;&lt;font face="Courier New"&gt;DateAdd("m",1,CDate(CStr(DatePart("m",Date())) &amp;amp; "/1"))&lt;/font&gt;&lt;/li&gt;&lt;/ul&gt; &lt;p&gt;Used in a UDF (User Defined Function). Any of the above formulas can be substituted:&lt;/p&gt; &lt;p&gt;&lt;font face="Courier New"&gt;Function FirstofNextMonth(vdate As Date)&lt;br&gt;FirstofNextMonth = DateAdd("m",1,CDate(CStr(DatePart("m",Date()))&amp;amp; "/1"))&lt;br&gt;End Function&lt;/font&gt;&lt;/p&gt; &lt;p&gt;&lt;font size="3"&gt;Last Day of the Next Month&lt;/font&gt;&lt;/p&gt; &lt;ul&gt; &lt;li&gt;&lt;font face="Courier New"&gt;DateSerial(DatePart("yyyy",Date()),DatePart("m",Date())+2,1)-1&lt;/font&gt;  &lt;li&gt;&lt;font face="Courier New"&gt;DateAdd("m",1,CDate(CStr(DatePart("m",Date())+1) &amp;amp; "/1")-1)&lt;/font&gt;&lt;/li&gt;&lt;/ul&gt; &lt;p&gt;Used in a UDF (User Defined Function). Any of the above formulas can be substituted:&lt;/p&gt; &lt;p&gt;&lt;font face="Courier New"&gt;Function LastofNextMonth(vdate As Date)&lt;br&gt;LastofNextMonth = &lt;font face="Courier New"&gt;DateAdd("m",1,CDate(CStr(DatePart("m",Date())+1) &amp;amp; "/1")-1)&lt;br&gt;&lt;/font&gt;End Function&lt;/font&gt;&lt;/p&gt; &lt;p&gt;&lt;font size="3"&gt;First Day of This Week&lt;/font&gt;&lt;/p&gt; &lt;ul&gt; &lt;li&gt;&lt;font face="Courier New"&gt;DateDiff("d",Weekday(Date()-1,1),Date())&lt;/font&gt;  &lt;li&gt;&lt;font face="Courier New"&gt;Date()-Weekday(Date())+1&lt;/font&gt;&lt;/li&gt;&lt;/ul&gt; &lt;p&gt;Used in a UDF (User Defined Function). Any of the above formulas can be substituted:&lt;/p&gt; &lt;p&gt;&lt;font face="Courier New"&gt;Function FirstofWeek(vdate As Date)&lt;/font&gt;&lt;font face="Courier New"&gt;&lt;br&gt;FirstofWeek= &lt;font face="Courier New"&gt;DateDiff("d",Weekday(Date()-1,1),Date())&lt;/font&gt;&amp;nbsp; &lt;br&gt;End Function&lt;/font&gt;&lt;/p&gt; &lt;p&gt;&lt;font size="3"&gt;Last Day of This Week&lt;/font&gt;&lt;/p&gt; &lt;ul&gt; &lt;li&gt;&lt;font face="Courier New"&gt;DateDiff("d",Weekday(Date()-1,1),Date())+6&lt;/font&gt;  &lt;li&gt;&lt;font face="Courier New"&gt;Date()+(7-Weekday(Date()))&lt;/font&gt;&lt;/li&gt;&lt;/ul&gt; &lt;p&gt;Used in a UDF (User Defined Function). Any of the above formulas can be substituted:&lt;/p&gt; &lt;blockquote&gt; &lt;p&gt;&lt;font face="Courier New"&gt;Function LastOfWeek(vdate As Date)&lt;/font&gt;&lt;font face="Courier New"&gt;&lt;br&gt;LastOfWeek= &lt;font face="Courier New"&gt;DateDiff("d",Weekday(Date()-1,1),Date())+6&lt;/font&gt; &lt;br&gt;End Function&lt;/font&gt;&lt;/p&gt;&lt;/blockquote&gt; &lt;p&gt;&lt;font size="3"&gt;First Work Day of This Week&lt;/font&gt;&lt;/p&gt; &lt;ul&gt; &lt;li&gt;&lt;font face="Courier New"&gt;DateDiff("d",Weekday(Date()-1,2),Date())&lt;/font&gt;  &lt;li&gt;&lt;font face="Courier New"&gt;Date()-Weekday(Date())+2&lt;/font&gt;&lt;/li&gt;&lt;/ul&gt; &lt;p&gt;Used in a UDF (User Defined Function). Any of the above formulas can be substituted:&lt;/p&gt; &lt;p&gt;&lt;font face="Courier New"&gt;Function FirstWorkdayOfWeek(vdate As Date)&lt;/font&gt;&lt;font face="Courier New"&gt;&lt;br&gt;FirstWorkdayOfWeek= &lt;font face="Courier New"&gt;DateDiff("d",Weekday(Date()-1,2),Date())&lt;/font&gt; &lt;br&gt;&lt;/font&gt;&lt;font face="Courier New"&gt;End Function&lt;/font&gt;&lt;/p&gt; &lt;p&gt;&lt;font size="3"&gt;Last Work Day of This Week&lt;/font&gt;&lt;/p&gt; &lt;ul&gt; &lt;li&gt;&lt;font face="Courier New"&gt;DateDiff("d",Weekday(Date()-1,2),Date())+4&lt;/font&gt;  &lt;li&gt;&lt;font face="Courier New"&gt;Date()+(6-Weekday(Date()))&lt;/font&gt;&lt;/li&gt;&lt;/ul&gt; &lt;p&gt;Used in a UDF (User Defined Function). Any of the above formulas can be substituted:&lt;/p&gt; &lt;p&gt;&lt;font face="Courier New"&gt;Function LastWorkdayOfWeek(vdate As Date)&lt;/font&gt;&lt;font face="Courier New"&gt;&lt;br&gt;LastWorkdayOfWeek= &lt;font face="Courier New"&gt;DateDiff("d",Weekday(Date()-1,2),Date())+4&lt;/font&gt;&amp;nbsp; &lt;br&gt;End Function&lt;/font&gt;&lt;/p&gt; &lt;p&gt;&lt;font size="3"&gt;Number of Week Days&lt;/font&gt;&lt;/p&gt; &lt;p&gt;This function will calculate the number of &lt;u&gt;week days&lt;/u&gt; between any two dates.&lt;/p&gt; &lt;p&gt;&lt;font face="Courier New"&gt;Function NumberOfWeekdays(begindate As Date, &lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; EndDate As Date, &lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/font&gt;&lt;font face="Courier New"&gt;bolInclusive As Boolean) As Integer&lt;/font&gt;&lt;/p&gt; &lt;p&gt;&lt;font face="Courier New"&gt;Dim intCounter As Integer&lt;br&gt;Dim intMovingDate As Date&lt;br&gt;intCounter = 0&lt;br&gt;If bolInclusive Then&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; intMovingDate = begindate&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; EndDate = EndDate + 1&lt;br&gt;Else&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; intMovingDate = begindate + 1&lt;br&gt;End If&lt;br&gt;Do While intMovingDate &amp;lt; EndDate&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; If WeekDay(intMovingDate) &amp;lt;&amp;gt; 1 And WeekDay(intMovingDate) &amp;lt;&amp;gt; 7 Then&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; intCounter = intCounter + 1&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; End If&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; intMovingDate = intMovingDate + 1&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; Loop&lt;br&gt;NumberOfWeekdays = intCounter&lt;br&gt;End Function&lt;/font&gt;&lt;/p&gt; &lt;p&gt;&lt;font face="Courier New"&gt;Calling the function. (The third argument determines whether you want to include the begin and end dates in the weekday count.)&lt;/font&gt;&lt;/p&gt; &lt;p&gt;&lt;font face="Courier New"&gt;Sub testweekdays()&lt;br&gt;&lt;/font&gt;&lt;font face="Arial"&gt;'uses the FirstOfMonth function above. Literal dates like #1/1/2012# can also be used.&lt;br&gt;&lt;/font&gt;&lt;font face="Courier New"&gt;Dim intdays As Integer&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; intdays = NumberOfWeekdays(FirstofMonth(Date), Date, True)&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; MsgBox intdays&lt;br&gt;End Sub&lt;/font&gt;&lt;/p&gt; &lt;p&gt;&lt;a href="http://lh4.ggpht.com/-DUvNNnR1wH4/Txlec7q3bRI/AAAAAAAAA38/HC_6J1AnfJs/s1600-h/image%25255B2%25255D.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://lh5.ggpht.com/-cos9ULaKqd4/TxledaQa4vI/AAAAAAAAA4E/K9z-C6V1WPw/image_thumb.png?imgmax=800" width="168" height="169"&gt;&lt;/a&gt;&lt;/p&gt; &lt;p&gt;&amp;nbsp;&lt;/p&gt; &lt;p&gt;&amp;nbsp;&lt;/p&gt; &lt;p&gt;&amp;nbsp;&lt;/p&gt; &lt;p&gt;&amp;nbsp;&lt;/p&gt; &lt;ul&gt; &lt;ul&gt;&lt;!--EndFragment--&gt;&lt;/ul&gt;&lt;/ul&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8134759511126815909-2647543447903198579?l=rogersaccessblog.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://rogersaccessblog.blogspot.com/feeds/2647543447903198579/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8134759511126815909&amp;postID=2647543447903198579' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8134759511126815909/posts/default/2647543447903198579'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8134759511126815909/posts/default/2647543447903198579'/><link rel='alternate' type='text/html' href='http://rogersaccessblog.blogspot.com/2012/01/date-stuff-useful-examples.html' title='Date Stuff – Useful Examples'/><author><name>Roger's Access Blog</name><uri>http://www.blogger.com/profile/12265719129831415014</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_JYohVQdgpwU/Scu5h2VvtBI/AAAAAAAAAQc/pr3JDoLaqnQ/S220/avi_Roger_1.JPG'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://lh5.ggpht.com/-cos9ULaKqd4/TxledaQa4vI/AAAAAAAAA4E/K9z-C6V1WPw/s72-c/image_thumb.png?imgmax=800' height='72' width='72'/><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8134759511126815909.post-6150025461371388851</id><published>2012-01-18T06:36:00.001-05:00</published><updated>2012-01-23T07:01:42.560-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Access 101'/><title type='text'>Date Stuff–Using the Functions</title><content type='html'>&lt;p&gt;This post is the second in a 3-part series on Date functions in Access. If you have not read the first post (Date Stuff – Functions), it might be worth while to do so.&lt;/p&gt; &lt;ol&gt; &lt;li&gt;&lt;a href="http://rogersaccessblog.blogspot.com/2012/01/date-stufffunctions.html" target="_blank"&gt;&lt;u&gt;&lt;strong&gt;Date Stuff – Functions&lt;/strong&gt;&lt;/u&gt;&lt;/a&gt;&lt;u&gt; &lt;/u&gt; &lt;li&gt;&lt;strong&gt;Date Stuff – Using the functions (this post)&lt;/strong&gt;  &lt;li&gt;&lt;a href="http://rogersaccessblog.blogspot.com/2012/01/date-stuff-useful-examples.html" target="_blank"&gt;&lt;strong&gt;&lt;u&gt;Date Stuff – Useful Examples&lt;/u&gt;&lt;/strong&gt;&lt;/a&gt;&lt;/li&gt;&lt;/ol&gt; &lt;p&gt;To see working examples, please see the following sample: &lt;a href="http://www.rogersaccesslibrary.com/forum/topic356.html" target="_blank"&gt;DateStuff.mdb&lt;/a&gt; on my website: &lt;a href="http://www.RogersAccessLibrary.com"&gt;&lt;u&gt;www.RogersAccessLibrary.com&lt;/u&gt;&lt;/a&gt;.&lt;/p&gt; &lt;p&gt;&lt;font size="3"&gt;&lt;strong&gt;Using Date Functions&lt;/strong&gt;&lt;/font&gt;&lt;/p&gt; &lt;p&gt;Date functions (as indeed any built in or user defined functions) can be used in several ways within the database application:&lt;/p&gt; &lt;ul&gt; &lt;li&gt;Control Source  &lt;li&gt;Queries  &lt;li&gt;UDF (User Defined Functions)  &lt;li&gt;Default Value of Field&lt;/li&gt;&lt;/ul&gt; &lt;p&gt;&lt;font size="3"&gt;&lt;strong&gt;Date Functions in the Control Source&lt;/strong&gt;&lt;/font&gt;&lt;/p&gt; &lt;p&gt;Perhaps the easiest way to use date functions is directly in the ControlSource property of a form or report control.&amp;nbsp; What is a control?&amp;nbsp; Control is a general name for objects on forms or reports like textboxes, comboboxes, listboxes, buttons, and so forth.&amp;nbsp; Most of the time, this is going to be a textbox,&lt;/p&gt; &lt;p&gt;The ControlSource property determines where the control (textbox) will get it's value.&amp;nbsp; On a "bound" control, this is usually a field from the underlying RecordSource (table or query) of the form or report.&amp;nbsp; However, controls can also be "unbound", which means it is not tied to a particular field.&amp;nbsp; In this case, you can use the Control Source to display any number of things.&amp;nbsp; An unbound control will ONLY display values.&amp;nbsp; They won't be saved to the underlying table.&lt;/p&gt; &lt;p&gt;To use a date function in the ControlSource of a textbox, simply preface it with an equal sign (=), much like you do with a function in an Excel cell.&amp;nbsp; &lt;/p&gt; &lt;p&gt;For instance to display the current date in a textbox:&lt;/p&gt; &lt;p&gt;&lt;font face="Courier New"&gt;=Date()&lt;/font&gt;&lt;/p&gt; &lt;p&gt;To display the someone's age, you can use the DateDiff function to find the difference between the current date and their birth date which would have to be stored in a field like DOB.&lt;/p&gt; &lt;p&gt;&lt;font face="Courier New"&gt;=DateDiff("yyyy",[DOB],Date())&lt;/font&gt;&lt;/p&gt; &lt;p&gt;Notice that when the Date() function is used, you MUST use the parentheses after it.&amp;nbsp; If you don't, Access will assume it's a field named [Date] and it will produce an error. &lt;/p&gt; &lt;p&gt;&lt;font size="3"&gt;&lt;strong&gt;Date Functions in Queries&lt;/strong&gt;&lt;/font&gt;&lt;/p&gt; &lt;p&gt;Instead of calculating the date at the form level, you can also use the date functions in the query underlying the form or report.&lt;/p&gt; &lt;p&gt;To do the same age calculation in the &lt;strong&gt;Query Builder&lt;/strong&gt;, I could add a calculated field like this:&lt;/p&gt; &lt;p&gt;&lt;font face="Courier New"&gt;Age: DateDiff("yyyy", [DOB], Date())&lt;/font&gt;&lt;/p&gt; &lt;p&gt;&lt;a href="http://lh6.ggpht.com/-uylV1I5yIkQ/Txaul-4Af5I/AAAAAAAAA10/OUdHFNa9fco/s1600-h/image16.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://lh6.ggpht.com/-GAotX47JW0I/TxaumALv6UI/AAAAAAAAA14/EmWxC0Gqfgg/image_thumb10.png?imgmax=800" width="559" height="299"&gt;&lt;/a&gt;&lt;/p&gt; &lt;p&gt;In &lt;strong&gt;SQL View&lt;/strong&gt;, it would look like this:&lt;/p&gt; &lt;p&gt;&lt;font face="Courier New"&gt;SELECT FirstName, LastName, DOB, DateDiff("yyyy",[DOB],Date()) AS Age&lt;br&gt;FROM BirthDates;&lt;/font&gt;&lt;/p&gt; &lt;p&gt;Now, suppose instead of just listing everyone's ages, I want to select just those records for people between 12 and 45.&amp;nbsp; I can add a criteria to my calculated field like this:&lt;/p&gt; &lt;p&gt;&lt;a href="http://lh5.ggpht.com/-XwuNzlRrUrY/TxaumZkZs4I/AAAAAAAAA2A/aCm034Kwl_w/s1600-h/image15.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://lh6.ggpht.com/-sdZX5r2q9ng/TxaumjAJNcI/AAAAAAAAA2M/ct9yMqm-I9E/image_thumb9.png?imgmax=800" width="570" height="307"&gt;&lt;/a&gt;&lt;/p&gt; &lt;p&gt;&lt;font face="Courier New"&gt;SELECT FirstName, LastName, DOB, DateDiff("yyyy",[DOB],Date()) AS Age&lt;br&gt;FROM BirthDates&lt;br&gt;WHERE DateDiff("yyyy",[DOB],Date()) Between 12 And 45;&lt;/font&gt;&lt;/p&gt; &lt;p&gt;Of course, that's not the only way to do this.&amp;nbsp; I could also use the DateAdd function in the criteria of DOB, like this:&lt;/p&gt; &lt;p&gt;&lt;a href="http://lh5.ggpht.com/-ssAeqSUHfv4/Txaum9W3kxI/AAAAAAAAA2U/QgCyNz8l4B0/s1600-h/image14%25255B1%25255D.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://lh5.ggpht.com/-vYWCNZxYomY/TxaunKe2EhI/AAAAAAAAA2c/a8fwOFNtUI0/image_thumb8.png?imgmax=800" width="578" height="284"&gt;&lt;/a&gt;&lt;/p&gt; &lt;p&gt;&lt;font face="Courier New"&gt;SELECT FirstName, LastName, DOB&lt;br&gt;FROM BirthDates&lt;br&gt;WHERE DOB Between DateAdd("yyyy",-12,Date()) And DateAdd("yyyy",-45,Date())&lt;/font&gt;&lt;/p&gt; &lt;p&gt;Here, I'm using the DateAdd with a negative value to subtract 12 and 45 years from the current date to yield the correct date range.&lt;/p&gt; &lt;p&gt;&lt;font size="3"&gt;&lt;strong&gt;Date Functions in User Defined Functions (UDF)&lt;/strong&gt;&lt;/font&gt;&lt;/p&gt; &lt;p&gt;Date functions can also be used in VBA code. If I use a particular date conversion frequently, I can create a User Defined Function (UDF) to calculate the value, which saves me from typing in the formula over and over.&lt;/p&gt; &lt;p&gt;For instance, if I need to calculate the age in many places, I could create the following function:&lt;/p&gt; &lt;p&gt;&lt;font face="Courier New"&gt;Function Age(DOB As Date) As String&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; Age = (DateDiff("yyyy", [DOB], Date))&lt;br&gt;End Function&lt;/font&gt;&lt;/p&gt; &lt;p&gt;Notice that the current date function &lt;font face="Courier New"&gt;Date()&lt;/font&gt; is NOT followed by parentheses as it is in the ControlSource or in a Query.&lt;/p&gt; &lt;p&gt;This function can then be called from the ControlSource of a textbox:&lt;/p&gt; &lt;p&gt;&lt;a href="http://lh6.ggpht.com/-tfWeoBUuy_w/TxaunZuK5OI/AAAAAAAAA2k/baGkwjJ8HrQ/s1600-h/image4%25255B1%25255D.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://lh3.ggpht.com/-y-vfgvNF0GE/Txaun9_xiEI/AAAAAAAAA2s/nhCvuhyi5b0/image_thumb1.png?imgmax=800" width="359" height="69"&gt;&lt;/a&gt;&lt;/p&gt; &lt;p&gt;or in a Query: &lt;/p&gt; &lt;p&gt;&lt;a href="http://lh3.ggpht.com/-kxOUhFbZnEQ/TxauoKn4wMI/AAAAAAAAA20/NxWsDPF40kY/s1600-h/image8%25255B1%25255D.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://lh5.ggpht.com/-nR21B8h9VRQ/TxauoV-h9VI/AAAAAAAAA28/wjULXzUcWfs/image_thumb3.png?imgmax=800" width="530" height="328"&gt;&lt;/a&gt;&lt;/p&gt; &lt;p&gt;&lt;font face="Courier New"&gt;SELECT FirstName, LastName, DOB, age([DOB]) AS AgeInYears&lt;br&gt;FROM BirthDates;&lt;/font&gt;&lt;/p&gt; &lt;p&gt;In order to be used globally, that is, throughout the application, this function must be in a General Module rather than a module behind a form.&amp;nbsp; If it is in a module behind a form, it can ONLY be used in that form.&lt;/p&gt; &lt;p&gt;&lt;font size="3"&gt;&lt;strong&gt;Date () in Default Value of Field&lt;/strong&gt;&lt;/font&gt;&lt;/p&gt; &lt;p&gt;Lastly, you can use date functions in the Default Value of a field.&amp;nbsp; This can be useful if you want to enter a value when the record is created.&amp;nbsp; For instance, if you want the date and time the record was created, you can put &lt;font face="Courier New"&gt;=Date()&lt;/font&gt; in the Default Value property of a field.&lt;/p&gt; &lt;p&gt;&lt;a href="http://lh6.ggpht.com/-DXZbDdT1QoU/Tx1MILEgg4I/AAAAAAAAA4M/sPR_r29aCVc/s1600-h/image%25255B5%25255D.png"&gt;&lt;img style="background-image: none; border-bottom: 0px; border-left: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://lh3.ggpht.com/-PoHawrHQKo4/Tx1MIfRI6MI/AAAAAAAAA4U/Fi-c8dgpBpI/image_thumb%25255B2%25255D.png?imgmax=800" width="537" height="444"&gt;&lt;/a&gt;&lt;/p&gt; &lt;p&gt;Which will result in a the current date and time in the new record:&lt;/p&gt; &lt;p&gt;&lt;a href="http://lh3.ggpht.com/-U_tz91nDosw/Tx1MIqRvQHI/AAAAAAAAA4c/OI6eYcKsUPY/s1600-h/image%25255B10%25255D.png"&gt;&lt;img style="background-image: none; border-bottom: 0px; border-left: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://lh4.ggpht.com/-XX39y_aDblw/Tx1MI76rhuI/AAAAAAAAA4k/GSsheQwKN4Y/image_thumb%25255B5%25255D.png?imgmax=800" width="447" height="255"&gt;&lt;/a&gt;&lt;/p&gt; &lt;p&gt;It is worth noting, however that this records the date and time that the record was CREATED, not when it was SAVED.&amp;nbsp; If the difference is important, you need to use a different method to calculate the date and time.&amp;nbsp; When using the&lt;strong&gt; Date()&lt;/strong&gt; function, you usually don't have any problem.&amp;nbsp; However, the &lt;strong&gt;Now()&lt;/strong&gt; function WILL store the wrong value for the time.&amp;nbsp; I'll discuss this more fully in a later post.&lt;/p&gt; &lt;p&gt;Similarly, if I wanted to create a date 30 days from the time the record is created, I could add:&lt;/p&gt; &lt;p&gt;&lt;font face="Courier New"&gt;=DateAdd("d",30,Date())&lt;/font&gt;&lt;/p&gt; &lt;p&gt;to the Default Value of the Net30 field:&lt;/p&gt; &lt;p&gt;&lt;a href="http://lh6.ggpht.com/-WW61xGp-Bw4/Tx1MJDwAoDI/AAAAAAAAA4s/Zf1qJ9bw8Vk/s1600-h/image%25255B14%25255D.png"&gt;&lt;img style="background-image: none; border-bottom: 0px; border-left: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://lh5.ggpht.com/-4CgmPa-MEWI/Tx1MJTTyGMI/AAAAAAAAA40/5Bl9JsCqexo/image_thumb%25255B7%25255D.png?imgmax=800" width="525" height="251"&gt;&lt;/a&gt;&lt;/p&gt; &lt;p&gt;There are some limitations, however.&amp;nbsp; &lt;/p&gt; &lt;p&gt;You can't use a User Defined Function in the Default Value property.&amp;nbsp; They can only be the built-in functions. You also can't reference another field in the table.&lt;/p&gt; &lt;p&gt;So the following WILL NOT work in the Default Value:&lt;/p&gt; &lt;p&gt;&lt;font face="Courier New"&gt;=DateDiff("yyyy", [DOB], Date)&lt;br&gt;=Age([DOB])&lt;/font&gt;&lt;/p&gt; &lt;p&gt;Next time, in &lt;a href="http://rogersaccessblog.blogspot.com/2012/01/date-stuff-useful-examples.html" target="_blank"&gt;&lt;u&gt;Date Stuff – Useful Examples&lt;/u&gt;&lt;/a&gt;, I'll show some examples of date functions to do useful things like: 1) finding the first and last days of the current, previous, and next weeks; the first and last days of the current, previous, and next months; and so forth.&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8134759511126815909-6150025461371388851?l=rogersaccessblog.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://rogersaccessblog.blogspot.com/feeds/6150025461371388851/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8134759511126815909&amp;postID=6150025461371388851' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8134759511126815909/posts/default/6150025461371388851'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8134759511126815909/posts/default/6150025461371388851'/><link rel='alternate' type='text/html' href='http://rogersaccessblog.blogspot.com/2012/01/date-stuffusing-functions.html' title='Date Stuff–Using the Functions'/><author><name>Roger's Access Blog</name><uri>http://www.blogger.com/profile/12265719129831415014</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_JYohVQdgpwU/Scu5h2VvtBI/AAAAAAAAAQc/pr3JDoLaqnQ/S220/avi_Roger_1.JPG'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://lh6.ggpht.com/-GAotX47JW0I/TxaumALv6UI/AAAAAAAAA14/EmWxC0Gqfgg/s72-c/image_thumb10.png?imgmax=800' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8134759511126815909.post-905119684398311037</id><published>2012-01-09T07:29:00.001-05:00</published><updated>2012-01-23T06:38:35.011-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Access 101'/><title type='text'>Date Stuff–Functions</title><content type='html'>&lt;p&gt;This post is the first in a 3-part series on Date functions in Access.&lt;/p&gt; &lt;ol&gt; &lt;li&gt;&lt;strong&gt;Date Stuff – Functions (this post) &lt;/strong&gt; &lt;li&gt;&lt;a href="http://rogersaccessblog.blogspot.com/2012/01/date-stuffusing-functions.html" target="_blank"&gt;&lt;u&gt;&lt;strong&gt;Date Stuff – Using the functions&lt;/strong&gt;&lt;/u&gt;&lt;/a&gt;&lt;strong&gt; &lt;/strong&gt; &lt;li&gt;&lt;a href="http://rogersaccessblog.blogspot.com/2012/01/date-stuff-useful-examples.html" target="_blank"&gt;&lt;u&gt;&lt;strong&gt;Date Stuff – Useful Examples&lt;/strong&gt;&lt;/u&gt;&lt;/a&gt;&lt;/li&gt;&lt;/ol&gt; &lt;p&gt;To see working examples, please see the following sample: &lt;a href="http://www.rogersaccesslibrary.com/forum/topic356.html" target="_blank"&gt;&lt;u&gt;DateStuff.mdb&lt;/u&gt;&lt;/a&gt; on my website: &lt;a href="http://www.RogersAccessLibrary.com"&gt;www.RogersAccessLibrary.com&lt;/a&gt;.&lt;/p&gt; &lt;p&gt;&lt;font size="3"&gt;&lt;strong&gt;Date Functions&lt;/strong&gt;&lt;/font&gt;&lt;/p&gt; &lt;p&gt;Microsoft Access has a variety of date functions to help you manipulate date values. Because there are so many functions, there are often multiple ways to do the same thing.&amp;nbsp; For instance, if I wanted to find the first day of this month, I could do:&lt;/p&gt; &lt;p&gt;&lt;font face="Courier New"&gt;DateAdd("d",1-DatePart("d",Date()),Date())&lt;br&gt;&lt;/font&gt;or&lt;br&gt;&lt;font face="Courier New"&gt;CDate(CStr(DatePart("m",Date())) &amp;amp; "/1")&lt;/font&gt;&lt;/p&gt; &lt;p&gt;And there are probably other ways as well. The purpose of this post is to explain how dates work and a few of the functions used to manipulate them.&lt;/p&gt; &lt;p&gt;&lt;font size="3"&gt;&lt;strong&gt;What Are Dates?&lt;/strong&gt;&lt;/font&gt;&lt;/p&gt; &lt;p&gt;First of all, in Access a date field stores both the date and time values as a double-precision number, that is, a whole number and decimal.&amp;nbsp; For instance, right this second, the date/time is 1/5/2012 6:53:04 AM. This value is actually stored as 40913.28686.&amp;nbsp; &lt;/p&gt; &lt;p&gt;The whole number (40913) represents the date (1/5/2012).&amp;nbsp; This is the number of days since 1/1/1900.&amp;nbsp; The decimal (0.28686) is the fractional part of the day and represents the time (6:53:04 AM).&lt;/p&gt; &lt;p&gt;The implications of this is that if I want to add a day to a date value, I can simply add 1.&amp;nbsp; If I want to subtract 12 hours from a time, I can simply subtract 0.5.&amp;nbsp; This is all well and good if I am dealing only with days.&amp;nbsp; But if we want to add a month to, or find someone's age in years or months, or add add 10 &lt;em&gt;work&lt;/em&gt; days to a date, it becomes much more complicated.&lt;/p&gt; &lt;p&gt;This is where the built in date functions come in handy.&amp;nbsp; Used in combination, I can manipulate dates in terms of days, work days, weeks, months, and years; and times in hours, minutes, and seconds.&lt;/p&gt; &lt;p&gt;&lt;font size="3"&gt;&lt;strong&gt;Date Functions&lt;/strong&gt;&lt;/font&gt;&lt;/p&gt; &lt;p&gt;The some of the more useful date functions are:&lt;/p&gt; &lt;ol&gt; &lt;li&gt;Date() / Now()  &lt;li&gt;DatePart()  &lt;li&gt;DateSerial()  &lt;li&gt;DateAdd()  &lt;li&gt;DateDiff()  &lt;li&gt;CDate()&lt;/li&gt;&lt;/ol&gt; &lt;p&gt;&lt;font size="3"&gt;&lt;strong&gt;Date() / Now()&lt;/strong&gt;&lt;/font&gt;&lt;/p&gt; &lt;p&gt;The Date() function returns the current system date, that is, just the whole number part of the datetime (40913.0).&amp;nbsp; The Now() function returns the current date AND time, that is, both the whole number and decimal parts (40913.28686).&lt;/p&gt; &lt;p&gt;This is very useful (as we saw above) in determining dates relative to today's date.&amp;nbsp; I'll be using Date() in the examples below, but any date could be substituted.&lt;/p&gt; &lt;p&gt;&lt;font size="3"&gt;&lt;strong&gt;DatePart()&lt;/strong&gt;&lt;/font&gt;&lt;/p&gt; &lt;p&gt;The DatePart function allows you to determine the value of a given date in terms of specific intervals.&amp;nbsp; In other words, you can determine the year, month, day, week, quarter, etc, of a given date.&amp;nbsp; The syntax looks like this:&lt;/p&gt; &lt;p&gt;&lt;font face="Courier New"&gt;&lt;b&gt;DatePart&lt;/b&gt;&lt;b&gt;(&lt;/b&gt;&lt;b&gt;&lt;i&gt;interval, date&lt;/i&gt;&lt;/b&gt; [&lt;b&gt;&lt;i&gt;, &lt;/i&gt;&lt;/b&gt;&lt;b&gt;&lt;i&gt;firstdayofweek&lt;/i&gt;&lt;/b&gt;] [&lt;b&gt;&lt;i&gt;, &lt;/i&gt;&lt;/b&gt;&lt;b&gt;&lt;i&gt;firstweekofyear&lt;/i&gt;&lt;/b&gt;] &lt;b&gt;)&lt;/b&gt;&lt;/font&gt;  &lt;p&gt;&lt;strong&gt;&lt;font face="Courier New"&gt;Intervals:&lt;/font&gt;&lt;/strong&gt;&lt;/p&gt; &lt;table border="0" cellspacing="0" cellpadding="0"&gt; &lt;tbody&gt; &lt;tr&gt; &lt;td valign="top" width="90"&gt; &lt;p&gt;yyyy&lt;/p&gt;&lt;/td&gt; &lt;td valign="top" width="106"&gt; &lt;p&gt;Year&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt; &lt;tr&gt; &lt;td valign="top" width="90"&gt; &lt;p&gt;q&lt;/p&gt;&lt;/td&gt; &lt;td valign="top" width="106"&gt; &lt;p&gt;Quarter&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt; &lt;tr&gt; &lt;td valign="top" width="90"&gt; &lt;p&gt;m&lt;/p&gt;&lt;/td&gt; &lt;td valign="top" width="106"&gt; &lt;p&gt;Month&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt; &lt;tr&gt; &lt;td valign="top" width="90"&gt; &lt;p&gt;y&lt;/p&gt;&lt;/td&gt; &lt;td valign="top" width="106"&gt; &lt;p&gt;Day of year&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt; &lt;tr&gt; &lt;td valign="top" width="90"&gt; &lt;p&gt;d&lt;/p&gt;&lt;/td&gt; &lt;td valign="top" width="106"&gt; &lt;p&gt;Day&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt; &lt;tr&gt; &lt;td valign="top" width="90"&gt; &lt;p&gt;w&lt;/p&gt;&lt;/td&gt; &lt;td valign="top" width="106"&gt; &lt;p&gt;Weekday&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt; &lt;tr&gt; &lt;td valign="top" width="90"&gt; &lt;p&gt;ww&lt;/p&gt;&lt;/td&gt; &lt;td valign="top" width="106"&gt; &lt;p&gt;Week&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt; &lt;tr&gt; &lt;td valign="top" width="90"&gt; &lt;p&gt;h&lt;/p&gt;&lt;/td&gt; &lt;td valign="top" width="106"&gt; &lt;p&gt;Hour&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt; &lt;tr&gt; &lt;td valign="top" width="90"&gt; &lt;p&gt;n&lt;/p&gt;&lt;/td&gt; &lt;td valign="top" width="106"&gt; &lt;p&gt;Minute&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt; &lt;tr&gt; &lt;td valign="top" width="90"&gt; &lt;p&gt;s&lt;/p&gt;&lt;/td&gt; &lt;td valign="top" width="106"&gt; &lt;p&gt;Second&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt; &lt;p&gt;&lt;font face="Courier New"&gt;&lt;font face="Arial"&gt;The following are some examples intervals returned using DatePart:&lt;/font&gt;&lt;/font&gt;&lt;/p&gt; &lt;p&gt;&lt;font face="Courier New"&gt;DatePart("yyyy", Date()) &lt;font face="Arial"&gt;returns &lt;/font&gt;&lt;/font&gt;2012.&lt;br&gt;&lt;font face="Courier New"&gt;DatePart("m", Date()) &lt;font face="Arial"&gt;returns &lt;/font&gt;&lt;/font&gt;1.&lt;br&gt;&lt;font face="Courier New"&gt;DatePart("d", Date()) &lt;font face="Arial"&gt;returns &lt;/font&gt;&lt;/font&gt;5.&lt;br&gt;&lt;font face="Courier New"&gt;DatePart("h", Now()) &lt;font face="Arial"&gt;returns &lt;/font&gt;&lt;/font&gt;6.&lt;/p&gt; &lt;p&gt;As you can see, DatePart can also be used to return times, but you need to use the Now() function because the Date() function doesn't return a time.&lt;/p&gt; &lt;p&gt;For more information, see the MS Knowledgebase article &lt;a href="http://office.microsoft.com/en-us/access-help/datepart-function-HA001228812.aspx" target="_blank"&gt;&lt;u&gt;DatePart Function&lt;/u&gt;&lt;/a&gt;.&lt;/p&gt; &lt;p&gt;By the way, instead of the DatePart function, you can also use the Year(), Month(), Day() functions.&amp;nbsp; They work the same as the DatePart for each of their respective intervals.&amp;nbsp; I prefer to use DatePart because I can use it for everything.&lt;/p&gt; &lt;p&gt;&lt;font size="3"&gt;&lt;strong&gt;DateSerial()&lt;/strong&gt;&lt;/font&gt;&lt;/p&gt; &lt;p&gt;&lt;font face="Courier New"&gt;&lt;b&gt;DateSerial&lt;/b&gt;&lt;b&gt;(&lt;/b&gt;&lt;b&gt;&lt;i&gt;year, month, day&lt;/i&gt;&lt;/b&gt;&lt;b&gt;)&lt;/b&gt;&lt;/font&gt;  &lt;p&gt;The DateSerial function allows you to build a date value by giving values for the year, month, and day.&amp;nbsp; For instance, to build today's date, I could do this:&lt;/p&gt; &lt;p&gt;&lt;font face="Courier New"&gt;DateSerial(2012, 1, 5)&lt;/font&gt;&lt;/p&gt; &lt;p&gt;Of course, it's not all that useful when using literal values, but I can also use variable, which makes it extremely useful.&amp;nbsp; For instance, I could use it to create a date one year from today's date:&lt;/p&gt; &lt;p&gt;&lt;font size="2" face="Courier New"&gt;DateSerial(DatePart("yyyy", Date()), DatePart("m", Date()), DatePart("d", Date()))&lt;/font&gt;&lt;/p&gt; &lt;p&gt;So I can use the other date functions to build the separate intervals and then use the DateSerial to put them all together into a date value.&lt;/p&gt; &lt;p&gt;For more information, see the MS Knowledgebase article &lt;a href="http://office.microsoft.com/en-us/access-help/dateserial-function-HA001228813.aspx" target="_blank"&gt;&lt;u&gt;DateSerial Function&lt;/u&gt;&lt;/a&gt;.&lt;/p&gt; &lt;p&gt;&lt;font size="3"&gt;&lt;strong&gt;DateAdd()&lt;/strong&gt;&lt;/font&gt;&lt;/p&gt; &lt;p&gt;&lt;font face="Courier New"&gt;&lt;b&gt;DateAdd&lt;/b&gt;&lt;b&gt;(&lt;/b&gt;&lt;b&gt;&lt;i&gt;interval, number, date&lt;/i&gt;&lt;/b&gt;&lt;b&gt;)&lt;/b&gt;&lt;/font&gt;  &lt;p&gt;The DateAdd function allows you to add an interval to a given date.&amp;nbsp; The intervals are the same as those for the DatePart function, ie. year, month, day, week, etc. This is extremely useful because leap years and the different number of days in a month makes it difficult to accurately add some intervals by adding days.&amp;nbsp; The DateAdd function will accurately add intervals without any additional work on your part.&lt;/p&gt; &lt;p&gt;For instance, above, I built date one year from today with DateSerial.&amp;nbsp; However, I could do it much easier by using the DateAdd function:&lt;/p&gt; &lt;p&gt;&lt;font face="Courier New"&gt;DateAdd("yyyy", 1, Date())&lt;/font&gt;&lt;/p&gt; &lt;p&gt;A date six months is as easy as:&lt;/p&gt; &lt;p&gt;&lt;font face="Courier New"&gt;DateAdd("m", 6, Date())&lt;/font&gt;&lt;/p&gt; &lt;p&gt;For more information, see the MS Knowledgebase article &lt;a href="http://office.microsoft.com/en-us/access-help/dateadd-function-HA001228810.aspx" target="_blank"&gt;&lt;u&gt;DateAdd Function&lt;/u&gt;&lt;/a&gt;.&lt;/p&gt; &lt;p&gt;&lt;strong&gt;&lt;font size="3"&gt;DateDiff()&lt;/font&gt;&lt;/strong&gt;&lt;/p&gt; &lt;p&gt;&lt;font face="Courier New"&gt;DateDiff(&lt;i&gt;interval, date1, date2&lt;/i&gt; [&lt;i&gt;, firstdayofweek&lt;/i&gt;] [&lt;i&gt;, firstweekofyear&lt;/i&gt;] )&lt;/font&gt;  &lt;p&gt;The DateDiff function allows you to find the interval between two dates.&amp;nbsp; The interval can be any of the standard intervals as seen in the DatePart function above, ie. year, month, day, week, etc.&amp;nbsp; This is the counterpart to the DateAdd function.  &lt;p&gt;The DateDiff is really useful for determining age.&amp;nbsp; For instance, to find someone's age in years, I can to this (assumes DOB is a field holding the date of birth):  &lt;p&gt;DateDiff("yyyy",[DOB],Now())  &lt;p&gt;But I could just as easily find the age in months:  &lt;p&gt;DateDiff("m",[DOB],Now())  &lt;p&gt;For more information, see the MS Knowledgebase article &lt;a href="http://office.microsoft.com/en-us/access-help/datediff-function-HA001228811.aspx" target="_blank"&gt;&lt;u&gt;DateDiff() function&lt;/u&gt;&lt;/a&gt;.  &lt;p&gt;&lt;strong&gt;&lt;font size="3"&gt;CDate()&lt;/font&gt;&lt;/strong&gt;  &lt;p&gt;The CDate() function is not a date function so much as a data conversion function.&amp;nbsp; You can use the CDate function to convert a string value into a Date.&amp;nbsp; The string has to be a recognizable date:  &lt;p&gt;"January 2, 2012"  &lt;p&gt;"12/1/2012"  &lt;p&gt;You have to be a little careful if you don't use the US Standard date format of Month Day Year.&amp;nbsp; CDate will correctly interpret both "1 December, 2011" and December 1, 2011" as 12/1/2011.&amp;nbsp; But "12/1/2011" will evaluate to December 12 even if you meant it to be January 12.  &lt;p&gt;While CDate is not a date function per se, it is sometimes useful for calculating certain date values.  &lt;p&gt;For more information, see the MS Knowledgebase article: &lt;a href="http://office.microsoft.com/en-us/access-help/type-conversion-functions-HA001229018.aspx?CTT=1" target="_blank"&gt;&lt;u&gt;Conversion Functions&lt;/u&gt;&lt;/a&gt;.  &lt;p&gt;Next time, I'll discuss various ways to use the Date Functions in &lt;a href="http://rogersaccessblog.blogspot.com/2012/01/date-stuffusing-functions.html" target="_blank"&gt;&lt;u&gt;Date Stuff – Using the functions&lt;/u&gt;&lt;/a&gt;.&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8134759511126815909-905119684398311037?l=rogersaccessblog.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://rogersaccessblog.blogspot.com/feeds/905119684398311037/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8134759511126815909&amp;postID=905119684398311037' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8134759511126815909/posts/default/905119684398311037'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8134759511126815909/posts/default/905119684398311037'/><link rel='alternate' type='text/html' href='http://rogersaccessblog.blogspot.com/2012/01/date-stufffunctions.html' title='Date Stuff–Functions'/><author><name>Roger's Access Blog</name><uri>http://www.blogger.com/profile/12265719129831415014</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_JYohVQdgpwU/Scu5h2VvtBI/AAAAAAAAAQc/pr3JDoLaqnQ/S220/avi_Roger_1.JPG'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8134759511126815909.post-4838281936792737979</id><published>2011-10-31T06:42:00.001-04:00</published><updated>2011-10-31T06:43:09.929-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='New Samples'/><category scheme='http://www.blogger.com/atom/ns#' term='Tejpal'/><title type='text'>New Sample: Query_A2K10_MultiValueFields</title><content type='html'>&lt;p&gt;By AD Tejpal&lt;/p&gt; &lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; This sample db demonstrates query based approach to bulk appending / updating / make table actions involving multi-value&amp;nbsp; fields in Access 2010, without resorting to use of recordset or recordset2 objects, thus overcoming a known limitation associated with such fields. &lt;/p&gt; &lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; In this arrangement, an extra field named TempID is provided in the destination table. The append query is executed in two stages. In first stage, primary key values from source table are appended to TempID field in destination table. In 2nd stage, value elements of MVF field get appended, using an inner join between the source and destination tables (PK of source table equals TempID of destination table). &lt;/p&gt; &lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; Three styles are covered as follows:&lt;/p&gt; &lt;ul&gt; &lt;li&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; (a) Bulk appending of selected records (having multi-value fields) from one table to another.&lt;/li&gt; &lt;li&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; (b) Bulk updating of multi-value field elements (addition or replacement of values) in destination table based upon values held in source table.&lt;/li&gt; &lt;li&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; (c) Make table action covering multi-value fields based upon selected records in source table.&amp;nbsp;&amp;nbsp; &lt;/li&gt;&lt;/ul&gt; &lt;p&gt;&lt;strong&gt;Version:&lt;/strong&gt; Access 2010 accdb file.&lt;/p&gt; &lt;p&gt;You can find the sample here: &lt;a title="http://www.rogersaccesslibrary.com/forum/topic575_post593.html#593" href="http://www.rogersaccesslibrary.com/forum/topic575_post593.html#593"&gt;&lt;u&gt;http://www.rogersaccesslibrary.com/forum/topic575_post593.html#593&lt;/u&gt;&lt;/a&gt;&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8134759511126815909-4838281936792737979?l=rogersaccessblog.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://rogersaccessblog.blogspot.com/feeds/4838281936792737979/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8134759511126815909&amp;postID=4838281936792737979' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8134759511126815909/posts/default/4838281936792737979'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8134759511126815909/posts/default/4838281936792737979'/><link rel='alternate' type='text/html' href='http://rogersaccessblog.blogspot.com/2011/10/new-sample-querya2k10multivaluefields.html' title='New Sample: Query_A2K10_MultiValueFields'/><author><name>Roger's Access Blog</name><uri>http://www.blogger.com/profile/12265719129831415014</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_JYohVQdgpwU/Scu5h2VvtBI/AAAAAAAAAQc/pr3JDoLaqnQ/S220/avi_Roger_1.JPG'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8134759511126815909.post-8489772132272753727</id><published>2011-10-26T07:16:00.001-04:00</published><updated>2012-01-05T06:35:26.660-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Access 101'/><title type='text'>Referencing Forms, Subforms, and Sub-subforms</title><content type='html'>&lt;h3&gt;&amp;nbsp;&lt;/h3&gt; &lt;h4&gt;Introduction&lt;/h4&gt; &lt;p&gt;One of the powerful features of Microsoft Access is the ability to reference controls from other controls, in queries, and in code. And yet, while most beginning developers master referencing controls on forms relatively quickly, referencing controls on subforms and sub-subforms causes a lot of confusion. Most of this confusion is due to the way in which controls are named.  &lt;p&gt;First, however, a word about naming conventions. I generally encourage the use of meaningful names for controls on a form. In this case, however, I am using generic names like Form1, Subform1, Subsubform1, subControl1, subsubControl1, Text1, and so forth. Objects that I create and name will have a number at the end (Text1). Form collections or properties (like Forms, Controls, Value, or SetFocus) will not have a number at the end.  &lt;h4&gt;&amp;nbsp;&lt;/h4&gt; &lt;h4&gt;Syntax&lt;/h4&gt; &lt;p&gt;There are three different, yet equally correct, syntaxes. For instance, suppose I want to reference the value of a textbox named &lt;b&gt;Text1&lt;/b&gt; on a form named &lt;b&gt;Form1. &lt;/b&gt;All of the following work:  &lt;p&gt;&lt;font face="Courier New"&gt;Forms!Form1!Text1.Value&lt;br&gt;Forms("form1")("Text1").Value&lt;br&gt;Forms(0)(0).Value &lt;/font&gt; &lt;h4&gt;&amp;nbsp;&lt;/h4&gt; &lt;h4&gt;Bang Syntax&lt;/h4&gt; &lt;p&gt;The Bang (!) syntax is the most commonly used syntax in Access, and is generally recommended in Microsoft documentation and articles. The Bang Syntax uses both bangs (!) and dots(.). Bangs are used to separate levels in the DAO hierarchy and dots serve to separate an object from its properties.  &lt;p&gt;So, in the case of our textbox, "Forms" is the Access collection of forms. "Form1" is the name of the form I'm referencing. And "Text1" is the name of the control (textbox).  &lt;p&gt;&lt;font face="Courier New"&gt;Forms!Form1.Controls!Text1.Value&lt;/font&gt;  &lt;p&gt;The bangs go between the object hierarchy levels. Form1 is a member of the Forms collection. Text1 is a member of the Controls collection of Form1. The dots separate the objects from their collections or properties: The Controls collection of the Form1 object, and the Value property of the Text1 object.  &lt;p&gt;However, every object has a default property and it just happens that Controls is the default for a form and Value is the default for control objects. Therefore you don't actually have to reference them, so I can do something like this:  &lt;p&gt;&lt;font face="Courier New"&gt;Forms!Form1!Text1&lt;/font&gt;  &lt;p&gt;But if I wanted to reference the caption of the textbox, I'd have to explicitly name it.  &lt;p&gt;&lt;font face="Courier New"&gt;Forms!Form1!Text1.Caption&lt;/font&gt;  &lt;p&gt;As a general rule of thumb, Bangs (!) go before objects that you, the developer, name like forms, controls, and such. Dots (.) go before things that Access names, like properties and methods. For more on the difference between Bang and Dot, see my post: &lt;a href="http://rogersaccessblog.blogspot.com/2009/04/bang-vs-dot-in-dao.html"&gt;&lt;u&gt;Bang Vs. Dot In DAO&lt;/u&gt;&lt;/a&gt;.  &lt;p&gt;It is worthwhile to note that there are times when Bangs and Dots are interchangeable and at other times, it is not.&amp;nbsp; For instance, in:  &lt;p&gt;&lt;font face="Courier New"&gt;Forms!Form1!Text1&lt;br&gt;&lt;font face="Courier New"&gt;Forms.Form1!Text1&lt;/font&gt;&lt;/font&gt;&lt;/p&gt; &lt;p&gt;the Bang between "Forms" and "Form1" can be replaced with a Dot.&amp;nbsp; However, the Bang between "Form1" and "Text1" can &lt;strong&gt;not&lt;/strong&gt;.&amp;nbsp; Best practice is to use the Bangs and Dots correctly to avoid any problems.&lt;/p&gt; &lt;h4&gt;&amp;nbsp;&lt;/h4&gt; &lt;h4&gt;Paren Syntax&lt;/h4&gt; &lt;p&gt;The Paren Syntax simply surrounds each object with parentheses and the name of the object is surrounded in quote marks without bangs or dots:  &lt;p&gt;&lt;font face="Courier New"&gt;Forms("form1")("Text1").value&lt;/font&gt;  &lt;p&gt;This syntax is most useful in programming where you want to use a variable in place of the object name:  &lt;p&gt;&lt;font face="Courier New"&gt;Dim strControlName as String&lt;br&gt;&lt;/font&gt;&lt;font face="Courier New"&gt;Dim strFormName as String&lt;br&gt;&lt;/font&gt;&lt;font face="Courier New"&gt;strFormName = "Form1"&lt;br&gt;&lt;/font&gt;&lt;font face="Courier New"&gt;strControlName = "Text1"&lt;br&gt;&lt;/font&gt;&lt;font face="Courier New"&gt;Forms(strFormName)(strControlName).value = "Hello"&lt;/font&gt;  &lt;p&gt;Notice the variables are NOT surrounded by quotes.  &lt;h4&gt;&amp;nbsp;&lt;/h4&gt; &lt;h4&gt;Index Syntax&lt;/h4&gt; &lt;p&gt;The Index Syntax is rarely used. It is most commonly used for programmatically looping through controls on a form. For instance, if I wanted to print out the names of all the controls on a form, I could do something like this:  &lt;p&gt;&lt;font face="Courier New"&gt;Dim i As Integer&lt;br&gt;For i = 0 To Forms(0).Controls.Count – 1&lt;br&gt;&amp;nbsp;&amp;nbsp; Debug.Print Forms(0)(i).Name&lt;br&gt;Next i&lt;/font&gt;  &lt;p&gt;I could list the names of all the controls on all open forms like this:  &lt;p&gt;&lt;font face="Courier New"&gt;Dim i As Integer&lt;br&gt;Dim j As Integer&lt;br&gt;For i = 0 To Forms.Count – 1&lt;br&gt;&amp;nbsp;&amp;nbsp; For j = 0 To Forms(i).Controls.Count – 1&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Debug.Print Forms(i).Name &amp;amp; ": " &amp;amp; Forms(i)(j).Name&lt;br&gt;&amp;nbsp;&amp;nbsp; Next j&lt;br&gt;Next i&lt;/font&gt;  &lt;p&gt;The reality is, however, that there are better ways to do this by directly accessing the Forms Collection and the Controls Collection. Still, it's useful to know about because it can be used in conjunction with other syntax.  &lt;h4&gt;&amp;nbsp;&lt;/h4&gt; &lt;h4&gt;Mixing and Matching Syntaxes&lt;/h4&gt; &lt;p&gt;The various syntaxes can be mixed and matched. For instance, all of the following work identically:  &lt;p&gt;&lt;font face="Courier New"&gt;Forms!Form1!Text1&lt;br&gt;Forms!Form1("Text1")&lt;br&gt;Forms!Form1(0)&lt;br&gt;Forms.Form1!Text1&lt;br&gt;Forms("Form1")!Text1&lt;br&gt;Forms!Form1.Controls(0).Value&lt;br&gt;Forms(0)(0).Value&lt;br&gt;Forms(0)("Text1").Value&lt;br&gt;Forms!Form1.Controls!Text1.Value&lt;/font&gt;  &lt;p&gt;In general, however, unless you have a good reason for it, it's best to stick with the Bang and Dot syntax.  &lt;h4&gt;&amp;nbsp;&lt;/h4&gt; &lt;h4&gt;A Word About ME&lt;/h4&gt; &lt;p&gt;The ME object is a special construct used in forms and reports. It refers to the form or report which has the Focus. If you are referencing objects from within the form/report, you can substitute Me. or Me!  &lt;p&gt;for Forms!Form1.  &lt;p&gt;&lt;font face="Courier New"&gt;Me!Text1&lt;/font&gt;  &lt;p&gt;&lt;font face="Courier New"&gt;Me.Text1&lt;br&gt;Me!Text1.value&lt;br&gt;Me.Text1.value&lt;br&gt;Me.Controls!Text1&lt;br&gt;Me(Controls)("Text1").value&lt;/font&gt;  &lt;p&gt;It doesn't matter which.  &lt;p&gt;If, however, you are referencing the control from outside the form/report -- say in a query -- then you must use explicitly reference the Forms collection and the Form name. That is: &lt;font face="Courier New"&gt;Forms!Form1&lt;/font&gt;.  &lt;p&gt;For more on the ME object and using the Bang and the Dot in forms, see my blog post&lt;a href="http://rogersaccessblog.blogspot.com/2011/10/bang-vs-dot-in-forms.html"&gt;: &lt;u&gt;Bang Vs. Dot in Forms&lt;/u&gt;&lt;/a&gt;.  &lt;p&gt;To see specific examples of referencing subforms and subsubforms, download my sample: &lt;a href="http://www.rogersaccesslibrary.com/forum/topic261.html" target="_blank"&gt;&lt;u&gt;SubformReference.mdb&lt;/u&gt;&lt;/a&gt; from my website (&lt;a href="http://www.rogersaccesslibrary.com"&gt;www.rogersaccesslibrary.com&lt;/a&gt;).&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8134759511126815909-8489772132272753727?l=rogersaccessblog.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://rogersaccessblog.blogspot.com/feeds/8489772132272753727/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8134759511126815909&amp;postID=8489772132272753727' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8134759511126815909/posts/default/8489772132272753727'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8134759511126815909/posts/default/8489772132272753727'/><link rel='alternate' type='text/html' href='http://rogersaccessblog.blogspot.com/2011/10/referencing-forms-subforms-and-sub.html' title='Referencing Forms, Subforms, and Sub-subforms'/><author><name>Roger's Access Blog</name><uri>http://www.blogger.com/profile/12265719129831415014</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_JYohVQdgpwU/Scu5h2VvtBI/AAAAAAAAAQc/pr3JDoLaqnQ/S220/avi_Roger_1.JPG'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8134759511126815909.post-8553575227856969755</id><published>2011-10-21T07:18:00.001-04:00</published><updated>2011-10-21T07:22:25.158-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Featured Samples'/><title type='text'>Featured Sample: Training Registration Database</title><content type='html'>&lt;p&gt;By Roger Carlson&lt;/p&gt; &lt;p&gt;This database is a fairly robust application for registering employees for training classes.&amp;nbsp; In it, you can create Courses, create Students, create Classrooms, schedule Courses to Classrooms, and assign Students to Scheduled Courses.&amp;nbsp;&amp;nbsp; Your first stop will be the Main Menu, where you will have the following choices:&lt;/p&gt; &lt;h2&gt;Schedule Classes&lt;/h2&gt; &lt;p&gt;Clicking "Scheduled Classes" from the Main Menu brings you to the Scheduled Classes screen. It gives you a summary of the classes already scheduled.&lt;/p&gt; &lt;p&gt;From there,&amp;nbsp; select an Application and a Course Name to see a listing of all the scheduled classes.&amp;nbsp; You can filter the selection further by choosing the Past Classes or Future Classes buttons.&amp;nbsp; You can also schedule a new class with the New Class button.&lt;/p&gt; &lt;p&gt;To view or edit a particular class, click the GO&amp;gt; button to be sent to the Scheduled Class Entry screen.&lt;/p&gt; &lt;p&gt;The Scheduled Class Entry screen allows you to view, edit, or a class, and assign students to it.&amp;nbsp; By default, the fields in BLUE are locked to prevent accidental changes.&amp;nbsp; To unlock these fields, click the Unlock button.&lt;/p&gt; &lt;p&gt;Below the Class Information, you can assign students to the class.&amp;nbsp; The student fields are NOT editable (even if you unlock the other fields).&amp;nbsp; The only editable field is the Attended field to indicate the student attended the class. Even though you can't edit the information in the student fields, you can re-size and re-arraign the fields at run-time.&lt;/p&gt; &lt;p&gt;You can print a Class Roster from this screen and also certificates of attendance.&amp;nbsp; The certificates are bare-bones versions that you will want to modify for your company.&lt;/p&gt; &lt;p&gt;As you enroll students, the screen will track the number of seats taken and the number remaining.&amp;nbsp; It will allow you to over-book a class, but will give you a warning when you do.&lt;/p&gt; &lt;p&gt;By default, the Scheduled Class Entry screen shows just the records you selected on the Scheduled Classes screen.&amp;nbsp; Clicking the Show All Records button (bottom) will fill the form with all the classes for that Application.&amp;nbsp; You can use the Record Selector buttons to scroll between them.&lt;/p&gt; &lt;h2&gt; Schedule Students&lt;/h2&gt; &lt;p&gt;The Schedule Students screen allows you to enter students and assign classes in one step.&amp;nbsp; &lt;/p&gt; &lt;p&gt;The screen opens to the New Record all set for you to enter a new student.&amp;nbsp; If you want to edit a student, you can select one from the drop down box at the top of the screen.&amp;nbsp; You can also use the scroll buttons at the very bottom of the screen.&amp;nbsp; When you enter an existing student, the fields turn BLUE, which indicates they are locked.&amp;nbsp; This is to prevent accidental changes.&amp;nbsp; Click the Edit Record button to unlock the fields.&lt;/p&gt; &lt;p&gt;You can also assign classes to students in this form.&amp;nbsp; Just select the class from the drop down list.&amp;nbsp; The particulars of the class will appear below in the blue fields.&amp;nbsp; Thes cannot be edited in this form.&amp;nbsp; To do so, you must go to the Scheduled Classes screen.&lt;/p&gt; &lt;h2&gt;Reports&lt;/h2&gt; &lt;p&gt;The Reports screen allows you to run various reports.&amp;nbsp; Each report appears as an item in a listbox.&amp;nbsp; Select the item and click "Open".&lt;/p&gt; &lt;p&gt;There are two types of reports: 1) Simple Reports and 2) Parameter Reports&lt;/p&gt; &lt;p&gt;Simple reports show the same information all the time.&amp;nbsp; The "All Courses" report is an example.&amp;nbsp; It displays all of the current courses offered.&lt;/p&gt; &lt;p&gt;Parameter reports are a little more useful in that the user can select options from a drop down box to limit the output.&amp;nbsp; The "Classes By Instructor" report is an example.&amp;nbsp; When you run the report, a small form appears that allows you to choose an instructor.&lt;/p&gt; &lt;p&gt;Simple Reports:&amp;nbsp; To add a simple report to the Reports List, simply name the report with an "rpt" prefix.&amp;nbsp; By using a prefix, you can prevent reports (like the "ClassCertificate" report) from showing up in the list.&amp;nbsp; (ClassCertificate runs from the "Scheduled Class Entry" screen only.)&lt;/p&gt; &lt;p&gt;Parameter Reports:&amp;nbsp; To add a parameter report to the Reports List is a little more complex.&amp;nbsp; First of all, you have to create a form which has controls to hold the parameters and reference those controls in the Report Source query.&amp;nbsp; Next, name the form with an "rpt" prefix.&amp;nbsp; Lastly, you'll want to name the report itself with an "rpf" prefix.&amp;nbsp; This will prevent these reports from appearing in the Reports List.&lt;/p&gt; &lt;p&gt;Example:&amp;nbsp; The form "rptClasses By Instructor" is the form which will run the "rpfClasses by Instuctor" report.&amp;nbsp; The Record Source for the report (a saved query called: Scheduled Classes Query by Instructor) references the combo box value on the form to restrict the values in the report.&lt;/p&gt; &lt;h2&gt;Maintain Information&lt;/h2&gt; &lt;p&gt;The Maintain Information screen allows you to edit your reference tables.&amp;nbsp; In it, you can add or edit Applications, Buildings, Classrooms, Courses, and so forth.&amp;nbsp; These options will then be available to the drop down lists in the data entry screens.&lt;/p&gt; &lt;p&gt;If you add new reference tables, you'll also want to create a simple edit form for the table.&amp;nbsp; If you name the form with a prefix of "Maintain ", it will automatically appear in the Table Mainenance list.&lt;/p&gt; &lt;p&gt;The Getting Started screen is a little different from the rest as it allows you to turn the Getting Started screen on and off.&lt;/p&gt; &lt;p&gt;&lt;br&gt;You can find this sample here: &lt;a title="http://www.rogersaccesslibrary.com/forum/topic232.html" href="http://www.rogersaccesslibrary.com/forum/topic232.html"&gt;&lt;u&gt;http://www.rogersaccesslibrary.com/forum/topic232.html&lt;/u&gt;&lt;/a&gt;&lt;u&gt;.&lt;/u&gt;&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8134759511126815909-8553575227856969755?l=rogersaccessblog.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://rogersaccessblog.blogspot.com/feeds/8553575227856969755/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8134759511126815909&amp;postID=8553575227856969755' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8134759511126815909/posts/default/8553575227856969755'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8134759511126815909/posts/default/8553575227856969755'/><link rel='alternate' type='text/html' href='http://rogersaccessblog.blogspot.com/2011/10/featured-sample-training-registration.html' title='Featured Sample: Training Registration Database'/><author><name>Roger's Access Blog</name><uri>http://www.blogger.com/profile/12265719129831415014</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_JYohVQdgpwU/Scu5h2VvtBI/AAAAAAAAAQc/pr3JDoLaqnQ/S220/avi_Roger_1.JPG'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8134759511126815909.post-2313566848573988487</id><published>2011-10-11T07:01:00.001-04:00</published><updated>2011-10-11T07:01:16.239-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Access 101'/><title type='text'>Bang Vs. Dot in Forms</title><content type='html'>&lt;p&gt;In a previous post (&lt;a href="http://rogersaccessblog.blogspot.com/2009/04/bang-vs-dot-in-dao.html"&gt;&lt;u&gt;Bang Vs. Dot In DAO&lt;/u&gt;&lt;/a&gt;), I wrote about the difference between the Dot (.) and the Bang (!) in DAO. It's pretty straight forward. Dot is used to separate one level of the DAO hierarchy, separating an object from its methods and properties. Bang is used to separate an object from the collection in which it is contained. &lt;p&gt;This is true as far as it goes, but two types of objects in Access, Forms and Reports, muddy the waters considerably. Because form and reports are classes, controls on them are members of both the Objects Collection and a property of the form or report itself. &lt;p&gt;You can verify this by creating a new form or report object and looking at the Object Browser in the Visual Basic Editor. &lt;p&gt;(While it works the same on reports, I'm going to concentrate on forms for the moment.) &lt;p&gt;Create a new form: Form2 with no controls or Record Source. Open the Visual Basic editor and push &amp;lt;F2&amp;gt; to open the Object Browser . To the right, you'll see a list of Classes and Members. These members represent the properties and methods and properties of the selected Class. Access creates a number of default methods and properties which I'll ignore for now. &lt;p&gt;Next, create a new table: Table1(Table1ID, Field1, Field2). (See Figure 1) &lt;p&gt;&lt;a href="http://lh4.ggpht.com/-RwFkLwXa4o0/TpQh7deem9I/AAAAAAAAA08/oEAwDf2Uvmc/s1600-h/clip_image002%25255B4%25255D.jpg"&gt;&lt;img style="background-image: none; border-bottom: 0px; border-left: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="clip_image002" border="0" alt="clip_image002" src="http://lh3.ggpht.com/-0j10LmFDUv0/TpQh8K6-XSI/AAAAAAAAA1E/is8aM4yCJTs/clip_image002_thumb%25255B1%25255D.jpg?imgmax=800" width="351" height="147"&gt;&lt;/a&gt; &lt;p&gt;&lt;b&gt;Figure 1: Table1&lt;/b&gt; &lt;p&gt;(Note: throughout this post, my form's name will be MyForm and the control is called ControlName -- it could be any control, a textbox, combobox, label, or whatever) &lt;p&gt;Make this table the RecordSource for Form2. Table1ID, Field1, and Field2 appear in the member list. This demonstrates that the fields in the record source behind the form are properties of the form. See Figure 2. &lt;p&gt;&lt;a href="http://lh5.ggpht.com/-IX9qgMEZLWA/TpQh9b2r-NI/AAAAAAAAA1M/As_dbZgAyNI/s1600-h/clip_image004%25255B5%25255D.jpg"&gt;&lt;img style="background-image: none; border-bottom: 0px; border-left: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="clip_image004" border="0" alt="clip_image004" src="http://lh4.ggpht.com/-Yre22rCFpWY/TpQh-qEZSUI/AAAAAAAAA1U/L8WF8roRw_k/clip_image004_thumb%25255B2%25255D.jpg?imgmax=800" width="423" height="359"&gt;&lt;/a&gt; &lt;p&gt;&lt;b&gt;Figure 2: Table1ID in the members list of the Form2 class&lt;/b&gt; &lt;p&gt;Next I'll reference the fields behind the form. To reference an object on a form, you start with the Forms collection, followed by a Bang (!), followed by the form name. Like this Forms!Form2. This gives me a reference to the form itself. &lt;p&gt;Now, according to my definition above, following the form reference with a dot and the field name should work (because the fields are properties of the form) but the bang should not because I haven't created any controls yet. However, on testing, I find that both: &lt;p&gt;&lt;font face="Courier New"&gt;Forms!Form2.Table1ID&lt;br&gt;&lt;/font&gt;and&lt;br&gt;&lt;font face="Courier New"&gt;Forms!Form2!Table1ID&lt;/font&gt; &lt;p&gt;Return the value of Table1ID. &lt;p&gt;But even though they produce the same result, they aren't the same. It's really a case of two objects that mean different things but nevertheless almost always give the same result.&amp;nbsp; The bang (!) notation specifically denotes that what follows is a member of a collection; in this case, a member of the form object's default collection, the Controls collection. The dot (.) notation denotes that what follows is a property or method of the preceding object.&amp;nbsp;&amp;nbsp; &lt;p&gt;&lt;b&gt;ME Object&lt;/b&gt; &lt;p&gt;And then, just to muddy the waters even further, there's the "Me" object. The Me object is used in Visual Basic for Applications (VBA) to reference an instance of a class module. It is an implicitly declared variable and is available to every procedure within the class module and only within the class module. &lt;p&gt;Since Access Form and Reports Modules are classes, you can also use the Me object to refer to the Form or Report itself. This allows us to take a shorthand reference to object on a form. I'll address form referencing in a later post, but for now, I can reference a control on a form explicitly: &lt;p&gt;&lt;font face="Courier New"&gt;Forms!MyForm!ControlName&lt;br&gt;&lt;/font&gt;Or &lt;br&gt;&lt;font face="Courier New"&gt;Me!ControlName&lt;/font&gt; &lt;p&gt;But as I said, the Me object muddies the water because Me.ControlName also works. &lt;p&gt;I know why Me!ControlName works. It is really just a short-hand way of referring to the default collection and property of the Form object. &lt;p&gt;The Controls collection is the default collection of the Form object, and Items is the default property of the Controls collection. An explicit reference to a control looks like either of these: &lt;p&gt;&lt;font face="Courier New"&gt;Me.Controls.Item(0)&lt;/font&gt; (assuming 0 is the correct index)&lt;br&gt;&lt;font face="Courier New"&gt;Me.Controls.Item("ControlName")&lt;/font&gt; &lt;p&gt;Since Item is the default property, you can also do these: &lt;p&gt;&lt;font face="Courier New"&gt;Me.Controls("ControlName")&lt;br&gt;Me.Controls!ControlName&lt;/font&gt; &lt;p&gt;and since Controls is the default collection, you can do these: &lt;p&gt;&lt;font face="Courier New"&gt;Me("ControlName")&lt;br&gt;Me!ControlName&lt;/font&gt; &lt;p&gt;So what about Me.ControlName? &lt;p&gt;This is the really cool part about forms -- when a form loads, it helps you out by adding every control on the form as a property of the form.&amp;nbsp; This is why  &lt;p&gt;&lt;font face="Courier New"&gt;&amp;nbsp; Me.txtTextBox&lt;/font&gt; &lt;p&gt;.. works.&amp;nbsp; You're asking for the "txtTextBox" property of Forms!MyForm -- which is a pointer to the control, in this case, the text box object.  &lt;p&gt;&lt;b&gt;Which should you use?&lt;/b&gt; &lt;p&gt;So, which is actually preferred? The answer is ... it depends. &lt;p&gt;&lt;b&gt;Reasons to use Me Dot (Me.ControlName)&lt;/b&gt; &lt;ol&gt; &lt;li&gt;Automatic Intellisense support.&lt;/li&gt; &lt;li&gt;Runtime error if control is missing or mis-spelled.&lt;/li&gt; &lt;li&gt;Slightly faster than Me Bang.&lt;/li&gt;&lt;/ol&gt; &lt;p&gt;&lt;b&gt;Reasons to use Me Bang (Me!ControlName)&lt;/b&gt; &lt;ol&gt; &lt;li&gt;Me Bang ALWAYS works to reference the value of a control.&lt;/li&gt; &lt;li&gt;If a control is named the same as a reserved word (i.e. "Name"), Me Bang will correctly reference the control.&lt;/li&gt; &lt;li&gt;If the Record Source of a form is modified at run-time, Me Bang will continue to work.&lt;/li&gt; &lt;li&gt;Intellisense can be initiated with &amp;lt;ctrl&amp;gt;+&amp;lt;space&amp;gt;.&lt;/li&gt;&lt;/ol&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8134759511126815909-2313566848573988487?l=rogersaccessblog.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://rogersaccessblog.blogspot.com/feeds/2313566848573988487/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8134759511126815909&amp;postID=2313566848573988487' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8134759511126815909/posts/default/2313566848573988487'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8134759511126815909/posts/default/2313566848573988487'/><link rel='alternate' type='text/html' href='http://rogersaccessblog.blogspot.com/2011/10/bang-vs-dot-in-forms.html' title='Bang Vs. Dot in Forms'/><author><name>Roger's Access Blog</name><uri>http://www.blogger.com/profile/12265719129831415014</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_JYohVQdgpwU/Scu5h2VvtBI/AAAAAAAAAQc/pr3JDoLaqnQ/S220/avi_Roger_1.JPG'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://lh3.ggpht.com/-0j10LmFDUv0/TpQh8K6-XSI/AAAAAAAAA1E/is8aM4yCJTs/s72-c/clip_image002_thumb%25255B1%25255D.jpg?imgmax=800' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8134759511126815909.post-3748184931143645561</id><published>2011-10-10T06:35:00.001-04:00</published><updated>2011-10-10T06:56:52.514-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Access 101'/><title type='text'>How do I calculate a Median in Access? Part 2</title><content type='html'>&lt;p&gt;&lt;strong&gt;The DMedian() Function:&lt;/strong&gt;&lt;/p&gt; &lt;p&gt;In my last post, (&lt;a href="http://rogersaccessblog.blogspot.com/2011/10/how-do-i-calculate-median-in-access.html" target="_blank"&gt;&lt;u&gt;How do I calculate a Median in Access? Part 1&lt;/u&gt;&lt;/a&gt;&lt;u&gt;)&lt;/u&gt; I defined Median and described in general terms how to calculate a median in Access. This time, I'm going to describe in detail the &lt;strong&gt;DMedian()&lt;/strong&gt; user-defined function.  &lt;p&gt;First of all, you have to create a Public Function in a General Module. Create a new module by selecting &lt;b&gt;Module&lt;/b&gt; in the &lt;b&gt;Database Window&lt;/b&gt; and click the &lt;b&gt;New&lt;/b&gt; button. Immediately click the &lt;b&gt;Save&lt;/b&gt; button and name the module "&lt;b&gt;basDMedian&lt;/b&gt;". Next, type the following function into the module.  &lt;p&gt;&lt;font face="Courier New"&gt;Public Function DMedian(FieldName As String, _ &lt;br&gt;&lt;/font&gt;&lt;font face="Courier New"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; TableName As String, _ &lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Optional Criteria As Variant) As Variant&lt;/font&gt;&lt;/p&gt; &lt;p&gt;&lt;font face="Courier New"&gt;&lt;font color="#008000"&gt;' Created by Roger J. Carlson&lt;br&gt;&lt;/font&gt;' &lt;/font&gt;&lt;font face="Courier New"&gt;&lt;a href="http://www.rogersaccesslibrary.com'"&gt;&lt;font color="#0000ff"&gt;&lt;u&gt;http://www.rogersaccesslibrary.com&lt;/u&gt;&lt;/font&gt;&lt;/font&gt;&lt;font color="#008000" face="Courier New"&gt;&lt;br&gt;'&lt;/a&gt; Terms of use: You may use this function in any application, but&lt;br&gt;' it must include this notice.&lt;br&gt;&lt;br&gt;'Returns the median of a given field in a given table.&lt;br&gt;'Returns -1 if no recordset is created&lt;/font&gt;&lt;/p&gt; &lt;p&gt;&lt;font color="#008000" face="Courier New"&gt;' You use this function much like the built-in Domain functions&lt;br&gt;' (DLookUp, DMax, and so on). That is, you must provide the&lt;br&gt;' 1) field name, 2) table name, and 3) a 'Where' Criteria.&lt;br&gt;' When used in an aggregate query, you MUST add each field&lt;br&gt;' in the GROUP BY clause into the into the Where Criteria&lt;br&gt;' of this function.&lt;/font&gt;&lt;/p&gt; &lt;p&gt;&lt;font color="#008000" face="Courier New"&gt;' See Help for more on Domain Aggregate functions.&lt;/font&gt;  &lt;p&gt;&lt;font face="Courier New"&gt;On Error GoTo Err_Median&lt;/font&gt;&lt;/p&gt; &lt;p&gt;&lt;font face="Courier New"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; Dim db As DAO.Database&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; Dim rs As DAO.Recordset&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; Dim strSQL As String&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; Dim RowCount As Long&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; Dim LowMedian As Double, HighMedian As Double&lt;/font&gt;&lt;/p&gt; &lt;p&gt;&lt;font face="Courier New"&gt;&lt;font color="#008000"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; 'Open a recordset on the table.&lt;br&gt;&lt;/font&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; Set db = CurrentDb&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; strSQL = "SELECT " &amp;amp; FieldName &amp;amp; " FROM " &amp;amp; TableName&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; If Not IsMissing(Criteria) Then&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; strSQL = strSQL &amp;amp; " WHERE " &amp;amp; Criteria &amp;amp; " ORDER BY " &amp;amp; FieldName&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; Else&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; strSQL = strSQL &amp;amp; " ORDER BY " &amp;amp; FieldName&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; End If&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; Set rs = db.OpenRecordset(strSQL, dbOpenDynaset)&lt;/font&gt;&lt;/p&gt; &lt;p&gt;&lt;font face="Courier New"&gt;&lt;font color="#008000"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; 'Find the number of rows in the table.&lt;br&gt;&lt;/font&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; rs.MoveLast&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; RowCount = rs.RecordCount&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; rs.MoveFirst&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br&gt;&lt;/font&gt;&lt;font face="Courier New"&gt;&lt;font color="#008000"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; 'Determine Even or Odd&lt;br&gt;&lt;/font&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; If RowCount Mod 2 = 0 Then&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 'There is an even number of records. Determine the low and high&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 'values in the middle and average them.&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; rs.Move Int(RowCount / 2) - 1&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; LowMedian = rs(FieldName)&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; rs.Move 1&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; HighMedian = rs(FieldName)&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 'Return Median&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; DMedian = (LowMedian + HighMedian) / 2&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; Else&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 'There is an odd number of records. Return the value exactly in&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 'the middle.&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; rs.Move Int(RowCount / 2)&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 'Return Median&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; DMedian = rs(FieldName)&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; End If&lt;/font&gt;&lt;/p&gt; &lt;p&gt;&lt;font face="Courier New"&gt;Exit_Median:&lt;br&gt;&lt;/font&gt;&lt;font face="Courier New"&gt;&lt;font color="#008000"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; 'close recordset&lt;br&gt;&lt;/font&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; rs.Close&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; Exit Function&lt;/font&gt;&lt;/p&gt; &lt;p&gt;&lt;font face="Courier New"&gt;Err_Median:&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; If Err.Number = 3075 Then&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; DMedian = 0&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Resume Exit_Median&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; ElseIf Err.Number = 3021 Then&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 'EOF or BOF ie no recordset created&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; DMedian = -1&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Resume Exit_Median&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; Else&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; MsgBox Err.Description&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Resume Exit_Median&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; End If&lt;br&gt;End Function&lt;/font&gt;&lt;/p&gt; &lt;p&gt;To use this function in a query, simply use the query above that calculates the average with the DAvg, and replace DAvg with DMedian, like this:  &lt;p&gt;&lt;font face="Courier New"&gt;SELECT Hospital, DMedian("TimeToProc", "tblTimeToProcedure", &lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; "[Hospital] = '" &amp;amp; Hospital &amp;amp;"'") AS MedianTimeToProc&lt;br&gt;FROM tblTimeToProcedure&lt;br&gt;GROUP BY Hospital&lt;/font&gt;  &lt;p&gt;Which will return this:  &lt;table border="1" cellspacing="0" cellpadding="0"&gt; &lt;tbody&gt; &lt;tr&gt; &lt;td valign="top" width="84"&gt; &lt;p&gt;Hospital&lt;/p&gt;&lt;/td&gt; &lt;td valign="top" width="149"&gt; &lt;p&gt;MedianTimeToProc&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt; &lt;tr&gt; &lt;td valign="top" width="84"&gt; &lt;p&gt;A&lt;/p&gt;&lt;/td&gt; &lt;td valign="bottom" width="149"&gt; &lt;p&gt;2.60&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt; &lt;tr&gt; &lt;td valign="top" width="84"&gt; &lt;p&gt;B&lt;/p&gt;&lt;/td&gt; &lt;td valign="bottom" width="149"&gt; &lt;p&gt;3.5&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt; &lt;p&gt;&lt;b&gt;Caveats:&lt;/b&gt;  &lt;p&gt;You have to be careful, however, because it is very easy to produce data that looks correct, but is not. If you're not careful to include every field contained in the Where clause and Group By clause of your main query in the Criteria of the DMedian function, your data will not be correct.  &lt;p&gt;Let's look at a slightly more complex example. Suppose we want to aggregate our TimeToProc data at the Hospital and Unit level. We might have a table that looks like this:  &lt;p&gt;tblTimeToProcedure  &lt;table border="1" cellspacing="0" cellpadding="0"&gt; &lt;tbody&gt; &lt;tr&gt; &lt;td valign="top" width="91"&gt; &lt;p&gt;PatientID&lt;/p&gt;&lt;/td&gt; &lt;td valign="top" width="84"&gt; &lt;p&gt;Hospital&lt;/p&gt;&lt;/td&gt; &lt;td valign="top" width="95"&gt; &lt;p&gt;Unit&lt;/p&gt;&lt;/td&gt; &lt;td valign="top" width="95"&gt; &lt;p&gt;TimeToProc&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt; &lt;tr&gt; &lt;td valign="top" width="91"&gt; &lt;p&gt;1&lt;/p&gt;&lt;/td&gt; &lt;td valign="top" width="84"&gt; &lt;p&gt;A&lt;/p&gt;&lt;/td&gt; &lt;td valign="top" width="95"&gt; &lt;p&gt;C1&lt;/p&gt;&lt;/td&gt; &lt;td valign="bottom" width="95"&gt; &lt;p&gt;1.5&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt; &lt;tr&gt; &lt;td valign="top" width="91"&gt; &lt;p&gt;2&lt;/p&gt;&lt;/td&gt; &lt;td valign="top" width="84"&gt; &lt;p&gt;A&lt;/p&gt;&lt;/td&gt; &lt;td valign="top" width="95"&gt; &lt;p&gt;C2&lt;/p&gt;&lt;/td&gt; &lt;td valign="bottom" width="95"&gt; &lt;p&gt;2&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt; &lt;tr&gt; &lt;td valign="top" width="91"&gt; &lt;p&gt;3&lt;/p&gt;&lt;/td&gt; &lt;td valign="top" width="84"&gt; &lt;p&gt;A&lt;/p&gt;&lt;/td&gt; &lt;td valign="top" width="95"&gt; &lt;p&gt;C1&lt;/p&gt;&lt;/td&gt; &lt;td valign="bottom" width="95"&gt; &lt;p&gt;2.2&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt; &lt;tr&gt; &lt;td valign="top" width="91"&gt; &lt;p&gt;4&lt;/p&gt;&lt;/td&gt; &lt;td valign="top" width="84"&gt; &lt;p&gt;A&lt;/p&gt;&lt;/td&gt; &lt;td valign="top" width="95"&gt; &lt;p&gt;C2&lt;/p&gt;&lt;/td&gt; &lt;td valign="bottom" width="95"&gt; &lt;p&gt;3&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt; &lt;tr&gt; &lt;td valign="top" width="91"&gt; &lt;p&gt;5&lt;/p&gt;&lt;/td&gt; &lt;td valign="top" width="84"&gt; &lt;p&gt;A&lt;/p&gt;&lt;/td&gt; &lt;td valign="top" width="95"&gt; &lt;p&gt;C1&lt;/p&gt;&lt;/td&gt; &lt;td valign="bottom" width="95"&gt; &lt;p&gt;2&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt; &lt;tr&gt; &lt;td valign="top" width="91"&gt; &lt;p&gt;6&lt;/p&gt;&lt;/td&gt; &lt;td valign="top" width="84"&gt; &lt;p&gt;A&lt;/p&gt;&lt;/td&gt; &lt;td valign="top" width="95"&gt; &lt;p&gt;C1&lt;/p&gt;&lt;/td&gt; &lt;td valign="bottom" width="95"&gt; &lt;p&gt;4&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt; &lt;tr&gt; &lt;td valign="top" width="91"&gt; &lt;p&gt;7&lt;/p&gt;&lt;/td&gt; &lt;td valign="top" width="84"&gt; &lt;p&gt;A&lt;/p&gt;&lt;/td&gt; &lt;td valign="top" width="95"&gt; &lt;p&gt;C2&lt;/p&gt;&lt;/td&gt; &lt;td valign="bottom" width="95"&gt; &lt;p&gt;5&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt; &lt;tr&gt; &lt;td valign="top" width="91"&gt; &lt;p&gt;8&lt;/p&gt;&lt;/td&gt; &lt;td valign="top" width="84"&gt; &lt;p&gt;A&lt;/p&gt;&lt;/td&gt; &lt;td valign="top" width="95"&gt; &lt;p&gt;C2&lt;/p&gt;&lt;/td&gt; &lt;td valign="bottom" width="95"&gt; &lt;p&gt;100&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt; &lt;tr&gt; &lt;td valign="top" width="91"&gt; &lt;p&gt;9&lt;/p&gt;&lt;/td&gt; &lt;td valign="top" width="84"&gt; &lt;p&gt;B&lt;/p&gt;&lt;/td&gt; &lt;td valign="top" width="95"&gt; &lt;p&gt;A1&lt;/p&gt;&lt;/td&gt; &lt;td valign="bottom" width="95"&gt; &lt;p&gt;2.5&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt; &lt;tr&gt; &lt;td valign="top" width="91"&gt; &lt;p&gt;10&lt;/p&gt;&lt;/td&gt; &lt;td valign="top" width="84"&gt; &lt;p&gt;B&lt;/p&gt;&lt;/td&gt; &lt;td valign="top" width="95"&gt; &lt;p&gt;A1&lt;/p&gt;&lt;/td&gt; &lt;td valign="bottom" width="95"&gt; &lt;p&gt;2&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt; &lt;tr&gt; &lt;td valign="top" width="91"&gt; &lt;p&gt;11&lt;/p&gt;&lt;/td&gt; &lt;td valign="top" width="84"&gt; &lt;p&gt;B&lt;/p&gt;&lt;/td&gt; &lt;td valign="top" width="95"&gt; &lt;p&gt;A1&lt;/p&gt;&lt;/td&gt; &lt;td valign="bottom" width="95"&gt; &lt;p&gt;4.2&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt; &lt;tr&gt; &lt;td valign="top" width="91"&gt; &lt;p&gt;12&lt;/p&gt;&lt;/td&gt; &lt;td valign="top" width="84"&gt; &lt;p&gt;B&lt;/p&gt;&lt;/td&gt; &lt;td valign="top" width="95"&gt; &lt;p&gt;A2&lt;/p&gt;&lt;/td&gt; &lt;td valign="bottom" width="95"&gt; &lt;p&gt;3&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt; &lt;tr&gt; &lt;td valign="top" width="91"&gt; &lt;p&gt;13&lt;/p&gt;&lt;/td&gt; &lt;td valign="top" width="84"&gt; &lt;p&gt;B&lt;/p&gt;&lt;/td&gt; &lt;td valign="top" width="95"&gt; &lt;p&gt;A2&lt;/p&gt;&lt;/td&gt; &lt;td valign="bottom" width="95"&gt; &lt;p&gt;2.2&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt; &lt;tr&gt; &lt;td valign="top" width="91"&gt; &lt;p&gt;14&lt;/p&gt;&lt;/td&gt; &lt;td valign="top" width="84"&gt; &lt;p&gt;B&lt;/p&gt;&lt;/td&gt; &lt;td valign="top" width="95"&gt; &lt;p&gt;A2&lt;/p&gt;&lt;/td&gt; &lt;td valign="bottom" width="95"&gt; &lt;p&gt;4&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt; &lt;tr&gt; &lt;td valign="top" width="91"&gt; &lt;p&gt;15&lt;/p&gt;&lt;/td&gt; &lt;td valign="top" width="84"&gt; &lt;p&gt;B&lt;/p&gt;&lt;/td&gt; &lt;td valign="top" width="95"&gt; &lt;p&gt;A1&lt;/p&gt;&lt;/td&gt; &lt;td valign="bottom" width="95"&gt; &lt;p&gt;5&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt; &lt;tr&gt; &lt;td valign="top" width="91"&gt; &lt;p&gt;16&lt;/p&gt;&lt;/td&gt; &lt;td valign="top" width="84"&gt; &lt;p&gt;B&lt;/p&gt;&lt;/td&gt; &lt;td valign="top" width="95"&gt; &lt;p&gt;A1&lt;/p&gt;&lt;/td&gt; &lt;td valign="bottom" width="95"&gt; &lt;p&gt;6&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt; &lt;p&gt;Suppose I create a totals query in Access that aggregates the data on &lt;b&gt;Hospital&lt;/b&gt; and &lt;b&gt;Unit&lt;/b&gt;, but only provide the &lt;b&gt;Hospital&lt;/b&gt; to the DMedian function's criteria. Like this:  &lt;p&gt;&lt;font face="Courier New"&gt;SELECT Hospital, Unit, DMedian("TimeToProc","tblTimeToProcedure",&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; "[Hospital] = '" &amp;amp; [Hospital] &amp;amp; "'") AS MedianTimeToProc&lt;br&gt;FROM tblTimeToProcedure&lt;br&gt;GROUP BY Hospital, Unit;&lt;/font&gt;  &lt;p&gt;I'll get a result that looks like this:  &lt;table border="1" cellspacing="0" cellpadding="0"&gt; &lt;tbody&gt; &lt;tr&gt; &lt;td width="64"&gt; &lt;p&gt;Hospital&lt;/p&gt;&lt;/td&gt; &lt;td width="38"&gt; &lt;p&gt;Unit&lt;/p&gt;&lt;/td&gt; &lt;td width="137"&gt; &lt;p&gt;MedianTimeToProc&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt; &lt;tr&gt; &lt;td valign="top" width="64"&gt; &lt;p&gt;A&lt;/p&gt;&lt;/td&gt; &lt;td valign="top" width="38"&gt; &lt;p&gt;C1&lt;/p&gt;&lt;/td&gt; &lt;td valign="top" width="137"&gt; &lt;p&gt;2.60&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt; &lt;tr&gt; &lt;td valign="top" width="64"&gt; &lt;p&gt;A&lt;/p&gt;&lt;/td&gt; &lt;td valign="top" width="38"&gt; &lt;p&gt;C2&lt;/p&gt;&lt;/td&gt; &lt;td valign="top" width="137"&gt; &lt;p&gt;2.60&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt; &lt;tr&gt; &lt;td valign="top" width="64"&gt; &lt;p&gt;B&lt;/p&gt;&lt;/td&gt; &lt;td valign="top" width="38"&gt; &lt;p&gt;A1&lt;/p&gt;&lt;/td&gt; &lt;td valign="top" width="137"&gt; &lt;p&gt;3.5&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt; &lt;tr&gt; &lt;td valign="top" width="64"&gt; &lt;p&gt;B&lt;/p&gt;&lt;/td&gt; &lt;td valign="top" width="38"&gt; &lt;p&gt;A2&lt;/p&gt;&lt;/td&gt; &lt;td valign="top" width="137"&gt; &lt;p&gt;3.5&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt; &lt;p&gt;This is obviously wrong. The DMedian function is aggregating ONLY at the Hospital level, while the main query is aggregating at both Hospital and Unit.  &lt;p&gt;In order to produce the correct median values, we have to supply both the Hospital and the Unit to the DMedian function.  &lt;p&gt;&lt;font face="Courier New"&gt;DMedian("TimeToProc", "tblTimeToProcedure", "[Hospital] = '" &amp;amp; Hospital &amp;amp;"' AND [Unit] = '" &amp;amp; Unit &amp;amp; "'")&lt;/font&gt;  &lt;p&gt;The finished query will look like this:  &lt;p&gt;&lt;font face="Courier New"&gt;SELECT Hospital, Unit, DMedian("TimeToProc", "tblTimeToProcedure", &lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; "[Hospital] = '" &amp;amp; Hospital &amp;amp;"'AND [Unit] = '" &amp;amp; Unit &amp;amp; "'") AS MedianTimeToProc&lt;br&gt;FROM tblTimeToProcedure&lt;br&gt;GROUP BY Hospital, Unit;&lt;/font&gt;  &lt;p&gt;This will produce the correct results:  &lt;table border="1" cellspacing="0" cellpadding="0"&gt; &lt;tbody&gt; &lt;tr&gt; &lt;td width="64"&gt; &lt;p&gt;Hospital&lt;/p&gt;&lt;/td&gt; &lt;td width="38"&gt; &lt;p&gt;Unit&lt;/p&gt;&lt;/td&gt; &lt;td width="137"&gt; &lt;p&gt;MedianTimeToProc&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt; &lt;tr&gt; &lt;td valign="top" width="64"&gt; &lt;p&gt;A&lt;/p&gt;&lt;/td&gt; &lt;td valign="top" width="38"&gt; &lt;p&gt;C1&lt;/p&gt;&lt;/td&gt; &lt;td valign="top" width="137"&gt; &lt;p&gt;2.10&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt; &lt;tr&gt; &lt;td valign="top" width="64"&gt; &lt;p&gt;A&lt;/p&gt;&lt;/td&gt; &lt;td valign="top" width="38"&gt; &lt;p&gt;C2&lt;/p&gt;&lt;/td&gt; &lt;td valign="top" width="137"&gt; &lt;p&gt;4&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt; &lt;tr&gt; &lt;td valign="top" width="64"&gt; &lt;p&gt;B&lt;/p&gt;&lt;/td&gt; &lt;td valign="top" width="38"&gt; &lt;p&gt;A1&lt;/p&gt;&lt;/td&gt; &lt;td valign="top" width="137"&gt; &lt;p&gt;4.2&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt; &lt;tr&gt; &lt;td valign="top" width="64"&gt; &lt;p&gt;B&lt;/p&gt;&lt;/td&gt; &lt;td valign="top" width="38"&gt; &lt;p&gt;A2&lt;/p&gt;&lt;/td&gt; &lt;td valign="top" width="137"&gt; &lt;p&gt;3&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt; &lt;p&gt;So the rule is that every field that participates in the &lt;b&gt;Where&lt;/b&gt; clause or &lt;b&gt;Group By&lt;/b&gt; clause of the main aggregate query must also be represented in the &lt;b&gt;Criteria&lt;/b&gt; argument of the domain aggregate function.  &lt;p&gt;&lt;strong&gt;Making Sure Your Query is Correct&lt;/strong&gt;&lt;/p&gt; &lt;p&gt;One way to make sure you are aggregating the data correctly in both the main query and the domain aggregate function is to create an aggregate query using the SQL AVG function, and then create the same query using the DAvg domain aggregate function. If the two queries produce the same values, you know you have created it correctly. Then just replace DAvg with your DMedian function.  &lt;p&gt;For instance, first create an AVERAGE aggregate query using the SQL AVG function. The SQL statement would look like this:  &lt;p&gt;&lt;font face="Courier New"&gt;SELECT Hospital, Unit, AVG(TimeToProc) AS AvgerageTimeToProc&lt;br&gt;FROM tblTimeToProcedure&lt;br&gt;GROUP BY Hospital, Unit&lt;/font&gt;&lt;/p&gt; &lt;p&gt; &lt;table border="1" cellspacing="0" cellpadding="0"&gt; &lt;tbody&gt; &lt;tr&gt; &lt;td width="64"&gt; &lt;p&gt;Hospital&lt;/p&gt;&lt;/td&gt; &lt;td width="38"&gt; &lt;p&gt;Unit&lt;/p&gt;&lt;/td&gt; &lt;td width="137"&gt; &lt;p&gt;AverageTimeToProc&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt; &lt;tr&gt; &lt;td valign="top" width="64"&gt; &lt;p&gt;A&lt;/p&gt;&lt;/td&gt; &lt;td valign="top" width="38"&gt; &lt;p&gt;C1&lt;/p&gt;&lt;/td&gt; &lt;td valign="top" width="137"&gt; &lt;p&gt;2.43&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt; &lt;tr&gt; &lt;td valign="top" width="64"&gt; &lt;p&gt;A&lt;/p&gt;&lt;/td&gt; &lt;td valign="top" width="38"&gt; &lt;p&gt;C2&lt;/p&gt;&lt;/td&gt; &lt;td valign="top" width="137"&gt; &lt;p&gt;27.50&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt; &lt;tr&gt; &lt;td valign="top" width="64"&gt; &lt;p&gt;B&lt;/p&gt;&lt;/td&gt; &lt;td valign="top" width="38"&gt; &lt;p&gt;A1&lt;/p&gt;&lt;/td&gt; &lt;td valign="top" width="137"&gt; &lt;p&gt;3.94&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt; &lt;tr&gt; &lt;td valign="top" width="64"&gt; &lt;p&gt;B&lt;/p&gt;&lt;/td&gt; &lt;td valign="top" width="38"&gt; &lt;p&gt;A2&lt;/p&gt;&lt;/td&gt; &lt;td valign="top" width="137"&gt; &lt;p&gt;3.07&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/p&gt; &lt;p&gt;Now create the same query, but this time use the DAvg function.  &lt;p&gt;&lt;font face="Courier New"&gt;SELECT Hospital, Unit, DAvg("TimeToProc", "tblTimeToProcedure", &lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; "[Hospital] = '" &amp;amp; Hospital &amp;amp;"' AND [Unit] = '" &amp;amp; Unit &amp;amp; "'") AS &lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; AverageTimeToProc&lt;br&gt;FROM tblTimeToProcedure&lt;br&gt;GROUP BY Hospital, Unit;&lt;/font&gt;&lt;/p&gt; &lt;p&gt; &lt;table border="1" cellspacing="0" cellpadding="0"&gt; &lt;tbody&gt; &lt;tr&gt; &lt;td width="64"&gt; &lt;p&gt;Hospital&lt;/p&gt;&lt;/td&gt; &lt;td width="38"&gt; &lt;p&gt;Unit&lt;/p&gt;&lt;/td&gt; &lt;td width="145"&gt; &lt;p&gt;AverageTimeToProc&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt; &lt;tr&gt; &lt;td valign="top" width="64"&gt; &lt;p&gt;A&lt;/p&gt;&lt;/td&gt; &lt;td valign="top" width="38"&gt; &lt;p&gt;C1&lt;/p&gt;&lt;/td&gt; &lt;td valign="top" width="145"&gt; &lt;p&gt;2.43&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt; &lt;tr&gt; &lt;td valign="top" width="64"&gt; &lt;p&gt;A&lt;/p&gt;&lt;/td&gt; &lt;td valign="top" width="38"&gt; &lt;p&gt;C2&lt;/p&gt;&lt;/td&gt; &lt;td valign="top" width="145"&gt; &lt;p&gt;27.50&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt; &lt;tr&gt; &lt;td valign="top" width="64"&gt; &lt;p&gt;B&lt;/p&gt;&lt;/td&gt; &lt;td valign="top" width="38"&gt; &lt;p&gt;A1&lt;/p&gt;&lt;/td&gt; &lt;td valign="top" width="145"&gt; &lt;p&gt;3.94&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt; &lt;tr&gt; &lt;td valign="top" width="64"&gt; &lt;p&gt;B&lt;/p&gt;&lt;/td&gt; &lt;td valign="top" width="38"&gt; &lt;p&gt;A2&lt;/p&gt;&lt;/td&gt; &lt;td valign="top" width="145"&gt; &lt;p&gt;3.07&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/p&gt; &lt;p&gt;Since the two queries produce identical results, we know that we have set up the domain function correctly. Now just replace DAvg with your DMedian function:  &lt;p&gt;&lt;font face="Courier New"&gt;SELECT Hospital, Unit, DMedian("TimeToProc", "tblTimeToProcedure", &lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; "[Hospital] = '" &amp;amp; Hospital &amp;amp;"' AND [Unit] = '" &amp;amp; Unit &amp;amp; "'") AS&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; MedianTimeToProc&lt;br&gt;FROM tblTimeToProcedure&lt;br&gt;GROUP BY Hospital, Unit;&lt;/font&gt;&lt;/p&gt; &lt;p&gt; &lt;table border="1" cellspacing="0" cellpadding="0"&gt; &lt;tbody&gt; &lt;tr&gt; &lt;td width="58"&gt; &lt;p&gt;Hospital&lt;/p&gt;&lt;/td&gt; &lt;td width="34"&gt; &lt;p&gt;Unit&lt;/p&gt;&lt;/td&gt; &lt;td width="132"&gt; &lt;p&gt;MedianTimeToProc&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt; &lt;tr&gt; &lt;td valign="top" width="58"&gt; &lt;p&gt;A&lt;/p&gt;&lt;/td&gt; &lt;td valign="top" width="34"&gt; &lt;p&gt;C1&lt;/p&gt;&lt;/td&gt; &lt;td valign="top" width="132"&gt; &lt;p&gt;2.10&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt; &lt;tr&gt; &lt;td valign="top" width="58"&gt; &lt;p&gt;A&lt;/p&gt;&lt;/td&gt; &lt;td valign="top" width="34"&gt; &lt;p&gt;C2&lt;/p&gt;&lt;/td&gt; &lt;td valign="top" width="132"&gt; &lt;p&gt;4&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt; &lt;tr&gt; &lt;td valign="top" width="58"&gt; &lt;p&gt;B&lt;/p&gt;&lt;/td&gt; &lt;td valign="top" width="34"&gt; &lt;p&gt;A1&lt;/p&gt;&lt;/td&gt; &lt;td valign="top" width="132"&gt; &lt;p&gt;4.2&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt; &lt;tr&gt; &lt;td valign="top" width="58"&gt; &lt;p&gt;B&lt;/p&gt;&lt;/td&gt; &lt;td valign="top" width="34"&gt; &lt;p&gt;A2&lt;/p&gt;&lt;/td&gt; &lt;td valign="top" width="132"&gt; &lt;p&gt;3&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/p&gt; &lt;p&gt;&lt;b&gt;On-line Database Sample:&lt;/b&gt;  &lt;p&gt;&lt;b&gt;&lt;/b&gt; &lt;p&gt;On my website, (&lt;a href="http://www.rogersaccesslibrary.com"&gt;&lt;u&gt;http://www.rogersaccesslibrary.com&lt;/u&gt;&lt;/a&gt;), there is a small sample database called Median.mdb. This sample has the above example and another, more complex, example in an Access database, so you can see how the DMedian function works.  &lt;p&gt;There are two versions of the DMedian function. DMedian is in the basMedianADO module, and DMedian is in the basMedianDAO. You must use the DMedian97 in an Access 97 database.  &lt;p&gt;&lt;b&gt;Conclusion:&lt;/b&gt;  &lt;p&gt;Because Microsoft Access&lt;sup&gt;®&lt;/sup&gt; does not have a Median aggregate function, finding the median value of a group of data can be a challenge. But with a little programming expertise, you can create your own DMedian domain-aggregate function that you can use to find the Median value for an aggregate query.&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8134759511126815909-3748184931143645561?l=rogersaccessblog.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://rogersaccessblog.blogspot.com/feeds/3748184931143645561/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8134759511126815909&amp;postID=3748184931143645561' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8134759511126815909/posts/default/3748184931143645561'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8134759511126815909/posts/default/3748184931143645561'/><link rel='alternate' type='text/html' href='http://rogersaccessblog.blogspot.com/2011/10/how-do-i-calculate-median-in-access_10.html' title='How do I calculate a Median in Access? Part 2'/><author><name>Roger's Access Blog</name><uri>http://www.blogger.com/profile/12265719129831415014</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_JYohVQdgpwU/Scu5h2VvtBI/AAAAAAAAAQc/pr3JDoLaqnQ/S220/avi_Roger_1.JPG'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8134759511126815909.post-7330326200954010381</id><published>2011-10-07T06:50:00.001-04:00</published><updated>2011-10-07T07:00:08.449-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Access 101'/><title type='text'>How do I calculate a Median in Access?</title><content type='html'>&lt;p&gt;Users of Microsoft Excel&lt;sup&gt;®&lt;/sup&gt; are familiar with the MEDIAN function. Select a range of cells, push the function button, select MEDIAN under statistical functions, and you have your median value. However, no such function exists in Microsoft Access&lt;sup&gt;®&lt;/sup&gt;. So if you need to figure a median in Access, what do you do? One solution is to create your own Median domain aggregate function or DMedian().  &lt;p&gt;But to understand how to program a median function, you must first understand how a median is calculated.  &lt;p&gt;&lt;b&gt;Calculating a Median:&lt;/b&gt;  &lt;p&gt;Almost everyone knows what an average is. Add up all the values in your list and divide that by the number of values. In general, it looks like this: (a&lt;sub&gt;1&lt;/sub&gt;+a&lt;sub&gt;2&lt;/sub&gt;+...+a&lt;sub&gt;n&lt;/sub&gt;)/n. In most cases, this gives a pretty good aggregate picture of your data. However, if you have values that fall way out of the general range of the others, it can give you a skewed picture.  &lt;p&gt;This is where the median becomes important. A median works differently. To find the median, you sort your list (ascending or descending, makes no difference), count the number of values in your list, and then divide that number by two. If the list has an odd number of values, the result will be a fraction (n.5). Take the integer portion of the resultant (n) and go to the (n+1)th value in your sorted list. That number becomes the median. If the list has an even number of values, the result will be a whole number (n). To figure a median with an even number of values, you take the integer portion of the resultant, (n) and average the nth and (n+1)th values.  &lt;p&gt;Example - Odd number of values:  &lt;table border="1" cellspacing="0" cellpadding="0"&gt; &lt;tbody&gt; &lt;tr&gt; &lt;td valign="bottom" width="64"&gt; &lt;p&gt;1.5&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt; &lt;tr&gt; &lt;td valign="bottom"&gt; &lt;p&gt;2&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt; &lt;tr&gt; &lt;td valign="bottom"&gt; &lt;p&gt;2.2&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt; &lt;tr&gt; &lt;td valign="bottom"&gt; &lt;p&gt;3&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt; &lt;tr&gt; &lt;td valign="bottom"&gt; &lt;p&gt;2&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt; &lt;tr&gt; &lt;td valign="bottom"&gt; &lt;p&gt;4&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt; &lt;tr&gt; &lt;td valign="bottom"&gt; &lt;p&gt;5&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt; &lt;p&gt;Average: (1.5+2+2.2+3+2+4+5)/7 = 2.814286.  &lt;p&gt;Median: First, order the list (1.5, 2, 2, 2.2, 3, 4, 5). Counting the number of values returns 7. Divide 7 by 2 and you get 3.5. Taking the integer portion of 3.5 yields 3. Go to the (3+1)th (or 4th) value in the ordered list and you have your median of 2.2.  &lt;p&gt;Example - Even number of values:  &lt;table border="1" cellspacing="0" cellpadding="0"&gt; &lt;tbody&gt; &lt;tr&gt; &lt;td valign="bottom" width="64"&gt; &lt;p&gt;1.5&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt; &lt;tr&gt; &lt;td valign="bottom"&gt; &lt;p&gt;2&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt; &lt;tr&gt; &lt;td valign="bottom"&gt; &lt;p&gt;2.2&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt; &lt;tr&gt; &lt;td valign="bottom"&gt; &lt;p&gt;3&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt; &lt;tr&gt; &lt;td valign="bottom"&gt; &lt;p&gt;2&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt; &lt;tr&gt; &lt;td valign="bottom"&gt; &lt;p&gt;4&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt; &lt;tr&gt; &lt;td valign="bottom"&gt; &lt;p&gt;5&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt; &lt;tr&gt; &lt;td valign="bottom"&gt; &lt;p&gt;1&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt; &lt;p&gt;Average: (1.5+2+2.2+3+2+4+5+1)/8 = 2.5875.  &lt;p&gt;Median: Again, order the list (1, 1.5, 2, 2, 2.2, 3, 4, 5). There are 8 values in the list. Dividing 8 by 2 equals 4. Select the 4th and (4+1)th (that is 5th) value in the ordered list results in 2 and 2.2. Averaging these numbers results in your median value of 2.1.  &lt;p&gt;As you can see, in both cases, the values of the average and median are fairly close. In the Odd case: 2.814286 vs. 2.2. In the Even case: 2.5875 vs. 2.1. But what if we had an outlier; say 100 (in place of 5), in the list?  &lt;table border="1" cellspacing="0" cellpadding="0"&gt; &lt;tbody&gt; &lt;tr&gt; &lt;td valign="bottom" width="64"&gt; &lt;p&gt;&lt;a name="OLE_LINK1"&gt;1.5&lt;/a&gt;&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt; &lt;tr&gt; &lt;td valign="bottom"&gt; &lt;p&gt;2&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt; &lt;tr&gt; &lt;td valign="bottom"&gt; &lt;p&gt;2.2&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt; &lt;tr&gt; &lt;td valign="bottom"&gt; &lt;p&gt;3&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt; &lt;tr&gt; &lt;td valign="bottom"&gt; &lt;p&gt;2&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt; &lt;tr&gt; &lt;td valign="bottom"&gt; &lt;p&gt;4&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt; &lt;tr&gt; &lt;td valign="bottom"&gt; &lt;p&gt;100&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt; &lt;tr&gt; &lt;td valign="bottom"&gt; &lt;p&gt;1&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt; &lt;p&gt;The median stays at 2.1, but the average jumps to 14.4625. Not very representative of your data. So there are times when it is statistically more accurate to use the median rather than the average.  &lt;p&gt;&lt;strong&gt;Calculating the Median in Access&lt;/strong&gt;  &lt;p&gt;But the question remains: how do you create a median in Microsoft Access? One answer is to create your own domain aggregate function: DMedian().  &lt;p&gt;So what are domain aggregate functions? Access has a number of built-in domain aggregate functions that work similarly to the SQL aggregate functions: MAX, MIN, SUM, COUNT, AVG (and others). They are DMax(), DMin, DSum, DCount, and DAvg. These functions act like mini totals (aggregate) queries that return a single value. You can use these functions as the source for a control on a form or report, or you can use them in a query. For more information on Domain functions, see my blog series: &lt;a href="http://rogersaccessblog.blogspot.com/2010/12/domain-functions-demystified.html"&gt;&lt;font color="#0000ff"&gt;&lt;u&gt;Domain Functions Demystified&lt;/u&gt;&lt;/font&gt;&lt;/a&gt;.  &lt;p&gt;There are three arguments you must provide to a domain function. Two are required: Fieldname, Tablename, and the third is optional: a Criteria (essentially a Where clause without the word WHERE). The domain function will then calculate the desired function (min, max, etc.) for that Field, against that Table, filtering it on the Criteria. In general: Dfunction("fieldname", "tablename", "criteria").  &lt;p&gt;Before we look at creating our own DMedian() function, let's look at how the DAvg() function works.  &lt;p&gt;Suppose we have the following table:  &lt;p&gt;&lt;b&gt;tblTimeToProcedure&lt;/b&gt;  &lt;table border="1" cellspacing="0" cellpadding="0"&gt; &lt;tbody&gt; &lt;tr&gt; &lt;td valign="top" width="91"&gt; &lt;p&gt;PatientID&lt;/p&gt;&lt;/td&gt; &lt;td valign="top" width="84"&gt; &lt;p&gt;Hospital&lt;/p&gt;&lt;/td&gt; &lt;td valign="top" width="95"&gt; &lt;p&gt;TimeToProc&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt; &lt;tr&gt; &lt;td valign="top" width="91"&gt; &lt;p&gt;1&lt;/p&gt;&lt;/td&gt; &lt;td valign="top" width="84"&gt; &lt;p&gt;A&lt;/p&gt;&lt;/td&gt; &lt;td valign="bottom" width="95"&gt; &lt;p&gt;1.5&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt; &lt;tr&gt; &lt;td valign="top" width="91"&gt; &lt;p&gt;2&lt;/p&gt;&lt;/td&gt; &lt;td valign="top" width="84"&gt; &lt;p&gt;A&lt;/p&gt;&lt;/td&gt; &lt;td valign="bottom" width="95"&gt; &lt;p&gt;2&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt; &lt;tr&gt; &lt;td valign="top" width="91"&gt; &lt;p&gt;3&lt;/p&gt;&lt;/td&gt; &lt;td valign="top" width="84"&gt; &lt;p&gt;A&lt;/p&gt;&lt;/td&gt; &lt;td valign="bottom" width="95"&gt; &lt;p&gt;2.2&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt; &lt;tr&gt; &lt;td valign="top" width="91"&gt; &lt;p&gt;4&lt;/p&gt;&lt;/td&gt; &lt;td valign="top" width="84"&gt; &lt;p&gt;A&lt;/p&gt;&lt;/td&gt; &lt;td valign="bottom" width="95"&gt; &lt;p&gt;3&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt; &lt;tr&gt; &lt;td valign="top" width="91"&gt; &lt;p&gt;5&lt;/p&gt;&lt;/td&gt; &lt;td valign="top" width="84"&gt; &lt;p&gt;A&lt;/p&gt;&lt;/td&gt; &lt;td valign="bottom" width="95"&gt; &lt;p&gt;2&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt; &lt;tr&gt; &lt;td valign="top" width="91"&gt; &lt;p&gt;6&lt;/p&gt;&lt;/td&gt; &lt;td valign="top" width="84"&gt; &lt;p&gt;A&lt;/p&gt;&lt;/td&gt; &lt;td valign="bottom" width="95"&gt; &lt;p&gt;4&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt; &lt;tr&gt; &lt;td valign="top" width="91"&gt; &lt;p&gt;7&lt;/p&gt;&lt;/td&gt; &lt;td valign="top" width="84"&gt; &lt;p&gt;A&lt;/p&gt;&lt;/td&gt; &lt;td valign="bottom" width="95"&gt; &lt;p&gt;5&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt; &lt;tr&gt; &lt;td valign="top" width="91"&gt; &lt;p&gt;8&lt;/p&gt;&lt;/td&gt; &lt;td valign="top" width="84"&gt; &lt;p&gt;A&lt;/p&gt;&lt;/td&gt; &lt;td valign="bottom" width="95"&gt; &lt;p&gt;100&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt; &lt;tr&gt; &lt;td valign="top" width="91"&gt; &lt;p&gt;9&lt;/p&gt;&lt;/td&gt; &lt;td valign="top" width="84"&gt; &lt;p&gt;B&lt;/p&gt;&lt;/td&gt; &lt;td valign="bottom" width="95"&gt; &lt;p&gt;2.5&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt; &lt;tr&gt; &lt;td valign="top" width="91"&gt; &lt;p&gt;10&lt;/p&gt;&lt;/td&gt; &lt;td valign="top" width="84"&gt; &lt;p&gt;B&lt;/p&gt;&lt;/td&gt; &lt;td valign="bottom" width="95"&gt; &lt;p&gt;2&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt; &lt;tr&gt; &lt;td valign="top" width="91"&gt; &lt;p&gt;11&lt;/p&gt;&lt;/td&gt; &lt;td valign="top" width="84"&gt; &lt;p&gt;B&lt;/p&gt;&lt;/td&gt; &lt;td valign="bottom" width="95"&gt; &lt;p&gt;4.2&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt; &lt;tr&gt; &lt;td valign="top" width="91"&gt; &lt;p&gt;12&lt;/p&gt;&lt;/td&gt; &lt;td valign="top" width="84"&gt; &lt;p&gt;B&lt;/p&gt;&lt;/td&gt; &lt;td valign="bottom" width="95"&gt; &lt;p&gt;3&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt; &lt;tr&gt; &lt;td valign="top" width="91"&gt; &lt;p&gt;13&lt;/p&gt;&lt;/td&gt; &lt;td valign="top" width="84"&gt; &lt;p&gt;B&lt;/p&gt;&lt;/td&gt; &lt;td valign="bottom" width="95"&gt; &lt;p&gt;2.2&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt; &lt;tr&gt; &lt;td valign="top" width="91"&gt; &lt;p&gt;14&lt;/p&gt;&lt;/td&gt; &lt;td valign="top" width="84"&gt; &lt;p&gt;B&lt;/p&gt;&lt;/td&gt; &lt;td valign="bottom" width="95"&gt; &lt;p&gt;4&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt; &lt;tr&gt; &lt;td valign="top" width="91"&gt; &lt;p&gt;15&lt;/p&gt;&lt;/td&gt; &lt;td valign="top" width="84"&gt; &lt;p&gt;B&lt;/p&gt;&lt;/td&gt; &lt;td valign="bottom" width="95"&gt; &lt;p&gt;5&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt; &lt;tr&gt; &lt;td valign="top" width="91"&gt; &lt;p&gt;16&lt;/p&gt;&lt;/td&gt; &lt;td valign="top" width="84"&gt; &lt;p&gt;B&lt;/p&gt;&lt;/td&gt; &lt;td valign="bottom" width="95"&gt; &lt;p&gt;6&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt; &lt;p&gt;(TimeToProc is the number of hours between patient admission and the time a give procedure was administered.)  &lt;p&gt;To calculate the average TimeToProc for the entire table with the DAvg function, you would provide the Field and Table without a Criteria. Like this:  &lt;p&gt;&lt;font face="Courier New"&gt;DAvg("TimeToProc", "tblTimeToProcedure")&lt;/font&gt;  &lt;p&gt;In this case, no Criteria required because we don't want to filter the data. However, suppose we wanted just the average of Hospital A? In that case, we would do this:  &lt;p&gt;&lt;font face="Courier New"&gt;DAvg("TimeToProc", "tblTimeToProcedure", "[Hospital]='A'")&lt;/font&gt;  &lt;p&gt;(Because Hospital is text data, you have to provide quotes (or single quotes) around it. Numeric data does not require delimiters. For more information on this, look in the Access Help at the DMin, DMax aggregate functions. Type DMin in the keywords.)  &lt;p&gt;You can also use the DAvg function in a Totals Query in Access to return the average for each hospital. Like this:  &lt;p&gt;&lt;font face="Courier New"&gt;SELECT Hospital, DAvg("TimeToProc", "tblTimeToProcedure", &lt;br&gt;"[Hospital] = '" &amp;amp; Hospital &amp;amp;"'") AS AverageTimeToProc&lt;br&gt;FROM tblTimeToProcedure&lt;br&gt;GROUP BY Hospital&lt;/font&gt;  &lt;p&gt;Which will return this:  &lt;table border="1" cellspacing="0" cellpadding="0"&gt; &lt;tbody&gt; &lt;tr&gt; &lt;td valign="top" width="84"&gt; &lt;p&gt;Hospital&lt;/p&gt;&lt;/td&gt; &lt;td valign="top" width="149"&gt; &lt;p&gt;AverageTimeToProc&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt; &lt;tr&gt; &lt;td valign="top" width="84"&gt; &lt;p&gt;A&lt;/p&gt;&lt;/td&gt; &lt;td valign="bottom" width="149"&gt; &lt;p&gt;14.9625&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt; &lt;tr&gt; &lt;td valign="top" width="84"&gt; &lt;p&gt;B&lt;/p&gt;&lt;/td&gt; &lt;td valign="bottom" width="149"&gt; &lt;p&gt;3.6125&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt; &lt;p&gt;The obvious question, of course, is why not just use the SQL aggregate function (AVG) to do the same thing:  &lt;p&gt;&lt;font face="Courier New"&gt;SELECT Hospital, AVG(Hospital) AS AverageTimeToProc&lt;br&gt;FROM tblTimeToProcedure&lt;br&gt;GROUP BY Hospital&lt;/font&gt;  &lt;p&gt;That's a good question, and in fact, it &lt;i&gt;would&lt;/i&gt; be better to us the SQL function. But what if Access didn't have an AVG function? This is the case with the MEDIAN. No such function exists, so we have to create our own DMedian function to replace it.  &lt;p&gt;Next time, I'll look at the details of the DMedian() function.&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8134759511126815909-7330326200954010381?l=rogersaccessblog.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://rogersaccessblog.blogspot.com/feeds/7330326200954010381/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8134759511126815909&amp;postID=7330326200954010381' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8134759511126815909/posts/default/7330326200954010381'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8134759511126815909/posts/default/7330326200954010381'/><link rel='alternate' type='text/html' href='http://rogersaccessblog.blogspot.com/2011/10/how-do-i-calculate-median-in-access.html' title='How do I calculate a Median in Access?'/><author><name>Roger's Access Blog</name><uri>http://www.blogger.com/profile/12265719129831415014</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_JYohVQdgpwU/Scu5h2VvtBI/AAAAAAAAAQc/pr3JDoLaqnQ/S220/avi_Roger_1.JPG'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8134759511126815909.post-1568610529179034132</id><published>2011-10-06T08:31:00.000-04:00</published><updated>2011-10-06T08:31:32.929-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='New Samples'/><category scheme='http://www.blogger.com/atom/ns#' term='Tejpal'/><title type='text'>New Sample: Form_ContinuousSimulated</title><content type='html'>By A. D. Tejpal&lt;br /&gt;&lt;br /&gt;This sample db demonstrates a simulated continuous form having unbound controls, facilitating row-wise display of unlimited colors.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;It displays 12 records at a time. Full coverage of data is provided via suitable navigation buttons. Apart from being able to edit the records, the user can add new records or delete the current record by clicking appropriate command buttons. In addition, two alternative modes for search have been provided: (a) By record number or (b) By ID number. All these actions are feasible directly on the form. Current record remains identified by special highlight in first column. &lt;br /&gt;&lt;br /&gt;First two columns (locked) display the record number and TrainID. Next two columns, holding TrainCode and ColorValue, are editable. Last column serves as ColorStrip, each row displaying the color represented by ColorValue in the previous column. &lt;br /&gt;&lt;br /&gt;There is two way synchronization between ColorValue and ColorStrip. Any value entered in the former gets reflected as corresponding color in the latter. On the other hand, double click on ColorStrip invokes the color dialog box, where the user has unlimited choice and the color finally selected gets displayed in the color strip. Simultaneously, appropriate value gets assigned in ColorValue column. &lt;br /&gt;&lt;br /&gt;You can find the sample here:&lt;br /&gt;&lt;a href="http://www.rogersaccesslibrary.com/forum/topic574_post592.html"&gt;http://www.rogersaccesslibrary.com/forum/topic574_post592.html&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8134759511126815909-1568610529179034132?l=rogersaccessblog.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://rogersaccessblog.blogspot.com/feeds/1568610529179034132/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8134759511126815909&amp;postID=1568610529179034132' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8134759511126815909/posts/default/1568610529179034132'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8134759511126815909/posts/default/1568610529179034132'/><link rel='alternate' type='text/html' href='http://rogersaccessblog.blogspot.com/2011/10/new-sample-formcontinuoussimulated.html' title='New Sample: Form_ContinuousSimulated'/><author><name>Roger's Access Blog</name><uri>http://www.blogger.com/profile/12265719129831415014</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_JYohVQdgpwU/Scu5h2VvtBI/AAAAAAAAAQc/pr3JDoLaqnQ/S220/avi_Roger_1.JPG'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8134759511126815909.post-8943108474259721971</id><published>2011-10-04T06:44:00.001-04:00</published><updated>2011-10-04T06:44:20.805-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Access 101'/><title type='text'>What To Do When You Take Over A Database Application</title><content type='html'>&lt;p&gt;Taking over an existing database project is much like designing one from scratch, except some of the work is done for you. Unfortunately, some of that work may be wrong, so you can't necessarily rely on it.  &lt;p&gt;First of all, are there relationships set up in the Relationship Window? Hopefully there are.&amp;nbsp; This will give you a map to see how the tables are related.  &lt;p&gt;Write all the relationships in pairs of sentences: See &lt;a href="http://rogersaccessblog.blogspot.com/2009/01/entity-relationship-diagramming-part-i.html" target="_blank"&gt;&lt;u&gt;Entity Relationship Diagramming&lt;/u&gt;&lt;/a&gt; for more information. &lt;p&gt;&lt;strong&gt;1:M&lt;/strong&gt; &lt;br&gt;Each Customer can have one or more Orders. &lt;br&gt;Each Order can be for one and only one Customer.  &lt;p&gt;&lt;a href="http://lh5.ggpht.com/-nW2ijeryMIs/Torjcf3tg9I/AAAAAAAAA0s/J8ePZguvHMg/s1600-h/image%25255B9%25255D.png"&gt;&lt;img style="background-image: none; border-bottom: 0px; border-left: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://lh4.ggpht.com/-MV1PFIpqktU/TorjedSEanI/AAAAAAAAA0w/BgnWsILkQJE/image_thumb%25255B5%25255D.png?imgmax=800" width="331" height="58"&gt;&lt;/a&gt; &lt;p&gt;&lt;strong&gt;M:M &lt;br&gt;&lt;/strong&gt;Each Student can be in one or more Classes &lt;br&gt;Each Class can have one or more Students  &lt;p&gt;&lt;a href="http://lh3.ggpht.com/-sEPXiU4Qeo0/Torjeym11NI/AAAAAAAAA00/rsTa9cQKUgk/s1600-h/image%25255B10%25255D.png"&gt;&lt;img style="background-image: none; border-bottom: 0px; border-left: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://lh6.ggpht.com/-ohGA4Cif5ow/TorjgV4mlPI/AAAAAAAAA04/eY-tc9LKQRQ/image_thumb%25255B6%25255D.png?imgmax=800" width="522" height="61"&gt;&lt;/a&gt; &lt;p&gt;Ask yourself (or your customer) if each and every one of these sentences is true. I will often go through this process with the customer. As they learn the database structure in this non-technical way, they will begin to spot relationship errors on their own.  &lt;p&gt;Don't be constrained by the existing product. It's quite possible that the original database design is wrong. Don't assume the previous developer knew what he or she was doing. Even if he did, chances are good that if it's been in production for more than a couple of years, the business rules have changed. This is where the customer having a grasp of the database design process can be very useful.  &lt;p&gt;If there are no relationships, you will have to infer these relationships based on the queries.&amp;nbsp; As you discover table relationships, you should create them in the Relationships Window.&amp;nbsp; If they're not there already, it is doubtful that you will be able to turn Referential Integrity ON, but at the very least, you will begin documenting the relationships.  &lt;p&gt;Next, I would start with the reports.&amp;nbsp; Open a report and check the RecordSource property see which query it uses.&amp;nbsp; Then open that query, see what it is based on and so forth.&amp;nbsp; When I have done this, I've used a separate piece of paper for each report and query tree.&amp;nbsp; Then I will actually draw out the tree structure.&amp;nbsp; I'll also create a list of all the queries and underneath each, list the queries or objects (form or reports) that it directly applies to.&amp;nbsp; This can help if a single query is a base for many others.&amp;nbsp; You may be able to find a tool to do this for you, but the discovery process is very educational.  &lt;p&gt;Do the same with the forms.  &lt;p&gt;Next, look through the Code Modules (including the code behind forms and reports).&amp;nbsp; Often code is used to create queries or uses saved queries to create recordsets.&amp;nbsp; &lt;p&gt;If DAO is used to CREATE the query, it might look something like this:&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;p&gt;&lt;font face="Courier New"&gt;strSQL = "SELECT * FROM MyTable" &lt;br&gt;Set qdf = db.CreateQueryDef("qryMyQuery", strSQL) &lt;/font&gt; &lt;p&gt;or  &lt;p&gt;&lt;font face="Courier New"&gt;Set qdf = db.CreateQueryDef("qryMyQuery", "SELECT * FROM MyTable") &lt;/font&gt; &lt;p&gt;The first method is preferable and it is worthwhile to convert any using the second method into the first. The reason for this is that you will also want to know what queries are being created in code. If you create the SQL as a separate string, you can use the Debug.Print line to have the code evaluate the SQL string and display the SQL code in the Immediate Window. Like this:  &lt;p&gt;&lt;font face="Courier New"&gt;strSQL = "SELECT * FROM MyTable" &lt;br&gt;Debug.Print strSQL &lt;br&gt;Set qdf = db.CreateQueryDef("qryMyQuery", strSQL) &lt;/font&gt; &lt;p&gt;Complex SQL statements are often difficult to read in VBA code, so this is a useful debugging technique as well.  &lt;p&gt;If a query is just being USED in code (not created in code) it might look like this:  &lt;p&gt;&lt;font face="Courier New"&gt;Set qdf = db.QueryDefs("qryMyQuery")&lt;/font&gt;  &lt;p&gt;or  &lt;p&gt;&lt;font face="Courier New"&gt;Set rs= db.OpenRecordset("qryMyQuery")&lt;/font&gt;  &lt;p&gt;or in ADO  &lt;p&gt;&lt;font face="Courier New"&gt;rs.Open "qryMyQuery', cnn, adOpenKeyset, adLockOptimistic &lt;/font&gt; &lt;p&gt;There are a lot of variations on this.&amp;nbsp; However, the important thing is that the query (or table for that matter) is being used by the code.&amp;nbsp; Make a note of that as well.&amp;nbsp; You can use the Find and Replace feature to find each query name.&amp;nbsp; You can set it to search through all of the code modules.  &lt;p&gt;Also, some Forms and Reports have SQL statements in their RecordSource properties, rather than saved queries.&amp;nbsp; You should also make a note of the elements that make up these.  &lt;p&gt;Once you've done all the reports, forms, and code, see which queries are not accounted for.&amp;nbsp; Chances are they are not used anywhere.&amp;nbsp; Many mature database projects have unused objects lying around.&amp;nbsp; DO NOT delete these. Instead, I favor renaming them with an XXX prefix, which tells me this is OK to delete at some future time.&amp;nbsp; Sometimes I will also add the date to the name so I know how long it's been since I "deleted" it.&amp;nbsp; If you run into a problem, you can always name it back.  &lt;p&gt;Lastly, I would try to make some kind of method out of all of this madness. Try to develop some sort of rational naming structure for your queries. There is no single correct way to do this.&amp;nbsp; Often if a series of queries is used ONLY by one report, I will name them after the report and indicate which level they are in.&amp;nbsp; However, for queries that are used as a base for multiple other queries, this doesn't work so well.&amp;nbsp; I've never come up with anything that worked in all cases.&amp;nbsp; And it doesn't matter.&amp;nbsp; Just try the best you can to do a rational job of renaming them.&amp;nbsp; CAUTION:&amp;nbsp; DO THIS ON PAPER FIRST!  &lt;p&gt;Once you have renamed your queries on paper in some rational manner, get your hands on a renaming tool.&amp;nbsp; Rick Fisher has a good shareware add-in called "Find and Replace".&amp;nbsp; You can find it here: &lt;a href="http://www.rickworld.com/download.html"&gt;&lt;u&gt;http://www.rickworld.com/download.html&lt;/u&gt;&lt;/a&gt;.&amp;nbsp; (It is worth registering for the extra features, though).&amp;nbsp; This product (and others like Speed Ferret) allow you to rename an object throughout the entire database including forms, queries, reports, macros, and code.  &lt;p&gt;By the time you get done with all this, you will know this database like the back of your hand.       &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8134759511126815909-8943108474259721971?l=rogersaccessblog.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://rogersaccessblog.blogspot.com/feeds/8943108474259721971/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8134759511126815909&amp;postID=8943108474259721971' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8134759511126815909/posts/default/8943108474259721971'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8134759511126815909/posts/default/8943108474259721971'/><link rel='alternate' type='text/html' href='http://rogersaccessblog.blogspot.com/2011/10/what-to-do-when-you-take-over-database.html' title='What To Do When You Take Over A Database Application'/><author><name>Roger's Access Blog</name><uri>http://www.blogger.com/profile/12265719129831415014</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_JYohVQdgpwU/Scu5h2VvtBI/AAAAAAAAAQc/pr3JDoLaqnQ/S220/avi_Roger_1.JPG'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://lh4.ggpht.com/-MV1PFIpqktU/TorjedSEanI/AAAAAAAAA0w/BgnWsILkQJE/s72-c/image_thumb%25255B5%25255D.png?imgmax=800' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8134759511126815909.post-2355239739835133973</id><published>2011-09-28T09:44:00.000-04:00</published><updated>2011-09-28T09:44:38.401-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='New Samples'/><category scheme='http://www.blogger.com/atom/ns#' term='Tejpal'/><title type='text'>Report_SubRepSetMaxRowsPerPg</title><content type='html'>By AD Tejpal&lt;br /&gt;&lt;br /&gt;This sample db demonstrates custom setting of maximum number of rows per page for one or more subreports. &lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Setting of forced page breaks in subreport's detail section, though effective, suffers from the drawback that the subreport control on parent report gets forced to full page height. As a result blank space on main report goes waste. Though not recommended, this method has been demonstrated as the last option, just for academic interest.&lt;br /&gt;&lt;br /&gt;Note: &lt;br /&gt;&lt;br /&gt;While using forced page break in subreport, it has to be ensured that CanGrow property of subreport control on parent report is set to Yes. Otherwise the subreport does not get displayed beyond first page. In fact what happens is that as a result of forced page break, the subreport control with CanGrow as Yes, expands suitably so as to span multiple pages as needed. &lt;br /&gt;&lt;br /&gt;Following styles are demonstrated in the sample db:&lt;br /&gt;&lt;br /&gt;1 - A1: Single SubReport Style 1:&lt;br /&gt;&lt;br /&gt;The desired effect is achieved by conditional cancellation of detail format of subreport for records not falling in the target block determined by parent report's current record number. It is like obtaining a filtered output matching target block of sequential numbers. The number of pages on parent report is restricted to the minimum required for the subreport.&lt;br /&gt;&lt;br /&gt;2 - A2: Single SubReport Style 2:&lt;br /&gt;&lt;br /&gt;Similar to A1. However, the number of pages on parent report is not restricted. It can exceed the minimum required for the subreport.&lt;br /&gt;&lt;br /&gt;3 - B1: Two SubReports Style 1:&lt;br /&gt;&lt;br /&gt;Similar to A1 above, but with two subreports, each with its own setting for max rows per page.&lt;br /&gt;&lt;br /&gt;4 - B2: Two SubReports Style 2:&lt;br /&gt;&lt;br /&gt;Similar to A2 above, but with two subreports, each with its own setting for max rows per page.&lt;br /&gt;&lt;br /&gt;5 - C: Single SubReport With Forced Page Break:&lt;br /&gt;&lt;br /&gt;Not recommended. Included for academic interest only. &lt;br /&gt;&lt;br /&gt;You can find the sample here: &lt;a href="http://www.rogersaccesslibrary.com/forum/report-subrepsetmaxrowsperpg_topic573.html"&gt;http://www.rogersaccesslibrary.com/forum/report-subrepsetmaxrowsperpg_topic573.html&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8134759511126815909-2355239739835133973?l=rogersaccessblog.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://rogersaccessblog.blogspot.com/feeds/2355239739835133973/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8134759511126815909&amp;postID=2355239739835133973' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8134759511126815909/posts/default/2355239739835133973'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8134759511126815909/posts/default/2355239739835133973'/><link rel='alternate' type='text/html' href='http://rogersaccessblog.blogspot.com/2011/09/reportsubrepsetmaxrowsperpg.html' title='Report_SubRepSetMaxRowsPerPg'/><author><name>Roger's Access Blog</name><uri>http://www.blogger.com/profile/12265719129831415014</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_JYohVQdgpwU/Scu5h2VvtBI/AAAAAAAAAQc/pr3JDoLaqnQ/S220/avi_Roger_1.JPG'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8134759511126815909.post-4499552242902869407</id><published>2011-09-22T07:01:00.001-04:00</published><updated>2011-09-22T07:02:13.544-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='New Samples'/><category scheme='http://www.blogger.com/atom/ns#' term='Tejpal'/><title type='text'>New Sample: Form_LastViewedAndCurRecToggle</title><content type='html'>&lt;p&gt;By A.D. Tejpal&lt;/p&gt; &lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; This sample db demonstrates toggling between last viewed record and the current record on subform in datasheet view, via a command button on parent form. &lt;/p&gt; &lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; Navigation through subform records is conducted through a combo box on the parent form. Alternatively, the user can click on the desired record directly on the subform. The current record gets highlighted in light green while the last viewed record is highlighted in light grey.&lt;/p&gt; &lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; The user can also flag one or more records by double clicking any of the columns on desired record. Last column of records flagged in this manner gets highlighted in pink color. These flags remain in force for the current session of access, even if the form is closed and then re-opened. Repeat double click on a flagged record will remove the flag. To remove all flags, command button captioned "Clear All Flags" can be clicked. &lt;/p&gt; &lt;p&gt;Version: Access 2000 file format&lt;/p&gt; &lt;p&gt;You can find the sample here: &lt;br&gt;&lt;a href="http://www.rogersaccesslibrary.com/forum/forum_posts.asp?TID=572&amp;amp;PID=590#590"&gt;www.rogersaccesslibrary.com/forum/forum_posts.asp?TID=572&amp;amp;PID=590#590&lt;/a&gt;&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8134759511126815909-4499552242902869407?l=rogersaccessblog.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://rogersaccessblog.blogspot.com/feeds/4499552242902869407/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8134759511126815909&amp;postID=4499552242902869407' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8134759511126815909/posts/default/4499552242902869407'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8134759511126815909/posts/default/4499552242902869407'/><link rel='alternate' type='text/html' href='http://rogersaccessblog.blogspot.com/2011/09/new-sample-formlastviewedandcurrectoggl.html' title='New Sample: Form_LastViewedAndCurRecToggle'/><author><name>Roger's Access Blog</name><uri>http://www.blogger.com/profile/12265719129831415014</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_JYohVQdgpwU/Scu5h2VvtBI/AAAAAAAAAQc/pr3JDoLaqnQ/S220/avi_Roger_1.JPG'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8134759511126815909.post-4660924371174058906</id><published>2011-08-31T07:08:00.001-04:00</published><updated>2011-08-31T07:08:17.797-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='New Samples'/><title type='text'>New Sample: Data Definition Language: SQL vs DAO</title><content type='html'>&lt;p&gt;This sample (with full documentation) illustrates how to do a variety of DDL (Data Definition Language) operations using both SQL and DAO. &lt;p&gt;DDL operations are those that modify the database structure, ie. tables, fields, indexes, and relationships. &lt;p&gt;You can find the sample here: &lt;a title="http://www.rogersaccesslibrary.com/forum/topic570_post587.html" href="http://www.rogersaccesslibrary.com/forum/topic570_post587.html"&gt;&lt;u&gt;http://www.rogersaccesslibrary.com/forum/topic570_post587.html&lt;/u&gt;&lt;/a&gt;&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8134759511126815909-4660924371174058906?l=rogersaccessblog.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://rogersaccessblog.blogspot.com/feeds/4660924371174058906/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8134759511126815909&amp;postID=4660924371174058906' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8134759511126815909/posts/default/4660924371174058906'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8134759511126815909/posts/default/4660924371174058906'/><link rel='alternate' type='text/html' href='http://rogersaccessblog.blogspot.com/2011/08/new-sample-data-definition-language-sql.html' title='New Sample: Data Definition Language: SQL vs DAO'/><author><name>Roger's Access Blog</name><uri>http://www.blogger.com/profile/12265719129831415014</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_JYohVQdgpwU/Scu5h2VvtBI/AAAAAAAAAQc/pr3JDoLaqnQ/S220/avi_Roger_1.JPG'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8134759511126815909.post-5603476808506261790</id><published>2011-08-30T11:01:00.001-04:00</published><updated>2011-08-31T14:25:17.528-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Access 101'/><title type='text'>Data Definition Language (DDL): DAO</title><content type='html'>&lt;p&gt;&lt;strong&gt;Data Definition Language (DDL) &lt;/strong&gt;&lt;/p&gt; &lt;p&gt;Data Definition Language (DDL) is a programmatic way to create and modify the database structure, that is, objects like tables, indexes, and relationships.  &lt;p&gt;Before Access came along, DDL statements were the only way to modify the database structure in most relational database management systems. Access introduced the graphical user interface (GUI) to do most DDL functions. Because the Access GUI is so easy to use, most Access users never have reason to use DDL statements. However, there are circumstances under which it is advantageous to use DDL statements.  &lt;p&gt;I use them a lot for automating data import processes. I can create a temporary table, import data to the temp table, change data types, remove indexes from the permanent table, append data from the temp to the permanent table, then rebuild the indexes -- all automatically, all in SQL code.  &lt;p&gt;But that's not the only use. You can also use DDL to set the seed and interval for an autonumber field, to remotely change the structure of a back-end database (useful for multi-user databases), or make any other change to a production database. In a web environment, it can be used to add databases to an active website.  &lt;p&gt;In this three-part series, I'll be addressing both SQL and DAO.  &lt;ul&gt; &lt;li&gt;&lt;strong&gt;&lt;a href="http://rogersaccessblog.blogspot.com/2011/08/data-definition-language-ddl-dao-vs-sql.html" target="_blank"&gt;&lt;u&gt;SQL vs. DAO&lt;/u&gt;&lt;/a&gt;&lt;/strong&gt;&lt;u&gt; &lt;/u&gt; &lt;li&gt;&lt;strong&gt;&lt;a href="http://rogersaccessblog.blogspot.com/2011/08/data-definition-language-sql.html" target="_blank"&gt;&lt;u&gt;DDL Using SQL&lt;/u&gt;&lt;/a&gt;&lt;/strong&gt;  &lt;li&gt;&lt;strong&gt;DDL Using DAO &lt;strong&gt;(this post)&lt;/strong&gt; &lt;/strong&gt; &lt;li&gt;You can download this whole series in a single document with sample database, here: &lt;a title="http://www.rogersaccesslibrary.com/forum/topic570_post587.html" href="http://www.rogersaccesslibrary.com/forum/topic570_post587.html"&gt;http://www.rogersaccesslibrary.com/forum/topic570_post587.html&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt; &lt;h4&gt;&amp;nbsp;&lt;/h4&gt; &lt;h3&gt;&lt;a name="_Toc302366321"&gt;DDL Using DAO&lt;/a&gt;&lt;/h3&gt; &lt;p&gt;DAO (Data Access Objects) works differently than SQL. DAO is an object model specific to Access (or rather for the MDB or ACCDB file types). One reason for using DAO over SQL is that you can set Access specific properties (like Default Value and Validation Rules) with it. Also, certain datatypes (like Hyperlink) can only be created with DAO.  &lt;p&gt;In general, with DAO, you:  &lt;ol&gt; &lt;li&gt;Declare object variables (e.g., Table, Field, Property, etc.)  &lt;li&gt;Instantiate the object (that is, create the object)  &lt;li&gt;Append it to the appropriate Collections (that is, a table to the Tables Collection)&lt;/li&gt;&lt;/ol&gt; &lt;p&gt;You can't create a Table without creating at least one Field, so you have to create and append the fields for the table before you append the table. If you set Properties for the field, you must do so before you append the field to the fields collection. So creating a table goes something like this:  &lt;ol&gt; &lt;li&gt;Declare variables (Table, Field)  &lt;li&gt;Instantiate Table  &lt;ul&gt; &lt;li&gt;Instantiate Field 1 &lt;/li&gt; &lt;ul&gt; &lt;li&gt;&lt;a name="OLE_LINK1"&gt;Instantiate Property 1&lt;/a&gt;  &lt;li&gt;Append Property 1  &lt;li&gt;Instantiate Property 2  &lt;li&gt;Append Property 2 &lt;/li&gt;&lt;/ul&gt; &lt;li&gt;Append Field1 &lt;li&gt;Instantiate Field 2  &lt;ul&gt; &lt;li&gt;Instantiate Property 1  &lt;li&gt;Append Property 1&lt;/li&gt;&lt;/ul&gt; &lt;li&gt;Append Field 2&lt;/li&gt;&lt;/ul&gt; &lt;li&gt;Append Table&lt;/li&gt;&lt;/ol&gt; &lt;p&gt;So, specific examples might go something like these:  &lt;p&gt;&amp;nbsp; &lt;h4&gt;&lt;a name="_Toc302366322"&gt;Create Table&lt;/a&gt;&lt;/h4&gt; &lt;p&gt;&lt;font face="Courier New"&gt;Sub exaCreateTableDAO()&lt;br&gt;&lt;font color="#008000"&gt;'DAO DDL example demonstrates creating a table, fields, properties&lt;/font&gt;&lt;/font&gt;  &lt;p&gt;&lt;font face="Courier New"&gt;&lt;font color="#008000"&gt;' Declare object variables&lt;br&gt;&lt;/font&gt;Dim db As DAO.Database&lt;br&gt;Dim tbl As DAO.TableDef&lt;br&gt;Dim fld As DAO.Field&lt;/font&gt;  &lt;p&gt;&lt;font face="Courier New"&gt;Set db = CurrentDb&lt;/font&gt;  &lt;blockquote&gt; &lt;p&gt;&lt;font face="Courier New"&gt;&lt;font color="#008000"&gt;' Create the table (BooksDAO)&lt;br&gt;&lt;/font&gt;Set tbl = db.CreateTableDef("BooksDAO")&lt;/font&gt;&lt;/p&gt;&lt;/blockquote&gt; &lt;blockquote&gt; &lt;p&gt;&lt;font face="Courier New"&gt;&lt;font color="#008000"&gt;' Create a field (ISBN)&lt;br&gt;&lt;/font&gt;Set fld = tblNew.CreateField("ISBN", dbText, 13)&lt;/font&gt;&lt;/p&gt;&lt;/blockquote&gt; &lt;blockquote&gt; &lt;p&gt;&lt;font face="Courier New"&gt;&lt;font color="#008000"&gt;' Set field properties&lt;br&gt;&lt;/font&gt;fld.Required = True&lt;/font&gt;&lt;/p&gt;&lt;/blockquote&gt; &lt;blockquote&gt; &lt;p&gt;&lt;font face="Courier New"&gt;&lt;font color="#008000"&gt;' Append field (ISBN) to Fields collection&lt;br&gt;&lt;/font&gt;tbl.Fields.Append fld&lt;/font&gt;&lt;/p&gt;&lt;/blockquote&gt; &lt;blockquote&gt; &lt;p&gt;&lt;font face="Courier New"&gt;&lt;font color="#008000"&gt;' Create a field (Title)&lt;br&gt;&lt;/font&gt;Set fld = tblNew.CreateField("ISBN", dbText, 100)&lt;/font&gt;&lt;/p&gt;&lt;/blockquote&gt; &lt;blockquote&gt; &lt;p&gt;&lt;font face="Courier New"&gt;&lt;font color="#008000"&gt;' Set field properties&lt;br&gt;&lt;/font&gt;fld.Required = True&lt;br&gt;fld.AllowZeroLength = False&lt;br&gt;fld.DefaultValue = "Unknown"&lt;/font&gt;&lt;/p&gt;&lt;/blockquote&gt; &lt;blockquote&gt; &lt;p&gt;&lt;font face="Courier New"&gt;&lt;font color="#008000"&gt;' Append field (Title) to Fields collection&lt;br&gt;&lt;/font&gt;tbl.Fields.Append fld&lt;/font&gt;&lt;/p&gt;&lt;/blockquote&gt; &lt;blockquote&gt; &lt;p&gt;&lt;font face="Courier New"&gt;&lt;font color="#008000"&gt;' Append table (BooksDAO) to TableDef collection&lt;br&gt;&lt;/font&gt;db.TableDefs.Append tbl&lt;/font&gt;&lt;/p&gt;&lt;/blockquote&gt; &lt;p&gt;&lt;font face="Courier New"&gt;End Sub&lt;/font&gt;  &lt;p&gt;&amp;nbsp; &lt;h4&gt;&lt;a name="_Toc302366323"&gt;Modify Table&lt;/a&gt;&lt;/h4&gt; &lt;p&gt;Modifying a table (that is, adding a new field or adding new properties to an existing field) is similar, except instead of instantiating a new table, you instantiate an existing table. When modifying an existing table or field, you do not need to append it to its collection.  &lt;p&gt;&lt;font face="Courier New"&gt;Sub exaModifyTable()&lt;br&gt;&lt;font color="#008000"&gt;'DAO DDL example'demonstrates modifying a table by adding a field &lt;br&gt;&lt;/font&gt;&lt;/font&gt;&lt;font color="#008000" face="Courier New"&gt;' and modifying an existing field's property&lt;/font&gt;&lt;/p&gt; &lt;p&gt;&lt;font face="Courier New"&gt;Dim db As DAO.Database&lt;br&gt;Dim tdf As DAO.TableDef&lt;br&gt;Dim fld As DAO.Field&lt;br&gt;Set db = CurrentDb&lt;/font&gt;&lt;/p&gt; &lt;blockquote&gt; &lt;p&gt;&lt;font face="Courier New"&gt;&lt;font color="#008000"&gt;' Instantiate existing table (BooksDAO)&lt;br&gt;&lt;/font&gt;Set tdf = db.TableDefs("BooksDAO")&lt;/font&gt;&lt;/p&gt;&lt;/blockquote&gt; &lt;blockquote&gt; &lt;p&gt;&lt;font face="Courier New"&gt;&lt;font color="#008000"&gt;' Create a field (Price)&lt;br&gt;&lt;/font&gt;Set fld = tdf.CreateField("Price", dbCurrency)&lt;/font&gt;&lt;/p&gt;&lt;/blockquote&gt; &lt;blockquote&gt; &lt;p&gt;&lt;font face="Courier New"&gt;&lt;font color="#008000"&gt;' Append field to Fields collection&lt;br&gt;&lt;/font&gt;tdf.Fields.Append fld &lt;/font&gt;&lt;/p&gt;&lt;/blockquote&gt; &lt;blockquote&gt; &lt;p&gt;&lt;font face="Courier New"&gt;&lt;font color="#008000"&gt;' Modify existing field (Title) with properties (validation rule&lt;br&gt;' and validation text)&lt;br&gt;&lt;/font&gt;Set fld = tdf.Fields("Title")&lt;br&gt;fld.ValidationRule = "Like 'A*' or Like 'Unknown'"&lt;br&gt;fld.ValidationText = "Known value must begin with A"&lt;/font&gt;&lt;/p&gt;&lt;/blockquote&gt; &lt;p&gt;&lt;font face="Courier New"&gt;End Sub&lt;/font&gt;  &lt;p&gt;&lt;font face="Courier New"&gt;&lt;/font&gt;&amp;nbsp; &lt;h4&gt;&lt;a name="_Toc302366332"&gt;Modify Field Names&lt;/a&gt;&lt;/h4&gt; &lt;p&gt;&lt;font face="Courier New"&gt;Sub exaModifyFieldNames()&lt;br&gt;&lt;font color="#008000"&gt;'DAO DDL example demonstrates modifying field names&lt;/font&gt;&lt;/font&gt;  &lt;p&gt;&lt;font face="Courier New"&gt;Dim db As DAO.Database&lt;br&gt;Dim tdf As DAO.TableDef&lt;br&gt;Dim fld As DAO.Field&lt;/font&gt;  &lt;blockquote&gt; &lt;p&gt;&lt;font face="Courier New"&gt;Set db = CurrentDb&lt;br&gt;Set tdf = db.TableDefs("MyTable")&lt;br&gt;For Each fld In tdf.Fields&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; fld.Name = fld.Name &amp;amp; "new"&lt;br&gt;Next&lt;/font&gt;&lt;/p&gt;&lt;/blockquote&gt; &lt;p&gt;&lt;font face="Courier New"&gt;End Sub&lt;/font&gt;&lt;/p&gt; &lt;h4&gt;&lt;a name="_Toc302366324"&gt;Create Database&lt;/a&gt;&lt;/h4&gt; &lt;p&gt;&lt;font face="Courier New"&gt;Sub exaCreateDB()&lt;br&gt;&lt;font color="#008000"&gt;'DAO DDL example demonstrates creating a database programmatically&lt;/font&gt;&lt;/font&gt;&lt;/p&gt; &lt;p&gt;&lt;font face="Courier New"&gt;Dim dbNew As DAO.Database&lt;br&gt;&lt;/font&gt;&lt;font face="Courier New"&gt;Set dbNew = CreateDatabase _&lt;br&gt;("C:\classes\cis253\winter98\MoreBks", dbLangGeneral)&lt;/font&gt;&lt;/p&gt; &lt;p&gt;&lt;font face="Courier New"&gt;End Sub&lt;/font&gt;&lt;/p&gt; &lt;h4&gt;&lt;a name="_Toc302366325"&gt;Delete Table&lt;/a&gt;&lt;/h4&gt; &lt;p&gt;&lt;font face="Courier New"&gt;Sub DeleteTable()&lt;br&gt;&lt;font color="#008000"&gt;'DAO DDL example demonstrates deleting a table&lt;/font&gt;&lt;/font&gt;&lt;/p&gt; &lt;p&gt;&lt;font face="Courier New"&gt;Dim db As DAO.Database&lt;br&gt;Set db = CurrentDb&lt;br&gt;db.TableDefs.Delete "tstBooks"&lt;/font&gt;&lt;/p&gt; &lt;p&gt;&lt;font face="Courier New"&gt;End Sub&lt;/font&gt;&lt;/p&gt; &lt;h4&gt;&lt;a name="_Toc302366326"&gt;Create Index&lt;/a&gt;&lt;/h4&gt; &lt;p&gt;&lt;font face="Courier New"&gt;Sub exaCreateIndex()&lt;br&gt;&lt;font color="#008000"&gt;'DAO DDL example demonstrates creating an index&lt;/font&gt;&lt;/font&gt;  &lt;p&gt;&lt;font face="Courier New"&gt;Dim db As DAO.Database&lt;br&gt;Dim tdf As DAO.TableDef&lt;br&gt;Dim idx As DAO.Index&lt;br&gt;Dim fld As DAO.Field&lt;/font&gt;  &lt;p&gt;&lt;font face="Courier New"&gt;Set db = CurrentDb&lt;br&gt;Set tdf = db.TableDefs!Books&lt;/font&gt;  &lt;blockquote&gt; &lt;p&gt;&lt;font face="Courier New"&gt;&lt;font color="#008000"&gt;' Create index&lt;br&gt;&lt;/font&gt;Set idx = tdf.CreateIndex("PriceTitle")&lt;/font&gt;&lt;/p&gt;&lt;/blockquote&gt; &lt;blockquote&gt; &lt;p&gt;&lt;font face="Courier New"&gt;&lt;font color="#008000"&gt;' Append fields to index&lt;br&gt;&lt;/font&gt;Set fld = idx.CreateField("Price")&lt;br&gt;idx.Fields.Append fld&lt;br&gt;Set fld = idx.CreateField("Title")&lt;br&gt;idx.Fields.Append fld&lt;/font&gt;&lt;/p&gt;&lt;/blockquote&gt; &lt;blockquote&gt; &lt;p&gt;&lt;font face="Courier New"&gt;&lt;font color="#008000"&gt;' Append index to table&lt;br&gt;&lt;/font&gt;tdf.Indexes.Append idx&lt;/font&gt;&lt;/p&gt;&lt;/blockquote&gt; &lt;p&gt;&lt;font face="Courier New"&gt;End Sub&lt;/font&gt;  &lt;p&gt;&amp;nbsp; &lt;h4&gt;&lt;a name="_Toc302366327"&gt;Create Primary Key&lt;/a&gt;&lt;/h4&gt; &lt;p&gt;&lt;font face="Courier New"&gt;Sub CreatePrimaryKey()&lt;br&gt;&lt;font color="#008000"&gt;'DAO DDL example demonstrates creating an index&lt;/font&gt;&lt;/font&gt;&lt;/p&gt; &lt;p&gt;&lt;font face="Courier New"&gt;Dim db As DAO.Database&lt;br&gt;Dim tdf As DAO.TableDef&lt;br&gt;Dim idx As DAO.Index&lt;br&gt;Dim fld As DAO.Field&lt;/font&gt;  &lt;p&gt;&lt;font face="Courier New"&gt;Set db = CurrentDb&lt;br&gt;Set tdf = db.TableDefs!Books&lt;/font&gt;  &lt;blockquote&gt; &lt;p&gt;&lt;font face="Courier New"&gt;&lt;font color="#008000"&gt;' Create index&lt;br&gt;&lt;/font&gt;Set idx = tdf.CreateIndex("PriceTitle")&lt;/font&gt;&lt;/p&gt;&lt;/blockquote&gt; &lt;blockquote&gt; &lt;p&gt;&lt;font face="Courier New"&gt;&lt;font color="#008000"&gt;' Append fields to index&lt;br&gt;&lt;/font&gt;Set fld = idx.CreateField("Price")&lt;br&gt;idx.Fields.Append fld&lt;br&gt;Set fld = idx.CreateField("Title")&lt;br&gt;idx.Fields.Append fld&lt;/font&gt;&lt;/p&gt;&lt;/blockquote&gt; &lt;blockquote&gt; &lt;p&gt;&lt;font face="Courier New"&gt;&lt;font color="#008000"&gt;' Make Index primary&lt;br&gt;&lt;/font&gt;idx.Primary = True&lt;/font&gt;&lt;/p&gt;&lt;/blockquote&gt; &lt;blockquote&gt; &lt;p&gt;&lt;font face="Courier New"&gt;&lt;font color="#008000"&gt;' Append index to table&lt;br&gt;&lt;/font&gt;tdf.Indexes.Append idx&lt;/font&gt;&lt;/p&gt;&lt;/blockquote&gt; &lt;p&gt;&lt;font face="Courier New"&gt;End Sub&lt;/font&gt;  &lt;p&gt;&amp;nbsp; &lt;h4&gt;&lt;a name="_Toc302366328"&gt;Delete Index&lt;/a&gt;&lt;/h4&gt; &lt;p&gt;&lt;font face="Courier New"&gt;Sub DeleteIndex()&lt;br&gt;&lt;font color="#008000"&gt;'DAO DDL example demonstrates creating a composite primary key&lt;/font&gt;&lt;/font&gt;  &lt;p&gt;&lt;font face="Courier New"&gt;Dim db As DAO.Database&lt;br&gt;Dim tdf As DAO.TableDef&lt;br&gt;Set db = CurrentDb&lt;br&gt;Set tdf = db.TableDefs!Books&lt;br&gt;tdf.Indexes.Delete "PriceTitle"&lt;/font&gt;  &lt;p&gt;&lt;font face="Courier New"&gt;End Sub&lt;/font&gt;  &lt;p&gt;&amp;nbsp; &lt;h4&gt;&lt;a name="_Toc302366329"&gt;Create Relationship&lt;/a&gt;&lt;/h4&gt; &lt;p&gt;&lt;font face="Courier New"&gt;Sub exaRelations()&lt;/font&gt;  &lt;p&gt;&lt;font color="#008000"&gt;&lt;font face="Courier New"&gt;'DAO DDL example creating a Relationship&lt;/font&gt; &lt;/font&gt; &lt;p&gt;&lt;font face="Courier New"&gt;Dim db As DAO.Database&lt;br&gt;Dim rel As DAO.Relation&lt;br&gt;Dim fld As DAO.Field&lt;/font&gt;  &lt;p&gt;&lt;font face="Courier New"&gt;Set db = CurrentDb&lt;/font&gt;  &lt;blockquote&gt; &lt;p&gt;&lt;font face="Courier New"&gt;&lt;font color="#008000"&gt;' Create relation&lt;br&gt;&lt;/font&gt;Set rel = db.CreateRelation("PublisherRegions", _&lt;br&gt;"PUBLISHERDAO", "SALESREGIONS")&lt;/font&gt;&lt;/p&gt;&lt;/blockquote&gt; &lt;blockquote&gt; &lt;p&gt;&lt;font face="Courier New"&gt;&lt;font color="#008000"&gt;' Set referential integrity w/ cascade updates&lt;br&gt;&lt;/font&gt;rel.Attributes = dbRelationUpdateCascade&lt;/font&gt;&lt;/p&gt;&lt;/blockquote&gt; &lt;blockquote&gt; &lt;p&gt;&lt;font face="Courier New"&gt;&lt;font color="#008000"&gt;' Specify key field in KeyTable (Publishers)&lt;br&gt;&lt;/font&gt;Set fld = rel.CreateField("PubID")&lt;/font&gt;&lt;/p&gt;&lt;/blockquote&gt; &lt;blockquote&gt; &lt;p&gt;&lt;font face="Courier New"&gt;&lt;font color="#008000"&gt;' Specify foreign key in ForeignTable (SalesRegions)&lt;br&gt;&lt;/font&gt;fld.ForeignName = "PubIDFK"&lt;/font&gt;&lt;/p&gt;&lt;/blockquote&gt; &lt;blockquote&gt; &lt;p&gt;&lt;font face="Courier New"&gt;&lt;font color="#008000"&gt;' Append field to Relation&lt;br&gt;&lt;/font&gt;rel.Fields.Append fld&lt;/font&gt;&lt;/p&gt;&lt;/blockquote&gt; &lt;blockquote&gt; &lt;p&gt;&lt;font face="Courier New"&gt;&lt;font color="#008000"&gt;' Append relation to Relations collection&lt;br&gt;&lt;/font&gt;db.Relations.Append rel&lt;/font&gt;&lt;/p&gt;&lt;/blockquote&gt; &lt;p&gt;&lt;font face="Courier New"&gt;End Sub&lt;/font&gt;  &lt;p&gt;&amp;nbsp; &lt;h4&gt;&lt;a name="_Toc302366330"&gt;Drop Relationship&lt;/a&gt;&lt;/h4&gt; &lt;p&gt;&lt;font face="Courier New"&gt;Sub exaDeleteRelation()&lt;/font&gt;  &lt;blockquote&gt; &lt;p&gt;&lt;font face="Courier New"&gt;&lt;font color="#008000"&gt;'DAO DDL example Deleting a Relationship&lt;br&gt;&lt;/font&gt;Dim db As Database&lt;br&gt;Set db = CurrentDb&lt;br&gt;db.Relations.Delete "PublisherRegions"&lt;/font&gt;&lt;/p&gt;&lt;/blockquote&gt; &lt;p&gt;&lt;font face="Courier New"&gt;End Sub&lt;/font&gt;  &lt;p&gt;&amp;nbsp; &lt;h4&gt;&lt;a name="_Toc302366331"&gt;Create Table With AutoNumber PrimaryKey And Hyperlink&lt;/a&gt;&lt;/h4&gt; &lt;p&gt;&lt;font face="Courier New"&gt;Sub exaCreateTableWithAutoNumberAndHyperlink()&lt;/font&gt;  &lt;p&gt;&lt;font face="Courier New"&gt;&lt;font color="#008000"&gt;'DAO DDL example demonstrates creating a table, fields, properties&lt;br&gt;&lt;/font&gt;Dim db As DAO.Database&lt;br&gt;Dim tblNew As DAO.TableDef&lt;br&gt;Dim fld As DAO.Field&lt;/font&gt;  &lt;blockquote&gt; &lt;p&gt;&lt;font face="Courier New"&gt;&lt;font color="#008000"&gt;' Create the table and a field&lt;br&gt;&lt;/font&gt;Set db = CurrentDb()&lt;br&gt;Set tblNew = db.CreateTableDef("NewTable")&lt;br&gt;Set fld = tblNew.CreateField("AutoField", dbLong)&lt;/font&gt;&lt;/p&gt;&lt;/blockquote&gt; &lt;blockquote&gt; &lt;p&gt;&lt;font face="Courier New"&gt;&lt;font color="#008000"&gt;' Set field properties&lt;br&gt;&lt;/font&gt;fld.Required = True&lt;br&gt;fld.Attributes = dbAutoIncrField&lt;/font&gt;&lt;/p&gt;&lt;/blockquote&gt; &lt;blockquote&gt; &lt;p&gt;&lt;font face="Courier New"&gt;&lt;font color="#008000"&gt;' Append field to Fields collection&lt;br&gt;&lt;/font&gt;tblNew.Fields.Append fld&lt;/font&gt;&lt;/p&gt;&lt;/blockquote&gt; &lt;blockquote&gt; &lt;p&gt;&lt;font face="Courier New"&gt;&lt;font color="#008000"&gt;' Create Primary Key&lt;br&gt;&lt;/font&gt;Set idx = tblNew.CreateIndex("PrimaryKey")&lt;/font&gt;&lt;/p&gt;&lt;/blockquote&gt; &lt;blockquote&gt; &lt;p&gt;&lt;font face="Courier New"&gt;&lt;font color="#008000"&gt;' Append fields to index&lt;br&gt;&lt;/font&gt;Set fld = idx.CreateField("AutoField")&lt;br&gt;idx.Fields.Append fld&lt;/font&gt;&lt;/p&gt;&lt;/blockquote&gt; &lt;blockquote&gt; &lt;p&gt;&lt;font face="Courier New"&gt;&lt;font color="#008000"&gt;' Make Index primary&lt;br&gt;&lt;/font&gt;idx.Primary = True&lt;/font&gt;&lt;/p&gt;&lt;/blockquote&gt; &lt;blockquote&gt; &lt;p&gt;&lt;font face="Courier New"&gt;&lt;font color="#008000"&gt;' Append index to table&lt;br&gt;&lt;/font&gt;tblNew.Indexes.Append idx&lt;/font&gt;&lt;/p&gt;&lt;/blockquote&gt; &lt;blockquote&gt; &lt;p&gt;&lt;font face="Courier New"&gt;&lt;font color="#008000"&gt;' Create hyperlink field&lt;br&gt;&lt;/font&gt;Set fld = tblNew.CreateField("HyperField", dbMemo)&lt;/font&gt;&lt;/p&gt;&lt;/blockquote&gt; &lt;blockquote&gt; &lt;p&gt;&lt;font face="Courier New"&gt;&lt;font color="#008000"&gt;' Set field properties&lt;br&gt;&lt;/font&gt;fld.Attributes = dbHyperlinkField&lt;/font&gt;&lt;/p&gt;&lt;/blockquote&gt; &lt;blockquote&gt; &lt;p&gt;&lt;font face="Courier New"&gt;&lt;font color="#008000"&gt;' Append field to Fields collection&lt;br&gt;&lt;/font&gt;tblNew.Fields.Append fld&lt;/font&gt;&lt;/p&gt;&lt;/blockquote&gt; &lt;blockquote&gt; &lt;p&gt;&lt;font face="Courier New"&gt;&lt;font color="#008000"&gt;' Append table to TableDef collection&lt;br&gt;&lt;/font&gt;db.TableDefs.Append tblNew&lt;/font&gt;&lt;/p&gt;&lt;/blockquote&gt; &lt;p&gt;&lt;font face="Courier New"&gt;End Sub&lt;/font&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8134759511126815909-5603476808506261790?l=rogersaccessblog.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://rogersaccessblog.blogspot.com/feeds/5603476808506261790/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8134759511126815909&amp;postID=5603476808506261790' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8134759511126815909/posts/default/5603476808506261790'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8134759511126815909/posts/default/5603476808506261790'/><link rel='alternate' type='text/html' href='http://rogersaccessblog.blogspot.com/2011/08/data-definition-language-ddl-ddl-using.html' title='Data Definition Language (DDL): DAO'/><author><name>Roger's Access Blog</name><uri>http://www.blogger.com/profile/12265719129831415014</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_JYohVQdgpwU/Scu5h2VvtBI/AAAAAAAAAQc/pr3JDoLaqnQ/S220/avi_Roger_1.JPG'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8134759511126815909.post-733939344761613545</id><published>2011-08-23T06:52:00.001-04:00</published><updated>2011-08-31T07:13:48.691-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Access 101'/><title type='text'>Data Definition Language: SQL</title><content type='html'>&lt;p&gt;&lt;strong&gt;Data Definition Language (DDL) &lt;/strong&gt;&lt;/p&gt; &lt;p&gt;Data Definition Language (DDL) is a programmatic way to create and modify the database structure, that is, objects like tables, indexes, and relationships.  &lt;p&gt;Before Access came along, DDL statements were the only way to modify the database structure in most relational database management systems. Access introduced the graphical user interface (GUI) to do most DDL functions. Because the Access GUI is so easy to use, most Access users never have reason to use DDL statements. However, there are circumstances under which it is advantageous to use DDL statements.  &lt;p&gt;I use them a lot for automating data import processes. I can create a temporary table, import data to the temp table, change data types, remove indexes from the permanent table, append data from the temp to the permanent table, then rebuild the indexes -- all automatically, all in SQL code.  &lt;p&gt;But that's not the only use. You can also use DDL to set the seed and interval for an autonumber field, to remotely change the structure of a back-end database (useful for multi-user databases), or make any other change to a production database. In a web environment, it can be used to add databases to an active website.  &lt;p&gt;In this three-part series, I'll be addressing both SQL and DAO.  &lt;ul&gt; &lt;li&gt;&lt;strong&gt;&lt;a href="http://rogersaccessblog.blogspot.com/2011/08/data-definition-language-ddl-dao-vs-sql.html" target="_blank"&gt;&lt;u&gt;SQL vs. DAO&lt;/u&gt;&lt;/a&gt;&lt;/strong&gt;&lt;u&gt; &lt;/u&gt; &lt;li&gt;&lt;strong&gt;DDL Using SQL&lt;/strong&gt; &lt;strong&gt;(this post)&lt;/strong&gt;  &lt;li&gt;&lt;strong&gt;&lt;a href="http://rogersaccessblog.blogspot.com/2011/08/data-definition-language-ddl-ddl-using.html" target="_blank"&gt;&lt;u&gt;DDL Using DAO&lt;/u&gt;&lt;/a&gt;&lt;/strong&gt;  &lt;li&gt;You can download this whole series in a single document with sample database, here: &lt;a href="http://www.rogersaccesslibrary.com/forum/topic570_post587.html"&gt;http://www.rogersaccesslibrary.com/forum/topic570_post587.html&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt; &lt;h4&gt;&amp;nbsp;&lt;/h4&gt; &lt;h4&gt;DDL Using SQL &lt;/h4&gt; &lt;p&gt;The SQL DDL statements can be executed in a query window, but the most useful way to do it is in VBA code. To do that, I need to build a string holding the SQL statement and run it using the Execute method of the database object. There are two ways to do this. The first (and simplest) is to run it directly against the built-in CurrentDb object:  &lt;p&gt;&lt;font face="Courier New"&gt;Dim strSQL As String &lt;br&gt;strSQL = "&amp;lt;The SQL Statement here&amp;gt;"&lt;br&gt;CurrentDb.Execute strSQL&lt;/font&gt;  &lt;p&gt;The other is a little more complicated, but in my opinion better. Create a database object and set it to the CurrentDb:  &lt;p&gt;&lt;font face="Courier New"&gt;Dim db As DAO.Database&lt;br&gt;Dim strSQL As String&lt;br&gt;Set db = CurrentDb&lt;br&gt;strSQL = "&amp;lt;The SQL Statement here&amp;gt;"&lt;br&gt;db.Execute strSQL&lt;/font&gt;  &lt;p&gt;This method is particularly useful when executing multiple SQL statements as I'll show later.  &lt;p&gt;&lt;b&gt;Drop Table&lt;/b&gt;  &lt;p&gt;&lt;font face="Courier New"&gt;CurrentDb.Execute "DROP TABLE BOOKSQL;" &lt;/font&gt; &lt;p&gt;&lt;b&gt;Create Table&lt;/b&gt;  &lt;p&gt;&lt;font color="#008000"&gt;'create a table with 4 fields and Primary key &lt;/font&gt; &lt;p&gt;&lt;font face="Courier New"&gt;Dim strSQL As String&lt;br&gt;strSQL = "CREATE TABLE BOOKSQL "&lt;br&gt;strSQL = strSQL &amp;amp; "(ISBN TEXT(13) CONSTRAINT PKey PRIMARY KEY, "&lt;br&gt;strSQL = strSQL &amp;amp; "Title TEXT(100), "&lt;br&gt;strSQL = strSQL &amp;amp; "Price CURRENCY, "&lt;br&gt;strSQL = strSQL &amp;amp; "PubID TEXT(10)); "&lt;br&gt;CurrentDb.Execute.Execute strSQL&lt;/font&gt;  &lt;p&gt;Datatype key words:  &lt;ul&gt; &lt;li&gt;Autonumber -- AUTOINCREMENT  &lt;li&gt;Text -- TEXT(&amp;lt;length&amp;gt;)  &lt;li&gt;Memo and Hyperlink - MEMO  &lt;li&gt;Byte -- BYTE  &lt;li&gt;Integer -- SHORT  &lt;li&gt;Long integer -- LONG  &lt;li&gt;Single -- SINGLE  &lt;li&gt;Double -- DOUBLE  &lt;li&gt;ReplicationID -- GUID  &lt;li&gt;Date/Time -- DATETIME  &lt;li&gt;Currency -- CURRENCY  &lt;li&gt;Yes/No -- LOGICAL  &lt;li&gt;OleObject -- OLEOBJECT&lt;b&gt; &lt;/b&gt;&lt;/li&gt;&lt;/ul&gt; &lt;p&gt;&lt;b&gt;&lt;/b&gt; &lt;p&gt;&lt;b&gt;Alter Table&lt;/b&gt;  &lt;p&gt;&lt;font face="Courier New"&gt;CurrentDb.Execute "ALTER TABLE PublisherSQL ADD PubAddress TEXT (50);"&lt;/font&gt;  &lt;p&gt;&lt;font face="Courier New"&gt;CurrentDb.Execute "ALTER TABLE BookSQL ADD Cost CURRENCY;"&lt;/font&gt;  &lt;p&gt;&lt;font face="Courier New"&gt;CurrentDb.Execute "ALTER TABLE BookSQL ADD PubDate DATETIME;"&lt;b&gt;&lt;/b&gt;&lt;/font&gt;  &lt;p&gt;&lt;b&gt;Drop Index&lt;/b&gt;  &lt;p&gt;&lt;font face="Courier New"&gt;CurrentDb.Execute "DROP INDEX idxTitle ON BookSQL;"&lt;/font&gt;  &lt;p&gt;&lt;b&gt;Create Index&lt;/b&gt;  &lt;p&gt;&lt;font face="Courier New"&gt;CurrentDb.Execute "CREATE UNIQUE INDEX idxTitle ON BookSQL (Title);"&lt;/font&gt;  &lt;p&gt;&lt;b&gt;Create Primary Key&lt;/b&gt;  &lt;p&gt;&lt;font face="Courier New"&gt;CurrentDb.Execute "CREATE UNIQUE INDEX PrimaryKey ON Books (BookID) WITH PRIMARY;"&lt;/font&gt;  &lt;p&gt;&lt;b&gt;Drop Relationship&lt;/b&gt;  &lt;p&gt;&lt;font face="Courier New"&gt;CurrentDb.Execute "ALTER TABLE BookSQL DROP CONSTRAINT PubBook;"&lt;/font&gt;  &lt;p&gt;&lt;b&gt;Create Relationship&lt;/b&gt;  &lt;p&gt;&lt;font face="Courier New"&gt;Dim strSQL As String&lt;br&gt;strSQL = "ALTER TABLE [BookSQL] "&lt;br&gt;strSQL = strSQL &amp;amp; " ADD CONSTRAINT [PubBook] FOREIGN KEY (PubID) "&lt;br&gt;strSQL = strSQL &amp;amp; " REFERENCES PublisherSQL (PubID);"&lt;br&gt;CurrentDb.Execute strSQL&lt;/font&gt;  &lt;p&gt;&lt;b&gt;Examples:&lt;/b&gt;&lt;b&gt;&lt;/b&gt;  &lt;p&gt;&lt;font face="Courier New"&gt;Sub DropTables()&lt;br&gt;&lt;font color="#008000"&gt;'deletes a Relationship and two tables&lt;br&gt;&lt;/font&gt;Dim db As DAO.Database&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; Set db = CurrentDb&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; db.Execute "ALTER TABLE BookSQL DROP CONSTRAINT PubBook;"&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; db.Execute "DROP TABLE PublisherSQL;"&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; db.Execute "DROP TABLE BookSQL;"&lt;br&gt;Set db = Nothing&lt;br&gt;End Sub&lt;/font&gt;  &lt;p&gt;&lt;font color="#008000"&gt;&lt;font face="Courier New"&gt;'---------------------------------&lt;/font&gt; &lt;/font&gt; &lt;p&gt;&lt;font face="Courier New"&gt;Sub BuildTables()&lt;br&gt;&lt;font color="#008000"&gt;' creates two tables and a Relationship between them&lt;br&gt;&lt;/font&gt;Dim db As DAO.Database&lt;br&gt;Dim strSQL As String&lt;br&gt;Set db = CurrentDb&lt;br&gt;&lt;font color="#008000"&gt;'create PublisherSQL table&lt;br&gt;&lt;/font&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; strSQL = "CREATE TABLE PublisherSQL "&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; strSQL = strSQL &amp;amp; "(PubID TEXT(10) CONSTRAINT "&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; strSQL = strSQL &amp;amp; "PrimaryKey PRIMARY KEY, "&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; strSQL = strSQL &amp;amp; "PubName TEXT(100), "&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; strSQL = strSQL &amp;amp; "PubPhone TEXT(20));"&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; db.Execute strSQL&lt;br&gt;&lt;font color="#008000"&gt;'create BookSQL table&lt;br&gt;&lt;/font&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; strSQL = "CREATE TABLE BookSQL "&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; strSQL = strSQL &amp;amp; "(ISBN TEXT(13) CONSTRAINT PKey PRIMARY KEY, "&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; strSQL = strSQL &amp;amp; "Title TEXT(100), "&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; strSQL = strSQL &amp;amp; "Price MONEY, "&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; strSQL = strSQL &amp;amp; "PubID TEXT(10)); "&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; db.Execute strSQL&lt;br&gt;&lt;font color="#008000"&gt;'create One-To-Many relationship between BookSQL and table&lt;br&gt;&lt;/font&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; strSQL = "ALTER TABLE [BookSQL] "&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; strSQL = strSQL &amp;amp; " ADD CONSTRAINT [PubBook] FOREIGN KEY (PubID) "&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; strSQL = strSQL &amp;amp; " REFERENCES PublisherSQL (PubID);"&lt;br&gt;&amp;nbsp;&amp;nbsp; db.Execute strSQL&lt;br&gt;Set db = Nothing&lt;br&gt;End Sub&lt;/font&gt;  &lt;p&gt;&lt;font color="#008000"&gt;&lt;font face="Courier New"&gt;'---------------------------------&lt;/font&gt; &lt;/font&gt; &lt;p&gt;&lt;font face="Courier New"&gt;Sub DropIndex_Import_CreateIndex()&lt;br&gt;&lt;font color="#008000"&gt;' this sample demonstrates removing an index, importing data, and &lt;br&gt;' re-creating the index.&lt;br&gt;&lt;/font&gt;Dim db As DAO.Database&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; Set db = CurrentDb&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; db.Execute "DROP INDEX idxTitle ON BookSQL;"&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; DoCmd.TransferText acImportDelim, "", "Books", _&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; CurrentProject.Path &amp;amp; "\Books.txt", True, ""&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; db.Execute "create unique index idxTitle on BookSQL(Title);"&lt;br&gt;Set db = Nothing&lt;br&gt;End Sub&lt;/font&gt;  &lt;p&gt;&lt;font color="#008000"&gt;&lt;font face="Courier New"&gt;'---------------------------------&lt;/font&gt; &lt;/font&gt; &lt;p&gt;&lt;font face="Courier New"&gt;Sub ModifyField()&lt;br&gt;&lt;font color="#008000"&gt;'you can't directly modify a field in a table you have to:&lt;br&gt;'1) create a new field with the new properties&lt;br&gt;'2) copy the data from the old field to the new field&lt;br&gt;'3) delete the old field&lt;br&gt;'Note: if the field to be deleted is part of an index, that&lt;br&gt;'index must be dropped and then re-established on the new field&lt;br&gt;&lt;/font&gt;Dim db As DAO.Database&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; Set db = CurrentDb&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; db.Execute "alter table [Books Copy] add Title2 text(100);"&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; db.Execute "UPDATE [Books Copy] SET Title2 = Title;"&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; db.Execute "alter table [Books Copy] drop column Title;"&lt;br&gt;Set db = Nothing&lt;br&gt;End Sub&lt;/font&gt;  &lt;p&gt;Next time, I'll take a closer look at specific &lt;a href="http://rogersaccessblog.blogspot.com/2011/08/data-definition-language-ddl-ddl-using.html" target="_blank"&gt;&lt;u&gt;Data Definition Language using DAO&lt;/u&gt;&lt;/a&gt;.    &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8134759511126815909-733939344761613545?l=rogersaccessblog.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://rogersaccessblog.blogspot.com/feeds/733939344761613545/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8134759511126815909&amp;postID=733939344761613545' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8134759511126815909/posts/default/733939344761613545'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8134759511126815909/posts/default/733939344761613545'/><link rel='alternate' type='text/html' href='http://rogersaccessblog.blogspot.com/2011/08/data-definition-language-sql.html' title='Data Definition Language: SQL'/><author><name>Roger's Access Blog</name><uri>http://www.blogger.com/profile/12265719129831415014</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_JYohVQdgpwU/Scu5h2VvtBI/AAAAAAAAAQc/pr3JDoLaqnQ/S220/avi_Roger_1.JPG'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8134759511126815909.post-1384042549088911426</id><published>2011-08-16T06:26:00.001-04:00</published><updated>2011-08-30T11:14:16.867-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Access 101'/><title type='text'>Data Definition Language (DDL) DAO vs. SQL</title><content type='html'>&lt;p&gt;&lt;strong&gt;Data Definition Language (DDL) &lt;/strong&gt;&lt;/p&gt; &lt;p&gt;Data Definition Language (DDL) is a programmatic way to create and modify the database structure, that is, objects like tables, indexes, and relationships.  &lt;p&gt;Before Access came along, DDL statements were the only way to modify the database structure in most relational database management systems. Access introduced the graphical user interface (GUI) to do most DDL functions. Because the Access GUI is so easy to use, most Access users never have reason to use DDL statements. However, there are circumstances under which it is advantageous to use DDL statements.  &lt;p&gt;I use them a lot for automating data import processes. I can create a temporary table, import data to the temp table, change data types, remove indexes from the permanent table, append data from the temp to the permanent table, then rebuild the indexes -- all automatically, all in SQL code.  &lt;p&gt;But that's not the only use. You can also use DDL to set the seed and interval for an autonumber field, to remotely change the structure of a back-end database (useful for multi-user databases), or make any other change to a production database. In a web environment, it can be used to add databases to an active website.  &lt;p&gt;In this three-part series, I'll be addressing both SQL and DAO.  &lt;ul&gt; &lt;li&gt;&lt;strong&gt;SQL vs. DAO (this post)&lt;/strong&gt;  &lt;li&gt;&lt;strong&gt;&lt;a href="http://rogersaccessblog.blogspot.com/2011/08/data-definition-language-sql.html" target="_blank"&gt;&lt;u&gt;DDL Using SQL&lt;/u&gt;&lt;/a&gt;&lt;/strong&gt;  &lt;li&gt;&lt;strong&gt;&lt;a href="http://rogersaccessblog.blogspot.com/2011/08/data-definition-language-ddl-ddl-using.html" target="_blank"&gt;&lt;u&gt;DDL Using DAO&lt;/u&gt;&lt;/a&gt;&lt;/strong&gt; &lt;li&gt;You can download this whole series in a single document with sample database, here: &lt;a href="http://www.rogersaccesslibrary.com/forum/topic570_post587.html"&gt;http://www.rogersaccesslibrary.com/forum/topic570_post587.html&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt; &lt;p&gt;&lt;strong&gt;DAO vs. SQL&lt;/strong&gt;&lt;/p&gt; &lt;p&gt;In Access, the two major methods are DAO methods and SQL Statements. DAO (Data Access Objects) is the object model that Access uses to programmatically manipulate the database and its data. (There are other object models you can use like ADO and ADO.net, but DAO is recommended for use with Access.) However, in many cases, you can also manipulate your database with in SQL statements. In general, SQL is more efficient than other methods, so if you can, it's recommended.  &lt;p&gt;SQL DDL statements have a number of advantages over other methods of modifying the database structure. For one thing, is independent of the object model you're using (ADO, DAO, ADO.Net) and can be executed from different platforms like VBA, C++, C#, and so forth. And while there are minor differences in implementation, DDL is fairly standard to most database platforms like Access, SQL Server, Oracle, and Sybase. It is also easier to read and understand.  &lt;p&gt;For instance, in SQL, I can create a simple table like so:  &lt;p&gt;&lt;font face="Courier New"&gt;Sub CreateTableSQL()&lt;br&gt;Dim db As DAO.Database&lt;br&gt;Dim strSQL As String&lt;br&gt;Set db = CurrentDb&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; strSQL = "CREATE TABLE NewTable2 " &amp;amp; _&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; "(NewField1 TEXT(100), " &amp;amp; _&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; "NewField2 SINGLE);"&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; db.Execute strSQL&lt;br&gt;End Sub&lt;/font&gt;  &lt;p&gt;By comparison, in DAO, I need to do the following:  &lt;p&gt;&lt;font face="Courier New"&gt;Sub CreateTableDAO()&lt;br&gt;Dim db As DAO.Database&lt;br&gt;Dim tblNew As DAO.TableDef&lt;br&gt;Dim fld As DAO.Field&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; Set tblNew = db.CreateTableDef("NewTable")&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; Set fld = tblNew.CreateField("NewField1", dbText, 100)&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; tblNew.Fields.Append fld&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; Set fld = tblNew.CreateField("NewField2", dbSingle)&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; tblNew.Fields.Append fld&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; db.TableDefs.Append tblNew&lt;br&gt;End Sub&lt;/font&gt;  &lt;p&gt;SQL DDL statements have a number of disadvantages as well. Most importantly, you cannot set a number of Access specific table properties, like Validation Rules, Validation Text, Default values, and so forth. To do that, you have to use an object model like DAO.  &lt;p&gt;Next time, I'll take a closer look at specific &lt;a href="http://rogersaccessblog.blogspot.com/2011/08/data-definition-language-sql.html" target="_blank"&gt;&lt;u&gt;SQL Data Definition Language&lt;/u&gt;&lt;/a&gt; statements.    &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8134759511126815909-1384042549088911426?l=rogersaccessblog.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://rogersaccessblog.blogspot.com/feeds/1384042549088911426/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8134759511126815909&amp;postID=1384042549088911426' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8134759511126815909/posts/default/1384042549088911426'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8134759511126815909/posts/default/1384042549088911426'/><link rel='alternate' type='text/html' href='http://rogersaccessblog.blogspot.com/2011/08/data-definition-language-ddl-dao-vs-sql.html' title='Data Definition Language (DDL) DAO vs. SQL'/><author><name>Roger's Access Blog</name><uri>http://www.blogger.com/profile/12265719129831415014</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_JYohVQdgpwU/Scu5h2VvtBI/AAAAAAAAAQc/pr3JDoLaqnQ/S220/avi_Roger_1.JPG'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8134759511126815909.post-834372674851631024</id><published>2011-08-15T06:44:00.001-04:00</published><updated>2011-08-15T06:44:20.350-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='New Samples'/><category scheme='http://www.blogger.com/atom/ns#' term='Tejpal'/><title type='text'>New Sample: ListFoldersAndFiles</title><content type='html'>&lt;p&gt;By &lt;a href="http://www.rogersaccesslibrary.com/forum/tejpal-ad_forum45.html" target="_blank"&gt;AD Tejpal&lt;/a&gt;&lt;/p&gt; &lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; This sample db demonstrates listing of folders and files contained within the selected top directory, as per desired file specifications. Based upon user's choice, subfolders can be included or ignored. If desired, more than one types of files can be included in a single file spec in the form of a comma separated string - e.g. :&amp;nbsp; "*.htm,*.pdf,*.txt" etc. &lt;/p&gt; &lt;p&gt;&lt;strong&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; Process Mode For Listing Folders and files:&lt;/strong&gt;&lt;/p&gt; &lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; Two alternative methods for listing of folders and files, under a given top folder, are covered as follows:&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; (a) &lt;strong&gt;Non-Recursive mode&lt;/strong&gt; - using Dir() function. Apart from being faster than (b), it has the advantage that there is no extra strain on memory resources (otherwise associated with recursive approach), thus avoiding the risk of potential hang up in case of very large and deep directory tree.&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; (b) &lt;strong&gt;Recursive mode&lt;/strong&gt; - using FileSystemObject&lt;/p&gt; &lt;p&gt;&lt;strong&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; Display Of Listed Folders And Files:&lt;/strong&gt;&lt;/p&gt; &lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; Each run for listing of folders and files is logged in table T_ProcessLog. On the viewing form, for the selected ProcessID, path and other details of topmost folder are displayed at top of the form. Similar details for the current subfolder are displayed just below the information for top-most folder. &lt;/p&gt; &lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; Subfolders and their files are displayed in adjacent subforms. For the current file, its details (like file type, size, attributes, DtCreated / DtLastModified / DtLastAccessed) are also displayed, apart from a hyperlink to the file itself. The hyperlink label becomes active only for permitted file types. The user can edit the contents of table T_AllowHyperLink for setting such&amp;nbsp; permissions. Three alternative styles of display are provided as follows:&lt;/p&gt; &lt;p&gt;&lt;strong&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; Style A - View Folders and Files In Hierarchical Chain:&lt;/strong&gt;&lt;/p&gt; &lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; The user can drill down the directory tree by expanding any of the subfolders which then assumes the role of current main folder, resulting in display of subfolders and files held by the erstwhile subfolder. This can be done indefinitely, till the last subfolder at deepest nesting level is reached. Similarly, by pressing a command button, the user can move up the directory tree. The process can be repeated till the current main folder becomes identical to the top folder (i.e. the original top most folder for which the listing was generated).&lt;/p&gt; &lt;p&gt;&lt;strong&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; Style B - View All Folders At A Glance - And Their Files:&lt;/strong&gt;&lt;/p&gt; &lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; For convenient viewing, the folders are sorted as per nesting level and path. The top-most folder (nesting level: zero) is highlighted in distinct color. For other folders, nested groups are shaded alternately in light and dark grey so as to facilitate visual transition from one nesting level to the other.&lt;/p&gt; &lt;p&gt;&lt;strong&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; Style C - View All Files At A Glance:&lt;/strong&gt;&lt;/p&gt; &lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; For convenient viewing, the files are sorted as per nesting level and path. Files in top-most folder (nesting level: zero) are&amp;nbsp; highlighted in distinct color. For other files, nested groups are shaded alternately in light and dark grey so as to facilitate visual transition from one nesting level to the other.&lt;/p&gt; &lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; General module named basCommDlg, an adaptation from Access Developer's Handbook, has been kindly provided by Bill Mosca. &lt;/p&gt; &lt;p&gt;&lt;strong&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; Note:&lt;br&gt;&lt;/strong&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; (a) While using file system object, all types of folders and files get covered (including System, Volume, Hidden, ReadOnly etc). On the other hand, while using Dir() function, such types don't get covered unless relevant arguments are explicitly supplied.&lt;/p&gt; &lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; (b) Using Dir command via DOS command prompt, listing of folders and files can be saved to a text file, which can then be imported into access table. Such a listing is quite fast and one might be tempted to try this route. However, there is a pitfall associated with this approach. If any special characters are present in the folder or file name (say in internet files), the same might not come through faithfully. For example Â® is found to come across as r - resulting in corrupted path.&amp;nbsp; &lt;/p&gt; &lt;p&gt;&lt;strong&gt;Version: Access 2000 file format. &lt;/strong&gt;&lt;/p&gt; &lt;p&gt;You can find the sample here: &lt;a title="http://www.rogersaccesslibrary.com/forum/topic568_post584.html" href="http://www.rogersaccesslibrary.com/forum/topic568_post584.html"&gt;&lt;u&gt;&lt;strong&gt;http://www.rogersaccesslibrary.com/forum/topic568_post584.html&lt;/strong&gt;&lt;/u&gt;&lt;/a&gt;&lt;/p&gt; &lt;p&gt;More samples by AD Tejpal: &lt;a title="http://www.rogersaccesslibrary.com/forum/tejpal-ad_forum45.html" href="http://www.rogersaccesslibrary.com/forum/tejpal-ad_forum45.html"&gt;&lt;strong&gt;&lt;u&gt;http://www.rogersaccesslibrary.com/forum/tejpal-ad_forum45.html&lt;/u&gt;&lt;/strong&gt;&lt;/a&gt;&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8134759511126815909-834372674851631024?l=rogersaccessblog.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://rogersaccessblog.blogspot.com/feeds/834372674851631024/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8134759511126815909&amp;postID=834372674851631024' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8134759511126815909/posts/default/834372674851631024'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8134759511126815909/posts/default/834372674851631024'/><link rel='alternate' type='text/html' href='http://rogersaccessblog.blogspot.com/2011/08/new-sample-listfoldersandfiles.html' title='New Sample: ListFoldersAndFiles'/><author><name>Roger's Access Blog</name><uri>http://www.blogger.com/profile/12265719129831415014</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_JYohVQdgpwU/Scu5h2VvtBI/AAAAAAAAAQc/pr3JDoLaqnQ/S220/avi_Roger_1.JPG'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8134759511126815909.post-8244835833909346537</id><published>2011-07-25T07:10:00.001-04:00</published><updated>2011-07-25T07:18:50.003-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Quick Tips'/><category scheme='http://www.blogger.com/atom/ns#' term='Access 101'/><title type='text'>Searching for a Wildcard in a “LIKE” Criteria</title><content type='html'>&lt;p&gt;In a query, the LIKE operator allows the user to query for a character or group of characters anywhere within a text field.&amp;nbsp; It does this by matching the field to a string which mixes the character(s) you want to match with wildcard characters like * and ?. Some examples:&lt;/p&gt; &lt;ul&gt; &lt;li&gt;&lt;font size="3" face="Courier New"&gt;FirstName LIKE "C*"&lt;/font&gt;&amp;nbsp; will return any name that starts with a C (i.e. Carlton, Clark) &lt;li&gt;&lt;font size="3" face="Courier New"&gt;LastName LIKE "*-*"&lt;/font&gt;&amp;nbsp; will return any last name that has a hyphen anywhere in the field (i.e. Flickema-Carlson, Smith-Jones) &lt;li&gt;&lt;font size="3" face="Courier New"&gt;PONum LIKE "C????"&lt;/font&gt;&amp;nbsp;&amp;nbsp; will return and PO number that starts with a C and has exactly 5 characters. (i.e. CSIDF, C24DG) &lt;li&gt;&lt;font size="3" face="Courier New"&gt;PONum LIKE "C##"&lt;/font&gt;&amp;nbsp; returns all values that start with a C, is exactly 3 characters long, and characters 2 and 3 MUST be numbers. (i.e. C45, C16)&lt;/li&gt;&lt;/ul&gt; &lt;p&gt;So how can I search for a wildcard character itself?&amp;nbsp; I can simply enclose the wildcard character with brackets [].&lt;/p&gt; &lt;p&gt;For instance, if I wanted to find any value that has an asterisk (*) anywhere in it, I could do this:&lt;/p&gt; &lt;ul&gt; &lt;li&gt; &lt;div align="left"&gt;&lt;font size="3" face="Courier New"&gt;PO LIKE "*[*]*"&lt;/font&gt;&lt;/div&gt;&lt;/li&gt;&lt;/ul&gt; &lt;p align="left"&gt;&lt;font size="2"&gt;If I wanted to find any value that starts with a hash mark(#), I could do this:&lt;/font&gt;&lt;/p&gt; &lt;ul&gt; &lt;li&gt; &lt;div align="left"&gt;&lt;font size="3" face="Courier New"&gt;Check LIKE "[#]*"&lt;/font&gt;&lt;/div&gt;&lt;/li&gt;&lt;/ul&gt; &lt;p align="left"&gt;&lt;font size="2"&gt;&lt;font size="2"&gt;If I wanted to find any value ending with a question mark (?), I could do this:&lt;/font&gt;&lt;/font&gt;&lt;/p&gt; &lt;ul&gt; &lt;li&gt; &lt;div align="left"&gt;&lt;font size="2"&gt;&lt;font size="3" face="Courier New"&gt;Comment LIKE "*[?]"&lt;br&gt;&lt;/font&gt;&lt;/div&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/font&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8134759511126815909-8244835833909346537?l=rogersaccessblog.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://rogersaccessblog.blogspot.com/feeds/8244835833909346537/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8134759511126815909&amp;postID=8244835833909346537' title='5 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8134759511126815909/posts/default/8244835833909346537'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8134759511126815909/posts/default/8244835833909346537'/><link rel='alternate' type='text/html' href='http://rogersaccessblog.blogspot.com/2011/07/searching-for-wildcard-in-like-criteria.html' title='Searching for a Wildcard in a “LIKE” Criteria'/><author><name>Roger's Access Blog</name><uri>http://www.blogger.com/profile/12265719129831415014</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_JYohVQdgpwU/Scu5h2VvtBI/AAAAAAAAAQc/pr3JDoLaqnQ/S220/avi_Roger_1.JPG'/></author><thr:total>5</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8134759511126815909.post-1329376418219884275</id><published>2011-07-22T07:23:00.001-04:00</published><updated>2011-07-22T07:23:59.840-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Tips and Tricks'/><category scheme='http://www.blogger.com/atom/ns#' term='Access 101'/><title type='text'>Showing Query Parameters in a Report</title><content type='html'>&lt;p&gt;A parameter query is one which asks the user for input.&amp;nbsp; For instance, suppose I have a query that pulls records for a date range. Suppose further, I want the query to ask me for a Start Date and End Date for the range.&amp;nbsp; I can create a query like this:&lt;/p&gt; &lt;p&gt;SELECT * FROM MyTable WHERE TheDate BETWEEN [Enter Start Date] AND [Enter End Date]&lt;/p&gt; &lt;p&gt;Running this query will bring up two dialog boxes:&lt;/p&gt; &lt;p&gt;&lt;a href="http://lh3.ggpht.com/-5sdyFvhnZ5U/Tildxq6bWVI/AAAAAAAAA0c/crk7LDVTKbs/s1600-h/image%25255B2%25255D.png"&gt;&lt;img style="background-image: none; border-bottom: 0px; border-left: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://lh6.ggpht.com/-veh-CVcrw00/TildyYkXKaI/AAAAAAAAA0g/3ejQO9VamP4/image_thumb.png?imgmax=800" width="234" height="136"&gt;&lt;/a&gt;&amp;nbsp; &lt;a href="http://lh6.ggpht.com/-egCe0FfXF5U/Tildy_PR33I/AAAAAAAAA0k/pzt-1lNnVkI/s1600-h/image%25255B5%25255D.png"&gt;&lt;img style="background-image: none; border-bottom: 0px; border-left: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://lh4.ggpht.com/-VPvVX9NHZL0/TildzuUkZaI/AAAAAAAAA0o/SStcHZrt54E/image_thumb%25255B1%25255D.png?imgmax=800" width="237" height="137"&gt;&lt;/a&gt;&lt;/p&gt; &lt;p&gt;Entering the values in the boxes will return the records in that range.&lt;/p&gt; &lt;p&gt;But if I create a Report based on this query, how do I show the selected date range?&amp;nbsp; After all, reports show information from records in the query’s record source, and Parameter values aren’t included. &lt;/p&gt;  &lt;p&gt;There are a couple of possibilities. &lt;/p&gt; &lt;p&gt;&lt;strong&gt;Method 1: Read Parameters Directly&lt;/strong&gt;&lt;/p&gt; &lt;p&gt;The first is to read the parameter directly.&amp;nbsp; To do that, I just put a text box on my report for each query parameter. In the control source for the text boxes, I put the query parameter preceded by an equal sign. &lt;/p&gt; &lt;p&gt;For instance, in the case of the above query, the control sources for my two text boxes will be:&lt;br&gt; =[Enter Start Date] and =[Enter End Date]&lt;/p&gt; &lt;p&gt;Alternately, I could have a single text box with your dates concatenated:&lt;br&gt;=[Enter Start Date] &amp;amp; " - " &amp;amp; [Enter End Date] &lt;/p&gt; &lt;p&gt;I can also fancy it up a bit like this:&lt;br&gt;=”Date Range: “ &amp;amp; [Enter Start Date] &amp;amp; " to " &amp;amp; [Enter End Date] &lt;/p&gt; &lt;p&gt;&lt;strong&gt;Method 2: Read Values Returned&lt;/strong&gt;&lt;/p&gt; &lt;p&gt;The second option is to read the actual minimum and maximum values from the records in the record source.&amp;nbsp; I can use the Min and Max functions of the report to do that.&amp;nbsp; Again, in the Control Source property of a text box:&lt;/p&gt; &lt;p&gt;=Min([Enter Start Date]) &amp;amp; " - " &amp;amp; Max([Enter End Date]) &lt;/p&gt; &lt;p&gt;I can, of course, put them in separate text boxes as well.&lt;/p&gt; &lt;p&gt;&lt;strong&gt;Comparing The Methods&lt;/strong&gt;&lt;/p&gt; &lt;p&gt;So what’s the difference between the two methods?&lt;/p&gt; &lt;p&gt;The first method returns the date range &lt;strong&gt;requested&lt;/strong&gt;.&amp;nbsp; The second method returns the date range actually &lt;strong&gt;returned.&lt;/strong&gt;&lt;/p&gt; &lt;p&gt;Isn’t that the same thing?&amp;nbsp; Not necessarily.&amp;nbsp; Suppose I request data from 1/1/2010 to 7/1/2011, but my table only has data starting with 2/1/2011.&amp;nbsp; The first method will return 1/1/2010 to 7/1/2011 (what I requested).&amp;nbsp; But the second method will return 2/1/2011 to 7/1/2011 (what’s actually in the record source of the report).&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8134759511126815909-1329376418219884275?l=rogersaccessblog.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://rogersaccessblog.blogspot.com/feeds/1329376418219884275/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8134759511126815909&amp;postID=1329376418219884275' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8134759511126815909/posts/default/1329376418219884275'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8134759511126815909/posts/default/1329376418219884275'/><link rel='alternate' type='text/html' href='http://rogersaccessblog.blogspot.com/2011/07/showing-query-parameters-in-report.html' title='Showing Query Parameters in a Report'/><author><name>Roger's Access Blog</name><uri>http://www.blogger.com/profile/12265719129831415014</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_JYohVQdgpwU/Scu5h2VvtBI/AAAAAAAAAQc/pr3JDoLaqnQ/S220/avi_Roger_1.JPG'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://lh6.ggpht.com/-veh-CVcrw00/TildyYkXKaI/AAAAAAAAA0g/3ejQO9VamP4/s72-c/image_thumb.png?imgmax=800' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8134759511126815909.post-3722108453688630992</id><published>2011-06-30T07:09:00.001-04:00</published><updated>2011-06-30T07:09:03.843-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Tips and Tricks'/><category scheme='http://www.blogger.com/atom/ns#' term='Opinion'/><title type='text'>Microsoft Releases Office 2010 SP1</title><content type='html'>&lt;p&gt;Office 2010 Service Pack 1 has been released by Microsoft.&amp;nbsp; See MS Knowledgebase article: &lt;a title="http://support.microsoft.com/kb/2460049" href="http://support.microsoft.com/kb/2460049"&gt;http://support.microsoft.com/kb/2460049&lt;/a&gt;.&amp;nbsp; The service pack can be downloaded from the article as well.&lt;/p&gt; &lt;p&gt;The main improvements to Access appear to be in the area of bug-fixes and stability.&amp;nbsp; There are only three improvements listed in the KB article, but in the &lt;a href="http://download.microsoft.com/download/5/1/2/512CC712-4070-4CFF-BC4A-B3AFF0E21A1E/Microsoft%20Office%202010%20Service%20Pack%201%20Changes.xlsx"&gt;downloadable fix list&lt;/a&gt; there’s a much longer list of fixes.&lt;/p&gt; &lt;p&gt;Everybody has different priorities, but to my mind, here are some of the more important problems that were fixed:&lt;/p&gt; &lt;ul&gt; &lt;li&gt;Access does not activate or return the user to the correct Ribbon tab for a previously opened database object when the user returns to that object.&lt;/li&gt; &lt;li&gt;Access Wizards are not loaded correctly when "Disable all controls without notification" is selected in Trust Center.&lt;/li&gt; &lt;li&gt;The program crashes when you apply a sort to a query that is based on a multi-value field.&lt;/li&gt; &lt;li&gt;"Reserved error -5500" occurs when you try to run a cross-tab query that would generate null values in the column names of the query.&lt;/li&gt; &lt;li&gt;"Object invalid or no longer set" error occurs when you try to use an ALTER TABLE query to change a field type or size.&lt;/li&gt; &lt;li&gt;You cannot relink tables in Access databases that have linked tables to other MDBs/ACCDBs that cannot be found&lt;/li&gt; &lt;li&gt;The file format that is displayed in the title bar for Access 2010 databases is "(Access 2007)."&lt;/li&gt; &lt;li&gt;Incorrect data is displayed when a user's query has a list that includes a combination of GroupBy and either OrderBy or Where&lt;/li&gt; &lt;li&gt;"Invalid precision for decimal data type" or results are truncated when the user runs a crosstab query.&lt;/li&gt;&lt;/ul&gt; &lt;p&gt;There are also a lot of “crash fixes”, all of which are important.&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8134759511126815909-3722108453688630992?l=rogersaccessblog.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://rogersaccessblog.blogspot.com/feeds/3722108453688630992/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8134759511126815909&amp;postID=3722108453688630992' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8134759511126815909/posts/default/3722108453688630992'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8134759511126815909/posts/default/3722108453688630992'/><link rel='alternate' type='text/html' href='http://rogersaccessblog.blogspot.com/2011/06/microsoft-releases-office-2010-sp1.html' title='Microsoft Releases Office 2010 SP1'/><author><name>Roger's Access Blog</name><uri>http://www.blogger.com/profile/12265719129831415014</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_JYohVQdgpwU/Scu5h2VvtBI/AAAAAAAAAQc/pr3JDoLaqnQ/S220/avi_Roger_1.JPG'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8134759511126815909.post-6807530497824290509</id><published>2011-06-24T06:26:00.001-04:00</published><updated>2011-06-24T06:26:29.619-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='New Samples'/><title type='text'>New Sample: NormalizingRepeatingColumnsVBA.mdb</title><content type='html'>&lt;p&gt;&lt;strong&gt;&lt;font size="3"&gt;By Roger Carlson&lt;/font&gt;&lt;/strong&gt;&lt;/p&gt; &lt;p&gt;This sample demonstrates how to normalize a table that has repeated columns with VBA.&amp;nbsp; It's purpose is to demonstrate the general principles of normalizing denormalized data from a spreadsheet with code rather than SQL statements.  &lt;p&gt;&lt;font color="#ff0000"&gt;Full Article Included&lt;/font&gt; &lt;p&gt;You can find the sample here:&lt;br&gt;&lt;a title="http://www.rogersaccesslibrary.com/forum/normalizingrepeatingcolumnsvbamdb_topic567.html" href="http://www.rogersaccesslibrary.com/forum/normalizingrepeatingcolumnsvbamdb_topic567.html"&gt;http://www.rogersaccesslibrary.com/forum/normalizingrepeatingcolumnsvbamdb_topic567.html&lt;/a&gt;&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8134759511126815909-6807530497824290509?l=rogersaccessblog.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://rogersaccessblog.blogspot.com/feeds/6807530497824290509/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8134759511126815909&amp;postID=6807530497824290509' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8134759511126815909/posts/default/6807530497824290509'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8134759511126815909/posts/default/6807530497824290509'/><link rel='alternate' type='text/html' href='http://rogersaccessblog.blogspot.com/2011/06/new-sample-normalizingrepeatingcolumnsv.html' title='New Sample: NormalizingRepeatingColumnsVBA.mdb'/><author><name>Roger's Access Blog</name><uri>http://www.blogger.com/profile/12265719129831415014</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_JYohVQdgpwU/Scu5h2VvtBI/AAAAAAAAAQc/pr3JDoLaqnQ/S220/avi_Roger_1.JPG'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8134759511126815909.post-5451650872921678673</id><published>2011-06-23T07:17:00.001-04:00</published><updated>2011-06-23T07:20:35.538-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Access 101'/><category scheme='http://www.blogger.com/atom/ns#' term='Problems With Repeated Columns'/><title type='text'>Normalizing Repeated Columns: VBA</title><content type='html'>&lt;p&gt;In my earlier blog series (&lt;a href="http://rogersaccessblog.blogspot.com/2011/03/problem-of-repeated-columns.html"&gt;The Problem of Repeated Columns&lt;/a&gt;), I defined repeated columns and talked about the data integrity problems associated with them. I also showed several different examples of repeated columns and the how difficult it is to query repeated columns as compared to the normalized equivalent. If you haven't read these yet, it would be worthwhile to read first. Similarly, if you are not familiar with the concept of Normalization, you should read my blog series &lt;a href="http://rogersaccessblog.blogspot.com/2008/12/what-is-normalization-part-i.html"&gt;What is Normalization?&lt;/a&gt;  &lt;p&gt;One of the comments suggested I should discuss how to convert a table with repeated columns into a normalized table structure. I thought that was a really good idea, so I'm going to spend the next few posts doing so. The difficulty in discussing this issue, however, is that the exact solution differs with each table, so there is no single solution. Fortunately, in the last series, I showed 5 different tables, each with a slightly different structure, so while I cannot show a single solution that will work for every table with repeated columns, hopefully, one of the following will work for most cases.  &lt;p&gt;Others in this series:  &lt;ul&gt; &lt;li&gt;&lt;a href="http://rogersaccessblog.blogspot.com/2011/05/normalizing-repeated-columns-simple-pc.html"&gt;Single Repeated Column&lt;/a&gt; &lt;/li&gt; &lt;li&gt;&lt;a href="http://rogersaccessblog.blogspot.com/2011/05/normalizing-repeated-columns-complex-pc.html"&gt;Multiple Repeated Columns&lt;/a&gt; &lt;/li&gt; &lt;li&gt;&lt;a href="http://rogersaccessblog.blogspot.com/2011/06/normalizing-repeated-columns-yesno.html"&gt;Yes/No Fields&lt;/a&gt; &lt;/li&gt; &lt;li&gt;Using VBA (this post) &lt;/li&gt;&lt;/ul&gt; &lt;p&gt;In my previous examples, I used purely SQL solutions. This time, I thought I'd illustrate how to do this with VBA. There's no real advantage to using VBA over the SQL solutions. In fact, in large databases, it will almost always be slower. Nevertheless, there may be situations with complex data that VBA would be the best solution, and besides, it represents another tool in your Access toolkit.  &lt;p&gt;In my post &lt;a href="http://rogersaccessblog.blogspot.com/2011/04/aggregating-across-repeated-columns.html"&gt;Aggregating Across Repeated Columns: Summing&lt;/a&gt;, I discussed an example of table with repeated columns that looked like this:  &lt;p&gt;&lt;a href="http://lh4.ggpht.com/-cRVpUUEhDnk/TgMgsImYHYI/AAAAAAAAA0A/HTyWrYEwcY0/s1600-h/image%25255B9%25255D.png"&gt;&lt;img style="background-image: none; border-bottom: 0px; border-left: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://lh3.ggpht.com/-Ws38TwEBhc0/TgMgsrX9GdI/AAAAAAAAA0E/r5VpDqEeuz4/image_thumb%25255B3%25255D.png?imgmax=800" width="566" height="156"&gt;&lt;/a&gt;&lt;b&gt;&lt;br&gt;Figure 1: Student Scores table with repeated columns&lt;/b&gt;  &lt;p&gt;Normalized to the First Normal Form (1NF) to remove the repeated columns, the table would look like this: &lt;br&gt;&lt;a href="http://lh3.ggpht.com/--l-gqeAoFXo/TgMgs9j4YkI/AAAAAAAAA0I/_iJOhCgWqj4/s1600-h/image%25255B11%25255D.png"&gt;&lt;img style="background-image: none; border-bottom: 0px; border-left: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://lh6.ggpht.com/-XwpFi-1rFVc/TgMgtcrdWyI/AAAAAAAAA0M/XWwLIVEqXaU/image_thumb%25255B5%25255D.png?imgmax=800" width="417" height="414"&gt;&lt;/a&gt;&lt;b&gt;&lt;br&gt;Figure 2: Patient Symptom table (1NF)&lt;/b&gt;  &lt;p&gt;Figure 2, however does not represent the final form of normalization. Because the student names are repeated, they should be removed to a separate table. So normalizing to &lt;b&gt;Third Normal Form (3NF)&lt;/b&gt; I would have something like this: &lt;br&gt;&lt;a href="http://lh4.ggpht.com/-ZLtmYEBBERE/TgMgtn3W5LI/AAAAAAAAA0Q/90zqmcVIROE/s1600-h/image%25255B14%25255D.png"&gt;&lt;img style="background-image: none; border-bottom: 0px; border-left: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://lh6.ggpht.com/-9DKPphfEgfM/TgMgvaMxDBI/AAAAAAAAA0U/11m0X5xSomc/image_thumb%25255B8%25255D.png?imgmax=800" width="519" height="548"&gt;&lt;/a&gt;&lt;br&gt;&lt;b&gt;Figure 3: Normalized to Third Normal Form (3NF) - Tables and Relationships Views&lt;/b&gt;  &lt;p&gt;(For a further explanation of both First and Third Normal Forms, see &lt;a href="http://rogersaccessblog.blogspot.com/2009/02/normal-forms-introduction.html"&gt;The Normal Forms&lt;/a&gt; , &lt;a href="http://rogersaccessblog.blogspot.com/2008/12/what-is-normalization-part-i.html"&gt;What is Normalization&lt;/a&gt;, and &lt;a href="http://rogersaccessblog.blogspot.com/2009/01/entity-relationship-diagramming-part-i.html"&gt;Entity-Relationship Diagramming&lt;/a&gt;).  &lt;p&gt;&lt;a name="_Toc296490155"&gt;&lt;strong&gt;Normalizing to First Normal Form (1NF)&lt;/strong&gt;&lt;/a&gt; &lt;p&gt;I'll start with the 1NF because it's less complex. Overall, it's a matter of looping through the records in the denormalized table (i.e. &lt;b&gt;StudentScores_RepeatedColumns&lt;/b&gt;) and writing each column value into a new record in the 1NF table (i.e&lt;b&gt;. StudentScores_1NF&lt;/b&gt;).  &lt;p&gt;&lt;font face="Courier New"&gt;&lt;strong&gt;Public Sub Normalize_RepeatedColumns_VBA_1NF(ParamArray FieldNames())&lt;/strong&gt;&lt;/font&gt;&lt;/p&gt; &lt;p&gt;&lt;font color="#008000" face="Courier New"&gt;'&amp;nbsp;&amp;nbsp; This routine writes data from a table with repeated columns into a&lt;br&gt;'&amp;nbsp;&amp;nbsp; table normalized to the First Normal Form (1NF)&lt;br&gt;'&amp;nbsp;&amp;nbsp; The field names which are send in via the parameter list of the call&lt;br&gt;'&amp;nbsp;&amp;nbsp; are written into an array called FieldNames.&lt;/font&gt;&lt;/p&gt; &lt;p&gt;&lt;font face="Courier New"&gt;&lt;font color="#008000"&gt;'&amp;nbsp;&amp;nbsp; declare variables&lt;br&gt;&lt;/font&gt;Dim i As Integer&lt;br&gt;Dim db As DAO.Database&lt;br&gt;Dim rsSource As DAO.Recordset&lt;br&gt;Dim rsTarget As DAO.Recordset&lt;/font&gt;&lt;/p&gt; &lt;p&gt;&lt;font face="Courier New"&gt;&lt;font color="#008000"&gt;'&amp;nbsp;&amp;nbsp; Open database object to current database&lt;br&gt;&lt;/font&gt;Set db = CurrentDb&lt;/font&gt;&lt;/p&gt; &lt;p&gt;&lt;font face="Courier New"&gt;&lt;font color="#008000"&gt;'&amp;nbsp;&amp;nbsp; Open the denormalized table to read the values&lt;br&gt;&lt;/font&gt;Set rsSource = db.OpenRecordset("StudentScores_RepeatedColumns")&lt;/font&gt;&lt;/p&gt; &lt;p&gt;&lt;font face="Courier New"&gt;&lt;font color="#008000"&gt;'&amp;nbsp;&amp;nbsp; Open the normalized table write the values&lt;br&gt;&lt;/font&gt;Set rsTarget = db.OpenRecordset("StudentScores_1NF")&lt;/font&gt;&lt;/p&gt; &lt;p&gt;&lt;font face="Courier New"&gt;&lt;font color="#008000"&gt;'&amp;nbsp;&amp;nbsp; Loop through the denormalized Source table&lt;br&gt;&lt;/font&gt;Do While Not rsSource.EOF&lt;/font&gt;&lt;/p&gt; &lt;p&gt;&lt;font face="Courier New"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;font color="#008000"&gt;'&amp;nbsp;&amp;nbsp; Loop through the fields, i.e. the values in the parameter array&lt;br&gt;&lt;/font&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; For i = LBound(FieldNames) To UBound(FieldNames)&lt;/font&gt;&lt;/p&gt; &lt;p&gt;&lt;font face="Courier New"&gt;&lt;font color="#008000"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; '&amp;nbsp;&amp;nbsp; Add a New record to the target table, write the values,&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; '&amp;nbsp;&amp;nbsp; and save (update) the record&lt;br&gt;&lt;/font&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; rsTarget.AddNew&lt;/font&gt;&lt;/p&gt; &lt;p&gt;&lt;font face="Courier New"&gt;&lt;font color="#008000"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; '&amp;nbsp;&amp;nbsp; write the student name&lt;br&gt;&lt;/font&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; rsTarget!StudentID = rsSource!Student&lt;br&gt;&amp;nbsp;&lt;font color="#008000"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; '&amp;nbsp;&amp;nbsp; write the field name&lt;br&gt;&lt;/font&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; rsTarget!TestNum = FieldNames(i)&lt;br&gt;&lt;font color="#008000"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; '&amp;nbsp;&amp;nbsp; write the field value&lt;br&gt;&lt;/font&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; rsTarget!Score = rsSource(FieldNames(i))&lt;/font&gt;&lt;/p&gt; &lt;p&gt;&lt;font face="Courier New"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; rsTarget.Update&lt;/font&gt;&lt;/p&gt; &lt;p&gt;&lt;font face="Courier New"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Next i&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; rsSource.MoveNext&lt;/font&gt;&lt;/p&gt; &lt;p&gt;&lt;font face="Courier New"&gt;Loop&lt;br&gt;&lt;strong&gt;End Sub&lt;/strong&gt;&lt;/font&gt;&lt;/p&gt; &lt;p&gt;You can call the subroutine like so:  &lt;p&gt;&lt;font face="Courier New"&gt;&lt;strong&gt;Sub test1NF()&lt;br&gt;&lt;/strong&gt;&lt;font color="#008000"&gt;'&amp;nbsp;&amp;nbsp; To run the subroutine, place the cursor in this sub and click "run"&lt;br&gt;'&amp;nbsp;&amp;nbsp; The arguments are the field names of the columns you want to &lt;br&gt;'&amp;nbsp;&amp;nbsp; normalize&lt;/font&gt;&lt;/font&gt;&lt;/p&gt; &lt;p&gt;&lt;font face="Courier New"&gt;&amp;nbsp; Call Normalize_RepeatedColumns_VBA_1N _&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; ("Test1", "Test2", "Test3", "Test4")&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br&gt;&lt;strong&gt;End Sub&lt;/strong&gt;&lt;/font&gt;&lt;/p&gt; &lt;p&gt;The end result looks like Figure 2 above.&lt;/p&gt; &lt;p&gt;&lt;b&gt;&lt;/b&gt; &lt;p&gt;&lt;a name="_Toc296490156"&gt;&lt;strong&gt;Normalizing to Third Normal Form (3NF)&lt;/strong&gt;&lt;/a&gt;&lt;/p&gt; &lt;p&gt;The process for normalizing repeated column data into 3NF is similar to the 1NF process. It does, however, require two loops, one to add records to the "one-side" table ("Student") , and an inner loop (For...Next) to write records to the "many-side" table ("StudentScores").  &lt;p&gt;It is important to note that the order in which the data is moved is vital. Data must be written into the "one-side" table first, and then data can be moved into the "many-side" table. Overall, the process is to loop through the records in the denormalized table, write common values to the a record in the one-side table, store the primary key value from the new record, and then for each field in the parameter array, create a new record in the many-side table.  &lt;p&gt;&lt;font face="Courier New"&gt;&lt;strong&gt;Public Sub Normalize_RepeatedColumns_VBA_3NF(ParamArray FieldNames())&lt;br&gt;&lt;/strong&gt;&lt;font color="#008000"&gt;'&amp;nbsp;&amp;nbsp; This routine writes data from a table with repeated columns into two&lt;br&gt;'&amp;nbsp;&amp;nbsp; normalized tables: Students and StudentScores&lt;br&gt;'&amp;nbsp;&amp;nbsp; The field names which are send in via the parameter list of the call&lt;br&gt;'&amp;nbsp;&amp;nbsp; are written into an array called FieldNames.&lt;/font&gt;&lt;/font&gt;&lt;/p&gt; &lt;p&gt;&lt;font face="Courier New"&gt;&lt;font color="#008000"&gt;'&amp;nbsp;&amp;nbsp; declare variables&lt;br&gt;&lt;/font&gt;Dim i As Integer&lt;br&gt;Dim db As DAO.Database&lt;br&gt;Dim rsSource As DAO.Recordset&lt;br&gt;Dim rsTargetOneSide As DAO.Recordset&lt;br&gt;Dim rsTargetManySide As DAO.Recordset&lt;br&gt;Dim StudentIDtemp As Long&lt;/font&gt;&lt;/p&gt; &lt;p&gt;&lt;font face="Courier New"&gt;&lt;font color="#008000"&gt;'&amp;nbsp;&amp;nbsp; Open database object to current database&lt;br&gt;&lt;/font&gt;Set db = CurrentDb&lt;/font&gt;&lt;/p&gt; &lt;p&gt;&lt;font face="Courier New"&gt;&lt;font color="#008000"&gt;'&amp;nbsp;&amp;nbsp; Open the denormalized table to read the values&lt;br&gt;&lt;/font&gt;Set rsSource = db.OpenRecordset("StudentScores_RepeatedColumns")&lt;br&gt;&lt;font color="#008000"&gt;'&amp;nbsp;&amp;nbsp; Open the "one-side" table to write the values&lt;br&gt;&lt;/font&gt;Set rsTargetOneSide = db.OpenRecordset("Student")&lt;br&gt;&lt;font color="#008000"&gt;'&amp;nbsp;&amp;nbsp; Open the "Many-side" table to write the values&lt;br&gt;&lt;/font&gt;Set rsTargetManySide = db.OpenRecordset("StudentScores")&lt;/font&gt;&lt;/p&gt; &lt;p&gt;&lt;font face="Courier New"&gt;&lt;font color="#008000"&gt;'&amp;nbsp;&amp;nbsp; Loop through the denormalized Source table&lt;br&gt;&lt;/font&gt;Do While Not rsSource.EOF&lt;/font&gt;&lt;/p&gt; &lt;p&gt;&lt;font face="Courier New"&gt;&lt;font color="#008000"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; '&amp;nbsp;&amp;nbsp; Add a New record to the "one-side" target table, save &lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; '&amp;nbsp;&amp;nbsp; the primary key value (autonumber) for use later,&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; '&amp;nbsp;&amp;nbsp; and save (update) the record&lt;br&gt;&lt;/font&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; rsTargetOneSide.AddNew&lt;/font&gt;&lt;/p&gt; &lt;p&gt;&lt;font face="Courier New"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; rsTargetOneSide!Student = rsSource!Student&lt;br&gt;&lt;font color="#008000"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; '&amp;nbsp;&amp;nbsp; save the StudentID created by the autonumber field&lt;br&gt;&lt;/font&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; StudentIDtemp = CLng(rsSource("StudentID"))&lt;/font&gt;&lt;/p&gt; &lt;p&gt;&lt;font face="Courier New"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; rsTargetOneSide.Update&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br&gt;&amp;nbsp;&lt;font color="#008000"&gt;&amp;nbsp;&amp;nbsp; '&amp;nbsp;&amp;nbsp; Loop through the fields in the Parameter Array&lt;br&gt;&lt;/font&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; For i = LBound(FieldNames) To UBound(FieldNames)&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br&gt;&lt;font color="#008000"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; '&amp;nbsp;&amp;nbsp; Add a New record to the "many-side" target table,&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; '&amp;nbsp;&amp;nbsp; write the values, and save (update) the record&lt;br&gt;&lt;/font&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; rsTargetManySide.AddNew&lt;/font&gt;&lt;/p&gt; &lt;p&gt;&lt;font face="Courier New"&gt;&lt;font color="#008000"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; '&amp;nbsp;&amp;nbsp; write the saved student id&lt;br&gt;&lt;/font&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; rsTargetManySide!StudentID = StudentIDtemp&lt;br&gt;&lt;font color="#008000"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; '&amp;nbsp;&amp;nbsp; write the field name&lt;br&gt;&lt;/font&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; rsTargetManySide!TestNum = FieldNames(i)&lt;br&gt;&lt;font color="#008000"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; '&amp;nbsp;&amp;nbsp; write the field value&lt;br&gt;&lt;/font&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; rsTargetManySide!Score = rsSource(FieldNames(i))&lt;/font&gt;&lt;/p&gt; &lt;p&gt;&lt;font face="Courier New"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; rsTargetManySide.Update&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; Next i&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; rsSource.MoveNext&lt;/font&gt;&lt;/p&gt; &lt;p&gt;&lt;font face="Courier New"&gt;Loop&lt;br&gt;&lt;strong&gt;End Sub&lt;/strong&gt;&lt;/font&gt;&lt;/p&gt; &lt;p&gt;You can call the subroutine like so:&lt;/p&gt; &lt;p&gt;&lt;font face="Courier New"&gt;&lt;strong&gt;Sub test3NF()&lt;br&gt;&lt;/strong&gt;&lt;/font&gt;&lt;font color="#008000" face="Courier New"&gt;'&amp;nbsp;&amp;nbsp; To run the subroutine, place the cursor in this sub and click "run"&lt;br&gt;'&amp;nbsp;&amp;nbsp; The arguments are the field names of the columns you want to&lt;br&gt;'&amp;nbsp;&amp;nbsp; normalize&amp;nbsp;&amp;nbsp; &lt;/font&gt;&lt;/p&gt; &lt;p&gt;&lt;font face="Courier New"&gt;&amp;nbsp; Call Normalize_RepeatedColumns_VBA_3NF _&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; ("Test1", "Test2", "Test3", "Test4")&lt;br&gt;&lt;strong&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br&gt;End Sub&lt;/strong&gt;&lt;/font&gt;&lt;/p&gt; &lt;p&gt;The end result looks like Figure 3 above. &lt;p&gt;You can find a sample which illustrates the above here:&lt;br&gt;&lt;a title="http://www.rogersaccesslibrary.com/forum/normalizingrepeatingcolumnsvbamdb_topic567.html" href="http://www.rogersaccesslibrary.com/forum/normalizingrepeatingcolumnsvbamdb_topic567.html"&gt;http://www.rogersaccesslibrary.com/forum/normalizingrepeatingcolumnsvbamdb_topic567.html&lt;/a&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8134759511126815909-5451650872921678673?l=rogersaccessblog.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://rogersaccessblog.blogspot.com/feeds/5451650872921678673/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8134759511126815909&amp;postID=5451650872921678673' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8134759511126815909/posts/default/5451650872921678673'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8134759511126815909/posts/default/5451650872921678673'/><link rel='alternate' type='text/html' href='http://rogersaccessblog.blogspot.com/2011/06/normalizing-repeated-columns-vba.html' title='Normalizing Repeated Columns: VBA'/><author><name>Roger's Access Blog</name><uri>http://www.blogger.com/profile/12265719129831415014</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_JYohVQdgpwU/Scu5h2VvtBI/AAAAAAAAAQc/pr3JDoLaqnQ/S220/avi_Roger_1.JPG'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://lh3.ggpht.com/-Ws38TwEBhc0/TgMgsrX9GdI/AAAAAAAAA0E/r5VpDqEeuz4/s72-c/image_thumb%25255B3%25255D.png?imgmax=800' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8134759511126815909.post-1210334145073144641</id><published>2011-06-17T07:23:00.001-04:00</published><updated>2011-06-17T07:23:06.611-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='New Samples'/><category scheme='http://www.blogger.com/atom/ns#' term='Tejpal'/><title type='text'>New Sample: Form_SynchronizedSubforms</title><content type='html'>&lt;h3&gt;By A.D. Tejpal&lt;/h3&gt; &lt;p&gt;This sample db demonstrates synchronized scrolling of two subforms (both in datasheet view). &lt;p&gt;Two modes are covered:&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; (a) One way synchronization: Top subform always functions as the master while the other one serves as the slave.&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; (b) Two way synchronization: Whichever subform happens to be the active one, functions as the master while the other one serves as the slave. &lt;p&gt;Note: For ready identification, the subform currently serving as the slave, has a darker back color as compared to the master. &lt;p&gt;For each of the above modes, three alternative styles of scroll synchronization are demonstrated:&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; (a) Synchronize horizontal scroll only.&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; (b) Synchronize vertical scroll only.&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; (c) Synchronize both horizontal and vertical scroll. &lt;p&gt;Sample data depicts student's scores in phase 1 (top subform) and phase 2 (bottom subform). For each student, wherever the scores in these two subform happen to differ, the same get highlighted as follows:&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; (a) Top subform: Light grey.&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; (b) Bottom subform: If value is greater than that in other subform, it gets highlighted in light green. On the other hand, if value is less than that in other subform, it gets highlighted in light pink. &lt;p&gt;Note: The above highlights get suitably updated promptly on editing of data in either of the two subforms. &lt;p&gt;&lt;b&gt;Version:&lt;/b&gt;&amp;nbsp; Access 2000 File Format &lt;p&gt;You can find the sample here:&lt;br&gt;&lt;a title="http://www.rogersaccesslibrary.com/forum/Form-synchronizedsubforms_topic566.html" href="http://www.rogersaccesslibrary.com/forum/Form-synchronizedsubforms_topic566.html"&gt;http://www.rogersaccesslibrary.com/forum/Form-synchronizedsubforms_topic566.html&lt;/a&gt;&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8134759511126815909-1210334145073144641?l=rogersaccessblog.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://rogersaccessblog.blogspot.com/feeds/1210334145073144641/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8134759511126815909&amp;postID=1210334145073144641' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8134759511126815909/posts/default/1210334145073144641'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8134759511126815909/posts/default/1210334145073144641'/><link rel='alternate' type='text/html' href='http://rogersaccessblog.blogspot.com/2011/06/new-sample-formsynchronizedsubforms.html' title='New Sample: Form_SynchronizedSubforms'/><author><name>Roger's Access Blog</name><uri>http://www.blogger.com/profile/12265719129831415014</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_JYohVQdgpwU/Scu5h2VvtBI/AAAAAAAAAQc/pr3JDoLaqnQ/S220/avi_Roger_1.JPG'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8134759511126815909.post-8255689588539682055</id><published>2011-06-15T06:47:00.001-04:00</published><updated>2011-06-15T06:49:21.711-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='New Samples'/><category scheme='http://www.blogger.com/atom/ns#' term='Tejpal'/><title type='text'>New Sample: Query_ComputeAcrossFields_CodeLess</title><content type='html'>&lt;p&gt;&lt;strong&gt;By A.D. Tejpal&lt;/strong&gt;&lt;/p&gt; &lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; This sample db demonstrates computations across fields via pure SQL. In the first step, normalization of data is accomplished, using Cartesian join between the source table and an ancillary single field table holding the field names. Thereafter, a totals query provides the desired results.&lt;/p&gt; &lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; Two styles are covered:&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; (a) Compute across all fields.&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; (b) Compute across top 3 fields (i.e. fields holding top 3 values).&lt;/p&gt; &lt;p&gt;Version:&amp;nbsp; Access 2000 File Format&lt;/p&gt; &lt;p&gt;You can find the sample here:&lt;br&gt;&lt;a title="http://www.rogersaccesslibrary.com/forum/query-computeacrossfields-codeless_topic565.html" href="http://www.rogersaccesslibrary.com/forum/query-computeacrossfields-codeless_topic565.html"&gt;http://www.rogersaccesslibrary.com/forum/query-computeacrossfields-codeless_topic565.html&lt;/a&gt;&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8134759511126815909-8255689588539682055?l=rogersaccessblog.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://rogersaccessblog.blogspot.com/feeds/8255689588539682055/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8134759511126815909&amp;postID=8255689588539682055' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8134759511126815909/posts/default/8255689588539682055'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8134759511126815909/posts/default/8255689588539682055'/><link rel='alternate' type='text/html' href='http://rogersaccessblog.blogspot.com/2011/06/new-sample-querycomputeacrossfieldscode.html' title='New Sample: Query_ComputeAcrossFields_CodeLess'/><author><name>Roger's Access Blog</name><uri>http://www.blogger.com/profile/12265719129831415014</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_JYohVQdgpwU/Scu5h2VvtBI/AAAAAAAAAQc/pr3JDoLaqnQ/S220/avi_Roger_1.JPG'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8134759511126815909.post-2202324938555520296</id><published>2011-06-15T06:42:00.001-04:00</published><updated>2011-06-15T06:48:48.115-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='New Samples'/><category scheme='http://www.blogger.com/atom/ns#' term='Problems With Repeated Columns'/><title type='text'>New Sample: NormalizingRepeatingColumns3.mdb</title><content type='html'>&lt;p&gt;By Roger Carlson  &lt;p&gt;This sample demonstrates how to normalize a table that has repeated Yes/No columns.&amp;nbsp; It's purpose is to demonstrate the general principles of normalizing denormalized data from a spreadsheet.  &lt;p&gt;&lt;font color="#ff0000"&gt;Full article included&lt;/font&gt;  &lt;p&gt;&lt;font color="#000000"&gt;You can find the sample here: &lt;a title="http://www.rogersaccesslibrary.com/forum/normalizingrepeatingcolumns3mdb_topic564.html" href="http://www.rogersaccesslibrary.com/forum/normalizingrepeatingcolumns3mdb_topic564.html"&gt;http://www.rogersaccesslibrary.com/forum/normalizingrepeatingcolumns3mdb_topic564.html&lt;/a&gt;&lt;/font&gt;&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8134759511126815909-2202324938555520296?l=rogersaccessblog.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://rogersaccessblog.blogspot.com/feeds/2202324938555520296/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8134759511126815909&amp;postID=2202324938555520296' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8134759511126815909/posts/default/2202324938555520296'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8134759511126815909/posts/default/2202324938555520296'/><link rel='alternate' type='text/html' href='http://rogersaccessblog.blogspot.com/2011/06/new-sample-normalizingrepeatingcolumns3.html' title='New Sample: NormalizingRepeatingColumns3.mdb'/><author><name>Roger's Access Blog</name><uri>http://www.blogger.com/profile/12265719129831415014</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_JYohVQdgpwU/Scu5h2VvtBI/AAAAAAAAAQc/pr3JDoLaqnQ/S220/avi_Roger_1.JPG'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8134759511126815909.post-7419015316719668200</id><published>2011-06-14T07:25:00.001-04:00</published><updated>2011-06-14T07:25:41.985-04:00</updated><title type='text'>Normalizing Repeated Columns: Yes/No Fields (Part2)</title><content type='html'>&lt;p&gt;Last time, in &lt;a href="http://rogersaccessblog.blogspot.com/2011/06/normalizing-repeated-columns-yesno.html"&gt;Normalizing Repeated Columns: Yes/No Fields (Part1)&lt;/a&gt; I talked about Normalizing data in repeated columns from a spreadsheet, like this: &lt;p&gt;&lt;a href="http://lh3.ggpht.com/-G4Hw4IbpJ20/TfdE0rn9VeI/AAAAAAAAAyo/Yw2GtgSMPrs/s1600-h/image%25255B48%25255D.png"&gt;&lt;img style="background-image: none; border-bottom: 0px; border-left: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://lh5.ggpht.com/-w13RZ9K3s5Y/TfdE4BthUxI/AAAAAAAAAys/xqC95ByYvIc/image_thumb%25255B26%25255D.png?imgmax=800" width="453" height="150"&gt;&lt;/a&gt; &lt;p&gt;Into a structure like this: &lt;p&gt;&lt;a href="http://lh3.ggpht.com/-qCJwFsh0b5A/TfdE65ww-4I/AAAAAAAAAyw/nLT7i9fuQj0/s1600-h/image%25255B45%25255D.png"&gt;&lt;img style="background-image: none; border-bottom: 0px; border-left: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://lh5.ggpht.com/-AaEkx958k9c/TfdE78722KI/AAAAAAAAAy0/yr6-3oaqhW0/image_thumb%25255B23%25255D.png?imgmax=800" width="550" height="254"&gt;&lt;/a&gt; &lt;p&gt;&lt;a href="http://lh3.ggpht.com/-tQUUrfJIaa8/TfdE8gjtoGI/AAAAAAAAAy4/2yYA_pVoGeQ/s1600-h/image%25255B44%25255D.png"&gt;&lt;img style="background-image: none; border-bottom: 0px; border-left: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://lh6.ggpht.com/-mV4XfeKYu2U/TfdE9kFEu1I/AAAAAAAAAy8/lJTiobXOpOI/image_thumb%25255B22%25255D.png?imgmax=800" width="570" height="212"&gt;&lt;/a&gt; &lt;p&gt;Using a One-Time process that creates the tables and relationships in addition to copying the data.&amp;nbsp; This time, I’ll talk about appending data to existing tables. &lt;p&gt;&amp;nbsp; &lt;h4&gt;Appending to Existing Tables&lt;/h4&gt; &lt;p&gt;The process for normalizing repeated column data into Existing Tables is similar to the One-Time process. It does, however, require you to create the tables and relationships correctly ahead of time. Because I've just created them in the previous section, I'll simply delete the data and re-append it from the linked spreadsheet. &lt;p&gt;The two processes are very similar, but I'll be using Append queries rather than Make-Table queries. &lt;h5&gt;Step 1: Remove the common Patient elements.&lt;/h5&gt; &lt;p&gt;Since I've already got a table, I'll use an Append query rather than the Make-Table query I used earlier: &lt;p&gt;&lt;font face="Courier New"&gt;INSERT INTO Patient ( Patient )&lt;br&gt;SELECT Patient&lt;br&gt;FROM Patient_RC&lt;br&gt;GROUP BY Patient;&lt;/font&gt; &lt;p&gt;Or in the Query Builder: &lt;p&gt;&lt;a href="http://lh4.ggpht.com/-nG4-Yz3HvHM/TfdE-VKTr9I/AAAAAAAAAzA/HEwVA-Hp7-Q/s1600-h/image%25255B43%25255D.png"&gt;&lt;img style="background-image: none; border-bottom: 0px; border-left: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://lh4.ggpht.com/-lw3wr5Ji3os/TfdE_TLfOiI/AAAAAAAAAzE/cIVrR033cEM/image_thumb%25255B21%25255D.png?imgmax=800" width="448" height="421"&gt;&lt;/a&gt; &lt;p&gt;The resulting table looks like this: &lt;p&gt;&lt;a href="http://lh3.ggpht.com/-C7tRSJVrDMc/TfdE_7TgE6I/AAAAAAAAAzI/Z1U-6ruCgPk/s1600-h/image%25255B41%25255D.png"&gt;&lt;img style="background-image: none; border-bottom: 0px; border-left: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://lh5.ggpht.com/-vKtPd3hJugQ/TfdFAgMXsLI/AAAAAAAAAzM/80_b4i30e7A/image_thumb%25255B19%25255D.png?imgmax=800" width="294" height="179"&gt;&lt;/a&gt; &lt;p&gt;Since I already have an autonumber primary key defined on the table, I don't need to do anything further. &lt;h5&gt;Step 2: Create Symptom_Temp table&lt;/h5&gt; &lt;p&gt;This step is exactly the same as Step 2 above. I'll use a Make-Table query to create a temporary table that will be used for creating the relationships later. &lt;p&gt;&lt;font face="Courier New"&gt;SELECT Patient, IIf([cough],"Cough") AS Symptom&lt;br&gt;FROM Patient_RC WHERE [Cough] = True&lt;br&gt;UNION ALL &lt;br&gt;SELECT Patient, IIf([Sneeze],"Sneeze") AS Symptom&lt;br&gt;FROM Patient_RC WHERE [Sneeze] = True&lt;br&gt;UNION ALL &lt;br&gt;SELECT Patient, IIf([Fever],"Fever") AS Symptom&lt;br&gt;FROM Patient_RC WHERE [Fever] = True&lt;br&gt;UNION ALL &lt;br&gt;SELECT Patient, IIf([Body_Aches],"Body aches") AS Symptom&lt;br&gt;FROM Patient_RC WHERE [Body_Aches] = True&lt;br&gt;UNION ALL SELECT Patient, IIf([Nausea],"Nausea") AS Symptom&lt;br&gt;FROM Patient_RC WHERE [Nausea] = True&lt;br&gt;ORDER BY Patient, Symptom;&lt;/font&gt; &lt;p&gt;As I showed in the previous post, there are two ways to make this into an Append query: 1) a Stacked Query, and 2) All In One. &lt;p&gt;&lt;b&gt;Stacked Query&lt;/b&gt; &lt;p&gt;A stacked query is simply a query which uses another query in the FROM clause instead of a table. So if I save the above query as "&lt;b&gt;qryTemp&lt;/b&gt;", I can use that query in the FROM clause of an Append query: &lt;p&gt;&lt;font face="Courier New"&gt;INSERT INTO Symptom_temp ( Patient, Symptom )&lt;br&gt;SELECT Patient, Symptom&lt;br&gt;FROM qryTemp;&lt;/font&gt; &lt;p&gt;Or in the Query Builder: &lt;p&gt;&lt;a href="http://lh6.ggpht.com/-v7wHqMpZjFA/TfdFBklkbyI/AAAAAAAAAzQ/8aRV7nRpoGs/s1600-h/image%25255B40%25255D.png"&gt;&lt;img style="background-image: none; border-bottom: 0px; border-left: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://lh4.ggpht.com/-20wI_exjgT4/TfdFDRmP_VI/AAAAAAAAAzU/DD7NUe0ECmg/image_thumb%25255B18%25255D.png?imgmax=800" width="485" height="410"&gt;&lt;/a&gt; &lt;p&gt;&lt;b&gt;All-in-one Query&lt;/b&gt; &lt;p&gt;It's also possible to it in a single query. To do that, I surround the SQL of &lt;b&gt;qryTemp&lt;/b&gt; in parentheses and use it in the From clause of the Append query. Like this: &lt;p&gt;&lt;font face="Courier New"&gt;INSERT INTO Symptom_temp ( Patient, Symptom )&lt;br&gt;SELECT qryTemp.Patient, qryTemp.Symptom&lt;br&gt;FROM &lt;/font&gt;&lt;font face="Courier New"&gt;&lt;b&gt;(SELECT Patient, IIf([cough],"Cough") AS Symptom&lt;br&gt;FROM Patient_RC WHERE [Cough] = True&lt;br&gt;UNION ALL &lt;br&gt;SELECT Patient, IIf([Sneeze],"Sneeze") AS Symptom&lt;br&gt;FROM Patient_RC WHERE [Sneeze] = True&lt;br&gt;UNION ALL &lt;br&gt;SELECT Patient, IIf([Fever],"Fever") AS Symptom&lt;br&gt;FROM Patient_RC WHERE [Fever] = True&lt;br&gt;UNION ALL &lt;br&gt;SELECT Patient, IIf([Body_Aches],"Body aches") AS Symptom&lt;br&gt;FROM Patient_RC WHERE [Body_Aches] = True&lt;br&gt;UNION ALL &lt;br&gt;SELECT Patient, IIf([Nausea],"Nausea") AS Symptom&lt;br&gt;FROM Patient_RC WHERE [Nausea] = True&lt;br&gt;ORDER BY Patient, Symptom)&lt;/b&gt; AS qryTemp;&lt;/font&gt; &lt;p&gt;The result will be a table that looks like this: &lt;p&gt;&lt;a href="http://lh6.ggpht.com/-pzH8tUKSQpk/TfdFD1Wng1I/AAAAAAAAAzY/EgTPkcCFO0w/s1600-h/image%25255B38%25255D.png"&gt;&lt;img style="background-image: none; border-bottom: 0px; border-left: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://lh6.ggpht.com/-4XHzCgqFx_I/TfdFEzHXuCI/AAAAAAAAAzc/QI6UsW7sP8A/image_thumb%25255B16%25255D.png?imgmax=800" width="282" height="277"&gt;&lt;/a&gt; &lt;h5&gt;Step 3: Remove Duplicates from Symptom_Temp&lt;/h5&gt; &lt;p&gt;To fulfill 3NF, I need to create a query that removes the duplicate Symptom values into the &lt;b&gt;Symptom &lt;/b&gt;table. The &lt;b&gt;GROUP BY&lt;/b&gt; clause works well for that. . As before, I'll use an Append write the data to my existing Software table: &lt;p&gt;&lt;font face="Courier New"&gt;INSERT INTO Symptoms ( Symptom )&lt;br&gt;SELECT Symptom&lt;br&gt;FROM Symptom_temp&lt;br&gt;GROUP BY Symptom;&lt;/font&gt; &lt;p&gt;Or in the Query Builder: &lt;p&gt;&lt;a href="http://lh5.ggpht.com/-T_w_OYTKEGo/TfdFGIVxeTI/AAAAAAAAAzg/qbKq0tn2I0M/s1600-h/image%25255B37%25255D.png"&gt;&lt;img style="background-image: none; border-bottom: 0px; border-left: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://lh4.ggpht.com/-MzlUQKNghq4/TfdFIHq8YWI/AAAAAAAAAzk/zP-MROCZhg8/image_thumb%25255B15%25255D.png?imgmax=800" width="497" height="405"&gt;&lt;/a&gt; &lt;p&gt;With the resulting table: &lt;p&gt;&lt;a href="http://lh3.ggpht.com/-OtpsweSlY0A/TfdFItsFviI/AAAAAAAAAzo/dH2gde8oSB4/s1600-h/image%25255B36%25255D.png"&gt;&lt;img style="background-image: none; border-bottom: 0px; border-left: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://lh6.ggpht.com/-_cL9onhEses/TfdFJnT01II/AAAAAAAAAzs/pgYKVpWxs7M/image_thumb%25255B14%25255D.png?imgmax=800" width="280" height="193"&gt;&lt;/a&gt; &lt;p&gt;Again, this table already has an autonumber primary key, so I don't need to create one. &lt;h5&gt;Step 4: Creating the Linking Table: Patient_Symptoms&lt;/h5&gt; &lt;p&gt;So now, I've got my two "One-Side" tables: Patient and Symptoms. Now I just need to fill the linking table. &lt;p&gt;I can do that with an Append Query and a simple join of Patient, Symptoms and Symptom_Temp: &lt;p&gt;&lt;font face="Courier New"&gt;INSERT INTO Patient_Symptoms ( PatientID, SymptomID )&lt;br&gt;SELECT Patient.PatientID, Symptoms.SymptomID&lt;br&gt;FROM (Patient INNER JOIN Symptom_temp ON Patient.Patient = Symptom_temp.Patient) &lt;br&gt;INNER JOIN Symptoms ON Symptom_temp.Symptom = Symptoms.Symptom; &lt;/font&gt; &lt;p&gt;Or in the Query Builder: &lt;p&gt;&lt;a href="http://lh4.ggpht.com/-_m9WtC-7Ss4/TfdFKNbEa2I/AAAAAAAAAzw/W9Tst5eq9GY/s1600-h/image%25255B35%25255D.png"&gt;&lt;img style="background-image: none; border-bottom: 0px; border-left: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://lh6.ggpht.com/-qKtdf1xriXo/TfdFLRpJPOI/AAAAAAAAAz0/AQYh4ac1xDY/image_thumb%25255B13%25255D.png?imgmax=800" width="560" height="386"&gt;&lt;/a&gt; &lt;p&gt;The final result will look like this: &lt;p&gt;&lt;a href="http://lh4.ggpht.com/-3MsViidX4o0/TfdFLyAppKI/AAAAAAAAAz4/5EGDZXak4qs/s1600-h/image%25255B33%25255D.png"&gt;&lt;img style="background-image: none; border-bottom: 0px; border-left: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://lh4.ggpht.com/-fMuLAoXbzWM/TfdFNHK4-TI/AAAAAAAAAz8/trUT6E5icM0/image_thumb%25255B11%25255D.png?imgmax=800" width="559" height="210"&gt;&lt;/a&gt; &lt;p&gt;It is important to note that the order in which the data is moved is vital. Data must be written into the "one-side" tables (Patient and Symptoms) first, and then data can be moved into the "linking" tables (Patient_ Symptom). &lt;p&gt;This two-part post is available as a single download (with sample database) here:&lt;br&gt;&lt;a href="http://www.rogersaccesslibrary.com/forum/topic564.html"&gt;http://www.rogersaccesslibrary.com/forum/topic564.html&lt;/a&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8134759511126815909-7419015316719668200?l=rogersaccessblog.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://rogersaccessblog.blogspot.com/feeds/7419015316719668200/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8134759511126815909&amp;postID=7419015316719668200' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8134759511126815909/posts/default/7419015316719668200'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8134759511126815909/posts/default/7419015316719668200'/><link rel='alternate' type='text/html' href='http://rogersaccessblog.blogspot.com/2011/06/normalizing-repeated-columns-yesno_14.html' title='Normalizing Repeated Columns: Yes/No Fields (Part2)'/><author><name>Roger's Access Blog</name><uri>http://www.blogger.com/profile/12265719129831415014</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_JYohVQdgpwU/Scu5h2VvtBI/AAAAAAAAAQc/pr3JDoLaqnQ/S220/avi_Roger_1.JPG'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://lh5.ggpht.com/-w13RZ9K3s5Y/TfdE4BthUxI/AAAAAAAAAys/xqC95ByYvIc/s72-c/image_thumb%25255B26%25255D.png?imgmax=800' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8134759511126815909.post-6888386384233902166</id><published>2011-06-07T06:47:00.001-04:00</published><updated>2011-06-23T07:23:51.451-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Access 101'/><category scheme='http://www.blogger.com/atom/ns#' term='Problems With Repeated Columns'/><title type='text'>Normalizing Repeated Columns: Yes/No Fields (Part1)</title><content type='html'>&lt;p&gt;In my earlier series (&lt;a href="http://rogersaccessblog.blogspot.com/2011/03/problem-of-repeated-columns.html"&gt;The Problem of Repeated Columns&lt;/a&gt;), I defined repeated columns and talked about the data integrity problems associated with them. I also showed several different examples of repeated columns and the how difficult it is to query repeated columns as compared to the normalized equivalent. If you haven't read these yet, it would be worthwhile to read first. Similarly, if you are not familiar with the concept of Normalization, you should read my blog series &lt;a href="http://rogersaccessblog.blogspot.com/2008/12/what-is-normalization-part-i.html"&gt;What is Normalization?&lt;/a&gt;  &lt;p&gt;One of the comments suggested I should discuss how to convert a table with repeated columns into a normalized table structure. I thought that was a really good idea, so I'm going to spend the next few posts doing so. The difficulty in discussing this issue, however, is that the exact solution differs with each table, so there is no single solution. Fortunately, in the last series, I showed 5 different tables, each with a slightly different structure, so while I cannot show a single solution that will work for every table with repeated columns, hopefully, one of the following will work for most cases.  &lt;p&gt;Others in this series:  &lt;ul&gt; &lt;li&gt;&lt;a href="http://rogersaccessblog.blogspot.com/2011/05/normalizing-repeated-columns-simple-pc.html"&gt;Single Repeated Column&lt;/a&gt;  &lt;li&gt;&lt;a href="http://rogersaccessblog.blogspot.com/2011/05/normalizing-repeated-columns-complex-pc.html"&gt;Multiple Repeated Columns&lt;/a&gt;  &lt;li&gt;&lt;strong&gt;Yes/No Fields (this post)&lt;/strong&gt;  &lt;li&gt;&lt;a href="http://rogersaccessblog.blogspot.com/2011/06/normalizing-repeated-columns-vba.html"&gt;Using VBA&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt; &lt;p&gt;&lt;b&gt;Patient Symptoms &lt;/b&gt;&lt;/p&gt; &lt;p&gt;In my post &lt;a href="http://rogersaccessblog.blogspot.com/2011/04/querying-repeated-columns-multiple-iifs.html"&gt;Querying Repeated Columns: Multiple IIFs&lt;/a&gt;, I discussed an example of a Patient table with repeated Yes/No columns that looked like this:  &lt;p&gt;&lt;a href="http://lh3.ggpht.com/-aVSNUT_a7pk/Te4BQG2w6UI/AAAAAAAAAwg/k6bXj7rFZNM/s1600-h/clip_image001%25255B4%25255D.jpg"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="clip_image001" border="0" alt="clip_image001" src="http://lh4.ggpht.com/-fIGqYlg-qNI/Te4BQ2IWPWI/AAAAAAAAAwk/n276c4p6hRo/clip_image001_thumb%25255B1%25255D.jpg?imgmax=800" width="581" height="158"&gt;&lt;/a&gt;&lt;br&gt;&lt;b&gt;Figure 1: Patient table with repeated, Yes/No columns &lt;/b&gt; &lt;p&gt;Normalized to remove the repeated columns, the tables relationships would look like this:  &lt;p&gt;&lt;a href="http://lh4.ggpht.com/-sTsh5VXwjqg/Te4BRfNoAoI/AAAAAAAAAwo/X99yjX8mJWQ/s1600-h/clip_image002%25255B5%25255D.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="clip_image002" border="0" alt="clip_image002" src="http://lh4.ggpht.com/-gssWGUUgKuU/Te4BSYeMM5I/AAAAAAAAAws/seMc6Flnc5k/clip_image002_thumb%25255B2%25255D.png?imgmax=800" width="585" height="245"&gt;&lt;/a&gt;&lt;br&gt;&lt;b&gt;Figure 2: Relationships for the Patient Symptom tables.&lt;/b&gt;  &lt;p&gt;So how do I get the data from Figure 1 to Figure 2? Well, first of all, it depends on whether this is a one-time process where you are creating new normalized tables, or whether it is an on-going process where you are appending the data to existing, normalized tables.  &lt;p&gt;In either case, however, not only do I have to split the records into separate tables, I also have to preserve the relationships between those records, so I have to do it is a specific order.  &lt;p&gt;&lt;b&gt;&lt;/b&gt; &lt;p&gt;&lt;a name="_Toc294157084"&gt;&lt;b&gt;One-Time Process&lt;/b&gt;&lt;/a&gt;&lt;b&gt;&lt;/b&gt;  &lt;p&gt;Suppose I'm given a spreadsheet of data like this:  &lt;p&gt;&lt;a href="http://lh3.ggpht.com/-20SP3Yz8Bmk/Te4BS0y9ctI/AAAAAAAAAww/rdrYiqMJllk/s1600-h/clip_image003%25255B5%25255D.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="clip_image003" border="0" alt="clip_image003" src="http://lh5.ggpht.com/-mxdh3HXPSpc/Te4BTh_IIoI/AAAAAAAAAw0/ZnKZ2zVophU/clip_image003_thumb%25255B2%25255D.png?imgmax=800" width="476" height="152"&gt;&lt;/a&gt;  &lt;p&gt;And I need to create a normalized database from it. I've already determined the tables I need (Figure 2 above), but if you're not certain what your table structure should be, you should read through my blog serie: &lt;a href="http://rogersaccessblog.blogspot.com/2008/12/what-is-normalization-part-i.html"&gt;What is Normalization&lt;/a&gt;, &lt;a href="http://rogersaccessblog.blogspot.com/2009/01/entity-relationship-diagramming-part-i.html"&gt;Entity-Relationship Diagramming&lt;/a&gt;, and &lt;a href="http://rogersaccessblog.blogspot.com/2009/02/normal-forms-introduction.html"&gt;The Normal Forms&lt;/a&gt;.  &lt;p&gt;&lt;b&gt;Link the spreadsheet into a database.&lt;/b&gt;  &lt;p&gt;First of all, I need to get the spreadsheet into the database. I find it is preferable to link rather than import the file. Since I'm not going to be changing the data, there's no reason to import it. Once the file is linked it acts just like a table, and I can begin the process of normalizing it.  &lt;p&gt;&lt;a name="_Toc294157085"&gt;&lt;b&gt;Step 1: Remove the common Patient elements&lt;/b&gt;&lt;/a&gt;&lt;b&gt;. &lt;/b&gt; &lt;p&gt;Since I need to create a new table (called &lt;b&gt;Patient&lt;/b&gt;) to hold these values, I'll use a Make-Table query:  &lt;p&gt;&lt;font face="Courier New"&gt;SELECT Patient_RC.Patient INTO Patient&lt;br&gt;FROM Patient_RC&lt;br&gt;GROUP BY Patient_RC.Patient;&lt;/font&gt;  &lt;p&gt;Or in the Query Builder:  &lt;p&gt;&lt;a href="http://lh3.ggpht.com/-a2zBGuqR2Xs/Te4BUcHD2kI/AAAAAAAAAw4/mi-ZS-J0wiE/s1600-h/clip_image004%25255B5%25255D.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="clip_image004" border="0" alt="clip_image004" src="http://lh3.ggpht.com/-YHOmFHj5n9k/Te4BVvbST3I/AAAAAAAAAw8/wjEcIWdLJb4/clip_image004_thumb%25255B2%25255D.png?imgmax=800" width="491" height="438"&gt;&lt;/a&gt;  &lt;p&gt;The resulting table looks like this:  &lt;p&gt;&lt;a href="http://lh6.ggpht.com/-w8PvMThcy48/Te4BVyDw8FI/AAAAAAAAAxA/6acVATryNsc/s1600-h/clip_image005%25255B4%25255D.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="clip_image005" border="0" alt="clip_image005" src="http://lh4.ggpht.com/-Rb_zTudaFtc/Te4BW9MurcI/AAAAAAAAAxE/PNNLovbt8Gg/clip_image005_thumb%25255B1%25255D.png?imgmax=800" width="371" height="227"&gt;&lt;/a&gt;  &lt;p&gt;At this point, I need to add a Primary Key to the table. I prefer to use Surrogate Keys. (For a discussion of natural vs. surrogate keys, see my blog post: &lt;a href="http://rogersaccessblog.blogspot.com/2008/12/what-is-primary-key.html"&gt;What is a Primary Key?)&lt;/a&gt;  &lt;p&gt;&lt;b&gt;Add surrogate key to PC table.&lt;/b&gt;  &lt;p&gt;Adding a surrogate key to a table is as simple as opening it in Design View, adding an Autonumber Field, and making it the Primary Key.  &lt;p&gt;&lt;a href="http://lh5.ggpht.com/-saRPppnPH3c/Te4BXZftulI/AAAAAAAAAxI/e3Q8vbR1BrE/s1600-h/clip_image006%25255B4%25255D.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="clip_image006" border="0" alt="clip_image006" src="http://lh6.ggpht.com/-moB26j9k6as/Te4BYo2Ni9I/AAAAAAAAAxM/TBPRAvFMb0Q/clip_image006_thumb%25255B1%25255D.png?imgmax=800" width="487" height="362"&gt;&lt;/a&gt;  &lt;p&gt;Saving the table will automatically fill the autonumber field with values:  &lt;p&gt;&lt;a href="http://lh5.ggpht.com/-YFQsRtgRG0A/Te4BZJFK1fI/AAAAAAAAAxQ/pRlF8gngenc/s1600-h/clip_image007%25255B4%25255D.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="clip_image007" border="0" alt="clip_image007" src="http://lh6.ggpht.com/-nhRcXbbIOB8/Te4BZ7oI_BI/AAAAAAAAAxU/0uBrVmSUtLs/clip_image007_thumb%25255B1%25255D.png?imgmax=800" width="366" height="188"&gt;&lt;/a&gt;  &lt;p&gt;&lt;a name="_Toc294157086"&gt;&lt;b&gt;Step 2: Create Symptom_Temp table&lt;/b&gt;&lt;/a&gt;&lt;b&gt;&lt;/b&gt;  &lt;p&gt;As I showed in my post &lt;a href="http://rogersaccessblog.blogspot.com/2011/04/querying-repeated-columns-multiple-iifs.html"&gt;Querying Repeated Columns: Multiple IIFs&lt;/a&gt; it is necessary to combine multiple UNION queries with multiple IIF statements to create a list of Symptoms. Something like this:  &lt;p&gt;&lt;font face="Courier New"&gt;SELECT Patient, IIf([cough],"Cough") AS Symptom&lt;br&gt;FROM Patient_RC WHERE [Cough] = True&lt;br&gt;UNION ALL &lt;br&gt;SELECT Patient, IIf([Sneeze],"Sneeze") AS Symptom&lt;br&gt;FROM Patient_RC WHERE [Sneeze] = True&lt;br&gt;UNION ALL &lt;br&gt;SELECT Patient, IIf([Fever],"Fever") AS Symptom&lt;br&gt;FROM Patient_RC WHERE [Fever] = True&lt;br&gt;UNION ALL &lt;br&gt;SELECT Patient, IIf([Body_Aches],"Body aches") AS Symptom&lt;br&gt;FROM Patient_RC WHERE [Body_Aches] = True&lt;br&gt;UNION ALL SELECT Patient, IIf([Nausea],"Nausea") AS Symptom&lt;br&gt;FROM Patient_RC WHERE [Nausea] = True&lt;br&gt;ORDER BY Patient, Symptom;&lt;/font&gt;  &lt;p&gt;&lt;i&gt;Note: You can't create or view this query in the Query Builder, however, you can create the first on (&lt;/i&gt;Cough&lt;i&gt;) in the QB, then switch to the SQL View and copy and paste, modifying each for the specific column.&lt;/i&gt;  &lt;p&gt;I still need to make this query into a Make-Table query. There are a couple of ways to do this. I can use a stacked query or all-in-one query.  &lt;p&gt;&lt;b&gt;Stacked Query&lt;/b&gt;  &lt;p&gt;A stacked query is simply a query which uses another query in the FROM clause instead of a table. So if I save the above query as "&lt;b&gt;qryTemp&lt;/b&gt;", I can use that query in the FROM clause of a Make-Table query:  &lt;p&gt;SELECT Patient, Symptom INTO Symptom_temp&lt;br&gt;FROM qryTemp;  &lt;p&gt;Or in the Query Builder:  &lt;p&gt;&lt;a href="http://lh5.ggpht.com/-_7gBtmgSQCs/Te4BaS5FiGI/AAAAAAAAAxY/fw1vAxU6hsU/s1600-h/clip_image008%25255B4%25255D.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="clip_image008" border="0" alt="clip_image008" src="http://lh4.ggpht.com/-991inS9iHj0/Te4Bberg98I/AAAAAAAAAxc/jLIeOfdwutQ/clip_image008_thumb%25255B1%25255D.png?imgmax=800" width="569" height="343"&gt;&lt;/a&gt;  &lt;p&gt;&lt;b&gt;All-in-one Query&lt;/b&gt;  &lt;p&gt;It's also possible to it in a single query. To do that, I surround the SQL of &lt;b&gt;qryTemp&lt;/b&gt; in parentheses and use it in the From clause of the Make-table. Like this:  &lt;p&gt;&lt;font face="Courier New"&gt;SELECT Patient, Symptom INTO Symptom_temp&lt;br&gt;FROM (SELECT Patient, IIf([cough],"Cough") AS Symptom&lt;br&gt;FROM Patient_RC WHERE [Cough] = True&lt;br&gt;UNION ALL &lt;br&gt;SELECT Patient, IIf([Sneeze],"Sneeze") AS Symptom&lt;br&gt;FROM Patient_RC WHERE [Sneeze] = True&lt;br&gt;UNION ALL &lt;br&gt;SELECT Patient, IIf([Fever],"Fever") AS Symptom&lt;br&gt;FROM Patient_RC WHERE [Fever] = True&lt;br&gt;UNION ALL &lt;br&gt;SELECT Patient, IIf([Body_Aches],"Body aches") AS Symptom&lt;br&gt;FROM Patient_RC WHERE [Body_Aches] = True&lt;br&gt;UNION ALL &lt;br&gt;SELECT Patient, IIf([Nausea],"Nausea") AS Symptom&lt;br&gt;FROM Patient_RC WHERE [Nausea] = True&lt;br&gt;ORDER BY Patient, Symptom) AS Temp;&lt;/font&gt;  &lt;p&gt;&lt;i&gt;(Note: This cannot be done in the Query Builder)&lt;/i&gt;  &lt;p&gt;The result (in either case) will be a table that looks like this:  &lt;p&gt;&lt;a href="http://lh3.ggpht.com/-JIVLglV5Jtg/Te4Bbzs1KQI/AAAAAAAAAxg/ExJRrywdZWQ/s1600-h/clip_image009%25255B4%25255D.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="clip_image009" border="0" alt="clip_image009" src="http://lh4.ggpht.com/-KmQHKrAEhyY/Te4BdBpBmQI/AAAAAAAAAxk/fqOdXyufglg/clip_image009_thumb%25255B1%25255D.png?imgmax=800" width="374" height="265"&gt;&lt;/a&gt;  &lt;p&gt;&lt;a name="_Toc294157087"&gt;&lt;b&gt;Step 3: Remove Symptoms to Symptom Table&lt;/b&gt;&lt;/a&gt;&lt;b&gt;&lt;/b&gt;  &lt;p&gt;To fulfill 3NF, I need to create a query that removes the duplicate Symptom values into their own table, which I will call &lt;b&gt;Symptom&lt;/b&gt;. The &lt;b&gt;GROUP BY&lt;/b&gt; clause works well for that. As before, I'll use a Make-Table query to create a new table to hold the software values:  &lt;p&gt;&lt;font face="Courier New"&gt;SELECT Symptom INTO Symptoms&lt;br&gt;FROM Symptom_temp&lt;br&gt;GROUP BY Symptom;&lt;/font&gt;  &lt;p&gt;And as before, I'll add an Autonumber primary key, so the table looks like this:  &lt;p&gt;&lt;a href="http://lh4.ggpht.com/-GJxj7Py4omM/Te4BdZFg3hI/AAAAAAAAAxo/A7N9Nk59H7w/s1600-h/clip_image010%25255B4%25255D.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="clip_image010" border="0" alt="clip_image010" src="http://lh6.ggpht.com/-E2Gki3vHU9k/Te4BebS2y_I/AAAAAAAAAxs/jkb0SUDy35s/clip_image010_thumb%25255B1%25255D.png?imgmax=800" width="297" height="223"&gt;&lt;/a&gt;  &lt;p&gt;&lt;a name="_Toc294157088"&gt;&lt;b&gt;Step 4: &lt;/b&gt;&lt;/a&gt;&lt;a name="_Toc294157089"&gt;&lt;/a&gt;&lt;b&gt;Creating the Linking Table: &lt;/b&gt;&lt;b&gt;Patient_Symptoms&lt;/b&gt;  &lt;p&gt;So now, I've got my two "One-Side" tables: &lt;b&gt;Patient&lt;/b&gt; and &lt;b&gt;Symptoms&lt;/b&gt;. Now I just need to create the linking table.  &lt;p&gt;A "Linking table" is a mechanism by which Many-To-Many relationships are built in a relational database. (See: &lt;a href="http://rogersaccessblog.blogspot.com/2009/01/what-is-normalization-part-v.html"&gt;What is Normalizion Part V&lt;/a&gt; for more information.)  &lt;p&gt;I can do that with a Make-Table Query and a join of Patient, Symptom_Temp, and Symptom:  &lt;p&gt;&lt;font face="Courier New"&gt;SELECT CLng([Patient].[PatientID]) AS PatientID, &lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; CLng([Symptoms].[SymptomID]) AS SymptomID&lt;br&gt;INTO Patient_Symptoms&lt;br&gt;FROM (Patient INNER JOIN Symptom_temp &lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; ON Patient.Patient = Symptom_temp.Patient) &lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; INNER JOIN Symptoms &lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; ON Symptom_temp.Symptom = Symptoms.Symptom;&lt;/font&gt;  &lt;p&gt;Or in the Query Builder:  &lt;p&gt;&lt;a href="http://lh4.ggpht.com/-PjEm2d8LqM4/Te4BfVLclMI/AAAAAAAAAxw/LPWokQuHzY0/s1600-h/clip_image011%25255B5%25255D.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="clip_image011" border="0" alt="clip_image011" src="http://lh6.ggpht.com/-LkzH4lduhxc/Te4BhAVLzWI/AAAAAAAAAx0/k_DzvGbXOFs/clip_image011_thumb%25255B2%25255D.png?imgmax=800" width="558" height="416"&gt;&lt;/a&gt;  &lt;p&gt;Notice the expressions in the SQL Statement and the Query Builder: &lt;b&gt;CLng([Patient].[PatientID]) &lt;/b&gt;and &lt;b&gt;CLng([Symptoms].[SymptomID])&lt;/b&gt;. These are necessary because the Make Table query will attempt to create both PatientID and SymptomID as autonumber fields. Since a table cannot have two autonumber fields, this will give me an error.  &lt;p&gt;Since they are both Foreign Keys, I don't want either to be autonumber. (A foreign key should NEVER be an autonumber.) So I use the CLng() function to convert them to the Long Integer datatype, which is the datatype which should ALWAYS be used to a foreign key to an autonumber primary key.  &lt;p&gt;This will give me a table with two fields: &lt;b&gt;PatientID&lt;/b&gt; and &lt;b&gt;SymptomID&lt;/b&gt;. I'll also want to make these fields a compound primary key:  &lt;p&gt;&lt;a href="http://lh4.ggpht.com/-44vLWxDxjMM/Te4Bh4NSNlI/AAAAAAAAAx4/bMk1FJc4TqM/s1600-h/clip_image012%25255B4%25255D.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="clip_image012" border="0" alt="clip_image012" src="http://lh5.ggpht.com/-STP5fE011ys/Te4BjV61vaI/AAAAAAAAAx8/fu465h4Dylk/clip_image012_thumb%25255B1%25255D.png?imgmax=800" width="465" height="350"&gt;&lt;/a&gt;  &lt;p&gt;The final result looks like this:  &lt;p&gt;&lt;a href="http://lh6.ggpht.com/-29XIhLaIH_I/Te4Bj-50faI/AAAAAAAAAyA/U5pfcDstzak/s1600-h/clip_image013%25255B4%25255D.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="clip_image013" border="0" hspace="12" alt="clip_image013" src="http://lh4.ggpht.com/-i8GFNFFSe9E/Te4BklCHSPI/AAAAAAAAAyE/NwAaE9rPkTU/clip_image013_thumb%25255B1%25255D.png?imgmax=800" width="330" height="280"&gt;&lt;/a&gt;  &lt;p&gt;The final step is to create the relationships between the three tables: &lt;b&gt;Patient&lt;/b&gt;, &lt;b&gt;Patient_Symptoms&lt;/b&gt;, and &lt;b&gt;Symptoms&lt;/b&gt;.  &lt;p&gt;&lt;a name="_Toc294157090"&gt;&lt;b&gt;Step 5: Create the Relationships&lt;/b&gt;&lt;/a&gt;  &lt;p&gt;The easiest way to create relationships in Access is to use the Relationship Window. Add the four tables to the Relationships Window:  &lt;p&gt;&lt;a href="http://lh4.ggpht.com/-jzQ56KcE8Ew/Te4BlE3hnTI/AAAAAAAAAyI/WiZil4lgvFE/s1600-h/clip_image014%25255B4%25255D.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="clip_image014" border="0" alt="clip_image014" src="http://lh5.ggpht.com/-6XuWPi9o4Lo/Te4BmBWOp0I/AAAAAAAAAyM/TV1nx4Nohug/clip_image014_thumb%25255B1%25255D.png?imgmax=800" width="576" height="266"&gt;&lt;/a&gt;  &lt;p&gt;Click and drag &lt;b&gt;PatientID&lt;/b&gt; from the &lt;b&gt;Patient&lt;/b&gt; table to &lt;b&gt;PC_ID&lt;/b&gt; in the &lt;b&gt;Patient _Symptoms &lt;/b&gt;table. In the pop-up window, choose the &lt;b&gt;Enforce Referential Integrity&lt;/b&gt; box:  &lt;p&gt;&lt;a href="http://lh6.ggpht.com/-gpx7hFwBiZE/Te4BnEpXsBI/AAAAAAAAAyQ/pZv4zHynhSY/s1600-h/clip_image015%25255B5%25255D.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="clip_image015" border="0" alt="clip_image015" src="http://lh5.ggpht.com/-lHc85bYAs_g/Te4Bo7VT26I/AAAAAAAAAyU/v10u6TDUyGo/clip_image015_thumb%25255B2%25255D.png?imgmax=800" width="490" height="409"&gt;&lt;/a&gt;  &lt;p&gt;And click &lt;b&gt;Create&lt;/b&gt;. Do the same for &lt;b&gt;SymptomID&lt;/b&gt; between &lt;b&gt;Symptoms&lt;/b&gt; and &lt;b&gt;Patient _Symptoms&lt;/b&gt;. The final result will look like this:  &lt;p&gt;&lt;a href="http://lh6.ggpht.com/-hY6yNw9tMZU/Te4BpezOxmI/AAAAAAAAAyY/T2CJqhdwJrU/s1600-h/clip_image016%25255B5%25255D.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="clip_image016" border="0" alt="clip_image016" src="http://lh4.ggpht.com/-J-5VxM1ljQA/Te4BqapAimI/AAAAAAAAAyc/uMXylyww4JQ/clip_image016_thumb%25255B2%25255D.png?imgmax=800" width="588" height="278"&gt;&lt;/a&gt;  &lt;p&gt;&lt;a href="http://lh5.ggpht.com/-dPUH8cgqK0o/Te4BrOI-jNI/AAAAAAAAAyg/hxDbVfBA2D8/s1600-h/clip_image017%25255B4%25255D.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="clip_image017" border="0" alt="clip_image017" src="http://lh5.ggpht.com/-UkHs672ELG8/Te4BsQCJTtI/AAAAAAAAAyk/hNEsnmksT-U/clip_image017_thumb%25255B1%25255D.png?imgmax=800" width="591" height="219"&gt;&lt;/a&gt;  &lt;h4&gt;&lt;/h4&gt; &lt;p&gt;&lt;b&gt;Next Time:&lt;/b&gt;  &lt;p&gt;Next time, I’ll finish up by importing the spreadsheet to existing tables: &lt;br&gt;&lt;a href="http://rogersaccessblog.blogspot.com/2011/06/normalizing-repeated-columns-yesno_14.html"&gt;Normalizing Repeated Columns: Yes/No Fields (Part2)&lt;/a&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8134759511126815909-6888386384233902166?l=rogersaccessblog.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://rogersaccessblog.blogspot.com/feeds/6888386384233902166/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8134759511126815909&amp;postID=6888386384233902166' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8134759511126815909/posts/default/6888386384233902166'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8134759511126815909/posts/default/6888386384233902166'/><link rel='alternate' type='text/html' href='http://rogersaccessblog.blogspot.com/2011/06/normalizing-repeated-columns-yesno.html' title='Normalizing Repeated Columns: Yes/No Fields (Part1)'/><author><name>Roger's Access Blog</name><uri>http://www.blogger.com/profile/12265719129831415014</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_JYohVQdgpwU/Scu5h2VvtBI/AAAAAAAAAQc/pr3JDoLaqnQ/S220/avi_Roger_1.JPG'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://lh4.ggpht.com/-fIGqYlg-qNI/Te4BQ2IWPWI/AAAAAAAAAwk/n276c4p6hRo/s72-c/clip_image001_thumb%25255B1%25255D.jpg?imgmax=800' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8134759511126815909.post-2035807995608209479</id><published>2011-06-02T06:36:00.001-04:00</published><updated>2011-06-02T06:37:19.690-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='New Samples'/><title type='text'>New Sample: NormalizingRepeatingColumns2.mdb</title><content type='html'>&lt;p&gt;By Roger Carlson &lt;p&gt;This sample demonstrates how to normalize a PC_Inventory table that has two sets of repeated columns.&amp;nbsp; It's purpose is to demonstrate the general principles of normalizing denormalized data from a spreadsheet.  &lt;p&gt;&lt;font color="#ff0000"&gt;Document Included&lt;/font&gt; &lt;p&gt;&lt;font color="#000000"&gt;You can find the sample here: &lt;a title="http://www.rogersaccesslibrary.com/forum/normalizingrepeatingcolumns2-beginner_topic563.html" href="http://www.rogersaccesslibrary.com/forum/normalizingrepeatingcolumns2-beginner_topic563.html"&gt;http://www.rogersaccesslibrary.com/forum/normalizingrepeatingcolumns2-beginner_topic563.html&lt;/a&gt;&lt;/font&gt;&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8134759511126815909-2035807995608209479?l=rogersaccessblog.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://rogersaccessblog.blogspot.com/feeds/2035807995608209479/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8134759511126815909&amp;postID=2035807995608209479' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8134759511126815909/posts/default/2035807995608209479'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8134759511126815909/posts/default/2035807995608209479'/><link rel='alternate' type='text/html' href='http://rogersaccessblog.blogspot.com/2011/06/new-sample-normalizingrepeatingcolumns2.html' title='New Sample: NormalizingRepeatingColumns2.mdb'/><author><name>Roger's Access Blog</name><uri>http://www.blogger.com/profile/12265719129831415014</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_JYohVQdgpwU/Scu5h2VvtBI/AAAAAAAAAQc/pr3JDoLaqnQ/S220/avi_Roger_1.JPG'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8134759511126815909.post-4862829748630490798</id><published>2011-05-31T07:13:00.001-04:00</published><updated>2011-06-08T06:27:59.188-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Access 101'/><category scheme='http://www.blogger.com/atom/ns#' term='Problems With Repeated Columns'/><title type='text'>Normalizing Repeated Columns: Multiple Columns (Part 2)</title><content type='html'>&lt;p&gt;Last time, in &lt;a href="http://rogersaccessblog.blogspot.com/2011/05/normalizing-repeated-columns-complex-pc.html"&gt;&lt;strong&gt;Normalizing Repeated Columns: Multiple Columns (Part1)&lt;/strong&gt;&lt;/a&gt; I talked about Normalizing data in repeated columns from a spreadsheet, like this:  &lt;p&gt;&lt;a href="http://lh4.ggpht.com/-fIGs7JoOzTU/TeTNIWu6alI/AAAAAAAAAvY/xYqwEPLozkU/s1600-h/image%25255B34%25255D.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://lh3.ggpht.com/-Ef7rC-21ZJw/TeTNJ9SvGEI/AAAAAAAAAvc/atGHttdDv7c/image_thumb%25255B12%25255D.png?imgmax=800" width="597" height="158"&gt;&lt;/a&gt;  &lt;p&gt;Into a structure like this:  &lt;p&gt;&lt;a href="http://lh4.ggpht.com/-gwwAZe0YvXE/TeTNKsihjPI/AAAAAAAAAvg/Shhq9hrUZpY/s1600-h/image%25255B35%25255D.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://lh6.ggpht.com/-fNiGB7u1sWs/TeTNLgFvyRI/AAAAAAAAAvk/UJnT6d_hsOw/image_thumb%25255B13%25255D.png?imgmax=800" width="604" height="214"&gt;&lt;/a&gt;  &lt;p&gt;&lt;a href="http://lh5.ggpht.com/-dmLUBZ9LNPQ/TeTNNV9jNBI/AAAAAAAAAvo/T8G_e7iK2-g/s1600-h/image%25255B37%25255D.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://lh4.ggpht.com/-gQ9H6t2EUfA/TeTNPcaew2I/AAAAAAAAAvs/b3TDM-geOdI/image_thumb%25255B15%25255D.png?imgmax=800" width="601" height="360"&gt;&lt;/a&gt;  &lt;p&gt;Using a One-Time process that creates the tables and relationships in addition to copying the data.  &lt;p&gt;&lt;strong&gt;Appending to Existing Tables&lt;/strong&gt;  &lt;p&gt;The process for normalizing repeated column data into Existing Tables is similar to the One-Time process. It does, however, require you to create the tables and relationships correctly ahead of time. Because I've just created them in the previous section, I'll simply delete the data and re-append it from the linked spreadsheet.  &lt;p&gt;The two processes are very similar, but I'll be using Append queries rather than Make-Table queries.  &lt;p&gt;&lt;strong&gt;Step 1: Remove the common PC elements.&lt;/strong&gt;&lt;/p&gt; &lt;p&gt;Since I've already got a table, I'll use an Append query rather than the Make-Table query I used earlier:  &lt;p&gt;&lt;font face="Courier New"&gt;INSERT INTO PC ( PC_Num, OS )&lt;br&gt;SELECT PC_RC.PC_Num, PC_RC.OS&lt;br&gt;FROM PC_RC;&lt;/font&gt;  &lt;p&gt;Or in the Query Builder:  &lt;p&gt;&lt;a href="http://lh3.ggpht.com/-J_ELzyyeAtE/TeTNPy1j5AI/AAAAAAAAAvw/iT0mAvDzsQs/s1600-h/image%25255B38%25255D.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://lh4.ggpht.com/-Gh1T5Xg6mjE/TeTNQ0boyaI/AAAAAAAAAv0/HYvyKxnGjqQ/image_thumb%25255B16%25255D.png?imgmax=800" width="479" height="370"&gt;&lt;/a&gt;  &lt;p&gt;The resulting table looks like this:  &lt;p&gt;&lt;a href="http://lh3.ggpht.com/-9pcWOi4l3jY/TeTNReCp3YI/AAAAAAAAAv4/vaMSkHYSAw8/s1600-h/image%25255B39%25255D.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://lh6.ggpht.com/-mpBMBcSosg0/TeTNSHD1ywI/AAAAAAAAAv8/byjtm0XI0hA/image_thumb%25255B17%25255D.png?imgmax=800" width="282" height="180"&gt;&lt;/a&gt;  &lt;p&gt;Since I already have an autonumber primary key defined on the table, I don't need to do anything further.  &lt;p&gt;&lt;strong&gt;Step 2: Create Software_Temp table&lt;/strong&gt;&lt;/p&gt; &lt;p&gt;This step is exactly the same as Step 2 above. I'll use a Make-Table query to create a temporary table that will be used for creating the relationships later.  &lt;p&gt;&lt;font face="Courier New"&gt;INSERT INTO Software_Temp ( PC_ID, Software, SoftwareType )&lt;br&gt;SELECT PC_Software_Temp.PC_ID, PC_Software_Temp.Software, PC_Software_Temp.SoftwareType&lt;br&gt;FROM (SELECT PC.PC_ID, Software1 As Software, PC_RC.Software1Type as SoftwareType&lt;br&gt;FROM PC INNER JOIN PC_RC ON PC.PC_Num = PC_RC.PC_Num&lt;br&gt;WHERE Software1 IS NOT NULL&lt;br&gt;UNION&lt;br&gt;SELECT PC.PC_ID, PC_RC.Software2, Software2Type as SoftwareType&lt;br&gt;FROM PC INNER JOIN PC_RC ON PC.PC_Num = PC_RC.PC_Num&lt;br&gt;WHERE Software2 IS NOT NULL&lt;br&gt;UNION&lt;br&gt;SELECT PC.PC_ID, PC_RC.Software3, Software3Type as SoftwareType&lt;br&gt;FROM PC INNER JOIN PC_RC ON PC.PC_Num = PC_RC.PC_Num&lt;br&gt;WHERE Software3 IS NOT NULL) AS PC_Software_Temp;&lt;/font&gt;  &lt;p&gt;The result will be a table that looks like this:  &lt;p&gt;&lt;a href="http://lh3.ggpht.com/-aTIpeiwwgpc/TeTNSuMMykI/AAAAAAAAAwA/xbp60-qo0SA/s1600-h/image%25255B40%25255D.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://lh4.ggpht.com/-WgEGK7LqcMk/TeTNUAZjshI/AAAAAAAAAwE/0m35fNejU6o/image_thumb%25255B18%25255D.png?imgmax=800" width="384" height="302"&gt;&lt;/a&gt;  &lt;p&gt;&lt;strong&gt;Step 3: Remove Duplicates from Software_Temp&lt;/strong&gt;&lt;/p&gt; &lt;p&gt;I need to create a query that removes the duplicate values. The DISTINCT predicate works well for that. As before, I'll use an Append write the data to my existing Software table:  &lt;p&gt;&lt;font face="Courier New"&gt;INSERT INTO Category ( SoftwareType )&lt;br&gt;SELECT DISTINCT Software_Temp.SoftwareType&lt;br&gt;FROM Software_Temp;&lt;/font&gt;  &lt;p&gt;With the resulting table:  &lt;p&gt;&lt;a href="http://lh6.ggpht.com/-kImX0Vjt2LE/TeTNUdsEVUI/AAAAAAAAAwI/M5HbtntSudc/s1600-h/image%25255B41%25255D.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://lh5.ggpht.com/-HH8ndDCUSqk/TeTNVfAvBRI/AAAAAAAAAwM/IpDoIOxSZQE/image_thumb%25255B19%25255D.png?imgmax=800" width="316" height="206"&gt;&lt;/a&gt;  &lt;p&gt;Again, this table already has an autonumber primary key, so I don't need to create one.  &lt;p&gt;&lt;strong&gt;Step 4: Creating the Linking Table: PC_Software&lt;/strong&gt;&lt;/p&gt; &lt;p&gt;So now, I've got my two "One-Side" tables: PC and Software. Now I just need to fill the linking table.  &lt;p&gt;I can do that with an Append Query and a simple join of Software and Software_Temp:  &lt;p&gt;&lt;font face="Courier New"&gt;INSERT INTO Software ( Software, CategoryID )&lt;br&gt;SELECT DISTINCT Software_Temp.Software, Category.CategoryID&lt;br&gt;FROM Software_Temp INNER JOIN Category ON Software_Temp.SoftwareType = Category.SoftwareType;&lt;/font&gt;  &lt;p&gt;Or in the Query Builder:  &lt;p&gt;&lt;a href="http://lh5.ggpht.com/-H81p50QfLNs/TeTNVzZ1saI/AAAAAAAAAwQ/908NjSI9i10/s1600-h/image%25255B42%25255D.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://lh5.ggpht.com/-qQZARSEoef8/TeTNXMIgKRI/AAAAAAAAAwU/vGys9DH7B6Q/image_thumb%25255B20%25255D.png?imgmax=800" width="492" height="386"&gt;&lt;/a&gt;  &lt;p&gt;The final result will look like this:  &lt;p&gt;&lt;a href="http://lh5.ggpht.com/-KN8oXycIYRM/TeTNYzaNGZI/AAAAAAAAAwY/_thdLYIz8UY/s1600-h/image%25255B43%25255D.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://lh5.ggpht.com/-jGZ1j54RrDw/TeTNa4HJ6oI/AAAAAAAAAwc/QYdMpGNsQ8w/image_thumb%25255B21%25255D.png?imgmax=800" width="602" height="350"&gt;&lt;/a&gt;  &lt;p&gt;It is important to note that the order in which the data is moved is vital. Data must be written into the "one-side" tables (PC and Software) first, and then data can be moved into the "linking" tables (PC_Software).  &lt;p&gt;This two-part post is available as a single download (with sample database) here: &lt;a href="http://www.rogersaccesslibrary.com/forum/normalizingrepeatingcolumns1_topic562.html"&gt;http://www.rogersaccesslibrary.com/forum/normalizingrepeatingcolumns1_topic562.html&lt;/a&gt;.    &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8134759511126815909-4862829748630490798?l=rogersaccessblog.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://rogersaccessblog.blogspot.com/feeds/4862829748630490798/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8134759511126815909&amp;postID=4862829748630490798' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8134759511126815909/posts/default/4862829748630490798'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8134759511126815909/posts/default/4862829748630490798'/><link rel='alternate' type='text/html' href='http://rogersaccessblog.blogspot.com/2011/05/normalizing-repeated-columns-complex-pc_31.html' title='Normalizing Repeated Columns: Multiple Columns (Part 2)'/><author><name>Roger's Access Blog</name><uri>http://www.blogger.com/profile/12265719129831415014</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_JYohVQdgpwU/Scu5h2VvtBI/AAAAAAAAAQc/pr3JDoLaqnQ/S220/avi_Roger_1.JPG'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://lh3.ggpht.com/-Ef7rC-21ZJw/TeTNJ9SvGEI/AAAAAAAAAvc/atGHttdDv7c/s72-c/image_thumb%25255B12%25255D.png?imgmax=800' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8134759511126815909.post-9075425101731279042</id><published>2011-05-27T06:46:00.001-04:00</published><updated>2011-06-23T07:29:40.227-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Access 101'/><category scheme='http://www.blogger.com/atom/ns#' term='Problems With Repeated Columns'/><title type='text'>Normalizing Repeated Columns: Multiple Columns (Part1)</title><content type='html'>&lt;p&gt;In my earlier series (&lt;a href="http://rogersaccessblog.blogspot.com/2011/03/problem-of-repeated-columns.html"&gt;The Problem of Repeated Columns&lt;/a&gt;), I defined repeated columns and talked about the data integrity problems associated with them. I also showed several different examples of repeated columns and the how difficult it is to query repeated columns as compared to the normalized equivalent. If you haven't read these yet, it would be worthwhile to read first. Similarly, if you are not familiar with the concept of Normalization, you should read my blog series &lt;a href="http://rogersaccessblog.blogspot.com/2008/12/what-is-normalization-part-i.html"&gt;What is Normalization?&lt;/a&gt;  &lt;p&gt;One of the comments suggested I should discuss how to convert a table with repeated columns into a normalized table structure. I thought that was a really good idea, so I'm going to spend the next few posts doing so. The difficulty in discussing this issue, however, is that the exact solution differs with each table, so there is no single solution. Fortunately, in the last series, I showed 5 different tables, each with a slightly different structure, so while I cannot show a single solution that will work for every table with repeated columns, hopefully, one of the following will work for most cases.  &lt;p&gt;Others in this series:  &lt;ul&gt; &lt;li&gt;&lt;a href="http://rogersaccessblog.blogspot.com/2011/05/normalizing-repeated-columns-simple-pc.html"&gt;Single Repeated Column&lt;/a&gt;  &lt;li&gt;Multiple Columns (this post)  &lt;li&gt;&lt;a href="http://rogersaccessblog.blogspot.com/2011/06/normalizing-repeated-columns-yesno.html"&gt;Normalizing Yes/No Fields&lt;/a&gt;&amp;nbsp; &lt;li&gt;&lt;a href="http://rogersaccessblog.blogspot.com/2011/06/normalizing-repeated-columns-vba.html"&gt;Using VBA&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt; &lt;p&gt;&lt;b&gt;&lt;/b&gt; &lt;p&gt;&lt;a name="_Toc294157083"&gt;&lt;b&gt;Complex PC Inventory&lt;/b&gt;&lt;/a&gt;&lt;b&gt;&lt;/b&gt;  &lt;p&gt;In my post &lt;a href="http://rogersaccessblog.blogspot.com/2011/03/querying-repeated-columns-multiple.html"&gt;Querying Repeated Columns: Multiple Unions&lt;/a&gt; I discussed a simple example of a PC Inventory table that looked like this:  &lt;p&gt;&lt;a href="http://lh3.ggpht.com/-tZtPCSzKo3k/Td-As4sYb_I/AAAAAAAAAtI/CSi4uEPQD2U/s1600-h/image%25255B79%25255D.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://lh5.ggpht.com/-9ripwqmq59A/Td-AtThkf0I/AAAAAAAAAtM/rs8Lm3oDGPc/image_thumb%25255B43%25255D.png?imgmax=800" width="592" height="151"&gt;&lt;/a&gt;&lt;b&gt;Figure 1: PC_Inventory table with repeated columns &lt;/b&gt; &lt;p&gt;Normalized to remove the repeated columns, the tables relationships would look like this:  &lt;p&gt;&lt;a href="http://lh6.ggpht.com/-aV6jHl7XFSk/Td-AtyJHgaI/AAAAAAAAAtQ/1OILpjKCjR0/s1600-h/image%25255B78%25255D.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://lh6.ggpht.com/-U0sXmcLnL9s/Td-AubwxYHI/AAAAAAAAAtU/hVqNLGSsXIc/image_thumb%25255B42%25255D.png?imgmax=800" width="603" height="216"&gt;&lt;/a&gt;&lt;b&gt;Figure 2: Relationships for the PC Inventory tables.&lt;/b&gt;  &lt;p&gt;So how do I get the data from Figure 1 to Figure 2? Well, first of all, it depends on whether this is a one-time process where you are creating new normalized tables, or whether it is an on-going process where you are appending the data to existing, normalized tables.  &lt;p&gt;In either case, however, not only do I have to split the records into separate tables, I also have to preserve the relationships between those records, so I have to do it is a specific order.  &lt;p&gt;&lt;b&gt;&lt;/b&gt; &lt;p&gt;&lt;a name="_Toc294157084"&gt;&lt;b&gt;One-Time Process&lt;/b&gt;&lt;/a&gt;&lt;b&gt;&lt;/b&gt;  &lt;p&gt;Suppose I'm given a spreadsheet of data like this:  &lt;p&gt;&lt;a href="http://lh5.ggpht.com/-hNiqfhUgKw8/Td-Au-XSdfI/AAAAAAAAAtY/l--IhMiItnc/s1600-h/image%25255B77%25255D.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://lh4.ggpht.com/-C_UozgFtUOI/Td-Avcu_kkI/AAAAAAAAAtc/azc0USmge6Q/image_thumb%25255B41%25255D.png?imgmax=800" width="588" height="150"&gt;&lt;/a&gt;  &lt;p&gt;And I need to create a normalized database from it. I've already determined the tables I need (Figure 2 above), but if you're not certain what your table structure should be, you should read through my blog serie: &lt;a href="http://rogersaccessblog.blogspot.com/2008/12/what-is-normalization-part-i.html"&gt;What is Normalization&lt;/a&gt;, &lt;a href="http://rogersaccessblog.blogspot.com/2009/01/entity-relationship-diagramming-part-i.html"&gt;Entity-Relationship Diagramming&lt;/a&gt;, and &lt;a href="http://rogersaccessblog.blogspot.com/2009/02/normal-forms-introduction.html"&gt;The Normal Forms&lt;/a&gt;.  &lt;p&gt;&lt;b&gt;Link the spreadsheet into a database.&lt;/b&gt;  &lt;p&gt;First of all, I need to get the spreadsheet into the database. I find it is preferable to link rather than import the file. Since I'm not going to be changing the data, there's no reason to import it. Once the file is linked it acts just like a table, and I can begin the process of normalizing it.  &lt;p&gt;&lt;a name="_Toc294157085"&gt;&lt;b&gt;Step 1: Remove the common PC elements&lt;/b&gt;&lt;/a&gt;&lt;b&gt;. &lt;/b&gt; &lt;p&gt;Since I need to create a new table to hold these values, I'll use a Make-Table query:  &lt;p&gt;&lt;font face="Courier New"&gt;SELECT PC_Num, OS INTO PC&lt;br&gt;FROM PC_RC &lt;/font&gt; &lt;p&gt;Or in the Query Builder:  &lt;p&gt;&lt;a href="http://lh3.ggpht.com/-WAgnpLE2UHA/Td-Avio5MVI/AAAAAAAAAtg/-4uPyitifD4/s1600-h/image%25255B76%25255D.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://lh3.ggpht.com/-QYTWDZUi9sc/Td-AwIOPlkI/AAAAAAAAAtk/FJOJBN3XPmo/image_thumb%25255B40%25255D.png?imgmax=800" width="500" height="496"&gt;&lt;/a&gt;  &lt;p&gt;The resulting table looks like this:  &lt;p&gt;&lt;a href="http://lh6.ggpht.com/-ZYPL7yyn-ys/Td-AwdD5pgI/AAAAAAAAAto/8aBFhbSQYRQ/s1600-h/image%25255B75%25255D.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://lh6.ggpht.com/-ne4fdCwR4fQ/Td-AwlN0oYI/AAAAAAAAAts/EfSEts8IZD8/image_thumb%25255B39%25255D.png?imgmax=800" width="269" height="212"&gt;&lt;/a&gt;  &lt;p&gt;Now, if I were using PC_NUM as a natural key, I could stop here. However, I prefer to use Surrogate Keys. It makes this process more difficult, but has many benefits in the long run. (For a discussion of natural vs. surrogate keys, see my blog post: &lt;a href="http://rogersaccessblog.blogspot.com/2008/12/what-is-primary-key.html"&gt;What is a Primary Key?)&lt;/a&gt;  &lt;p&gt;&lt;b&gt;Add surrogate key to PC table.&lt;/b&gt;  &lt;p&gt;Adding a surrogate key to a table is as simple as opening it in Design View, adding an Autonumber Field, and making it the Primary Key.  &lt;p&gt;&lt;a href="http://lh4.ggpht.com/-uAAeDPgu9xM/Td-AwwetHOI/AAAAAAAAAtw/UNy5YaZtCg8/s1600-h/image%25255B74%25255D.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://lh5.ggpht.com/-f1aTTloKv-I/Td-AxbcgbqI/AAAAAAAAAt0/VnSOAKGufDw/image_thumb%25255B38%25255D.png?imgmax=800" width="462" height="364"&gt;&lt;/a&gt;  &lt;p&gt;Saving the table will automatically fill the autonumber field with values:  &lt;p&gt;&lt;a href="http://lh4.ggpht.com/-WIh_8rlBkqo/Td-AyZoDZRI/AAAAAAAAAt4/5J22ps17uoQ/s1600-h/image%25255B71%25255D.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://lh5.ggpht.com/-YhqLjOReAJM/Td-Ayn3zQHI/AAAAAAAAAt8/M2lXEGuUnNo/image_thumb%25255B35%25255D.png?imgmax=800" width="334" height="172"&gt;&lt;/a&gt;  &lt;p&gt;&lt;a name="_Toc294157086"&gt;&lt;b&gt;Step 2: Create Software_Temp table&lt;/b&gt;&lt;/a&gt;&lt;b&gt;&lt;/b&gt;  &lt;p&gt;As I showed in my post &lt;a href="http://rogersaccessblog.blogspot.com/2011/03/querying-repeated-columns-multiple.html"&gt;Querying Repeated Columns: Multiple Unions&lt;/a&gt; it is necessary to use multiple UNION queries to create a list of Software. Something like this:  &lt;p&gt;&lt;font face="Courier New"&gt;SELECT PC.PC_ID, Software1 As Software, PC_RC.Software1Type as SoftwareType&lt;br&gt;FROM PC INNER JOIN PC_RC ON PC.PC_Num = PC_RC.PC_Num&lt;br&gt;WHERE Software1 IS NOT NULL&lt;br&gt;UNION&lt;br&gt;SELECT PC.PC_ID, PC_RC.Software2, Software2Type as SoftwareType&lt;br&gt;FROM PC INNER JOIN PC_RC ON PC.PC_Num = PC_RC.PC_Num&lt;br&gt;WHERE Software2 IS NOT NULL&lt;br&gt;UNION&lt;br&gt;SELECT PC.PC_ID, PC_RC.Software3, Software3Type as SoftwareType&lt;br&gt;FROM PC INNER JOIN PC_RC ON PC.PC_Num = PC_RC.PC_Num&lt;br&gt;WHERE Software3 IS NOT NULL&lt;/font&gt;  &lt;p&gt;&lt;i&gt;Note: You can't create or view this query in the Query Builder, however, you can create the first on (Software1) in the QB, then switch to the SQL View and copy and paste, modifying each for the specific column.&lt;/i&gt;  &lt;p&gt;I still need to make this query into a Make-Table query. To do that, I surround the above in parentheses and use it in the From clause of the Make-table. Like this:  &lt;p&gt;&lt;font face="Courier New"&gt;SELECT PC_Software_Temp.PC_ID, PC_Software_Temp.Software, PC_Software_Temp.SoftwareType INTO Software_Temp&lt;br&gt;FROM (SELECT PC.PC_ID, Software1 As Software, PC_RC.Software1Type as SoftwareType&lt;br&gt;FROM PC INNER JOIN PC_RC ON PC.PC_Num = PC_RC.PC_Num&lt;br&gt;WHERE Software1 IS NOT NULL&lt;br&gt;UNION&lt;br&gt;SELECT PC.PC_ID, PC_RC.Software2, Software2Type as SoftwareType&lt;br&gt;FROM PC INNER JOIN PC_RC ON PC.PC_Num = PC_RC.PC_Num&lt;br&gt;WHERE Software2 IS NOT NULL&lt;br&gt;UNION&lt;br&gt;SELECT PC.PC_ID, PC_RC.Software3, Software3Type as SoftwareType&lt;br&gt;FROM PC INNER JOIN PC_RC ON PC.PC_Num = PC_RC.PC_Num&lt;br&gt;WHERE Software3 IS NOT NULL) AS PC_Software_Temp;&lt;/font&gt;  &lt;p&gt;The result will be a table that looks like this:  &lt;p&gt;&lt;a href="http://lh5.ggpht.com/-X5OMERyn6Cg/Td-AzGdBMEI/AAAAAAAAAuA/Vim2a1SeLAA/s1600-h/image%25255B70%25255D.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://lh4.ggpht.com/-xed5Q3EZXl4/Td-Azrte5bI/AAAAAAAAAuE/ZlY3GO0z51k/image_thumb%25255B34%25255D.png?imgmax=800" width="338" height="266"&gt;&lt;/a&gt;  &lt;h5&gt;&lt;/h5&gt; &lt;p&gt;&lt;a name="_Toc294157087"&gt;&lt;b&gt;Step 3: Remove SoftwareType to Category Table&lt;/b&gt;&lt;/a&gt;&lt;b&gt;&lt;/b&gt;  &lt;p&gt;To fulfill 3NF, I need to create a query that removes the duplicate SoftwareType values into their own table, which I will call Category. The DISTINCT predicate works well for that. As before, I'll use a Make-Table query to create a new table to hold the software values:  &lt;p&gt;&lt;font face="Courier New"&gt;SELECT DISTINCT SoftwareType INTO Category&lt;br&gt;FROM Software_Temp;&lt;/font&gt;  &lt;p&gt;And as before, the PC table I created, I'll add an Autonumber primary key, so the table looks like this:  &lt;p&gt;&lt;a href="http://lh5.ggpht.com/-ZTbsZkhshFE/Td-Az8cGN4I/AAAAAAAAAuI/6jTIbms9SN4/s1600-h/image%25255B69%25255D.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://lh4.ggpht.com/-xl8DxhXMTqk/Td-A0TjfZgI/AAAAAAAAAuM/cQSWx8GAxKk/image_thumb%25255B33%25255D.png?imgmax=800" width="316" height="244"&gt;&lt;/a&gt;  &lt;p&gt;&lt;a name="_Toc294157088"&gt;&lt;b&gt;Step 4: Remove Duplicate Software Values from Software_Temp&lt;/b&gt;&lt;/a&gt;&lt;b&gt;&lt;/b&gt;  &lt;p&gt;Now I need to remove the duplicate Software values. However, this time I want to include the primary key of the Category table (Category) in the resulting table so I can create the relationship between the tables Software and Category.  &lt;p&gt;As before, I'll use a Make-Table query to create a new table to hold the software values:  &lt;p&gt;&lt;font face="Courier New"&gt;SELECT DISTINCT Software_Temp.Software, Category.CategoryID INTO Software&lt;br&gt;FROM Software_Temp INNER JOIN Category ON Software_Temp.SoftwareType = &lt;br&gt;Category.SoftwareType;&lt;/font&gt;  &lt;p&gt;Or in the Query Builder:  &lt;p&gt;&lt;a href="http://lh5.ggpht.com/-rBuELyNIpQs/Td-A0tBHPoI/AAAAAAAAAuQ/qdzeZyP3fqA/s1600-h/image%25255B67%25255D.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://lh5.ggpht.com/-4K_EdBOmGEQ/Td-A0xoqYWI/AAAAAAAAAuU/waT7cG3_7wU/image_thumb%25255B31%25255D.png?imgmax=800" width="484" height="414"&gt;&lt;/a&gt;  &lt;p&gt;Again, I'll add an Autonumber primary key. However, the make-table query will create CategoryID as an autonumber field. Since I can't have two autonumber fields in a table, I have to first change CategoryID to a Number (Long Integer) value. I have to do this first, save the table, then add the autonumber field (SoftwareID). The table ends up like this:  &lt;p&gt;&lt;a href="http://lh5.ggpht.com/-D54sutGTP4M/Td-A1eOYWgI/AAAAAAAAAuY/TdvkrK2hz0o/s1600-h/image%25255B65%25255D.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://lh5.ggpht.com/-DDeCkGevLi0/Td-A1xCiknI/AAAAAAAAAuc/V51eYyHxrkc/image_thumb%25255B29%25255D.png?imgmax=800" width="357" height="283"&gt;&lt;/a&gt;  &lt;p&gt;Notice the CategoryID field is already filled with the correct values from the Category table.  &lt;p&gt;&lt;a name="_Toc294157089"&gt;&lt;b&gt;Step 5: Creating the Linking Table: PC_Software&lt;/b&gt;&lt;/a&gt;&lt;b&gt;&lt;/b&gt;  &lt;p&gt;So now, I've got my two "One-Side" tables: PC and Software. Now I just need to create the linking table.  &lt;p&gt;A "Linking table" is a mechanism by which Many-To-Many relationships are built in a relational database. (See: &lt;a href="http://rogersaccessblog.blogspot.com/2009/01/what-is-normalization-part-v.html"&gt;What is Normalizion Part V&lt;/a&gt; for more information.)  &lt;p&gt;I can do that with a Make-Table Query and a simple join of Software and Software_Temp:  &lt;p&gt;&lt;font face="Courier New"&gt;SELECT Software.SoftwareID, Software_Temp.PC_ID INTO PC_Software&lt;br&gt;FROM Software INNER JOIN Software_Temp ON Software.Software = Software_Temp.Software;&lt;/font&gt;  &lt;p&gt;Or in the Query Builder:  &lt;p&gt;&lt;a href="http://lh5.ggpht.com/-fOe0zBanywU/Td-A2XGUIAI/AAAAAAAAAug/r4oepu7WiaA/s1600-h/image%25255B64%25255D.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://lh6.ggpht.com/-swgMDKQYIkE/Td-A235TvSI/AAAAAAAAAuk/oJV0OWOxiAY/image_thumb%25255B28%25255D.png?imgmax=800" width="483" height="482"&gt;&lt;/a&gt;  &lt;p&gt;This will give me a table with two fields: PC_ID and SoftwareID. I'll also want to make these fields a compound primary key. To do that, I'll also want to first convert the Autonumber field to a number field:  &lt;p&gt;&lt;a href="http://lh4.ggpht.com/-DuM0idF0Ps4/Td-A3E6Y0mI/AAAAAAAAAuo/5QONnFdMdY4/s1600-h/image%25255B62%25255D.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://lh3.ggpht.com/-OnxYaHFwlcM/Td-A3pu5TmI/AAAAAAAAAus/FaPT-xkl3kE/image_thumb%25255B26%25255D.png?imgmax=800" width="501" height="391"&gt;&lt;/a&gt;  &lt;p&gt;The final result looks like this:  &lt;p&gt;&lt;a href="http://lh3.ggpht.com/-0xcqO9S8qDo/Td-A343MK5I/AAAAAAAAAuw/q7cSUr9FJpU/s1600-h/image%25255B61%25255D.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://lh5.ggpht.com/-KTHkaOOJyXI/Td-A4CYZUQI/AAAAAAAAAu0/5VyHg9yuBXs/image_thumb%25255B25%25255D.png?imgmax=800" width="251" height="291"&gt;&lt;/a&gt;  &lt;p&gt;The final step is to create the relationships between the four tables: PC, PC_Software, Software, and Category.  &lt;p&gt;&lt;a name="_Toc294157090"&gt;&lt;b&gt;Step 6: Create the Relationships&lt;/b&gt;&lt;/a&gt;&lt;b&gt;&lt;/b&gt;  &lt;p&gt;The easiest way to create relationships in Access is to use the Relationship Window. Add the four tables to the Relationships Window:  &lt;p&gt;&lt;a href="http://lh6.ggpht.com/-9omOFzCmJx0/Td-A4o4YR6I/AAAAAAAAAu4/xAz22famE5w/s1600-h/image%25255B60%25255D.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://lh3.ggpht.com/-6QhiYccuFJM/Td-A42K4w7I/AAAAAAAAAu8/vUasG10rr5E/image_thumb%25255B24%25255D.png?imgmax=800" width="582" height="211"&gt;&lt;/a&gt;  &lt;p&gt;Click and drag &lt;b&gt;PC_ID&lt;/b&gt; from the &lt;b&gt;PC&lt;/b&gt; table to &lt;b&gt;PC_ID&lt;/b&gt; in the &lt;b&gt;PC_Software&lt;/b&gt; table. In the pop-up window, choose the &lt;b&gt;Enforce Referential Integrity&lt;/b&gt; box:  &lt;p&gt;&lt;a href="http://lh4.ggpht.com/-rw5Ny1nIqYI/Td-A5d2jp4I/AAAAAAAAAvA/9AoPL-xEEeI/s1600-h/image%25255B58%25255D.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://lh3.ggpht.com/-HgP7kbnbENw/Td-A56lkHAI/AAAAAAAAAvE/Se-Dzkw2598/image_thumb%25255B22%25255D.png?imgmax=800" width="589" height="386"&gt;&lt;/a&gt;  &lt;p&gt;And click &lt;b&gt;Create&lt;/b&gt;. Do the same for &lt;b&gt;SoftwareID&lt;/b&gt; between &lt;b&gt;Software&lt;/b&gt; and &lt;b&gt;PC_Software &lt;/b&gt;and for&lt;b&gt; CategoryID &lt;/b&gt;between&lt;b&gt; Software &lt;/b&gt;and&lt;b&gt; Category&lt;/b&gt;. The final result will look like this:  &lt;p&gt;&lt;a href="http://lh4.ggpht.com/-GuARpXOGUww/Td-A6BGkz0I/AAAAAAAAAvI/iIT0YKxB35w/s1600-h/image%25255B56%25255D.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://lh4.ggpht.com/-x2SCvqXJ03U/Td-A6kOiKNI/AAAAAAAAAvM/LwR-0XoGj68/image_thumb%25255B20%25255D.png?imgmax=800" width="571" height="202"&gt;&lt;/a&gt;  &lt;p&gt;&lt;a href="http://lh4.ggpht.com/-ub8QqGD-0Ig/Td-A7A2UikI/AAAAAAAAAvQ/JDnTwbsyhcM/s1600-h/image%25255B55%25255D.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://lh3.ggpht.com/-vFwIPYEYW1Y/Td-A7kwtKZI/AAAAAAAAAvU/OsfYmlM6C7w/image_thumb%25255B19%25255D.png?imgmax=800" width="571" height="343"&gt;&lt;/a&gt;  &lt;h4&gt;&lt;/h4&gt; &lt;p&gt;&lt;b&gt;Next Time:&lt;/b&gt;  &lt;p&gt;Next time, I’ll finish up by importing the spreadsheet to existing tables:&lt;br&gt;&lt;a href="http://rogersaccessblog.blogspot.com/2011/05/normalizing-repeated-columns-complex-pc_31.html"&gt;Normalizing Repeated Columns: Multiple Columns (Part2)&lt;/a&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8134759511126815909-9075425101731279042?l=rogersaccessblog.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://rogersaccessblog.blogspot.com/feeds/9075425101731279042/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8134759511126815909&amp;postID=9075425101731279042' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8134759511126815909/posts/default/9075425101731279042'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8134759511126815909/posts/default/9075425101731279042'/><link rel='alternate' type='text/html' href='http://rogersaccessblog.blogspot.com/2011/05/normalizing-repeated-columns-complex-pc.html' title='Normalizing Repeated Columns: Multiple Columns (Part1)'/><author><name>Roger's Access Blog</name><uri>http://www.blogger.com/profile/12265719129831415014</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_JYohVQdgpwU/Scu5h2VvtBI/AAAAAAAAAQc/pr3JDoLaqnQ/S220/avi_Roger_1.JPG'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://lh5.ggpht.com/-9ripwqmq59A/Td-AtThkf0I/AAAAAAAAAtM/rs8Lm3oDGPc/s72-c/image_thumb%25255B43%25255D.png?imgmax=800' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8134759511126815909.post-4909359137757861907</id><published>2011-05-25T07:00:00.000-04:00</published><updated>2011-05-27T07:03:48.416-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='New Samples'/><category scheme='http://www.blogger.com/atom/ns#' term='Problems With Repeated Columns'/><title type='text'>New Sample: NormalizingRepeatingColumns1.mdb</title><content type='html'>&lt;p&gt;By Roger Carlson &lt;p&gt;This sample demonstrates how to normalize a simple PC_Inventory table with a single set of repeated columns.&amp;nbsp; It's purpose is to demonstrate the general principles of normalizing denormalized data from a spreadsheet.  &lt;p&gt;&lt;font color="#ff0000"&gt;Document Included&lt;/font&gt; &lt;p&gt;&lt;font color="#000000"&gt;You can find the sample here: &lt;a title="http://www.rogersaccesslibrary.com/forum/normalizingrepeatingcolumns1_topic562.html" href="http://www.rogersaccesslibrary.com/forum/normalizingrepeatingcolumns1_topic562.html"&gt;http://www.rogersaccesslibrary.com/forum/normalizingrepeatingcolumns1_topic562.html&lt;/a&gt;&lt;/font&gt;&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8134759511126815909-4909359137757861907?l=rogersaccessblog.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://rogersaccessblog.blogspot.com/feeds/4909359137757861907/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8134759511126815909&amp;postID=4909359137757861907' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8134759511126815909/posts/default/4909359137757861907'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8134759511126815909/posts/default/4909359137757861907'/><link rel='alternate' type='text/html' href='http://rogersaccessblog.blogspot.com/2011/05/new-sample-normalizingrepeatingcolumns1.html' title='New Sample: NormalizingRepeatingColumns1.mdb'/><author><name>Roger's Access Blog</name><uri>http://www.blogger.com/profile/12265719129831415014</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_JYohVQdgpwU/Scu5h2VvtBI/AAAAAAAAAQc/pr3JDoLaqnQ/S220/avi_Roger_1.JPG'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8134759511126815909.post-2618636476432490381</id><published>2011-05-23T06:37:00.001-04:00</published><updated>2011-06-08T06:24:43.139-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Access 101'/><category scheme='http://www.blogger.com/atom/ns#' term='Problems With Repeated Columns'/><title type='text'>Normalizing Repeated Columns: Single Repeated Column (Part2)</title><content type='html'>&lt;p&gt;Last time, in &lt;a href="http://rogersaccessblog.blogspot.com/2011/05/normalizing-repeated-columns-simple-pc.html"&gt;Normalizing Repeated Columns: Single Repeated Column (Part 1)&lt;/a&gt;, I talked about Normalizing data in repeated columns from a spreadsheet, like this:  &lt;p&gt;&lt;a href="http://lh5.ggpht.com/_JYohVQdgpwU/Tdo4yn7ocXI/AAAAAAAAAsA/qa33TwIA1yw/s1600-h/image%5B3%5D.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://lh6.ggpht.com/_JYohVQdgpwU/Tdo4ywisanI/AAAAAAAAAsE/t0uyXBdxhgs/image_thumb%5B1%5D.png?imgmax=800" width="562" height="171"&gt;&lt;/a&gt;  &lt;p&gt;Into a structure like this:  &lt;p&gt;&lt;a href="http://lh5.ggpht.com/_JYohVQdgpwU/Tdo4zOiG_nI/AAAAAAAAAsI/rWLO1lQyQgk/s1600-h/image%5B7%5D.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://lh5.ggpht.com/_JYohVQdgpwU/Tdo4zcHN8sI/AAAAAAAAAsM/f25KTvHernc/image_thumb%5B3%5D.png?imgmax=800" width="551" height="252"&gt;&lt;/a&gt;  &lt;p&gt;&lt;a href="http://lh5.ggpht.com/_JYohVQdgpwU/Tdo4z-SL9qI/AAAAAAAAAsQ/JbV8Hc6Ap7c/s1600-h/image%5B12%5D.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://lh5.ggpht.com/_JYohVQdgpwU/Tdo40NgG4JI/AAAAAAAAAsU/JhwTDu4Bmys/image_thumb%5B6%5D.png?imgmax=800" width="570" height="243"&gt;&lt;/a&gt;  &lt;p&gt;Using a One-Time process that creates the tables and relationships in addition to copying the data.  &lt;p&gt;&lt;strong&gt;Appending to Existing Tables&lt;/strong&gt;  &lt;p&gt;The process for normalizing repeated column data into Existing Tables is similar to the One-Time process. It does, however, require you to create the tables and relationships correctly ahead of time. Because I've just created them in the previous section, I'll simply delete the data and re-append it from the linked spreadsheet.  &lt;p&gt;The two processes are very similar, but I'll be using Append queries rather than Make-Table queries.  &lt;p&gt;&lt;strong&gt;Step 1: Remove the common PC elements.&lt;/strong&gt;&lt;/p&gt; &lt;p&gt;Since I've already got a table, I'll use an Append query rather than the Make-Table query I used earlier:  &lt;p&gt;INSERT INTO PC ( PC_Num, OperatingSystem )&lt;br&gt;SELECT PC_Num, OperatingSystem&lt;br&gt;FROM PC_RepeatedColumns_Link;  &lt;p&gt;Or in the Query Builder:  &lt;p&gt;&lt;a href="http://lh3.ggpht.com/_JYohVQdgpwU/Tdo40ZIzaiI/AAAAAAAAAsY/djpGCbXpiQk/s1600-h/image%5B16%5D.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://lh3.ggpht.com/_JYohVQdgpwU/Tdo401TV6FI/AAAAAAAAAsc/UzI8kx7q440/image_thumb%5B8%5D.png?imgmax=800" width="478" height="492"&gt;&lt;/a&gt;  &lt;p&gt;The resulting table looks like this:  &lt;p&gt;&lt;a href="http://lh3.ggpht.com/_JYohVQdgpwU/Tdo41LnUCPI/AAAAAAAAAsg/W92roJmzKAA/s1600-h/image%5B20%5D.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://lh5.ggpht.com/_JYohVQdgpwU/Tdo41SwZLWI/AAAAAAAAAsk/q5a8IbG-FYI/image_thumb%5B10%5D.png?imgmax=800" width="327" height="195"&gt;&lt;/a&gt;  &lt;p&gt;Since I already have an autonumber primary key defined on the table, I don't need to do anything further.  &lt;p&gt;&lt;strong&gt;Step 2: Create Software_Temp table&lt;/strong&gt;&lt;/p&gt; &lt;p&gt;This step is exactly the same as Step 2 above. I'll use a Make-Table query to create a temporary table that will be used for creating the relationships later.  &lt;p&gt;&lt;font face="Courier New"&gt;SELECT PC_ID, Software INTO Software_Temp&lt;br&gt;FROM&lt;br&gt;(SELECT PC.PC_ID, PC_RC_Link.Software1 As Software&lt;br&gt;FROM PC INNER JOIN PC_RC_Link ON PC.PC_Num = PC_RC_Link.PC_Num&lt;br&gt;WHERE Software1 IS NOT NULL&lt;br&gt;UNION&lt;br&gt;SELECT PC.PC_ID, PC_RC_Link.Software2&lt;br&gt;FROM PC INNER JOIN PC_RC_Link ON PC.PC_Num = PC_RC_Link.PC_Num&lt;br&gt;WHERE Software2 IS NOT NULL&lt;br&gt;UNION&lt;br&gt;SELECT PC.PC_ID, PC_RC_Link.Software3&lt;br&gt;FROM PC INNER JOIN PC_RC_Link ON PC.PC_Num = PC_RC_Link.PC_Num&lt;br&gt;WHERE Software3 IS NOT NULL&lt;br&gt;UNION&lt;br&gt;SELECT PC.PC_ID, PC_RC_Link.Software4&lt;br&gt;FROM PC INNER JOIN PC_RC_Link ON PC.PC_Num = PC_RC_Link.PC_Num&lt;br&gt;WHERE Software4 IS NOT NULL&lt;br&gt;UNION&lt;br&gt;SELECT PC.PC_ID, PC_RC_Link.Software5&lt;br&gt;FROM PC INNER JOIN PC_RC_Link ON PC.PC_Num = PC_RC_Link.PC_Num&lt;br&gt;WHERE Software5 IS NOT NULL)&lt;/font&gt;  &lt;p&gt;The result will be a table that looks like this:  &lt;p&gt;&lt;a href="http://lh6.ggpht.com/_JYohVQdgpwU/Tdo41QwuaWI/AAAAAAAAAso/a-TP3k04SSA/s1600-h/image%5B24%5D.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://lh3.ggpht.com/_JYohVQdgpwU/Tdo41l46fwI/AAAAAAAAAss/s99wXuqkwDQ/image_thumb%5B12%5D.png?imgmax=800" width="291" height="311"&gt;&lt;/a&gt;  &lt;p&gt;&lt;strong&gt;Step 3: Remove Duplicates from Software_Temp&lt;/strong&gt;&lt;/p&gt; &lt;p&gt;I need to create a query that removes the duplicate values. The DISTINCT predicate works well for that. As before, I'll use an Append write the data to my existing Software table:  &lt;p&gt;&lt;font face="Courier New"&gt;INSERT INTO Software ( Software )&lt;br&gt;SELECT DISTINCT Software_Temp.Software&lt;br&gt;FROM Software_Temp;&lt;/font&gt;  &lt;p&gt;With the resulting table:  &lt;p&gt;&lt;a href="http://lh6.ggpht.com/_JYohVQdgpwU/Tdo419vcq6I/AAAAAAAAAsw/_ApaJsCMBAc/s1600-h/image%5B29%5D.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://lh4.ggpht.com/_JYohVQdgpwU/Tdo42HLxZRI/AAAAAAAAAs0/8mzDAaF51MM/image_thumb%5B15%5D.png?imgmax=800" width="294" height="302"&gt;&lt;/a&gt;  &lt;p&gt;Again, this table already has an autonumber primary key, so I don't need to create one.  &lt;p&gt;&lt;strong&gt;Step 4: Creating the Linking Table: PC_Software&lt;/strong&gt;&lt;/p&gt; &lt;p&gt;So now, I've got my two "One-Side" tables: PC and Software. Now I just need to fill the linking table.  &lt;p&gt;I can do that with an Append Query and a simple join of Software and Software_Temp:  &lt;p&gt;&lt;font face="Courier New"&gt;INSERT INTO PC_Software ( SoftwareID, PC_ID )&lt;br&gt;SELECT Software.SoftwareID, Software_Temp.PC_ID&lt;br&gt;FROM Software INNER JOIN Software_Temp ON Software.Software = Software_Temp.Software;&lt;/font&gt;  &lt;p&gt;Or in the Query Builder:  &lt;p&gt;&lt;a href="http://lh4.ggpht.com/_JYohVQdgpwU/Tdo42ZasD9I/AAAAAAAAAs4/CAGRJmb3xdA/s1600-h/image%5B34%5D.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://lh3.ggpht.com/_JYohVQdgpwU/Tdo4290xK0I/AAAAAAAAAs8/dflUonJoNRQ/image_thumb%5B18%5D.png?imgmax=800" width="426" height="452"&gt;&lt;/a&gt;  &lt;p&gt;The final result will look like this:  &lt;p&gt;&lt;a href="http://lh6.ggpht.com/_JYohVQdgpwU/Tdo43ABjDvI/AAAAAAAAAtA/f8cVHG8mOfc/s1600-h/image%5B38%5D.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://lh4.ggpht.com/_JYohVQdgpwU/Tdo43VkUbNI/AAAAAAAAAtE/kPW9Ju9wEC4/image_thumb%5B20%5D.png?imgmax=800" width="563" height="251"&gt;&lt;/a&gt;  &lt;p&gt;It is important to note that the order in which the data is moved is vital. Data must be written into the "one-side" tables (PC and Software) first, and then data can be moved into the "linking" tables (PC_Software).  &lt;p&gt;This two-part post is available as a single download (with sample database) here: &lt;a href="http://www.rogersaccesslibrary.com/forum/normalizingrepeatingcolumns1_topic562.html"&gt;http://www.rogersaccesslibrary.com/forum/normalizingrepeatingcolumns1_topic562.html&lt;/a&gt;.    &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8134759511126815909-2618636476432490381?l=rogersaccessblog.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://rogersaccessblog.blogspot.com/feeds/2618636476432490381/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8134759511126815909&amp;postID=2618636476432490381' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8134759511126815909/posts/default/2618636476432490381'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8134759511126815909/posts/default/2618636476432490381'/><link rel='alternate' type='text/html' href='http://rogersaccessblog.blogspot.com/2011/05/normalizing-repeated-columns-simple-pc_23.html' title='Normalizing Repeated Columns: Single Repeated Column (Part2)'/><author><name>Roger's Access Blog</name><uri>http://www.blogger.com/profile/12265719129831415014</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_JYohVQdgpwU/Scu5h2VvtBI/AAAAAAAAAQc/pr3JDoLaqnQ/S220/avi_Roger_1.JPG'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://lh6.ggpht.com/_JYohVQdgpwU/Tdo4ywisanI/AAAAAAAAAsE/t0uyXBdxhgs/s72-c/image_thumb%5B1%5D.png?imgmax=800' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8134759511126815909.post-1522242250054608039</id><published>2011-05-17T07:09:00.001-04:00</published><updated>2011-06-23T07:27:24.682-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Access 101'/><category scheme='http://www.blogger.com/atom/ns#' term='Problems With Repeated Columns'/><title type='text'>Normalizing Repeated Columns: Single Repeated Column(Part1)</title><content type='html'>&lt;p&gt;In my earlier series (&lt;a href="http://rogersaccessblog.blogspot.com/2011/03/problem-of-repeated-columns.html"&gt;The Problem of Repeated Columns&lt;/a&gt;), I defined repeated columns and talked about the data integrity problems associated with them. I also showed several different examples of repeated columns and the how difficult it is to query repeated columns as compared to the normalized equivalent. If you haven't read these yet, it would be worthwhile to read first. Similarly, if you are not familiar with the concept of Normalization, you should read my blog series &lt;a href="http://rogersaccessblog.blogspot.com/2008/12/what-is-normalization-part-i.html"&gt;What is Normalization?&lt;/a&gt;  &lt;p&gt;One of the comments suggested I should discuss how to convert a table with repeated columns into a normalized table structure. I thought that was a really good idea, so I'm going to spend the next few posts doing so.  &lt;p&gt;The difficulty in discussing this issue, however, is that the exact solution differs with each table, so there is no single solution. Fortunately, in the last series, I showed 5 different tables, each with a slightly different structure, so while I cannot show a single solution that will work for every table with repeated columns, hopefully, one of the following will work for most cases.  &lt;p&gt;Others in this series:  &lt;ul&gt; &lt;li&gt;Single Repeated Column&amp;nbsp; (this post)  &lt;li&gt;&lt;a href="http://rogersaccessblog.blogspot.com/2011/05/normalizing-repeated-columns-complex-pc.html"&gt;Multiple Columns&lt;/a&gt;  &lt;li&gt;&lt;a href="http://rogersaccessblog.blogspot.com/2011/06/normalizing-repeated-columns-yesno.html"&gt;Normalizing Yes/No Fields&lt;/a&gt;&amp;nbsp; &lt;li&gt;&lt;a href="http://rogersaccessblog.blogspot.com/2011/06/normalizing-repeated-columns-vba.html"&gt;Using VBA&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt; &lt;p&gt;&lt;b&gt;Simple PC Inventory &lt;/b&gt; &lt;p&gt;In my post &lt;a href="http://rogersaccessblog.blogspot.com/2011/03/querying-repeated-columns-multiple-ors.html"&gt;Querying Repeated Columns: Multiple ORs&lt;/a&gt; I discussed an extremely simple example of a PC Inventory table that looked like this:  &lt;p&gt;&lt;a href="http://lh3.ggpht.com/_JYohVQdgpwU/TdJWzkw6P0I/AAAAAAAAApw/JqvP_kFiG_4/s1600-h/image%5B3%5D.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://lh3.ggpht.com/_JYohVQdgpwU/TdJW0wCtD3I/AAAAAAAAAp0/JTNRIEAcE1s/image_thumb%5B1%5D.png?imgmax=800" width="612" height="153"&gt;&lt;/a&gt;&lt;br&gt;&lt;b&gt;Figure 1: PC_Inventory table with repeated columns &lt;/b&gt; &lt;p&gt;Normalized to remove the repeated columns, the tables would look like this:  &lt;p&gt;&lt;a href="http://lh3.ggpht.com/_JYohVQdgpwU/TdJW2Fa0zBI/AAAAAAAAAp4/MogDRhgnhjU/s1600-h/image%5B13%5D.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://lh4.ggpht.com/_JYohVQdgpwU/TdJW35ympOI/AAAAAAAAAp8/R0CQz3fV9wc/image_thumb%5B7%5D.png?imgmax=800" width="572" height="263"&gt;&lt;/a&gt;&lt;br&gt;&lt;b&gt;Figure 2: PC_Inventory normalized (First Normal Form (1NF)&lt;/b&gt;  &lt;p&gt;That was sufficient for illustrating the difficulty of querying repeated columns, but unfortunately it didn't go far enough. Proper normalization requires that, in addition to moving the PC_Num and OperatingSystem fields to their own table, Software should also be removed to its own table as well. So it really should look like this:  &lt;p&gt;&lt;b&gt;&lt;/b&gt;&lt;b&gt;&lt;/b&gt; &lt;p&gt;&lt;a href="http://lh4.ggpht.com/_JYohVQdgpwU/TdJW5cF8fPI/AAAAAAAAAqA/yU-bWr36koA/s1600-h/image%5B12%5D.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://lh4.ggpht.com/_JYohVQdgpwU/TdJW60G-qII/AAAAAAAAAqE/agvBHpq95mk/image_thumb%5B6%5D.png?imgmax=800" width="592" height="178"&gt;&lt;/a&gt;&lt;br&gt;&lt;b&gt;Figure 3: PC_Inventory fully normalized (Third Normal Form (3NF)&lt;/b&gt;  &lt;p&gt;The table relationships would look like this:  &lt;p&gt;&lt;a href="http://lh5.ggpht.com/_JYohVQdgpwU/TdJW7TztYFI/AAAAAAAAAqI/B3r0WTbAonI/s1600-h/image%5B17%5D.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://lh4.ggpht.com/_JYohVQdgpwU/TdJW8AR9bzI/AAAAAAAAAqM/HCI5Mme3hd0/image_thumb%5B9%5D.png?imgmax=800" width="511" height="222"&gt;&lt;/a&gt;&lt;br&gt;&lt;b&gt;Figure 4: Relationships for the PC Inventory tables.&lt;/b&gt;  &lt;p&gt;So how do I get the data from Figure 1 to Figure 3? Well, first of all, it depends on whether this is a one-time process where you are creating new normalized tables, or whether it is an on-going process where you are appending the data to existing, normalized tables.  &lt;p&gt;In either case, however, not only do I have to split the records into separate tables, I also have to preserve the relationships between those records, so I have to do it is a specific order.  &lt;p&gt;&lt;b&gt;One-Time Process&lt;/b&gt;  &lt;p&gt;Suppose I'm given a spreadsheet of data like this:  &lt;p&gt;&lt;a href="http://lh5.ggpht.com/_JYohVQdgpwU/TdJW8qykGaI/AAAAAAAAAqQ/CaHYN6jt514/s1600-h/image%5B57%5D.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://lh5.ggpht.com/_JYohVQdgpwU/TdJW9n64DAI/AAAAAAAAAqU/o1VE7MF3Wso/image_thumb%5B23%5D.png?imgmax=800" width="533" height="163"&gt;&lt;/a&gt;  &lt;p&gt;And I need to create a normalized database from it. I've already determined the tables I need (Figure 3 above), but if you're not certain what your table structure should be, you should read through my blog serie: &lt;a href="http://rogersaccessblog.blogspot.com/2008/12/what-is-normalization-part-i.html"&gt;What is Normalization&lt;/a&gt;, &lt;a href="http://rogersaccessblog.blogspot.com/2009/01/entity-relationship-diagramming-part-i.html"&gt;Entity-Relationship Diagramming&lt;/a&gt;, and &lt;a href="http://rogersaccessblog.blogspot.com/2009/02/normal-forms-introduction.html"&gt;The Normal Forms&lt;/a&gt;.  &lt;p&gt;&lt;b&gt;Link the spreadsheet into a database.&lt;/b&gt;  &lt;p&gt;First of all, I need to get the spreadsheet into the database. I find it is preferable to link rather than import the file. Since I'm not going to be changing the data, there's no reason to import it. Once the file is linked it acts just like a table, and I can begin the process of normalizing it.  &lt;p&gt;&lt;b&gt;Step 1: Remove the common PC elements. &lt;/b&gt; &lt;p&gt;Since I need to create a new table to hold these values, I'll use a Make-Table query:  &lt;p&gt;&lt;font face="Courier New"&gt;SELECT PC_Num, OperatingSystem INTO PC&lt;br&gt;FROM PC_RC_Link;&lt;/font&gt;  &lt;p&gt;Or in the Query Builder:  &lt;p&gt;&lt;a href="http://lh6.ggpht.com/_JYohVQdgpwU/TdJW-s7JnYI/AAAAAAAAAqY/W7kjXIzf3Ec/s1600-h/image%5B58%5D.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://lh5.ggpht.com/_JYohVQdgpwU/TdJXAQCFHiI/AAAAAAAAAqc/SSSd0ZANgdk/image_thumb%5B24%5D.png?imgmax=800" width="431" height="435"&gt;&lt;/a&gt;  &lt;p&gt;The resulting table looks like this:  &lt;p&gt;&lt;a href="http://lh3.ggpht.com/_JYohVQdgpwU/TdJXAkMGWBI/AAAAAAAAAqg/aB0Ikae3NYE/s1600-h/image%5B26%5D.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://lh5.ggpht.com/_JYohVQdgpwU/TdJXBeXtbUI/AAAAAAAAAqk/IjVzoErCaXk/image_thumb%5B12%5D.png?imgmax=800" width="244" height="155"&gt;&lt;/a&gt;  &lt;p&gt;Now, if I were using PC_NUM as a natural key, I could stop here. However, I prefer to use Surrogate Keys. It makes this process more difficult, but has many benefits in the long run. (For a discussion of natural vs. surrogate keys, see my blog post: &lt;a href="http://rogersaccessblog.blogspot.com/2008/12/what-is-primary-key.html"&gt;What is a Primary Key?)&lt;/a&gt;  &lt;p&gt;&lt;b&gt;Add surrogate key to PC table.&lt;/b&gt;  &lt;p&gt;Adding a surrogate key to a table is as simple as opening it in Design View, adding an Autonumber Field, and making it the Primary Key.  &lt;p&gt;&lt;a href="http://lh3.ggpht.com/_JYohVQdgpwU/TdJXBwNhxFI/AAAAAAAAAqo/6McD3tQAiHs/s1600-h/image%5B59%5D.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://lh4.ggpht.com/_JYohVQdgpwU/TdJXC_iOqZI/AAAAAAAAAqs/ViUumpUjiPg/image_thumb%5B25%5D.png?imgmax=800" width="449" height="241"&gt;&lt;/a&gt;  &lt;p&gt;Saving the table will automatically fill the autonumber field with values:  &lt;p&gt;&lt;a href="http://lh5.ggpht.com/_JYohVQdgpwU/TdJXDHlSufI/AAAAAAAAAqw/kMmL-bAKQG4/s1600-h/image%5B60%5D.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://lh4.ggpht.com/_JYohVQdgpwU/TdJXD4DGI1I/AAAAAAAAAq0/8t6q39vsBnA/image_thumb%5B26%5D.png?imgmax=800" width="340" height="166"&gt;&lt;/a&gt;  &lt;p&gt;&lt;b&gt;Step 2: Create Software_Temp table&lt;/b&gt;  &lt;p&gt;As I showed in my post &lt;a href="http://rogersaccessblog.blogspot.com/2011/03/querying-repeated-columns-multiple.html"&gt;Querying Repeated Columns: Multiple Unions&lt;/a&gt; it is necessary to use multiple UNION queries to create a list of Software. Something like this:  &lt;p&gt;&lt;font face="Courier New"&gt;SELECT PC.PC_ID, PC_RC_Link.Software1 As Software&lt;br&gt;FROM PC INNER JOIN PC_RC_Link ON PC.PC_Num = PC_RC_Link.PC_Num&lt;br&gt;WHERE Software1 IS NOT NULL&lt;br&gt;UNION&lt;br&gt;SELECT PC.PC_ID, PC_RC_Link.Software2&lt;br&gt;FROM PC INNER JOIN PC_RC_Link ON PC.PC_Num = PC_RC_Link.PC_Num&lt;br&gt;WHERE Software2 IS NOT NULL&lt;br&gt;UNION&lt;br&gt;SELECT PC.PC_ID, PC_RC_Link.Software3&lt;br&gt;FROM PC INNER JOIN PC_RC_Link ON PC.PC_Num = PC_RC_Link.PC_Num&lt;br&gt;WHERE Software3 IS NOT NULL&lt;br&gt;UNION&lt;br&gt;SELECT PC.PC_ID, PC_RC_Link.Software4&lt;br&gt;FROM PC INNER JOIN PC_RC_Link ON PC.PC_Num = PC_RC_Link.PC_Num&lt;br&gt;WHERE Software4 IS NOT NULL&lt;br&gt;UNION&lt;br&gt;SELECT PC.PC_ID, PC_RC_Link.Software5&lt;br&gt;FROM PC INNER JOIN PC_RC_Link ON PC.PC_Num = PC_RC_Link.PC_Num&lt;br&gt;WHERE Software5 IS NOT NULL&lt;/font&gt;  &lt;p&gt;&lt;i&gt;Note: You can't create or view this query in the Query Builder, however, you can create the first on (Software1) in the QB, then switch to the SQL View and copy and paste, modifying each for the specific column.&lt;/i&gt;  &lt;p&gt;I still need to make this query into a Make-Table query. To do that, I surround the above in parentheses and use it in the From clause of the Make-table. Like this:  &lt;p&gt;&lt;font face="Courier New"&gt;SELECT PC_ID, Software INTO Software_Temp&lt;br&gt;FROM&lt;br&gt;(SELECT PC.PC_ID, PC_RC_Link.Software1 As Software&lt;br&gt;FROM PC INNER JOIN PC_RC_Link ON PC.PC_Num = PC_RC_Link.PC_Num&lt;br&gt;WHERE Software1 IS NOT NULL&lt;br&gt;UNION&lt;br&gt;SELECT PC.PC_ID, PC_RC_Link.Software2&lt;br&gt;FROM PC INNER JOIN PC_RC_Link ON PC.PC_Num = PC_RC_Link.PC_Num&lt;br&gt;WHERE Software2 IS NOT NULL&lt;br&gt;UNION&lt;br&gt;SELECT PC.PC_ID, PC_RC_Link.Software3&lt;br&gt;FROM PC INNER JOIN PC_RC_Link ON PC.PC_Num = PC_RC_Link.PC_Num&lt;br&gt;WHERE Software3 IS NOT NULL&lt;br&gt;UNION&lt;br&gt;SELECT PC.PC_ID, PC_RC_Link.Software4&lt;br&gt;FROM PC INNER JOIN PC_RC_Link ON PC.PC_Num = PC_RC_Link.PC_Num&lt;br&gt;WHERE Software4 IS NOT NULL&lt;br&gt;UNION&lt;br&gt;SELECT PC.PC_ID, PC_RC_Link.Software5&lt;br&gt;FROM PC INNER JOIN PC_RC_Link ON PC.PC_Num = PC_RC_Link.PC_Num&lt;br&gt;WHERE Software5 IS NOT NULL)&lt;/font&gt;  &lt;p&gt;The result will be a table that looks like this:  &lt;p&gt;&lt;a href="http://lh5.ggpht.com/_JYohVQdgpwU/TdJXEcqxmFI/AAAAAAAAAq4/B6TRlD-RvAE/s1600-h/image%5B61%5D.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://lh4.ggpht.com/_JYohVQdgpwU/TdJXFtPnqwI/AAAAAAAAAq8/EftfwKPc7wo/image_thumb%5B27%5D.png?imgmax=800" width="290" height="320"&gt;&lt;/a&gt;  &lt;p&gt;If I added a surrogate key to this table, it would fulfill normalization to the First Normal Form as in Figure 1 above. However, I want to normalize it to 3NF. To do that, I still need to remove the duplicates from Software_Temp and create the linking table, PC_Software.  &lt;p&gt;&lt;b&gt;Step 3: Remove Duplicates from Software_Temp&lt;/b&gt;  &lt;p&gt;I need to create a query that removes the duplicate values. The DISTINCT predicate works well for that. As before, I'll use a Make-Table query to create a new table to hold the software values:  &lt;p&gt;&lt;font face="Courier New"&gt;SELECT DISTINCT Software INTO Software&lt;br&gt;FROM Software_Temp;&lt;/font&gt;  &lt;p&gt;And as before, the PC table I created, I'll add an Autonumber primary key, so the table ends up like this:  &lt;p&gt;&lt;a href="http://lh3.ggpht.com/_JYohVQdgpwU/TdJXGHhseFI/AAAAAAAAArA/npBOqyrLu0s/s1600-h/image%5B62%5D.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://lh4.ggpht.com/_JYohVQdgpwU/TdJXHNQ5oMI/AAAAAAAAArE/KQIXLCsqgkY/image_thumb%5B28%5D.png?imgmax=800" width="295" height="311"&gt;&lt;/a&gt;  &lt;p&gt;&lt;b&gt;Step 4: Creating the Linking Table: PC_Software&lt;/b&gt;  &lt;p&gt;So now, I've got my two "One-Side" tables: PC and Software. Now I just need to create the linking table.  &lt;p&gt;A "Linking table" is a mechanism by which Many-To-Many relationships are built in a relational database. (See: &lt;a href="http://rogersaccessblog.blogspot.com/2009/01/what-is-normalization-part-v.html"&gt;What is Normalizion Part V&lt;/a&gt; for more information.)  &lt;p&gt;I can do that with a Make-Table Query and a simple join of Software and Software_Temp:  &lt;p&gt;&lt;font face="Courier New"&gt;SELECT Software.SoftwareID, Software_Temp.PC_ID INTO PC_Software&lt;br&gt;FROM Software INNER JOIN Software_Temp ON Software.Software = Software_Temp.Software;&lt;/font&gt;  &lt;p&gt;Or in the Query Builder:  &lt;p&gt;&lt;a href="http://lh6.ggpht.com/_JYohVQdgpwU/TdJXIPlvruI/AAAAAAAAArI/Wmj3Mtk5exs/s1600-h/image%5B64%5D.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://lh3.ggpht.com/_JYohVQdgpwU/TdJXJsy2ODI/AAAAAAAAArM/zyTmDK8OAN4/image_thumb%5B30%5D.png?imgmax=800" width="442" height="441"&gt;&lt;/a&gt;  &lt;p&gt;This will give me a table with two fields: PC_ID and SoftwareID. I'll also want to make these fields a compound primary key:  &lt;p&gt;&lt;a href="http://lh3.ggpht.com/_JYohVQdgpwU/TdJXKV_nLbI/AAAAAAAAArQ/csZKADbOZNc/s1600-h/image%5B65%5D.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://lh4.ggpht.com/_JYohVQdgpwU/TdJXLNs5B1I/AAAAAAAAArU/2umrKcpRDWs/image_thumb%5B31%5D.png?imgmax=800" width="408" height="299"&gt;&lt;/a&gt;  &lt;p&gt;The final result looks like this:  &lt;p&gt;&lt;a href="http://lh3.ggpht.com/_JYohVQdgpwU/TdJXLlKO6UI/AAAAAAAAArY/6bdkbB866-s/s1600-h/image%5B66%5D.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://lh4.ggpht.com/_JYohVQdgpwU/TdJXMeJESEI/AAAAAAAAArc/EpIeP-pxOZI/image_thumb%5B32%5D.png?imgmax=800" width="396" height="288"&gt;&lt;/a&gt;  &lt;p&gt;The final step is to create the relationships between the three tables: PC, PC_Software, and Software.  &lt;p&gt;&lt;b&gt;Step 5: Create the Relationships&lt;/b&gt;  &lt;p&gt;The easiest way to create relationships in Access is to use the Relationship Window. Add the three tables to the Relationships Window:  &lt;p&gt;&lt;a href="http://lh6.ggpht.com/_JYohVQdgpwU/TdJXM7D4A1I/AAAAAAAAArg/XP-HRa9CFZ4/s1600-h/image%5B67%5D.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://lh5.ggpht.com/_JYohVQdgpwU/TdJXNgSomRI/AAAAAAAAArk/yFKgdi9qDqI/image_thumb%5B33%5D.png?imgmax=800" width="555" height="254"&gt;&lt;/a&gt;  &lt;p&gt;Click and drag &lt;b&gt;PC_ID&lt;/b&gt; from the &lt;b&gt;PC&lt;/b&gt; table to &lt;b&gt;PC_ID&lt;/b&gt; in the &lt;b&gt;PC_Software&lt;/b&gt; table. In the pop-up window, choose the &lt;b&gt;Enforce Referential Integrity&lt;/b&gt; box:  &lt;p&gt;&lt;a href="http://lh4.ggpht.com/_JYohVQdgpwU/TdJXOuhjwoI/AAAAAAAAAro/1STTzmHfMAU/s1600-h/image%5B69%5D.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://lh3.ggpht.com/_JYohVQdgpwU/TdJXQlsqLBI/AAAAAAAAArs/k4XDv6sFT7c/image_thumb%5B35%5D.png?imgmax=800" width="555" height="409"&gt;&lt;/a&gt;  &lt;p&gt;And click &lt;b&gt;Create&lt;/b&gt;. Do the same for &lt;b&gt;SoftwareID&lt;/b&gt; between &lt;b&gt;Software&lt;/b&gt; and &lt;b&gt;PC_Software&lt;/b&gt;. The final result will look like this:  &lt;p&gt;&lt;a href="http://lh6.ggpht.com/_JYohVQdgpwU/TdJXQ9S4m3I/AAAAAAAAArw/Zs9z_eX9XYk/s1600-h/image%5B70%5D.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://lh3.ggpht.com/_JYohVQdgpwU/TdJXR5GtFzI/AAAAAAAAAr0/725sAknBaw0/image_thumb%5B36%5D.png?imgmax=800" width="570" height="261"&gt;&lt;/a&gt;  &lt;p&gt;&lt;a href="http://lh3.ggpht.com/_JYohVQdgpwU/TdJXUMdY_eI/AAAAAAAAAr4/u1Em8MS5fBM/s1600-h/image%5B74%5D.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://lh3.ggpht.com/_JYohVQdgpwU/TdJXV-CNxLI/AAAAAAAAAr8/5pL1csV3DR4/image_thumb%5B38%5D.png?imgmax=800" width="578" height="241"&gt;&lt;/a&gt;  &lt;p&gt;&lt;b&gt;Next Time:&lt;/b&gt;  &lt;p&gt;Next time, I’ll finish up by importing the spreadsheet to existing tables:&lt;br&gt;&lt;a href="http://rogersaccessblog.blogspot.com/2011/05/normalizing-repeated-columns-simple-pc_23.html"&gt;Normalizing Repeated Columns: Single Repeated Column (Part2)&lt;/a&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8134759511126815909-1522242250054608039?l=rogersaccessblog.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://rogersaccessblog.blogspot.com/feeds/1522242250054608039/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8134759511126815909&amp;postID=1522242250054608039' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8134759511126815909/posts/default/1522242250054608039'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8134759511126815909/posts/default/1522242250054608039'/><link rel='alternate' type='text/html' href='http://rogersaccessblog.blogspot.com/2011/05/normalizing-repeated-columns-simple-pc.html' title='Normalizing Repeated Columns: Single Repeated Column(Part1)'/><author><name>Roger's Access Blog</name><uri>http://www.blogger.com/profile/12265719129831415014</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_JYohVQdgpwU/Scu5h2VvtBI/AAAAAAAAAQc/pr3JDoLaqnQ/S220/avi_Roger_1.JPG'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://lh3.ggpht.com/_JYohVQdgpwU/TdJW0wCtD3I/AAAAAAAAAp0/JTNRIEAcE1s/s72-c/image_thumb%5B1%5D.png?imgmax=800' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8134759511126815909.post-7346141179056023960</id><published>2011-05-09T06:48:00.001-04:00</published><updated>2011-05-09T06:48:30.925-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='New Samples'/><category scheme='http://www.blogger.com/atom/ns#' term='Tejpal'/><title type='text'>New Sample: Form_DatasheetHighLightStyles</title><content type='html'>&lt;p&gt;&amp;nbsp;&amp;nbsp; This sample db demonstrates an interesting approach to conditional highlighting of datasheet forms. It is remarkably generic, using a set of functions in general module and does not depend upon any primary key field. In fact, no field name or form name is used and hardly any code is needed in the form module (except for style (b) below). &lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; Following styles for highlighting the records are covered:&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; (a) Highlight current record, First record, new record.&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; (b) Flag desired row or rows by dbl clicking (Dbl click again to remove the flag). &lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Once a flag is set for a record, and unless it is removed subsequently by user action (another dbl click on flagged record), it remains in force for current database session, even if the form is closed and then re-opened.&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; (c) Highlight odd rows.&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; (d) Highlight even rows.&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; (e) Highlight every third row.&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; (f) Highlight top - mid - last row.&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; (g) Highlight top 2 - mid 2 - last 2 rows. &lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; Note: The solution is equally applicable to continuous forms. &lt;p&gt;&lt;b&gt;Version: &lt;/b&gt;Access 2000 file format. &lt;p&gt;You can find the sample here: &lt;a title="http://www.rogersaccesslibrary.com/forum/Form-datasheethighlightstyles_topic561.html" href="http://www.rogersaccesslibrary.com/forum/Form-datasheethighlightstyles_topic561.html"&gt;http://www.rogersaccesslibrary.com/forum/Form-datasheethighlightstyles_topic561.html&lt;/a&gt; &lt;p&gt;.&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8134759511126815909-7346141179056023960?l=rogersaccessblog.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://rogersaccessblog.blogspot.com/feeds/7346141179056023960/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8134759511126815909&amp;postID=7346141179056023960' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8134759511126815909/posts/default/7346141179056023960'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8134759511126815909/posts/default/7346141179056023960'/><link rel='alternate' type='text/html' href='http://rogersaccessblog.blogspot.com/2011/05/new-sample-formdatasheethighlightstyles.html' title='New Sample: Form_DatasheetHighLightStyles'/><author><name>Roger's Access Blog</name><uri>http://www.blogger.com/profile/12265719129831415014</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_JYohVQdgpwU/Scu5h2VvtBI/AAAAAAAAAQc/pr3JDoLaqnQ/S220/avi_Roger_1.JPG'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8134759511126815909.post-3143566603005618875</id><published>2011-05-02T07:03:00.001-04:00</published><updated>2011-05-02T07:21:31.006-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Access 101'/><category scheme='http://www.blogger.com/atom/ns#' term='Problems With Repeated Columns'/><title type='text'>Aggregating Across Repeated Columns: Averaging</title><content type='html'>&lt;p&gt;In the first post in this series (&lt;a href="http://rogersaccessblog.blogspot.com/2011/03/problem-of-repeated-columns.html"&gt;The Problem of Repeated Columns&lt;/a&gt;), I defined repeated columns and talked about the data integrity problems associated with them. If you haven't read that yet, it would be worthwhile to read first. Similarly, if you are not familiar with the concept of Normalization, you should read my blog series &lt;a href="http://rogersaccessblog.blogspot.com/2008/12/what-is-normalization-part-i.html"&gt;What is Normalization?&lt;/a&gt;  &lt;p&gt;So far in this series, I've discussed the problem with querying textual or Boolean (Yes/No) data from repeated columns. But numeric data offers new challenges because we often want to do math on them. The most common kind of math is aggregation, that is, summing, counting, and averaging numeric values. This time, I'll talk about counting data in repeated columns.  &lt;p&gt;Others in this series:  &lt;ul&gt; &lt;li&gt;&lt;a href="http://rogersaccessblog.blogspot.com/2011/03/querying-repeated-columns-multiple-ors.html"&gt;Multiple ORs&lt;/a&gt;  &lt;li&gt;&lt;a href="http://rogersaccessblog.blogspot.com/2011/03/querying-repeated-columns-multiple.html"&gt;Multiple Unions&lt;/a&gt;  &lt;li&gt;&lt;a href="http://rogersaccessblog.blogspot.com/2011/03/querying-repeated-columns-multiple_28.html"&gt;Multiple Joins&lt;/a&gt;  &lt;li&gt;&lt;a href="http://rogersaccessblog.blogspot.com/2011/04/querying-repeated-columns-multiple-iifs.html"&gt;Multiple Ifs&lt;/a&gt;  &lt;li&gt;&lt;a href="http://rogersaccessblog.blogspot.com/2011/04/querying-repeated-columns-impossible.html"&gt;Impossible Joins&lt;/a&gt;  &lt;li&gt;&lt;a href="http://rogersaccessblog.blogspot.com/2011/04/aggregating-across-repeated-columns.html"&gt;Summing&lt;/a&gt;  &lt;li&gt;&lt;a href="http://rogersaccessblog.blogspot.com/2011/04/aggregating-across-repeated-columns_25.html"&gt;Counting&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt; &lt;p&gt;&lt;b&gt;&lt;/b&gt; &lt;p&gt;&lt;b&gt;Averaging Across Columns&lt;/b&gt;  &lt;p&gt;The most common type of data aggregation, perhaps, is in calculating an Average where you divide the sum of the values by the count of the values.  &lt;p&gt;In Excel, there is an AVERAGE function which will average the cells that have a value. The Excel function will work for any range of cells, across or down. In Access, however, the Average() function only works down columns, not across columns. So, averaging values in repeated rows is easy (see below), but just like summing and counting, averaging across repeated columns is more challenging.  &lt;p&gt;For instance, suppose I had a table of student test scores:  &lt;p&gt;&lt;b&gt;&lt;/b&gt;&lt;a href="http://lh4.ggpht.com/_JYohVQdgpwU/Tb6Piiu7NYI/AAAAAAAAApA/o2aPg8YnZ0M/s1600-h/image%5B3%5D.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://lh5.ggpht.com/_JYohVQdgpwU/Tb6PjBKC58I/AAAAAAAAApE/t8Vl9w57HzM/image_thumb%5B1%5D.png?imgmax=800" width="582" height="163"&gt;&lt;/a&gt;&lt;b&gt;&lt;br&gt;&lt;/b&gt; &lt;p&gt;If I wanted to average the test values for each student, I need to create an expression that sums the values for the numerator and counts the values for the denominator. For details on how to sum the values, see &lt;a href="http://rogersaccessblog.blogspot.com/2011/04/aggregating-across-repeated-columns.html"&gt;Aggregating Across Repeated Columns: Summing&lt;/a&gt;. To count the values, see &lt;a href="http://rogersaccessblog.blogspot.com/2011/04/aggregating-across-repeated-columns_25.html"&gt;Aggregating Across Repeated Columns: Counting&lt;/a&gt;  &lt;p&gt;To do this, I need 4 stages.  &lt;p&gt;&lt;b&gt;1. &lt;/b&gt;&lt;b&gt;Numerator: Sum the Values&lt;/b&gt;  &lt;p&gt;&lt;font face="Courier New"&gt;Nz([Test1])+Nz([Test2])+Nz([Test3])+Nz([Test4]) AS TestSum&lt;/font&gt;  &lt;p&gt;&lt;b&gt;2. &lt;/b&gt;&lt;b&gt;Denominator: Count the Values&lt;/b&gt;  &lt;p&gt;&lt;font face="Courier New"&gt;Abs((Not IsNull([Test1])) + (Not IsNull([Test2])) + (Not IsNull([Test3])) + (Not IsNull([Test4])))&lt;/font&gt;  &lt;p&gt;&lt;b&gt;3. &lt;/b&gt;&lt;b&gt;Handle Denominator of Zero&lt;/b&gt;  &lt;p&gt;If the denominator is 0 (zero) the calculation will return the #Num! error. Therefore, I have to test the denominator for zero and convert it to a NULL. Dividing any value with a NULL will return NULL.  &lt;p&gt;&lt;font face="Courier New"&gt;IIf((Abs((Not IsNull([Test1]))+(Not IsNull([Test2]))+(Not IsNull([Test3]))+(Not IsNull([Test4]))))=0,Null,(Abs((Not IsNull([Test1]))+(Not IsNull([Test2]))+(Not IsNull([Test3]))+(Not IsNull([Test4]))))&lt;/font&gt;  &lt;p&gt;&lt;b&gt;4. &lt;/b&gt;&lt;b&gt;Average: Divide Numerator (Sum) by Denominator (Count)&lt;/b&gt;  &lt;p&gt;&lt;font face="Courier New"&gt;(Nz([Test1])+Nz([Test2])+Nz([Test3])+Nz([Test4]))/IIf((Abs((Not IsNull([Test1]))+(Not IsNull([Test2]))+(Not IsNull([Test3]))+(Not IsNull([Test4]))))=0,Null,(Abs((Not IsNull([Test1]))+(Not IsNull([Test2]))+(Not IsNull([Test3]))+(Not IsNull([Test4])))))AS TestAverage&lt;/font&gt;  &lt;p&gt;The full query would look like this:  &lt;p&gt;&lt;font face="Courier New"&gt;SELECT Student, (Nz([Test1])+Nz([Test2])+Nz([Test3])+Nz([Test4]))/IIf((Abs((Not IsNull([Test1]))+(Not IsNull([Test2]))+(Not IsNull([Test3]))+(Not IsNull([Test4]))))=0,Null,(Abs((Not IsNull([Test1]))+(Not IsNull([Test2]))+(Not IsNull([Test3]))+(Not IsNull([Test4]))))) AS TestAverage&lt;br&gt;FROM StudentScores_RepeatedColumns&lt;br&gt;ORDER BY Student;&lt;/font&gt;  &lt;p&gt;Or in the Query Builder:  &lt;p&gt;&lt;a href="http://lh5.ggpht.com/_JYohVQdgpwU/Tb6PjYox06I/AAAAAAAAApI/ISkSKiDDoc8/s1600-h/image%5B25%5D.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://lh5.ggpht.com/_JYohVQdgpwU/Tb6Pj_w7cYI/AAAAAAAAApM/WDdBEUrYrGA/image_thumb%5B16%5D.png?imgmax=800" width="601" height="451"&gt;&lt;/a&gt;  &lt;p&gt;The result would look like this:  &lt;p&gt;&lt;a href="http://lh5.ggpht.com/_JYohVQdgpwU/Tb6PkNNqUNI/AAAAAAAAApQ/Y_kHdeh9cxI/s1600-h/image%5B26%5D.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://lh5.ggpht.com/_JYohVQdgpwU/Tb6PkShpsqI/AAAAAAAAApU/DUdbiDJktrU/image_thumb%5B17%5D.png?imgmax=800" width="285" height="187"&gt;&lt;/a&gt;  &lt;p&gt;&lt;b&gt;Averaging Down Rows&lt;/b&gt;  &lt;p&gt;By contrast, suppose I normalize the table to remove the repeated columns. The table should look something like this:  &lt;p&gt;&lt;a href="http://lh5.ggpht.com/_JYohVQdgpwU/Tb6PkvBIIrI/AAAAAAAAApY/7SzFQ6l71iY/s1600-h/image%5B15%5D.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://lh6.ggpht.com/_JYohVQdgpwU/Tb6PkzoYeaI/AAAAAAAAApc/qkThWxF7HAs/image_thumb%5B7%5D.png?imgmax=800" width="411" height="309"&gt;&lt;/a&gt;  &lt;p&gt;Since the table is normalized (that is, the values go down a row), I can use the aggregate (or "Totals") functions that are built in to SQL. In this case, it's the Avg() function.  &lt;p&gt;&lt;font face="Courier New"&gt;Avg(Score) AS TestAverage&lt;/font&gt;  &lt;p&gt;The full query would look like this:  &lt;p&gt;&lt;font face="Courier New"&gt;SELECT StudentID, Avg(Score) AS TestAverage&lt;br&gt;FROM StudentScores_Rows&lt;br&gt;GROUP BY StudentID&lt;br&gt;ORDER BY StudentID;;&lt;/font&gt;  &lt;p&gt;Or in the Query Builder:  &lt;p&gt;&lt;a href="http://lh5.ggpht.com/_JYohVQdgpwU/Tb6PlILKDtI/AAAAAAAAApg/HaYy36d0MP0/s1600-h/image%5B19%5D.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://lh5.ggpht.com/_JYohVQdgpwU/Tb6PlWep81I/AAAAAAAAApk/jG1weJn52b8/image_thumb%5B9%5D.png?imgmax=800" width="407" height="363"&gt;&lt;/a&gt;  &lt;p&gt;Once again, the results of the queries are identical:  &lt;p&gt;&lt;a href="http://lh5.ggpht.com/_JYohVQdgpwU/Tb6Pl-PzqbI/AAAAAAAAApo/rtxWtjl6etE/s1600-h/image%5B23%5D.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://lh4.ggpht.com/_JYohVQdgpwU/Tb6PmJxJWrI/AAAAAAAAAps/38atFPyetbM/image_thumb%5B11%5D.png?imgmax=800" width="491" height="164"&gt;&lt;/a&gt;  &lt;p&gt;&lt;b&gt;&lt;/b&gt; &lt;p&gt;Now, with only 4 test scores, the expression to average repeated columns is manageable. But what if there were 20 or 50? The expression quickly becomes long and cumbersome. But with the normalized structure, the query doesn't change no matter how many test values there are.  &lt;p&gt;&lt;b&gt;Alternate Solution: User Defined Function (UDF)&lt;/b&gt;  &lt;p&gt;Although the best solution is to normalize your database, it is often not practical with an existing database. In that case, a User Defined Function (UDF) may be a solution.  &lt;p&gt;Fellow MVP, John Spencer has created a UDF for averaging any number of columns:  &lt;p&gt;&lt;font face="Courier New"&gt;Public Function fRowAverage(ParamArray Values())&lt;br&gt;'John Spencer &lt;br&gt;'Last Update: April 5, 2000&lt;br&gt;'Calculates the arithmetic average (mean) of a group of values passed to it.&lt;br&gt;'Sample call:&lt;br&gt;'myAvg = fRowAverage("1","TEST","2", "3",4,5,6,0) returns 3 (21/7)&lt;br&gt;'Ignores values that cannot be treated as numbers.&lt;br&gt;'&lt;br&gt;' Max of 29 arguments can be passed to a function in Access SQL&lt;br&gt;Dim i As Integer, intElementCount As Integer, dblSum As Double&lt;br&gt;&amp;nbsp; intElementCount = 0&lt;br&gt;&amp;nbsp; dblSum = 0&lt;br&gt;&amp;nbsp; For i = LBound(Values) To UBound(Values)&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; If IsNumeric(Values(i)) Then 'Ignore Non-numeric values&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; dblSum = dblSum + Values(i)&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; intElementCount = intElementCount + 1&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; End If&lt;br&gt;&amp;nbsp; Next I&amp;nbsp; &lt;/font&gt;&lt;/p&gt; &lt;p&gt;&lt;font face="Courier New"&gt;&amp;nbsp; If intElementCount &amp;gt; 0 Then 'At least one number in the group of values&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; fRowAverage = dblSum / intElementCount &lt;br&gt;&amp;nbsp; Else 'No number in the group of values&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; fRowAverage = Null &lt;br&gt;&amp;nbsp; End If&lt;/font&gt; &lt;p&gt;&lt;font face="Courier New"&gt;End Function&lt;/font&gt;  &lt;p&gt;The function should go in a General Module. Then it can be used in a query as follows:  &lt;p&gt;&lt;font face="Courier New"&gt;SELECT Student, fRowAverage([Test1],[test2],[Test3],[Test4]) AS TestAverage&lt;br&gt;FROM StudentScores_RepeatedColumns&lt;br&gt;ORDER BY Student;&lt;/font&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8134759511126815909-3143566603005618875?l=rogersaccessblog.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://rogersaccessblog.blogspot.com/feeds/3143566603005618875/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8134759511126815909&amp;postID=3143566603005618875' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8134759511126815909/posts/default/3143566603005618875'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8134759511126815909/posts/default/3143566603005618875'/><link rel='alternate' type='text/html' href='http://rogersaccessblog.blogspot.com/2011/05/aggregating-across-repeated-columns.html' title='Aggregating Across Repeated Columns: Averaging'/><author><name>Roger's Access Blog</name><uri>http://www.blogger.com/profile/12265719129831415014</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_JYohVQdgpwU/Scu5h2VvtBI/AAAAAAAAAQc/pr3JDoLaqnQ/S220/avi_Roger_1.JPG'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://lh5.ggpht.com/_JYohVQdgpwU/Tb6PjBKC58I/AAAAAAAAApE/t8Vl9w57HzM/s72-c/image_thumb%5B1%5D.png?imgmax=800' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8134759511126815909.post-2020396970012621462</id><published>2011-04-25T07:09:00.001-04:00</published><updated>2011-05-02T07:07:43.354-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Access 101'/><category scheme='http://www.blogger.com/atom/ns#' term='Problems With Repeated Columns'/><title type='text'>Aggregating Across Repeated Columns: Counting</title><content type='html'>&lt;p&gt;In the first post in this series (&lt;a href="http://rogersaccessblog.blogspot.com/2011/03/problem-of-repeated-columns.html"&gt;The Problem of Repeated Columns&lt;/a&gt;), I defined repeated columns and talked about the data integrity problems associated with them. If you haven't read that yet, it would be worthwhile to read first. Similarly, if you are not familiar with the concept of Normalization, you should read my blog series &lt;a href="http://rogersaccessblog.blogspot.com/2008/12/what-is-normalization-part-i.html"&gt;What is Normalization?&lt;/a&gt;  &lt;p&gt;So far in this series, I've discussed the problem with querying textual or Boolean (Yes/No) data from repeated columns. But numeric data offers new challenges because we often want to do math on them. The most common kind of math is aggregation, that is, summing, counting, and averaging numeric values. This time, I'll talk about counting data in repeated columns.  &lt;p&gt;Others in this series:  &lt;ul&gt; &lt;li&gt;&lt;a href="http://rogersaccessblog.blogspot.com/2011/03/querying-repeated-columns-multiple-ors.html"&gt;Multiple ORs&lt;/a&gt;  &lt;li&gt;&lt;a href="http://rogersaccessblog.blogspot.com/2011/03/querying-repeated-columns-multiple.html"&gt;Multiple Unions&lt;/a&gt;  &lt;li&gt;&lt;a href="http://rogersaccessblog.blogspot.com/2011/03/querying-repeated-columns-multiple_28.html"&gt;Multiple Joins&lt;/a&gt;  &lt;li&gt;&lt;a href="http://rogersaccessblog.blogspot.com/2011/04/querying-repeated-columns-multiple-iifs.html"&gt;Multiple Ifs&lt;/a&gt;  &lt;li&gt;&lt;a href="http://rogersaccessblog.blogspot.com/2011/04/querying-repeated-columns-impossible.html"&gt;Impossible Joins&lt;/a&gt;  &lt;li&gt;&lt;a href="http://rogersaccessblog.blogspot.com/2011/04/aggregating-across-repeated-columns.html"&gt;Summing&lt;/a&gt;  &lt;li&gt;&lt;a href="http://rogersaccessblog.blogspot.com/2011/05/aggregating-across-repeated-columns.html"&gt;Averaging&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt; &lt;p&gt;&lt;b&gt;&lt;/b&gt; &lt;p&gt;&lt;b&gt;Counting Across Columns&lt;/b&gt;  &lt;p&gt;The ability to count values is useful in a number of applications. In Excel, there is a Count function which will count the cells that have a value. The Excel function will work for any range of cells, across or down.  &lt;p&gt;In Access, however, the Count() function only works down columns, not across columns. So, counting values in repeated rows is easy (see below), but just like summing, counting across repeated columns is more challenging.  &lt;p&gt;For instance, suppose I had a table of student test scores:  &lt;p&gt;&lt;b&gt;&lt;/b&gt;&lt;a href="http://lh6.ggpht.com/_JYohVQdgpwU/TbVWU--txgI/AAAAAAAAAoQ/mbG8RumJbEg/s1600-h/image%5B3%5D.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://lh6.ggpht.com/_JYohVQdgpwU/TbVWVWW3qUI/AAAAAAAAAoU/RU7GaxsoMyc/image_thumb%5B1%5D.png?imgmax=800" width="579" height="162"&gt;&lt;/a&gt;&lt;b&gt;&lt;br&gt;&lt;/b&gt; &lt;p&gt;If I wanted to count the number of tests each student has taken, I need to create an expression that will return a 1 if the field has a value and a 0 if it does not. In this way, I can sum the returned values and that will equal the number of fields that have a value. To do this, I need 3 stages.  &lt;p&gt;&lt;b&gt;1. &lt;/b&gt;&lt;b&gt;Checking for NULL&lt;/b&gt;  &lt;p&gt;To test whether a field has a value or not, I can test for a NULL value. In Access a blank column is NULL, which does NOT mean either "zero" or "empty string"(see &lt;a href="http://rogersaccessblog.blogspot.com/2008/10/what-does-null-mean-how-is-it-different.html"&gt;What does NULL mean? How is it different than the Empty String?&lt;/a&gt;). To test for a NULL, I have to use the IsNull() function. Like this:  &lt;p&gt;&lt;font face="Courier New"&gt;IsNull([Test1])&lt;/font&gt;  &lt;p&gt;This will return a value of -1 (Yes) if the field is NULL and 0 (No) if it is not.  &lt;p&gt;&lt;b&gt;2. &lt;/b&gt;&lt;b&gt;Checking for NOT NULL&lt;/b&gt;  &lt;p&gt;Unfortunately, this is the opposite of what I want. I want a Yes if the field has a value and a No if it does not. To correct this, I can reverse the value returned by the IsNull() function by prefacing it with the NOT operator:  &lt;p&gt;&lt;font face="Courier New"&gt;Not IsNull([Test1])&lt;/font&gt;  &lt;p&gt;This will return a value of -1 if the field is NOT NULL and 0 if it is. So now I can sum my columns:  &lt;p&gt;&lt;font face="Courier New"&gt;(Not IsNull([Test1])) + (Not IsNull([Test2])) + (Not IsNull([Test3])) + (Not IsNull([Test4]))&lt;/font&gt;  &lt;p&gt;Note: the extra parentheses around the individual expressions are necessary to evaluate properly.  &lt;p&gt;&lt;b&gt;3. &lt;/b&gt;&lt;b&gt;Returning the Absolute Value&lt;/b&gt;  &lt;p&gt;This is close, but not exactly what I want because it will return a negative value for the sum. One last thing I have to do is return the absolute value of the returned value:  &lt;p&gt;&lt;font face="Courier New"&gt;Abs((Not IsNull([Test1])) + (Not IsNull([Test2])) + (Not IsNull([Test3])) + (Not IsNull([Test4])))&lt;/font&gt;  &lt;p&gt;The full query would look like this:  &lt;p&gt;&lt;font face="Courier New"&gt;SELECT Student, Abs((Not IsNull([Test1]))+(Not IsNull([Test2]))+(Not IsNull([Test3]))+(Not IsNull([Test4]))) AS TestCount&lt;br&gt;FROM StudentScores_RepeatedColumns&lt;br&gt;ORDER BY Student;&lt;/font&gt;  &lt;p&gt;Or in the Query Builder:  &lt;p&gt;&lt;a href="http://lh4.ggpht.com/_JYohVQdgpwU/TbVWVUdZM2I/AAAAAAAAAoY/d8Pwt7JT42Y/s1600-h/image%5B32%5D.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://lh5.ggpht.com/_JYohVQdgpwU/TbVWVhdJe_I/AAAAAAAAAoc/8nyD-Hz4QM4/image_thumb%5B18%5D.png?imgmax=800" width="581" height="298"&gt;&lt;/a&gt;  &lt;p&gt;The result would look like this:  &lt;p&gt;&lt;a href="http://lh6.ggpht.com/_JYohVQdgpwU/TbVWV3dtwNI/AAAAAAAAAog/YtmWAKgpVG0/s1600-h/image%5B35%5D.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://lh4.ggpht.com/_JYohVQdgpwU/TbVWWJyGkAI/AAAAAAAAAok/sYx3Gvy-mrE/image_thumb%5B19%5D.png?imgmax=800" width="244" height="175"&gt;&lt;/a&gt;  &lt;p&gt;&lt;b&gt;Count Down Rows&lt;/b&gt;  &lt;p&gt;By contrast, suppose I normalize the table to remove the repeated columns. The table should look something like this:  &lt;p&gt;&lt;a href="http://lh4.ggpht.com/_JYohVQdgpwU/TbVWWIh2r_I/AAAAAAAAAoo/l_whBwRWpdQ/s1600-h/image%5B17%5D.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://lh4.ggpht.com/_JYohVQdgpwU/TbVWWVVhrUI/AAAAAAAAAos/fFH5swcpOkI/image_thumb%5B9%5D.png?imgmax=800" width="417" height="324"&gt;&lt;/a&gt;  &lt;p&gt;&lt;b&gt;&lt;/b&gt; &lt;p&gt;Since the table is normalized (that is, the values go down a row), I can use the aggregate (or "Totals") functions that are built in to SQL. In this case, it's the Count() function.  &lt;p&gt;&lt;font face="Courier New"&gt;Count(Score) AS TestCount&lt;/font&gt;  &lt;p&gt;The full query would look like this:  &lt;p&gt;&lt;font face="Courier New"&gt;SELECT StudentID, Count(Score) AS TestCount&lt;br&gt;FROM StudentScores_Rows&lt;br&gt;GROUP BY StudentID&lt;br&gt;ORDER BY StudentID;&lt;/font&gt;  &lt;p&gt;Or in the Query Builder:  &lt;p&gt;&lt;a href="http://lh5.ggpht.com/_JYohVQdgpwU/TbVWWiTpixI/AAAAAAAAAow/AbhB71O1hms/s1600-h/image%5B27%5D.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://lh3.ggpht.com/_JYohVQdgpwU/TbVWXBrhuLI/AAAAAAAAAo0/ys_v3j2g61Y/image_thumb%5B15%5D.png?imgmax=800" width="439" height="380"&gt;&lt;/a&gt;  &lt;p&gt;Once again, the results of the queries are identical:  &lt;p&gt;&lt;a href="http://lh4.ggpht.com/_JYohVQdgpwU/TbVWXBvW5GI/AAAAAAAAAo4/NBcnlUWHZpw/s1600-h/image%5B26%5D.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://lh3.ggpht.com/_JYohVQdgpwU/TbVWXbnQq5I/AAAAAAAAAo8/xLhnbdosNeA/image_thumb%5B14%5D.png?imgmax=800" width="459" height="186"&gt;&lt;/a&gt;  &lt;p&gt;&lt;b&gt;&lt;/b&gt; &lt;p&gt;Now, with only 4 test scores, the expression to count repeated columns is manageable. But what if there were 20 or 50? The expression quickly becomes long and cumbersome. But with the normalized structure, the query doesn't change no matter how many test values there are.    &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8134759511126815909-2020396970012621462?l=rogersaccessblog.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://rogersaccessblog.blogspot.com/feeds/2020396970012621462/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8134759511126815909&amp;postID=2020396970012621462' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8134759511126815909/posts/default/2020396970012621462'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8134759511126815909/posts/default/2020396970012621462'/><link rel='alternate' type='text/html' href='http://rogersaccessblog.blogspot.com/2011/04/aggregating-across-repeated-columns_25.html' title='Aggregating Across Repeated Columns: Counting'/><author><name>Roger's Access Blog</name><uri>http://www.blogger.com/profile/12265719129831415014</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_JYohVQdgpwU/Scu5h2VvtBI/AAAAAAAAAQc/pr3JDoLaqnQ/S220/avi_Roger_1.JPG'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://lh6.ggpht.com/_JYohVQdgpwU/TbVWVWW3qUI/AAAAAAAAAoU/RU7GaxsoMyc/s72-c/image_thumb%5B1%5D.png?imgmax=800' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8134759511126815909.post-5140924260225644877</id><published>2011-04-18T07:15:00.001-04:00</published><updated>2011-05-02T07:09:17.433-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Access 101'/><category scheme='http://www.blogger.com/atom/ns#' term='Problems With Repeated Columns'/><title type='text'>Aggregating Across Repeated Columns: Summing</title><content type='html'>&lt;p&gt;In the first post in this series (&lt;a href="http://rogersaccessblog.blogspot.com/2011/03/problem-of-repeated-columns.html"&gt;The Problem of Repeated Columns&lt;/a&gt;), I defined repeated columns and talked about the data integrity problems associated with them. If you haven't read that yet, it would be worthwhile to read first. Similarly, if you are not familiar with the concept of Normalization, you should read my blog series &lt;a href="http://rogersaccessblog.blogspot.com/2008/12/what-is-normalization-part-i.html"&gt;What is Normalization?&lt;/a&gt;  &lt;p&gt;So far in this series, I've discussed the problem with querying textual or Boolean (Yes/No) data from repeated columns. But numeric data offers new challenges because we often want to do math on them. The most common kind of math is aggregation, that is, summing, counting, and averaging numeric values. This time, I'll talk about summing data in repeated columns.  &lt;p&gt;Others in this series:  &lt;ul&gt; &lt;li&gt;&lt;a href="http://rogersaccessblog.blogspot.com/2011/03/querying-repeated-columns-multiple-ors.html"&gt;Multiple ORs&lt;/a&gt;  &lt;li&gt;&lt;a href="http://rogersaccessblog.blogspot.com/2011/03/querying-repeated-columns-multiple.html"&gt;Multiple Unions&lt;/a&gt;  &lt;li&gt;&lt;a href="http://rogersaccessblog.blogspot.com/2011/03/querying-repeated-columns-multiple_28.html"&gt;Multiple Joins&lt;/a&gt;  &lt;li&gt;&lt;a href="http://rogersaccessblog.blogspot.com/2011/04/querying-repeated-columns-multiple-iifs.html"&gt;Multiple Ifs&lt;/a&gt;  &lt;li&gt;&lt;a href="http://rogersaccessblog.blogspot.com/2011/04/querying-repeated-columns-impossible.html"&gt;Impossible Joins&lt;/a&gt;  &lt;li&gt;&lt;a href="http://rogersaccessblog.blogspot.com/2011/04/aggregating-across-repeated-columns_25.html"&gt;Counting&lt;/a&gt;  &lt;li&gt;&lt;a href="http://rogersaccessblog.blogspot.com/2011/05/aggregating-across-repeated-columns.html"&gt;Averaging&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt; &lt;p&gt;&lt;b&gt;&lt;/b&gt; &lt;p&gt;&lt;b&gt;Summing Across Columns&lt;/b&gt;  &lt;p&gt;The ability to sum values is useful in a number of applications. In Excel, there is a SUM function which will add up the values of cells that have a value. The Excel function will work for any range of cells, across or down.  &lt;p&gt;In Access, however, the Sum() function only works down columns, not across columns. So, summing values in repeated rows is easy (see below), but just like summing across repeated columns is much more challenging.  &lt;p&gt;For instance, suppose I had a table of student test scores:&lt;/p&gt;&lt;b&gt;&lt;/b&gt; &lt;p&gt;&lt;a href="http://lh3.ggpht.com/_JYohVQdgpwU/TawdD2E7hZI/AAAAAAAAAnY/ZEcYFEe7qeM/s1600-h/image%5B3%5D.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://lh6.ggpht.com/_JYohVQdgpwU/TawdE8euZUI/AAAAAAAAAnc/_3e89Md-wXE/image_thumb%5B1%5D.png?imgmax=800" width="575" height="161"&gt;&lt;/a&gt;&lt;/p&gt; &lt;p&gt;Summing across a row with repeated columns is much like doing it in a spreadsheet. I can create an expression addressing the field names rather than cell references:&lt;/p&gt; &lt;p&gt;&lt;font face="Courier New"&gt;[Test1]+[Test2]+[Test3]+[Test4] AS TestSum&lt;/font&gt;  &lt;p&gt;Unfortunately, this won't work as is, because some of the columns are blank. In Access a blank column is NULL, which is different than in a spreadsheet, where a blank cell means "zero" or "empty string"(see &lt;a href="http://rogersaccessblog.blogspot.com/2008/10/what-does-null-mean-how-is-it-different.html"&gt;What does NULL mean? How is it different than the Empty String?&lt;/a&gt;). If I used the expression as written so far, both Ralph and Sue would have blanks:  &lt;p&gt;&lt;a href="http://lh6.ggpht.com/_JYohVQdgpwU/TawdFZbwBGI/AAAAAAAAAng/2LLbFz92JIE/s1600-h/image%5B8%5D.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://lh5.ggpht.com/_JYohVQdgpwU/TawdGK8cRMI/AAAAAAAAAnk/HXQ7K1y7tz8/image_thumb%5B4%5D.png?imgmax=800" width="267" height="163"&gt;&lt;/a&gt;  &lt;p&gt;Adding any value to a NULL returns a NULL. So I have to explicitly handle the NULL values and turn them into zeros. Fortunately, Access has the Nz() function which will do that:  &lt;p&gt;&lt;font face="Courier New"&gt;Nz([Test1])+Nz([Test2])+Nz([Test3])+Nz([Test4]) AS TestSum&lt;/font&gt;  &lt;p&gt;The full query would look like this:  &lt;p&gt;&lt;font face="Courier New"&gt;SELECT Student, Nz([Test1])+Nz([Test2])+Nz([Test3])+Nz([Test4]) AS TestSum&lt;br&gt;FROM StudentScores_RepeatedColumns&lt;br&gt;ORDER BY Student;&lt;/font&gt;  &lt;p&gt;Or in the Query Builder:  &lt;p&gt;&lt;a href="http://lh3.ggpht.com/_JYohVQdgpwU/TawdGvpYWOI/AAAAAAAAAno/E8N5AoWGVDE/s1600-h/image%5B14%5D.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://lh5.ggpht.com/_JYohVQdgpwU/TawdHoZ-vCI/AAAAAAAAAns/yRZgpVobBpQ/image_thumb%5B8%5D.png?imgmax=800" width="577" height="357"&gt;&lt;/a&gt;  &lt;p&gt;The result would look like this:  &lt;p&gt;&lt;a href="http://lh4.ggpht.com/_JYohVQdgpwU/TawdIBOn9WI/AAAAAAAAAnw/bllz9jcpajk/s1600-h/image%5B18%5D.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://lh4.ggpht.com/_JYohVQdgpwU/TawdIzok4fI/AAAAAAAAAn0/YTfYH0lQwUw/image_thumb%5B10%5D.png?imgmax=800" width="297" height="179"&gt;&lt;/a&gt;  &lt;p&gt;&lt;b&gt;Summing Down Rows&lt;/b&gt;  &lt;p&gt;By contrast, suppose I normalize the table to remove the repeated columns. The table should look something like this:  &lt;p&gt;&lt;a href="http://lh6.ggpht.com/_JYohVQdgpwU/TawdJboYYdI/AAAAAAAAAn4/oLJC5UTRkf4/s1600-h/image%5B35%5D.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://lh6.ggpht.com/_JYohVQdgpwU/TawdKjKX3DI/AAAAAAAAAn8/Psw-JhvXsv4/image_thumb%5B21%5D.png?imgmax=800" width="437" height="333"&gt;&lt;/a&gt;  &lt;p&gt;Since the table is normalized (that is, the values go down a row), I can use the aggregate (or "Totals") functions that are built in to SQL. In this case, it's the SUM() function.  &lt;p&gt;&lt;font face="Courier New"&gt;Sum(Nz(Score)) AS TestScore&lt;/font&gt;  &lt;p&gt;The full query would look like this:  &lt;p&gt;&lt;font face="Courier New"&gt;SELECT StudentID, sum(Nz(Score)) AS TestScore&lt;br&gt;FROM StudentScores_Rows&lt;br&gt;GROUP BY StudentID&lt;br&gt;ORDER BY StudentID;&lt;/font&gt;  &lt;p&gt;Or in the Query Builder:  &lt;p&gt;&lt;a href="http://lh5.ggpht.com/_JYohVQdgpwU/TawdLA-YXtI/AAAAAAAAAoA/631FGMfpy5U/s1600-h/image%5B33%5D.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://lh3.ggpht.com/_JYohVQdgpwU/TawdMfRJI2I/AAAAAAAAAoE/WdGDpbffc7c/image_thumb%5B19%5D.png?imgmax=800" width="471" height="377"&gt;&lt;/a&gt;  &lt;p&gt;Once again, the results of the queries are identical:  &lt;p&gt;&lt;a href="http://lh3.ggpht.com/_JYohVQdgpwU/TawdM9hbMfI/AAAAAAAAAoI/f-6vO2F2pXs/s1600-h/image%5B36%5D.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://lh3.ggpht.com/_JYohVQdgpwU/TawdN8e73dI/AAAAAAAAAoM/I4Nid_hl_IM/image_thumb%5B22%5D.png?imgmax=800" width="478" height="168"&gt;&lt;/a&gt;  &lt;p&gt;Now, this doesn't look like too much of an issue, especially compared to some of the problems we've seen with querying repeated columns. However, this is just 4 test scores. What if there were 20 or 50? The expression in the Repeated Columns table quickly becomes long and cumbersome. But with the normalized structure, the query doesn't change no matter how many test values there are.  &lt;p&gt;&lt;b&gt;Alternate Solution: User Defined Function (UDF)&lt;/b&gt;  &lt;p&gt;Although the best solution is to normalize your database, it is often not practical with an existing database. In that case, a User Defined Function (UDF) may be a solution.  &lt;p&gt;Fellow MVP, John Spencer has created a UDF for summing any number of columns (up to 29):  &lt;p&gt;&lt;font face="Courier New"&gt;Public Function fRowSum(ParamArray Values())&lt;br&gt;'John Spencer&lt;br&gt;'Last Update: April 5, 2000&lt;br&gt;'Calculates the arithmetic sum of a group of values passed to it.&lt;br&gt;'Sample call:&lt;br&gt;'mySum = fRowSum("1","TEST","2", "3",4,5,6,0) returns 21&lt;br&gt;'Ignores values that cannot be treated as numbers.&lt;br&gt;'&lt;br&gt;' Max of 29 arguments can be passed to a function in Access SQL&lt;br&gt;Dim i As Integer, intElementCount As Integer, dblSum As Double&lt;br&gt;intElementCount = 0&lt;br&gt;dblSum = 0&lt;br&gt;For i = LBound(Values) To UBound(Values)&lt;br&gt;&amp;nbsp; If IsNumeric(Values(i)) Then 'Ignore Non-numeric values&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; dblSum = dblSum + Values(i)&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; intElementCount = intElementCount + 1&lt;br&gt;&amp;nbsp; End If&lt;br&gt;Next i&lt;br&gt;If intElementCount &amp;gt; 0 Then 'At least one number in the group of values&lt;br&gt;&amp;nbsp; fRowSum = dblSum&lt;br&gt;Else 'No number in the group of values&lt;br&gt;&amp;nbsp; fRowSum = 0&lt;br&gt;End If&lt;br&gt;End Function&lt;/font&gt;  &lt;p&gt;The function should go in a General Module. Then it can be used in a query as follows:  &lt;p&gt;&lt;font face="Courier New"&gt;SELECT Student, fRowSum([Test1],[Test2],[Test3],[Test4]) AS TestSum&lt;br&gt;FROM StudentScores_RepeatedColumns&lt;br&gt;ORDER BY Student;&lt;/font&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8134759511126815909-5140924260225644877?l=rogersaccessblog.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://rogersaccessblog.blogspot.com/feeds/5140924260225644877/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8134759511126815909&amp;postID=5140924260225644877' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8134759511126815909/posts/default/5140924260225644877'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8134759511126815909/posts/default/5140924260225644877'/><link rel='alternate' type='text/html' href='http://rogersaccessblog.blogspot.com/2011/04/aggregating-across-repeated-columns.html' title='Aggregating Across Repeated Columns: Summing'/><author><name>Roger's Access Blog</name><uri>http://www.blogger.com/profile/12265719129831415014</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_JYohVQdgpwU/Scu5h2VvtBI/AAAAAAAAAQc/pr3JDoLaqnQ/S220/avi_Roger_1.JPG'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://lh6.ggpht.com/_JYohVQdgpwU/TawdE8euZUI/AAAAAAAAAnc/_3e89Md-wXE/s72-c/image_thumb%5B1%5D.png?imgmax=800' height='72' width='72'/><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8134759511126815909.post-1997122567939260131</id><published>2011-04-11T07:25:00.002-04:00</published><updated>2011-05-02T07:12:13.948-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Access 101'/><category scheme='http://www.blogger.com/atom/ns#' term='Problems With Repeated Columns'/><title type='text'>Querying Repeated Columns: Impossible Joins</title><content type='html'>&lt;p&gt;In the first post in this series (&lt;a href="http://rogersaccessblog.blogspot.com/2011/03/problem-of-repeated-columns.html"&gt;The Problem of Repeated Columns&lt;/a&gt;), I defined repeated columns and talked about the data integrity problems associated with them. If you haven't read that yet, it would be worthwhile to read first. Similarly, if you are not familiar with the concept of Normalization, you should read my blog series &lt;a href="http://rogersaccessblog.blogspot.com/2008/12/what-is-normalization-part-i.html"&gt;What is Normalization?&lt;/a&gt;  &lt;p&gt;What I'm going to concentrate in the next few post are specific problems with querying data stored in repeated columns. This time, it's Impossible Joins statements.  &lt;p&gt;Others in this series:  &lt;ul&gt; &lt;li&gt;&lt;a href="http://rogersaccessblog.blogspot.com/2011/03/querying-repeated-columns-multiple-ors.html"&gt;Multiple ORs&lt;/a&gt;  &lt;li&gt;&lt;a href="http://rogersaccessblog.blogspot.com/2011/03/querying-repeated-columns-multiple.html"&gt;Multiple Unions&lt;/a&gt;  &lt;li&gt;&lt;a href="http://rogersaccessblog.blogspot.com/2011/03/querying-repeated-columns-multiple_28.html"&gt;Multiple Joins&lt;/a&gt;  &lt;li&gt;&lt;a href="http://rogersaccessblog.blogspot.com/2011/04/querying-repeated-columns-multiple-iifs.html"&gt;Multiple IFs&lt;/a&gt;  &lt;li&gt;&lt;a href="http://rogersaccessblog.blogspot.com/2011/04/aggregating-across-repeated-columns.html"&gt;Summing&lt;/a&gt;  &lt;li&gt;&lt;a href="http://rogersaccessblog.blogspot.com/2011/04/aggregating-across-repeated-columns_25.html"&gt;Counting&lt;/a&gt;  &lt;li&gt;&lt;a href="http://rogersaccessblog.blogspot.com/2011/05/aggregating-across-repeated-columns.html"&gt;Averaging&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt; &lt;p&gt;&lt;b&gt;&lt;/b&gt; &lt;p&gt;&lt;b&gt;Impossible Joins&lt;/b&gt;  &lt;p&gt;Last time, I discussed problems associated with the sort of repeated columns typified by the &lt;a href="http://rogersaccessblog.blogspot.com/2011/04/querying-repeated-columns-multiple-iifs.html"&gt;use of multiple Yes/No fields&lt;/a&gt;. For instance, a patient table with a listing of symptoms as Yes/No fields. Like this:  &lt;p&gt;&lt;a href="http://lh3.ggpht.com/_JYohVQdgpwU/TaLlJF3cAXI/AAAAAAAAAmo/nknvfip47RY/s1600-h/clip_image002%5B4%5D.jpg"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="clip_image002" border="0" alt="clip_image002" src="http://lh3.ggpht.com/_JYohVQdgpwU/TaLlJnLa0fI/AAAAAAAAAms/Uy1v_OXD5h0/clip_image002_thumb%5B1%5D.jpg?imgmax=800" width="574" height="156"&gt;&lt;/a&gt;&lt;br&gt;&lt;b&gt;Figure 1: Patient table with Yes/No fields representing symptoms.&lt;/b&gt;  &lt;p&gt;Another problem with this sort of table is the impossibility of joining it to another table based on the column names. For instance, suppose I wanted to group the symptoms into disease groupings. I could create a table like this:  &lt;p&gt;&lt;a href="http://lh6.ggpht.com/_JYohVQdgpwU/TaLlJ3Zoq9I/AAAAAAAAAmw/bX8v2SCb4_Q/s1600-h/clip_image004%5B4%5D.jpg"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="clip_image004" border="0" alt="clip_image004" src="http://lh3.ggpht.com/_JYohVQdgpwU/TaLlKJnEFBI/AAAAAAAAAm0/y7afp9BFCM0/clip_image004_thumb%5B1%5D.jpg?imgmax=800" width="325" height="225"&gt;&lt;/a&gt;&lt;br&gt;&lt;b&gt;Figure 2: Disease grouping table.&lt;/b&gt;  &lt;p&gt;The problem here is there's no way to join this table back to the Patient table. I can't join values in one table to the field names in another. In a relational database, all information is supposed to be stored as values in tables. When you put the value as the field name, you lose much of the capabilities built in the SQL, the query language designed for relational databases.  &lt;p&gt;To solve this, I need to use the solution to the patient/symptom list problem discussed last time. That is, using multiple IIF statements and multiple unions to convert the field names into values. Like this:  &lt;p&gt;&lt;font face="Courier New"&gt;SELECT Patient, IIf([cough] = True,"Cough") AS Symptom&lt;br&gt;FROM Patient_RepeatedColumns WHERE [Cough] = True&lt;br&gt;UNION ALL &lt;br&gt;SELECT Patient, IIf([Sneeze] = True,"Sneeze") AS Symptom&lt;br&gt;FROM Patient_RepeatedColumns WHERE [Sneeze] = True&lt;br&gt;UNION ALL &lt;br&gt;SELECT Patient, IIf([Fever] = True,"Fever") AS Symptom&lt;br&gt;FROM Patient_RepeatedColumns WHERE [Fever] = True&lt;br&gt;UNION ALL &lt;br&gt;SELECT Patient, IIf([Body_Aches] = True,"Body aches") AS Symptom&lt;br&gt;FROM Patient_RepeatedColumns WHERE [Body_Aches] = True&lt;br&gt;UNION ALL &lt;br&gt;SELECT Patient, IIf([Nausea] = True,"Nausea") AS Symptom&lt;br&gt;FROM Patient_RepeatedColumns WHERE [Nausea] = True&lt;br&gt;ORDER BY Patient, Symptom;&lt;/font&gt;  &lt;p&gt;Which gives me the following result:  &lt;p&gt;&lt;a href="http://lh5.ggpht.com/_JYohVQdgpwU/TaLlKakVoaI/AAAAAAAAAm4/c9siUv2mmTE/s1600-h/clip_image006%5B4%5D.jpg"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="clip_image006" border="0" alt="clip_image006" src="http://lh3.ggpht.com/_JYohVQdgpwU/TaLlK0DJvnI/AAAAAAAAAm8/As7aNezzpfA/clip_image006_thumb%5B1%5D.jpg?imgmax=800" width="264" height="277"&gt;&lt;/a&gt;&lt;br&gt;&lt;b&gt;Figure 3: Intermediate query to be used as a subquery&lt;/b&gt;  &lt;p&gt;I can now use this query as a subquery in the From clause of another query by giving it an alias (Symptoms):  &lt;p&gt;&lt;font face="Courier New"&gt;SELECT Symptoms.Patient, DiseaseGroup.Group&lt;br&gt;FROM &lt;br&gt;(SELECT Patient, IIf([cough],"Cough") AS Symptom&lt;br&gt;FROM Patient_RepeatedColumns WHERE [Cough] = True&lt;br&gt;UNION ALL &lt;br&gt;SELECT Patient, IIf([Sneeze],"Sneeze") AS Symptom&lt;br&gt;FROM Patient_RepeatedColumns WHERE [Sneeze] = True&lt;br&gt;UNION ALL &lt;br&gt;SELECT Patient, IIf([Fever],"Fever") AS Symptom&lt;br&gt;FROM Patient_RepeatedColumns WHERE [Fever] = True&lt;br&gt;UNION ALL &lt;br&gt;SELECT Patient, IIf([Body_Aches],"Body aches") AS Symptom&lt;br&gt;FROM Patient_RepeatedColumns WHERE [Body_Aches] = True&lt;br&gt;UNION ALL SELECT Patient, IIf([Nausea],"Nausea") AS Symptom&lt;br&gt;FROM Patient_RepeatedColumns WHERE [Nausea] = True&lt;br&gt;ORDER BY Patient, Symptom) as Symptoms&lt;br&gt;INNER JOIN DiseaseGroup ON Symptoms.Symptom = DiseaseGroup.Symptom&lt;br&gt;GROUP BY Symptoms.Patient, DiseaseGroup.Group;&lt;/font&gt;  &lt;p&gt;By contrast, using a normalized structure like this:  &lt;p&gt;&lt;a href="http://lh3.ggpht.com/_JYohVQdgpwU/TaLlLBKMlUI/AAAAAAAAAnA/Xz-ZQ7lLUss/s1600-h/clip_image008%5B6%5D.jpg"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="clip_image008" border="0" alt="clip_image008" src="http://lh5.ggpht.com/_JYohVQdgpwU/TaLlLXNNobI/AAAAAAAAAnE/0o0OMzWNCoM/clip_image008_thumb%5B3%5D.jpg?imgmax=800" width="509" height="270"&gt;&lt;/a&gt;&lt;br&gt;&lt;b&gt;Figure 4: Repeated columns normalized into repeated rows in Symptoms table&lt;/b&gt;  &lt;p&gt;I can query the data much more simply:  &lt;p&gt;&lt;font face="Courier New"&gt;SELECT Patient.Patient, DiseaseGroup.Group&lt;br&gt;FROM &lt;br&gt;(Symptoms INNER JOIN DiseaseGroup ON Symptoms.Symptom = DiseaseGroup.Symptom) &lt;br&gt;INNER JOIN Patient ON Symptoms.PatientID = Patient.PatientID&lt;br&gt;GROUP BY Patient.Patient, DiseaseGroup.Group;&lt;/font&gt;  &lt;p&gt;Or in the query builder:  &lt;p&gt;&lt;a href="http://lh4.ggpht.com/_JYohVQdgpwU/TaLlL3caXGI/AAAAAAAAAnI/K0bs7wTxgIM/s1600-h/clip_image010%5B5%5D.jpg"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="clip_image010" border="0" alt="clip_image010" src="http://lh5.ggpht.com/_JYohVQdgpwU/TaLlMATuJJI/AAAAAAAAAnM/ogLEzuwpNj8/clip_image010_thumb%5B2%5D.jpg?imgmax=800" width="502" height="344"&gt;&lt;/a&gt;&lt;br&gt;&lt;b&gt;Figure 5: Normalized query in the Query Builder.&lt;/b&gt;  &lt;p&gt;Once again, the results of the queries are identical:  &lt;p&gt;&lt;a href="http://lh5.ggpht.com/_JYohVQdgpwU/TaLlMaFlZOI/AAAAAAAAAnQ/zAOn9IxRdIs/s1600-h/clip_image012%5B4%5D.jpg"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="clip_image012" border="0" alt="clip_image012" src="http://lh5.ggpht.com/_JYohVQdgpwU/TaLlMy-3DOI/AAAAAAAAAnU/1kaeHIH8JtQ/clip_image012_thumb%5B1%5D.jpg?imgmax=800" width="507" height="186"&gt;&lt;/a&gt;&lt;br&gt;&lt;b&gt;Figure : Results of both queries&lt;/b&gt;  &lt;p&gt;But querying repeated columns proves much more complicated and much less flexible than querying a normalized table structure.    &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8134759511126815909-1997122567939260131?l=rogersaccessblog.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://rogersaccessblog.blogspot.com/feeds/1997122567939260131/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8134759511126815909&amp;postID=1997122567939260131' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8134759511126815909/posts/default/1997122567939260131'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8134759511126815909/posts/default/1997122567939260131'/><link rel='alternate' type='text/html' href='http://rogersaccessblog.blogspot.com/2011/04/querying-repeated-columns-impossible.html' title='Querying Repeated Columns: Impossible Joins'/><author><name>Roger's Access Blog</name><uri>http://www.blogger.com/profile/12265719129831415014</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_JYohVQdgpwU/Scu5h2VvtBI/AAAAAAAAAQc/pr3JDoLaqnQ/S220/avi_Roger_1.JPG'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://lh3.ggpht.com/_JYohVQdgpwU/TaLlJnLa0fI/AAAAAAAAAms/Uy1v_OXD5h0/s72-c/clip_image002_thumb%5B1%5D.jpg?imgmax=800' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8134759511126815909.post-1960624318227899101</id><published>2011-04-04T06:41:00.001-04:00</published><updated>2011-05-02T07:11:35.500-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Access 101'/><category scheme='http://www.blogger.com/atom/ns#' term='Problems With Repeated Columns'/><title type='text'>Querying Repeated Columns: Multiple IIFs</title><content type='html'>&lt;p&gt;In the first post in this series (&lt;a href="http://rogersaccessblog.blogspot.com/2011/03/problem-of-repeated-columns.html"&gt;The Problem of Repeated Columns&lt;/a&gt;), I defined repeated columns and talked about the data integrity problems associated with them. If you haven't read that yet, it would be worthwhile to read first. Similarly, if you are not familiar with the concept of Normalization, you should read my blog series &lt;a href="http://rogersaccessblog.blogspot.com/2008/12/what-is-normalization-part-i.html"&gt;What is Normalization?&lt;/a&gt;  &lt;p&gt;What I'm going to concentrate in the next few post are specific problems with querying data stored in repeated columns. This time, it's Multiple IIF statements.  &lt;p&gt;Others in this series:  &lt;ul&gt; &lt;li&gt;&lt;a href="http://rogersaccessblog.blogspot.com/2011/03/querying-repeated-columns-multiple-ors.html"&gt;Multiple ORs&lt;/a&gt;  &lt;li&gt;&lt;a href="http://rogersaccessblog.blogspot.com/2011/03/querying-repeated-columns-multiple.html"&gt;Multiple Unions&lt;/a&gt;  &lt;li&gt;&lt;a href="http://rogersaccessblog.blogspot.com/2011/03/querying-repeated-columns-multiple_28.html"&gt;Multiple Joins&lt;/a&gt;  &lt;li&gt;&lt;a href="http://rogersaccessblog.blogspot.com/2011/04/querying-repeated-columns-impossible.html"&gt;Impossible Joins&lt;/a&gt;  &lt;li&gt;&lt;a href="http://rogersaccessblog.blogspot.com/2011/04/aggregating-across-repeated-columns.html"&gt;Summing&lt;/a&gt;  &lt;li&gt;&lt;a href="http://rogersaccessblog.blogspot.com/2011/04/aggregating-across-repeated-columns_25.html"&gt;Counting&lt;/a&gt;  &lt;li&gt;&lt;a href="http://rogersaccessblog.blogspot.com/2011/05/aggregating-across-repeated-columns.html"&gt;Averaging&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt; &lt;p&gt;&lt;b&gt;&lt;/b&gt; &lt;p&gt;&lt;b&gt;Multiple IIF Statements&lt;/b&gt;  &lt;p&gt;Another sort of repeated columns can be seen by the use of multiple Yes/No fields, each of which are just values of a specific category. For instance, suppose I had a patient table with a listing of symptoms as Yes/No fields. Like this:  &lt;p&gt;&lt;a href="http://lh5.ggpht.com/_JYohVQdgpwU/TZmgVdH2akI/AAAAAAAAAmI/ZUNapnFeDmQ/s1600-h/clip_image002%5B4%5D.jpg"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="clip_image002" border="0" alt="clip_image002" src="http://lh3.ggpht.com/_JYohVQdgpwU/TZmgV4p6D8I/AAAAAAAAAmM/deXCpHLFmmw/clip_image002_thumb%5B1%5D.jpg?imgmax=800" width="578" height="157"&gt;&lt;/a&gt;&lt;br&gt;&lt;b&gt;Figure : Patient table with Yes/No fields representing symptoms.&lt;/b&gt;  &lt;p&gt;If I wanted to create a simple list of patient's symptoms, in addition to the multiple Unions, I need multiple IIF statements to convert the field names into values. Something like this:  &lt;p&gt;&lt;font face="Courier New"&gt;SELECT Patient, IIf([cough] = True,"Cough") AS Symptom&lt;br&gt;FROM Patient_RepeatedColumns WHERE [Cough] = True&lt;br&gt;UNION ALL &lt;br&gt;SELECT Patient, IIf([Sneeze] = True,"Sneeze") AS Symptom&lt;br&gt;FROM Patient_RepeatedColumns WHERE [Sneeze] = True&lt;br&gt;UNION ALL &lt;br&gt;SELECT Patient, IIf([Fever] = True,"Fever") AS Symptom&lt;br&gt;FROM Patient_RepeatedColumns WHERE [Fever] = True&lt;br&gt;UNION ALL &lt;br&gt;SELECT Patient, IIf([Body_Aches] = True,"Body aches") AS Symptom&lt;br&gt;FROM Patient_RepeatedColumns WHERE [Body_Aches] = True&lt;br&gt;UNION ALL &lt;br&gt;SELECT Patient, IIf([Nausea] = True,"Nausea") AS Symptom&lt;br&gt;FROM Patient_RepeatedColumns WHERE [Nausea] = True&lt;br&gt;ORDER BY Patient, Symptom;&lt;/font&gt;  &lt;p&gt;By contrast, if I normalized the table to convert the repeated columns to repeated rows, it might look something like this:  &lt;p&gt;&lt;a href="http://lh4.ggpht.com/_JYohVQdgpwU/TZmgWCQ2IkI/AAAAAAAAAmQ/qEtUwm7Qjpc/s1600-h/clip_image004%5B4%5D.jpg"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="clip_image004" border="0" alt="clip_image004" src="http://lh4.ggpht.com/_JYohVQdgpwU/TZmgWmF8ndI/AAAAAAAAAmU/PqBzZUJNuTY/clip_image004_thumb%5B1%5D.jpg?imgmax=800" width="450" height="238"&gt;&lt;/a&gt;&lt;br&gt;Figure : Repeated columns converted to repeated rows in Symptoms table.  &lt;p&gt;Creating the patient/symptom list is now simple:  &lt;p&gt;&lt;font face="Courier New"&gt;SELECT Patient, Symptom&lt;br&gt;FROM Symptoms INNER JOIN Patient ON Symptoms.PatientID = Patient.PatientID&lt;br&gt;ORDER BY Patient, Symptom;&lt;/font&gt;  &lt;p&gt;Or in the query builder:  &lt;p&gt;&lt;a href="http://lh4.ggpht.com/_JYohVQdgpwU/TZmgWzr_LSI/AAAAAAAAAmY/jZZkOSQy4Rw/s1600-h/clip_image006%5B4%5D.jpg"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="clip_image006" border="0" alt="clip_image006" src="http://lh3.ggpht.com/_JYohVQdgpwU/TZmgXeWQtkI/AAAAAAAAAmc/J-19dzw5wec/clip_image006_thumb%5B1%5D.jpg?imgmax=800" width="447" height="307"&gt;&lt;/a&gt;&lt;br&gt;&lt;b&gt;Figure : Normalized query in the Query Builder.&lt;/b&gt;  &lt;p&gt;The results of the queries are identical except the normalized query is updateable:  &lt;p&gt;&lt;a href="http://lh3.ggpht.com/_JYohVQdgpwU/TZmgXlnLWYI/AAAAAAAAAmg/5WiPAF0OZXo/s1600-h/clip_image008%5B4%5D.jpg"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="clip_image008" border="0" alt="clip_image008" src="http://lh3.ggpht.com/_JYohVQdgpwU/TZmgX6NGHhI/AAAAAAAAAmk/ZOWfn9iHR5E/clip_image008_thumb%5B1%5D.jpg?imgmax=800" width="418" height="227"&gt;&lt;/a&gt;&lt;br&gt;&lt;b&gt;Figure : Results of both queries&lt;/b&gt;  &lt;p&gt;Once again, querying repeated columns proves much more complicated and much less flexible than querying a normalized table structure.    &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8134759511126815909-1960624318227899101?l=rogersaccessblog.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://rogersaccessblog.blogspot.com/feeds/1960624318227899101/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8134759511126815909&amp;postID=1960624318227899101' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8134759511126815909/posts/default/1960624318227899101'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8134759511126815909/posts/default/1960624318227899101'/><link rel='alternate' type='text/html' href='http://rogersaccessblog.blogspot.com/2011/04/querying-repeated-columns-multiple-iifs.html' title='Querying Repeated Columns: Multiple IIFs'/><author><name>Roger's Access Blog</name><uri>http://www.blogger.com/profile/12265719129831415014</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_JYohVQdgpwU/Scu5h2VvtBI/AAAAAAAAAQc/pr3JDoLaqnQ/S220/avi_Roger_1.JPG'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://lh3.ggpht.com/_JYohVQdgpwU/TZmgV4p6D8I/AAAAAAAAAmM/deXCpHLFmmw/s72-c/clip_image002_thumb%5B1%5D.jpg?imgmax=800' height='72' width='72'/><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8134759511126815909.post-7973697464306290347</id><published>2011-03-28T06:43:00.001-04:00</published><updated>2011-05-02T07:10:12.618-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Access 101'/><category scheme='http://www.blogger.com/atom/ns#' term='Problems With Repeated Columns'/><title type='text'>Querying Repeated Columns: Multiple Joins</title><content type='html'>&lt;p&gt;In the first post in this series (&lt;a href="http://rogersaccessblog.blogspot.com/2011/03/problem-of-repeated-columns.html"&gt;The Problem of Repeated Columns&lt;/a&gt;), I defined repeated columns and talked about the data integrity problems associated with them. If you haven't read that yet, it would be worthwhile to read first. Similarly, if you are not familiar with the concept of Normalization, you should read my blog series &lt;a href="http://rogersaccessblog.blogspot.com/2008/12/what-is-normalization-part-i.html"&gt;What is Normalization?&lt;/a&gt;  &lt;p&gt;What I'm going to concentrate in the next few post are specific problems with querying data stored in repeated columns. This time, it's Multiple Joins.  &lt;p&gt;Others in this series:  &lt;ul&gt; &lt;li&gt;&lt;a href="http://rogersaccessblog.blogspot.com/2011/03/querying-repeated-columns-multiple-ors.html"&gt;Multiple ORs&lt;/a&gt;  &lt;li&gt;&lt;a href="http://rogersaccessblog.blogspot.com/2011/03/querying-repeated-columns-multiple.html"&gt;Multiple Unions&lt;/a&gt;  &lt;li&gt;&lt;a href="http://rogersaccessblog.blogspot.com/2011/04/querying-repeated-columns-multiple-iifs.html"&gt;Multiple IIFs&lt;/a&gt;  &lt;li&gt;&lt;a href="http://rogersaccessblog.blogspot.com/2011/04/querying-repeated-columns-impossible.html"&gt;Impossible Joins&lt;/a&gt;  &lt;li&gt;&lt;a href="http://rogersaccessblog.blogspot.com/2011/04/aggregating-across-repeated-columns.html"&gt;Summing&lt;/a&gt;  &lt;li&gt;&lt;a href="http://rogersaccessblog.blogspot.com/2011/04/aggregating-across-repeated-columns_25.html"&gt;Counting&lt;/a&gt;  &lt;li&gt;&lt;a href="http://rogersaccessblog.blogspot.com/2011/05/aggregating-across-repeated-columns.html"&gt;Averaging&lt;/a&gt; &lt;/li&gt;&lt;/ul&gt; &lt;p&gt;&lt;b&gt;&lt;/b&gt; &lt;p&gt;&lt;b&gt;Multiple Joins&lt;/b&gt;  &lt;p&gt;In my last post, Querying Repeated Columns: Multiple Unions, I discussed the problem of repeated columns with multiple categories. That is, in addition to Software1, Software2, ... SoftwareX, the table also has a Type field for each software: Software1Type, Software2Type, ... SoftwareXType. One solution that people come up with to solve this problem is to have a separate, "look-up" table for the software. Something like this:  &lt;p&gt;&lt;a href="http://lh3.ggpht.com/_JYohVQdgpwU/TZBmTkgMQ4I/AAAAAAAAAlQ/xyL3Al2t6nk/s1600-h/clip_image002%5B4%5D.jpg"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="clip_image002" border="0" alt="clip_image002" src="http://lh6.ggpht.com/_JYohVQdgpwU/TZBmUGlxr1I/AAAAAAAAAlU/eK86BmRDZ3Q/clip_image002_thumb%5B1%5D.jpg?imgmax=800" width="568" height="347"&gt;&lt;/a&gt;&lt;br&gt;&lt;b&gt;Figure 1: Incorrect solution to the Multiple Category problem&lt;/b&gt;  &lt;p&gt;This solution does have some advantages from a data integrity standpoint. I can use the Software table as the Row Source in a combo box on a form, which will limit data entry errors. I also can't assign an incorrect category to a software, since it is stored in the Software table.  &lt;p&gt;The downside, however, is that I have to create multiple Joins with the Software table, one for each Software column. So just to get my original table back, I have to do something like this:  &lt;p&gt;&lt;font face="Courier New"&gt;SELECT PC_ID, Software1, Software.Category AS Software1Type, &lt;br&gt;Software2, Software_1.Category AS Software2Type, &lt;br&gt;Software3, Software_2.Category AS Software3Type&lt;br&gt;FROM ((PC_RepeatedColumns LEFT JOIN Software &lt;br&gt;ON PC_RepeatedColumns.Software1 = Software.Software) &lt;br&gt;LEFT JOIN Software AS Software_1 &lt;br&gt;ON PC_RepeatedColumns.Software2 = Software_1.Software) &lt;br&gt;LEFT JOIN Software AS Software_2 &lt;br&gt;ON PC_RepeatedColumns.Software3 = Software_2.Software;&lt;/font&gt;  &lt;p&gt;Or in the Query Builder:  &lt;p&gt;&lt;a href="http://lh4.ggpht.com/_JYohVQdgpwU/TZBmUpopWXI/AAAAAAAAAlY/cKAYyda3ooc/s1600-h/clip_image004%5B4%5D.jpg"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="clip_image004" border="0" alt="clip_image004" src="http://lh6.ggpht.com/_JYohVQdgpwU/TZBmU8dij2I/AAAAAAAAAlc/f083oJEfS60/clip_image004_thumb%5B1%5D.jpg?imgmax=800" width="592" height="401"&gt;&lt;/a&gt;&lt;br&gt;&lt;b&gt;Figure 2: Query with multiple joins to display software types.&lt;/b&gt;  &lt;p&gt;Unfortunately, this just gets me back to the original, repeated columns table.  &lt;p&gt;&lt;b&gt;&lt;a href="http://lh6.ggpht.com/_JYohVQdgpwU/TZBmVF91jMI/AAAAAAAAAlg/0_7tEqJo06c/s1600-h/clip_image006%5B4%5D.jpg"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="clip_image006" border="0" alt="clip_image006" src="http://lh3.ggpht.com/_JYohVQdgpwU/TZBmVioMukI/AAAAAAAAAlk/hoFZGhUEEeY/clip_image006_thumb%5B1%5D.jpg?imgmax=800" width="575" height="135"&gt;&lt;/a&gt;&lt;/b&gt;&lt;br&gt;&lt;b&gt;Figure 3: Result of query in Figure 2.&lt;/b&gt;  &lt;p&gt;But as I showed last time, it does nothing to solve the underlying problem of querying the repeated columns. I would still need to use multiple UNION queries to return a list of PCs with word processors. However, unlike last time, each query in the Union must be joined to the Software table:  &lt;p&gt;&lt;font face="Courier New"&gt;SELECT PC_ID, Software1, Software.Category&lt;br&gt;FROM PC_RepeatedColumns LEFT JOIN Software &lt;br&gt;ON PC_RepeatedColumns.Software1 = Software.Software&lt;br&gt;WHERE Category="word processor"&lt;br&gt;UNION&lt;br&gt;SELECT PC_ID, Software2, Software.Category&lt;br&gt;FROM PC_RepeatedColumns LEFT JOIN Software &lt;br&gt;ON PC_RepeatedColumns.Software2 = Software.Software&lt;br&gt;WHERE Category="word processor"&lt;br&gt;UNION&lt;br&gt;SELECT PC_ID, Software3, Software.Category&lt;br&gt;FROM PC_RepeatedColumns LEFT JOIN Software &lt;br&gt;ON PC_RepeatedColumns.Software3 = Software.Software&lt;br&gt;WHERE Category="word processor"&lt;/font&gt;  &lt;p&gt;&lt;a href="http://lh5.ggpht.com/_JYohVQdgpwU/TZBmV5ox15I/AAAAAAAAAlo/Demti6kCCK8/s1600-h/clip_image008%5B4%5D.jpg"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="clip_image008" border="0" alt="clip_image008" src="http://lh5.ggpht.com/_JYohVQdgpwU/TZBmWB7IG5I/AAAAAAAAAls/1aUoYPnfOhc/clip_image008_thumb%5B1%5D.jpg?imgmax=800" width="344" height="175"&gt;&lt;/a&gt;&lt;br&gt;&lt;b&gt;Figure 3: Query with Multiple Unions and Multiple Joins&lt;/b&gt;  &lt;p&gt;&lt;b&gt;&lt;/b&gt; &lt;p&gt;But as I showed last time, using a truly normalized structure, like this:  &lt;p&gt;&lt;a href="http://lh5.ggpht.com/_JYohVQdgpwU/TZBmWaiXZPI/AAAAAAAAAmA/mppM4RdbcCQ/s1600-h/clip_image010%5B1%5D.jpg"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="clip_image010" border="0" alt="clip_image010" src="http://lh4.ggpht.com/_JYohVQdgpwU/TZBmWqChZmI/AAAAAAAAAmE/i6kpLoGcua4/clip_image010_thumb.jpg?imgmax=800" width="625" height="189"&gt;&lt;/a&gt;&lt;br&gt;&lt;b&gt;Figure 4: Fully normalized with Category column added.&lt;/b&gt;  &lt;p&gt;I need only a simple join to query the data:  &lt;p&gt;&lt;font face="Courier New"&gt;SELECT PC.PC_Num, Software, Category&lt;br&gt;FROM Software INNER JOIN &lt;br&gt;(PC INNER JOIN PC_Software ON PC.PC_ID = PC_Software.PCID) &lt;br&gt;ON Software.SoftwareID = PC_Software.SoftwareID&lt;br&gt;WHERE Category="word processor";&lt;/font&gt;  &lt;p&gt;This one can be viewed in the Query Builder:  &lt;p&gt;&lt;a href="http://lh5.ggpht.com/_JYohVQdgpwU/TZBmW0l1E5I/AAAAAAAAAl4/CcSrrzFoXnc/s1600-h/clip_image012%5B6%5D.jpg"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="clip_image012" border="0" alt="clip_image012" src="http://lh3.ggpht.com/_JYohVQdgpwU/TZBmXNU2EbI/AAAAAAAAAl8/QjPBt6_-G3Y/clip_image012_thumb%5B3%5D.jpg?imgmax=800" width="550" height="297"&gt;&lt;/a&gt;&lt;br&gt;&lt;b&gt;Figure 5: Word processor for each PC with normalized structure.&lt;/b&gt;  &lt;p&gt;Once again, while the results of the queries are the same, querying repeated columns proves much more complicated and much less flexible than querying a normalized table structure.  &lt;p&gt;&amp;nbsp; &lt;p&gt;.    &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8134759511126815909-7973697464306290347?l=rogersaccessblog.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://rogersaccessblog.blogspot.com/feeds/7973697464306290347/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8134759511126815909&amp;postID=7973697464306290347' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8134759511126815909/posts/default/7973697464306290347'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8134759511126815909/posts/default/7973697464306290347'/><link rel='alternate' type='text/html' href='http://rogersaccessblog.blogspot.com/2011/03/querying-repeated-columns-multiple_28.html' title='Querying Repeated Columns: Multiple Joins'/><author><name>Roger's Access Blog</name><uri>http://www.blogger.com/profile/12265719129831415014</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_JYohVQdgpwU/Scu5h2VvtBI/AAAAAAAAAQc/pr3JDoLaqnQ/S220/avi_Roger_1.JPG'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://lh6.ggpht.com/_JYohVQdgpwU/TZBmUGlxr1I/AAAAAAAAAlU/eK86BmRDZ3Q/s72-c/clip_image002_thumb%5B1%5D.jpg?imgmax=800' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8134759511126815909.post-7090415472888102119</id><published>2011-03-22T14:58:00.000-04:00</published><updated>2011-03-22T14:58:47.736-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='New Samples'/><category scheme='http://www.blogger.com/atom/ns#' term='Tejpal'/><title type='text'>New Sample: Form_ControlDefaultValues</title><content type='html'>Form_ControlDefaultValues&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;By A.D. Tejpal&lt;br /&gt;&lt;br /&gt;This sample db demonstrates programmatic setting of default values for form controls, covering different data types (Date, Text and Number). Two methods are illustrated as follows:&lt;br /&gt;&lt;br /&gt;1 - Concatenation of intended default value into a string assigned as control's default value property.&lt;br /&gt;&lt;br /&gt;2 - Generation of intended default value through embedded reference within the string assigned as control's default value property. &lt;br /&gt;&lt;br /&gt;For each of the above styles, two alternative modes are demonstrated as follows:&lt;br /&gt;&lt;br /&gt;(a) Set defaults as per current record (On editing or double clicking pertinent controls).&lt;br /&gt;&lt;br /&gt;(b) Set defaults as per last record (On editing or double clicking pertinent controls - only if the cursor is on last record).&lt;br /&gt;&lt;br /&gt;You can find the sample here: &lt;a href="http://www.rogersaccesslibrary.com/forum/Form-controldefaultvalues_topic559.html"&gt;http://www.rogersaccesslibrary.com/forum/Form-controldefaultvalues_topic559.html&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8134759511126815909-7090415472888102119?l=rogersaccessblog.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://rogersaccessblog.blogspot.com/feeds/7090415472888102119/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8134759511126815909&amp;postID=7090415472888102119' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8134759511126815909/posts/default/7090415472888102119'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8134759511126815909/posts/default/7090415472888102119'/><link rel='alternate' type='text/html' href='http://rogersaccessblog.blogspot.com/2011/03/new-sample-formcontroldefaultvalues.html' title='New Sample: Form_ControlDefaultValues'/><author><name>Roger's Access Blog</name><uri>http://www.blogger.com/profile/12265719129831415014</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_JYohVQdgpwU/Scu5h2VvtBI/AAAAAAAAAQc/pr3JDoLaqnQ/S220/avi_Roger_1.JPG'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8134759511126815909.post-6588000969829153018</id><published>2011-03-21T06:48:00.001-04:00</published><updated>2011-05-02T07:13:05.835-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Access 101'/><category scheme='http://www.blogger.com/atom/ns#' term='Problems With Repeated Columns'/><title type='text'>Querying Repeated Columns: Multiple Unions</title><content type='html'>&lt;p&gt;In the first post in this series (&lt;a href="http://rogersaccessblog.blogspot.com/2011/03/problem-of-repeated-columns.html"&gt;The Problem of Repeated Columns&lt;/a&gt;), I defined R&lt;strong&gt;epeated Columns&lt;/strong&gt; and talked about the data integrity problems associated with them. If you haven't read that yet, it would be worthwhile to read first. Similarly, if you are not familiar with the concept of &lt;strong&gt;Normalization&lt;/strong&gt;, you should read my blog series &lt;a href="http://rogersaccessblog.blogspot.com/2008/12/what-is-normalization-part-i.html"&gt;What is Normalization?&lt;/a&gt;  &lt;p&gt;What I'm going to concentrate in the next few post are specific problems with querying data stored in repeated columns. This time, it's Multiple Unions.  &lt;p&gt;Others in this series:  &lt;ul&gt; &lt;li&gt;&lt;a href="http://rogersaccessblog.blogspot.com/2011/03/querying-repeated-columns-multiple-ors.html"&gt;Multiple ORs&lt;/a&gt;  &lt;li&gt;&lt;a href="http://rogersaccessblog.blogspot.com/2011/03/querying-repeated-columns-multiple_28.html"&gt;Multiple Joins&lt;/a&gt;  &lt;li&gt;&lt;a href="http://rogersaccessblog.blogspot.com/2011/04/querying-repeated-columns-multiple-iifs.html"&gt;Multiple IIFs&lt;/a&gt;  &lt;li&gt;&lt;a href="http://rogersaccessblog.blogspot.com/2011/04/querying-repeated-columns-impossible.html"&gt;Impossible Joins&lt;/a&gt;  &lt;li&gt;&lt;a href="http://rogersaccessblog.blogspot.com/2011/04/aggregating-across-repeated-columns.html"&gt;Summing&lt;/a&gt;  &lt;li&gt;&lt;a href="http://rogersaccessblog.blogspot.com/2011/04/aggregating-across-repeated-columns_25.html"&gt;Counting&lt;/a&gt;  &lt;li&gt;&lt;a href="http://rogersaccessblog.blogspot.com/2011/05/aggregating-across-repeated-columns.html"&gt;Averaging&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt; &lt;p&gt;&lt;b&gt;&lt;/b&gt; &lt;p&gt;&lt;b&gt;Multiple UNIONs&lt;/b&gt;  &lt;p&gt;Last time, in &lt;a href="http://rogersaccessblog.blogspot.com/2011/03/querying-repeated-columns-multiple-ors.html"&gt;Querying Repeated Columns: Multiple ORs&lt;/a&gt;, I talked about the difficulties of querying data in repeated columns. But it gets worse. Suppose I want to categorize each type of software. In which case, I'd have to add a category column for each software column (Software1Type, Software2Type, ...etc). It could look something like this:  &lt;p&gt;&lt;b&gt;&lt;a href="http://lh6.ggpht.com/_JYohVQdgpwU/TYctBtqgt8I/AAAAAAAAAko/8Pbokbw73Gw/s1600-h/clip_image002%5B4%5D.jpg"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="clip_image002" border="0" alt="clip_image002" src="http://lh3.ggpht.com/_JYohVQdgpwU/TYctCDCXrRI/AAAAAAAAAks/mH7wf5bj7Tc/clip_image002_thumb%5B1%5D.jpg?imgmax=800" width="575" height="135"&gt;&lt;/a&gt;&lt;br&gt;&lt;/b&gt;&lt;b&gt;Figure 1: Repeated Columns with repeated category columns. &lt;/b&gt; &lt;p&gt;By comparison, the normalized version would look like Figure 3, with both Software and Category removed to their own table:  &lt;p&gt;&lt;a href="http://lh6.ggpht.com/_JYohVQdgpwU/TYctCW-NHJI/AAAAAAAAAkw/cPWKY2yuNog/s1600-h/clip_image004%5B4%5D.jpg"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="clip_image004" border="0" alt="clip_image004" src="http://lh6.ggpht.com/_JYohVQdgpwU/TYctCqPcRLI/AAAAAAAAAk0/it0YReQeBck/clip_image004_thumb%5B1%5D.jpg?imgmax=800" width="579" height="162"&gt;&lt;/a&gt;&lt;br&gt;&lt;b&gt;Figure 2: Fully normalized with Category column added.&lt;/b&gt;  &lt;p&gt;Now, suppose I want to query a listing of all word processors for each PC. To create such a query with repeated columns, I'd have to resort to multiple UNIONs:  &lt;p&gt;&lt;font face="Courier New"&gt;SELECT PC_ID, Software1 as Software, Software1Type as Category&lt;br&gt;FROM Worse_PC_RepeatedColumns&lt;br&gt;WHERE Software1Type="word processor"&lt;br&gt;UNION&lt;br&gt;SELECT PC_ID, Software2 as Software, Software2Type as Category&lt;br&gt;FROM Worse_PC_RepeatedColumns&lt;br&gt;WHERE Software2Type="word processor"&lt;br&gt;UNION&lt;br&gt;SELECT PC_ID, Software3 as Software, Software3Type as Category&lt;br&gt;FROM Worse_PC_RepeatedColumns&lt;br&gt;WHERE Software3Type="word processor"&lt;/font&gt;  &lt;p&gt;Unfortunately, this must be done in SQL view since union queries cannot be viewed in the Query Builder.  &lt;p&gt;&lt;a href="http://lh4.ggpht.com/_JYohVQdgpwU/TYctC6YZFXI/AAAAAAAAAk4/wGd1j5Fhnw4/s1600-h/clip_image006%5B4%5D.jpg"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="clip_image006" border="0" alt="clip_image006" src="http://lh5.ggpht.com/_JYohVQdgpwU/TYctDB0N6fI/AAAAAAAAAk8/yUFkB33se8w/clip_image006_thumb%5B1%5D.jpg?imgmax=800" width="402" height="220"&gt;&lt;/a&gt;&lt;br&gt;&lt;b&gt;Figure 3: Word processor for each PC with repeated columns.&lt;/b&gt;  &lt;p&gt;By contrast, creating the query in the normalized structure looks like this:  &lt;p&gt;&lt;font face="Courier New"&gt;SELECT PC.PC_Num, Software, Category&lt;br&gt;FROM Software INNER JOIN &lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; (PC INNER JOIN PC_Software ON PC.PC_ID = PC_Software.PCID) &lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; ON Software.SoftwareID = PC_Software.SoftwareID&lt;br&gt;WHERE Category="word processor";&lt;/font&gt;  &lt;p&gt;This one can be viewed in the Query Builder:  &lt;p&gt;&lt;a href="http://lh3.ggpht.com/_JYohVQdgpwU/TYctDRrSfQI/AAAAAAAAAlA/4Oc171EJL9Y/s1600-h/clip_image008%5B5%5D.jpg"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="clip_image008" border="0" alt="clip_image008" src="http://lh3.ggpht.com/_JYohVQdgpwU/TYctD7YolaI/AAAAAAAAAlE/lADdLIUFMOA/clip_image008_thumb%5B2%5D.jpg?imgmax=800" width="564" height="295"&gt;&lt;/a&gt;&lt;br&gt;&lt;b&gt;Figure 4: Word processor for each PC with normalized structure.&lt;/b&gt;  &lt;p&gt;Again, the results of both queries are identical with the exception that the query with repeated columns (UNION) is not updateable.  &lt;p&gt;&lt;a href="http://lh6.ggpht.com/_JYohVQdgpwU/TYctEL0KZaI/AAAAAAAAAlI/sqhKWZ5faG8/s1600-h/clip_image010%5B5%5D.jpg"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="clip_image010" border="0" alt="clip_image010" src="http://lh3.ggpht.com/_JYohVQdgpwU/TYctEpRtPlI/AAAAAAAAAlM/X5pUjM8LYbw/clip_image010_thumb%5B2%5D.jpg?imgmax=800" width="576" height="160"&gt;&lt;/a&gt;&lt;br&gt;&lt;b&gt;Figure 5: Results of two queries.&lt;/b&gt;  &lt;p&gt;More columns in the repeated columns table makes the query more complex. I'd have to add another UNION for each column pair. Worse yet, there is a limit to the number of Unions you can use in a query, so if you have too many categories, your query may not just be difficult, but impossible.  &lt;p&gt;By contrast, adding more values to the the normalized tables requires no change to the query at all, and there is virtually no limit on the number of categories.&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8134759511126815909-6588000969829153018?l=rogersaccessblog.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://rogersaccessblog.blogspot.com/feeds/6588000969829153018/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8134759511126815909&amp;postID=6588000969829153018' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8134759511126815909/posts/default/6588000969829153018'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8134759511126815909/posts/default/6588000969829153018'/><link rel='alternate' type='text/html' href='http://rogersaccessblog.blogspot.com/2011/03/querying-repeated-columns-multiple.html' title='Querying Repeated Columns: Multiple Unions'/><author><name>Roger's Access Blog</name><uri>http://www.blogger.com/profile/12265719129831415014</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_JYohVQdgpwU/Scu5h2VvtBI/AAAAAAAAAQc/pr3JDoLaqnQ/S220/avi_Roger_1.JPG'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://lh3.ggpht.com/_JYohVQdgpwU/TYctCDCXrRI/AAAAAAAAAks/mH7wf5bj7Tc/s72-c/clip_image002_thumb%5B1%5D.jpg?imgmax=800' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8134759511126815909.post-6188264382335604742</id><published>2011-03-15T07:11:00.001-04:00</published><updated>2011-05-02T07:10:54.288-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Access 101'/><category scheme='http://www.blogger.com/atom/ns#' term='Problems With Repeated Columns'/><title type='text'>Querying Repeated Columns: Multiple ORs</title><content type='html'>&lt;p&gt;In the first post in this series (&lt;a href="http://rogersaccessblog.blogspot.com/2011/03/problem-of-repeated-columns.html"&gt;The Problem of Repeated Columns&lt;/a&gt;), I defined repeated columns and talked about the data integrity problems associated with them. If you haven't read that yet, it would be worthwhile to read first. Similarly, if you are not familiar with the concept of Normalization, you should read my blog series &lt;a href="http://rogersaccessblog.blogspot.com/2008/12/what-is-normalization-part-i.html"&gt;What is Normalization?&lt;/a&gt;  &lt;p&gt;What I'm going to concentrate in the next few posts are specific problems with querying data stored in repeated columns. The first of these is Multiple ORs.  &lt;p&gt;Others in this series:  &lt;ul&gt; &lt;li&gt;&lt;a href="http://rogersaccessblog.blogspot.com/2011/03/querying-repeated-columns-multiple.html"&gt;Multiple Unions&lt;/a&gt;  &lt;li&gt;&lt;a href="http://rogersaccessblog.blogspot.com/2011/03/querying-repeated-columns-multiple_28.html"&gt;Multiple Joins&lt;/a&gt;  &lt;li&gt;&lt;a href="http://rogersaccessblog.blogspot.com/2011/04/querying-repeated-columns-multiple-iifs.html"&gt;Multiple IIFs&lt;/a&gt;  &lt;li&gt;&lt;a href="http://rogersaccessblog.blogspot.com/2011/04/querying-repeated-columns-impossible.html"&gt;Impossible Joins&lt;/a&gt;  &lt;li&gt;&lt;a href="http://rogersaccessblog.blogspot.com/2011/04/aggregating-across-repeated-columns.html"&gt;Summing&lt;/a&gt;  &lt;li&gt;&lt;a href="http://rogersaccessblog.blogspot.com/2011/04/aggregating-across-repeated-columns_25.html"&gt;Counting&lt;/a&gt;  &lt;li&gt;&lt;a href="http://rogersaccessblog.blogspot.com/2011/05/aggregating-across-repeated-columns.html"&gt;Averaging&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt; &lt;p&gt;&lt;b&gt;Multiple "OR" Conditions&lt;/b&gt;  &lt;p&gt;One of the most common examples of repeated columns in a table come from PC Inventory databases. This is likely because they are created by Help Desk personnel who are familiar with spreadsheet software like Excel. Since repeated columns are allowed, even necessary, in a spreadsheet, it is logical to build a database table the same way. Logical, perhaps, but wrong.  &lt;p&gt;An extremely simplified example of such a table might look like this:  &lt;p&gt;&lt;a href="http://lh4.ggpht.com/_JYohVQdgpwU/TX9JTbuHt5I/AAAAAAAAAkA/5HjPUJIysQ0/s1600-h/clip_image002%5B5%5D.jpg"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="clip_image002" border="0" alt="clip_image002" src="http://lh4.ggpht.com/_JYohVQdgpwU/TX9JT2FRQBI/AAAAAAAAAkE/0cq3vGILzmk/clip_image002_thumb%5B2%5D.jpg?imgmax=800" width="595" height="155"&gt;&lt;/a&gt;&lt;br&gt;&lt;b&gt;Figure 1: PC_Inventory table with repeated columns&lt;/b&gt;  &lt;p&gt;Normalized to remove the repeated columns, the table would look like this:  &lt;p&gt;&lt;a href="http://lh4.ggpht.com/_JYohVQdgpwU/TX9JUCAUpCI/AAAAAAAAAkI/ibDd6TmXxyY/s1600-h/clip_image004%5B6%5D.jpg"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="clip_image004" border="0" alt="clip_image004" src="http://lh3.ggpht.com/_JYohVQdgpwU/TX9JUexiUuI/AAAAAAAAAkM/EiWcfchie2E/clip_image004_thumb%5B3%5D.jpg?imgmax=800" width="598" height="279"&gt;&lt;/a&gt;&lt;br&gt;&lt;b&gt;Figure 2: PC_Inventory normalized&lt;/b&gt;  &lt;p&gt;&lt;i&gt;Note: Proper normalization requires the fields PC_Num and OperatingSystem to be removed to their own table. Technically, Software should also be removed to its own table, but I'll address that later.&lt;/i&gt;  &lt;p&gt;So suppose I want to produce a simple list of those PCs which have "Word 2010" installed. With the repeated columns table, my SQL statement would look like this:  &lt;p&gt;&lt;font face="Courier New"&gt;SELECT PC_ID&lt;br&gt;&lt;/font&gt;&lt;font face="Courier New"&gt;FROM PC_RepeatedColumns&lt;br&gt;&lt;/font&gt;&lt;font face="Courier New"&gt;WHERE Software1="Word 2010" OR &lt;br&gt;&lt;/font&gt;&lt;font face="Courier New"&gt;&amp;nbsp; Software2="Word 2010" OR &lt;br&gt;&lt;/font&gt;&lt;font face="Courier New"&gt;&amp;nbsp; Software3="Word 2010" OR &lt;br&gt;&lt;/font&gt;&lt;font face="Courier New"&gt;&amp;nbsp; Software4="Word 2010" OR &lt;br&gt;&lt;/font&gt;&lt;font face="Courier New"&gt;&amp;nbsp; Software5="Word 2010";&lt;/font&gt;  &lt;p&gt;In the Query Builder, it would look like this:  &lt;p&gt;&lt;a href="http://lh3.ggpht.com/_JYohVQdgpwU/TX9JUq-1jbI/AAAAAAAAAkQ/eu5gu7HGUhs/s1600-h/clip_image006%5B5%5D.jpg"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="clip_image006" border="0" alt="clip_image006" src="http://lh5.ggpht.com/_JYohVQdgpwU/TX9JVNwBk5I/AAAAAAAAAkU/txTS7Fs31bI/clip_image006_thumb%5B2%5D.jpg?imgmax=800" width="574" height="309"&gt;&lt;/a&gt;&lt;br&gt;&lt;b&gt;Figure 3: Query with repeated columns&lt;/b&gt;  &lt;p&gt;By contrast, querying the normalized tables would look like this:  &lt;p&gt;&lt;font face="Courier New"&gt;SELECT PC_Num &lt;br&gt;&lt;/font&gt;&lt;font face="Courier New"&gt;FROM PC INNER JOIN PCInventory ON PC.PC_ID = PCInventory.PC_ID&lt;br&gt;&lt;/font&gt;&lt;font face="Courier New"&gt;WHERE PCInventory.Software ="Word 2010";&lt;/font&gt;  &lt;p&gt;In the Query Builder:  &lt;p&gt;&lt;a href="http://lh3.ggpht.com/_JYohVQdgpwU/TX9JVT0-NNI/AAAAAAAAAkY/RDRbwVoupBM/s1600-h/clip_image008%5B5%5D.jpg"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="clip_image008" border="0" alt="clip_image008" src="http://lh4.ggpht.com/_JYohVQdgpwU/TX9JVgsqJrI/AAAAAAAAAkc/doFOAZxXu-Y/clip_image008_thumb%5B2%5D.jpg?imgmax=800" width="385" height="312"&gt;&lt;/a&gt;&lt;br&gt;&lt;b&gt;Figure 4: Query with normalized tables&lt;/b&gt;  &lt;p&gt;The results of both queries are identical:  &lt;p&gt;&lt;a href="http://lh3.ggpht.com/_JYohVQdgpwU/TX9JWIuDmII/AAAAAAAAAkg/EDVjGajC9zE/s1600-h/clip_image010%5B5%5D.jpg"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="clip_image010" border="0" alt="clip_image010" src="http://lh5.ggpht.com/_JYohVQdgpwU/TX9JWdf8F9I/AAAAAAAAAkk/4o2Zgo9FKwQ/clip_image010_thumb%5B2%5D.jpg?imgmax=800" width="441" height="150"&gt;&lt;/a&gt;&lt;b&gt;&lt;br&gt;Figure 5: Query Results &lt;/b&gt; &lt;p&gt;Adding more columns just makes matters worse. With 20 columns in your repeated columns table, you have to have 20 "OR" conditions in your Where clause. What's more, if you find you have to add another column down the road, you have to modify your query (and every query that uses that table). On the other hand, adding a new value to the normalized table requires no change to the SQL statement.  &lt;p&gt;Thus, with the repeated columns table, I have to do a whole lot more work than with the normalized structure, both initially and when a new software category is added. But it gets worse. Next time I'll look at the same PC_Inventory tables with a software type field added for each software.    &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8134759511126815909-6188264382335604742?l=rogersaccessblog.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://rogersaccessblog.blogspot.com/feeds/6188264382335604742/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8134759511126815909&amp;postID=6188264382335604742' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8134759511126815909/posts/default/6188264382335604742'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8134759511126815909/posts/default/6188264382335604742'/><link rel='alternate' type='text/html' href='http://rogersaccessblog.blogspot.com/2011/03/querying-repeated-columns-multiple-ors.html' title='Querying Repeated Columns: Multiple ORs'/><author><name>Roger's Access Blog</name><uri>http://www.blogger.com/profile/12265719129831415014</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_JYohVQdgpwU/Scu5h2VvtBI/AAAAAAAAAQc/pr3JDoLaqnQ/S220/avi_Roger_1.JPG'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://lh4.ggpht.com/_JYohVQdgpwU/TX9JT2FRQBI/AAAAAAAAAkE/0cq3vGILzmk/s72-c/clip_image002_thumb%5B2%5D.jpg?imgmax=800' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8134759511126815909.post-8194297240921191465</id><published>2011-03-07T15:04:00.000-05:00</published><updated>2011-05-02T07:13:57.933-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Access 101'/><category scheme='http://www.blogger.com/atom/ns#' term='Problems With Repeated Columns'/><title type='text'>The Problem of Repeated Columns</title><content type='html'>&lt;p&gt;I've discussed this topic briefly in another post: &lt;a href="http://rogersaccessblog.blogspot.com/2008/12/what-is-normalization-part-i.html"&gt;Why Normalization?&lt;/a&gt;, but the issue of Repeated Columns comes up so often that I think it deserves a post of its own.  &lt;p&gt;First of all, what are repeated columns?  &lt;p&gt;A table has repeated columns when the same category of information is stored in multiple columns (or fields). For instance, if I have workstation inventory table, I would want to store all the software installed on a given PC. I could solve this problem with fields like this: Software1, Software2, Software3, and so on. Anytime you have fields which need a suffix to differentiate one field from another, you have repeated columns.  &lt;p&gt;&lt;a href="http://lh3.ggpht.com/_JYohVQdgpwU/TXYbSAJokvI/AAAAAAAAAjk/5-X-0WR8hPA/s1600-h/figure1%5B4%5D.jpg"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="figure1" border="0" alt="figure1" src="http://lh6.ggpht.com/_JYohVQdgpwU/TXYbSVT-5yI/AAAAAAAAAjo/6oXYbvHoft0/figure1_thumb%5B2%5D.jpg?imgmax=800" width="535" height="163"&gt;&lt;/a&gt;  &lt;p&gt;&lt;strong&gt;Figure 1: Inventory table with repeated fields.&lt;/strong&gt;  &lt;p&gt;But although fields with suffixes are the easiest to spot, there are other cases. Suppose I had these fields: OS, Wordprocessor, Spreadsheet, PhotoManager, and so forth. This is essentially the same as Software1, Software2. I've just substituted the name of software type for a generic name. This reduces the number of fields from the above example, but it still has problems of its own.  &lt;p&gt;&lt;a href="http://lh4.ggpht.com/_JYohVQdgpwU/TXYbSkcLq_I/AAAAAAAAAjs/P-y3Niiqsyo/s1600-h/figure2%5B3%5D.jpg"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="figure2" border="0" alt="figure2" src="http://lh6.ggpht.com/_JYohVQdgpwU/TXYbS8o8uzI/AAAAAAAAAjw/oCu7xMT4Ess/figure2_thumb%5B1%5D.jpg?imgmax=800" width="557" height="135"&gt;&lt;/a&gt;  &lt;p&gt;&lt;strong&gt;Figure 2: Inventory table with repeated fields with different names.&lt;/strong&gt;  &lt;p&gt;Yet a third type is Multiple Yes/No fields. Suppose I have a Patient table with fields for symptoms (Cough, Fever, Sneeze, etc) which are Yes/No fields. At first glance, these are not repeated fields, but in fact they are. They store the same category of information, that is, each is a symptom.  &lt;p&gt;&lt;a href="http://lh5.ggpht.com/_JYohVQdgpwU/TXYbTCnCSTI/AAAAAAAAAj0/HWG-CSJjuUQ/figure3%5B3%5D.jpg?imgmax=800"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="figure3" border="0" alt="figure3" src="http://lh6.ggpht.com/_JYohVQdgpwU/TXYbTxnU--I/AAAAAAAAAj4/j_Td4M25uuI/figure3_thumb%5B1%5D.jpg?imgmax=800" width="563" height="146"&gt;&lt;/a&gt;  &lt;p&gt;&lt;strong&gt;Figure 3: Patient table with repeated symptom Yes/No columns.&lt;/strong&gt;  &lt;p&gt;&lt;b&gt;Problems:&lt;/b&gt;  &lt;p&gt;So what's so wrong with repeated columns? After all, we do it all the time in spreadsheets. Unfortunately, while a database table might look like a spreadsheet, it's not, and the rules for using them are different.  &lt;p&gt;&lt;b&gt;It's in the Rules&lt;/b&gt;  &lt;p&gt;In a table ALL information should be stored as values in fields, not in the field names. When a table has fields like Cough, Sneeze, Fever, etc, information is being stored in the field name. Field names should identify a category of information, not individual values. This is part of the basic definition of a relational database table (called the &lt;a href="http://rogersaccessblog.blogspot.com/2009/02/normal-forms-first-normal-form-1nf.html"&gt;First Normal Form&lt;/a&gt;) and all SQL (Structured Query Language) implementations are built for it. When a table has repeated columns, queries that should be easy become difficult -- sometimes impossible. I'll show specifics later.  &lt;p&gt;&lt;b&gt;Data Anomolies&lt;/b&gt;  &lt;p&gt;In a normalized database, I can create a unique index to prevent duplicate values being entered in a field. There is no way to prevent this at the database level with repeated columns. For instance, there is nothing to prevent a user from entering the operating system in both Software1 and Software2.  &lt;p&gt;&lt;b&gt;How Many, How Few?&lt;/b&gt;  &lt;p&gt;And then there's the question of how many repeated fields I should have. For instance, how many software fields (Software1, Software2, etc) should I have in my PC_RepeatedColumns table? Choose some arbitrary minimum (5?) and lose the rest? Or do I try to determine the maximum I could ever have (255?) and leave most of them empty? Or do I pick some arbitrary middle ground and modify my database afterward if I need more? There are disadvantages to each approach and few advantages in any of them.  &lt;p&gt;&lt;b&gt;Columns Are Expensive, Rows Are Cheap&lt;/b&gt;  &lt;p&gt;A truism of database design is: "Columns are expensive. Rows are cheap." One of the powerful features of Access (or any database, for that matter) is the ability to create a user application to help end-users enter and query data. Adding a new column requires the developer to make changes in the table and all queries, forms, and reports that use that table as well. This makes adding a column to a table one of the most "expensive" (in terms of development time) development operations.  &lt;p&gt;By contrast, adding a new value to a column, rather than adding a new column, can be done by the user and requires no development time.  &lt;p&gt;&lt;b&gt;Querying:&lt;/b&gt;  &lt;p&gt;Querying with repeated columns often turns a simple task into an exercise in frustration, requiring massive, nested IIF statements, multiple OR conditions, or both. Aggregate queries like Sum, Count, and Average are particularly difficult.  &lt;p&gt;These problems are magnified by the "How Many, How Few" issue. If I have a maximum possible number, I need to add columns to my query that may never be used. But if I only have the minimum, adding a new column requires me to modify every query. Either way, it's a lot of unnecessary work.  &lt;p&gt;To illustrate the problems with repeated fields, especially with querying, I'm going to spend the next few posts showing specific examples:  &lt;p&gt;Querying Repeated Columns  &lt;ul&gt; &lt;li&gt;&lt;a href="http://rogersaccessblog.blogspot.com/2011/03/querying-repeated-columns-multiple-ors.html"&gt;Multiple ORs&lt;/a&gt;  &lt;li&gt;&lt;a href="http://rogersaccessblog.blogspot.com/2011/03/querying-repeated-columns-multiple.html"&gt;Multiple Unions&lt;/a&gt;  &lt;li&gt;&lt;a href="http://rogersaccessblog.blogspot.com/2011/03/querying-repeated-columns-multiple_28.html"&gt;Multiple Joins&lt;/a&gt;  &lt;li&gt;&lt;a href="http://rogersaccessblog.blogspot.com/2011/04/querying-repeated-columns-multiple-iifs.html"&gt;Multiple IIFs&lt;/a&gt;  &lt;li&gt;&lt;a href="http://rogersaccessblog.blogspot.com/2011/04/querying-repeated-columns-impossible.html"&gt;Impossible Joins&lt;/a&gt;  &lt;li&gt;&lt;a href="http://rogersaccessblog.blogspot.com/2011/04/aggregating-across-repeated-columns.html"&gt;Summing&lt;/a&gt;  &lt;li&gt;&lt;a href="http://rogersaccessblog.blogspot.com/2011/04/aggregating-across-repeated-columns_25.html"&gt;Counting&lt;/a&gt;  &lt;li&gt;&lt;a href="http://rogersaccessblog.blogspot.com/2011/05/aggregating-across-repeated-columns.html"&gt;Averaging&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8134759511126815909-8194297240921191465?l=rogersaccessblog.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://rogersaccessblog.blogspot.com/feeds/8194297240921191465/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8134759511126815909&amp;postID=8194297240921191465' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8134759511126815909/posts/default/8194297240921191465'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8134759511126815909/posts/default/8194297240921191465'/><link rel='alternate' type='text/html' href='http://rogersaccessblog.blogspot.com/2011/03/problem-of-repeated-columns.html' title='The Problem of Repeated Columns'/><author><name>Roger's Access Blog</name><uri>http://www.blogger.com/profile/12265719129831415014</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_JYohVQdgpwU/Scu5h2VvtBI/AAAAAAAAAQc/pr3JDoLaqnQ/S220/avi_Roger_1.JPG'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://lh6.ggpht.com/_JYohVQdgpwU/TXYbSVT-5yI/AAAAAAAAAjo/6oXYbvHoft0/s72-c/figure1_thumb%5B2%5D.jpg?imgmax=800' height='72' width='72'/><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8134759511126815909.post-1342881559468497785</id><published>2011-02-08T06:51:00.001-05:00</published><updated>2011-02-08T06:51:52.511-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='New Samples'/><category scheme='http://www.blogger.com/atom/ns#' term='Tejpal'/><title type='text'>New Sample: Query_FillMissingValues</title><content type='html'>by AD Tejpal&lt;br /&gt;&lt;br /&gt;This sample db demonstrates filling up of missing values in a sequential series. Three types of source data are covered:&lt;br /&gt;(a) Number series.&lt;br /&gt;(b) Alpha-numeric series.&lt;br /&gt;(c) Date series.&lt;br /&gt;&lt;br /&gt;Subform on left shows the start and end values for each missing range of sequential values per PersonID. Subform at right depicts all the missing sequential values duly filled in. Each record representing start of a new sequence is highlighted in color.&lt;br /&gt;&lt;br /&gt;Note:&lt;br /&gt;A single field table (T_Ref) populated with sequential numbers from 0 onwards is used as the driver for eliciting the missing values. The number of records in this table should be such as to cover the maximum likely range of missing values.&lt;br /&gt;&lt;br /&gt;Version: Access 2000 file format.&lt;br /&gt;&lt;br /&gt;You can find the sample here: &lt;a href="http://www.rogersaccesslibrary.com/forum/topic558_post573.html"&gt;http://www.rogersaccesslibrary.com/forum/topic558_post573.html&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8134759511126815909-1342881559468497785?l=rogersaccessblog.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://rogersaccessblog.blogspot.com/feeds/1342881559468497785/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8134759511126815909&amp;postID=1342881559468497785' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8134759511126815909/posts/default/1342881559468497785'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8134759511126815909/posts/default/1342881559468497785'/><link rel='alternate' type='text/html' href='http://rogersaccessblog.blogspot.com/2011/02/new-sample-queryfillmissingvalues.html' title='New Sample: Query_FillMissingValues'/><author><name>Roger's Access Blog</name><uri>http://www.blogger.com/profile/12265719129831415014</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_JYohVQdgpwU/Scu5h2VvtBI/AAAAAAAAAQc/pr3JDoLaqnQ/S220/avi_Roger_1.JPG'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8134759511126815909.post-7340548956759466692</id><published>2011-02-07T07:04:00.002-05:00</published><updated>2011-02-07T07:06:31.852-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Access 101'/><category scheme='http://www.blogger.com/atom/ns#' term='Domain Functions'/><title type='text'>Begin Date and End Date from Effective Date</title><content type='html'>So far in this series on Domain Functions, I've discussed the general syntax (&lt;a href="http://rogersaccessblog.blogspot.com/2010/12/domain-functions-demystified.html"&gt;Domain Functions Demystified&lt;/a&gt;) and problems involved in building criteria expressions (&lt;a href="http://rogersaccessblog.blogspot.com/2010/12/domain-functions-demystified-criteria.html"&gt;Domain Functions Demystified: Criteria Expressions&lt;/a&gt;). Unfortunately, many of the examples I've given are relatively trivial. So for my next few blog posts, I thought I'd give what I consider truly useful applications of domain functions.&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;Other Examples:&lt;/strong&gt;&lt;br /&gt;&lt;ol&gt;&lt;li&gt;&lt;a href="http://rogersaccessblog.blogspot.com/2010/12/domain-function-example-simulate.html"&gt;Simulate AutoNumber with DMax&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://rogersaccessblog.blogspot.com/2011/01/domain-function-example-running-sum.html"&gt;Running Sum with DSum&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://rogersaccessblog.blogspot.com/2010/12/domain-function-examples-numbered-query.html"&gt;Numbered Query with DCount&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://rogersaccessblog.blogspot.com/2011/01/between-in-query.html"&gt;Rolling Average with DAvg and DCount &lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://rogersaccessblog.blogspot.com/2011/01/between-in-query.html"&gt;"Difference Between" with DLookup/DMax&lt;/a&gt;&lt;/li&gt;&lt;/ol&gt;Earlier, I looked at the Difference Between query. A similar problem is the BeginDate/EndDate query. Unlike a difference between, which subtracts the value of the previous record from the value of the current record to show the difference, the BeginDate/EndDate query infers the value of the EndDate from the value of the Effective Date in the next record. &lt;br /&gt;&lt;br /&gt;&lt;div&gt;&lt;/div&gt;For instance, suppose I have a PriceList table where the price for each product is in effect for only a certain date range. But since maintaining a Begin Date and End Date is prone to error, I'd like to simply store an Effective Date in the record. Conceptually, I don't really need both a Begin Date and End Date. A record is in effect from its own Effective Date to the Effective Date (minus 1) of the next row. In other words, consider figure 1below. In row 1, Product 1 is $3 from 1/1/2009 (EffectiveDate) to 12/1/2009 (Effective Date of row 2 minus 1). &lt;br /&gt;&lt;br /&gt;&lt;div&gt;&lt;/div&gt;So from this data:&lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://1.bp.blogspot.com/_JYohVQdgpwU/TU_chgsm9qI/AAAAAAAAAio/2UxuiJ-7I7c/s1600/BeginDateEndDateFigure1.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" h5="true" height="337" src="http://1.bp.blogspot.com/_JYohVQdgpwU/TU_chgsm9qI/AAAAAAAAAio/2UxuiJ-7I7c/s400/BeginDateEndDateFigure1.jpg" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div style="text-align: center;"&gt;Figure 1&lt;/div&gt;&lt;br /&gt;&lt;div&gt;&lt;/div&gt;I'd like to a create query which would produce following result:&lt;br /&gt;&lt;br /&gt;&lt;div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://2.bp.blogspot.com/_JYohVQdgpwU/TU_c9KNjEMI/AAAAAAAAAis/bngEW3cbNfc/s1600/BeginDateEndDateFigure2.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" h5="true" height="346" src="http://2.bp.blogspot.com/_JYohVQdgpwU/TU_c9KNjEMI/AAAAAAAAAis/bngEW3cbNfc/s400/BeginDateEndDateFigure2.jpg" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;div style="text-align: center;"&gt;Figure 2&lt;/div&gt;&lt;br /&gt;&lt;div&gt;&lt;/div&gt;The problem is that SQL does not have positional notation like Excel does. There's no way to simply point to the record following the one you're on. The only way to do it is to somehow identify the next record in terms of the data stored in the record.&lt;br /&gt;&lt;br /&gt;For this method to work, I must have a unique record ID. The Autonumber field is ideal for this. It doesn't matter if there are gaps in the sequence, but I have to sort on this field, so there cannot be duplicates and they must be in the order I need displayed. In the above sample, PriceID fits the bill.&lt;br /&gt;&lt;br /&gt;The technique is similar to creating a Difference Between Query, but instead of finding the difference between a field on this row and the same field on a previous row, I want to show the value of the field on the NEXT row in the current row.&lt;br /&gt;&lt;br /&gt;Over all, I need three steps:&lt;br /&gt;&lt;br /&gt;1. Identify the Primary Key of next row. &lt;br /&gt;2. Feed that value to a function (or query) that identifies the next date value, &lt;br /&gt;3. Display the next date value on the current record (manipulating as necessary)&lt;br /&gt;&lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;strong&gt;DMax Method&lt;/strong&gt;&lt;br /&gt;&lt;br /&gt;Domain Aggregate functions are an Access-only method to return statistical information about a specific set of records, whether from a table or query. They have three arguments: 1) an expression that identifies a field, 2) a string expression that identifies a domain (that is, the table or query), and 3) a Criteria, which is essentially an SQL Where clause without the word WHERE.&lt;br /&gt;&lt;br /&gt;Here are the steps:&lt;br /&gt;&lt;br /&gt;&lt;div&gt;&lt;/div&gt;I need a DMin function to return the PriceID of the next record in the table: &lt;br /&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;, Courier, monospace;"&gt;DMin("PriceID","PriceList","ProductID = " &amp;amp; [ProductID] &amp;amp; " And PriceID &amp;gt; " &amp;amp; [PriceID]&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Next, I'll feed that to a DLookup function, which will return the date value from the next row given number of records. &lt;br /&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;, Courier, monospace;"&gt;DLookUp("EffectiveDate","PriceList","PriceID = " &amp;amp; DMin("PriceID","PriceList","ProductID = " &amp;amp; [ProductID] &amp;amp; " And PriceID &amp;gt; " &amp;amp; [PriceID])) &lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Lastly, I'll subtract 1 from [EffectiveDate] of the current record and give the column an alias:&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;, Courier, monospace;"&gt;DateAdd("d",-1,DLookUp("EffectiveDate","PriceList","PriceID = " &amp;amp; Nz(DMin("PriceID","PriceList","ProductID = " &amp;amp; [ProductID] &amp;amp; " And PriceID &amp;gt; " &amp;amp; [PriceID]),0))) AS EndDate &lt;/span&gt;&lt;br /&gt;&lt;br /&gt;The full query, looks like this:&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;, Courier, monospace;"&gt;SELECT PriceID, ProductID, EffectiveDate AS BeginDate, DateAdd("d",-1,DLookUp("EffectiveDate","PriceList","PriceID = " &amp;amp; Nz(DMin("PriceID","PriceList","ProductID = " &amp;amp; [ProductID] &amp;amp; " And PriceID &amp;gt; " &amp;amp; [PriceID]),0))) AS EndDate, Price&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;, Courier, monospace;"&gt;FROM PriceList&lt;br /&gt;ORDER BY PriceID; &lt;/span&gt;&lt;br /&gt;&lt;br /&gt;The NZ() function is needed to prevent the last row of a group from displaying an ERROR.&lt;br /&gt;&lt;br /&gt;The Order By clause in the query is important. This will sort the query on the PriceID field. I'll need to have that order to use the criteria argument in the DMin. &lt;br /&gt;&lt;br /&gt;It is not necessary that the Order By field is an unbroken sequence. As long as that field has unique values and is sorted, it will work.&lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://1.bp.blogspot.com/_JYohVQdgpwU/TU_eeYJngHI/AAAAAAAAAi0/R2YNktgHRRw/s1600/BeginDateEndDateFigure3a.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" h5="true" height="341" src="http://1.bp.blogspot.com/_JYohVQdgpwU/TU_eeYJngHI/AAAAAAAAAi0/R2YNktgHRRw/s400/BeginDateEndDateFigure3a.jpg" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;Figure 3a: Shows the calculated EndDate with NULL for the end date of the last record in the group.&lt;br /&gt;&lt;br /&gt;This, of course, accurately represents the data because I don't know the end date of the currently effective price. Having a NULL in the field is most correct from a design standpoint. However, null values are difficult to query, so it is easier from a practical standpoint to put an actual value in the end date that is far in the future. I usually use 12/31/9999 for a future date that's not likely to become obsolete any time soon.&lt;br /&gt;&lt;br /&gt;I can easily accomplish this with another NZ function:&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;, Courier, monospace;"&gt;SELECT PriceID, ProductID, EffectiveDate AS BeginDate, Nz(DateAdd("d",-1,DLookUp("EffectiveDate","PriceList","PriceID = " &amp;amp; Nz(DMin("PriceID","PriceList","ProductID = " &amp;amp; [ProductID] &amp;amp; " And PriceID &amp;gt; " &amp;amp; [PriceID]),0))),#12/31/9999#) AS EndDate, Price&lt;/span&gt;&lt;br /&gt;&lt;div&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;, Courier, monospace;"&gt;&lt;/span&gt;&lt;/div&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;, Courier, monospace;"&gt;FROM PriceList&lt;/span&gt;&lt;br /&gt;&lt;div&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;, Courier, monospace;"&gt;&lt;/span&gt;&lt;/div&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;, Courier, monospace;"&gt;ORDER BY PriceID;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://1.bp.blogspot.com/_JYohVQdgpwU/TU_egGV-rFI/AAAAAAAAAi4/PrAWpTxXbLk/s1600/BeginDateEndDateFigure3b.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" h5="true" height="342" src="http://1.bp.blogspot.com/_JYohVQdgpwU/TU_egGV-rFI/AAAAAAAAAi4/PrAWpTxXbLk/s400/BeginDateEndDateFigure3b.jpg" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div style="border-bottom: medium none; border-left: medium none; border-right: medium none; border-top: medium none; text-align: left;"&gt;Figure 3b: Shows the calculated EndDate with an artificial end date far in the future in the last record of the group.&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;strong&gt;Subquery and Outer Join Methods&lt;/strong&gt;&lt;br /&gt;&lt;br /&gt;&lt;div&gt;&lt;/div&gt;This query can also be done with a correlated subquery or with an Outer Join, which I may discuss at a later date. However, you can find all three methods on my website in this sample: &lt;a href="http://www.rogersaccesslibrary.com/forum/begindateenddatequerymdb_topic555.html"&gt;BeginDateEndDateQuery.mdb&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;.&lt;br /&gt;&lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8134759511126815909-7340548956759466692?l=rogersaccessblog.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://rogersaccessblog.blogspot.com/feeds/7340548956759466692/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8134759511126815909&amp;postID=7340548956759466692' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8134759511126815909/posts/default/7340548956759466692'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8134759511126815909/posts/default/7340548956759466692'/><link rel='alternate' type='text/html' href='http://rogersaccessblog.blogspot.com/2011/02/begin-date-and-end-date-from-effective.html' title='Begin Date and End Date from Effective Date'/><author><name>Roger's Access Blog</name><uri>http://www.blogger.com/profile/12265719129831415014</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_JYohVQdgpwU/Scu5h2VvtBI/AAAAAAAAAQc/pr3JDoLaqnQ/S220/avi_Roger_1.JPG'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://1.bp.blogspot.com/_JYohVQdgpwU/TU_chgsm9qI/AAAAAAAAAio/2UxuiJ-7I7c/s72-c/BeginDateEndDateFigure1.jpg' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8134759511126815909.post-8324759252407025866</id><published>2011-02-01T12:25:00.001-05:00</published><updated>2011-02-01T12:26:23.395-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='New Samples'/><title type='text'>New Sample: Mode.mdb</title><content type='html'>by Roger Carlson&lt;br /&gt;&lt;br /&gt;Access has no Mode function, so you have to write one of your own. This sample database shows how to do that. &lt;br /&gt;&lt;br /&gt;You use the function much like the built-in Domain functions (DLookUp, DMax, and so on). That is, you must provide the 1) field name, 2) table name, and 3) a 'Where' Criteria. When used in an aggregate query (see below) you MUST add each field 'grouped by' into the Where Criteria. &lt;br /&gt;&lt;br /&gt;See this for more on Domain Aggregate functions see &lt;a href="http://rogersaccessblog.blogspot.com/2010/12/domain-functions-demystified.html"&gt;Domain Functions Demystified&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;You can find the sample here: &lt;a href="http://www.rogersaccesslibrary.com/forum/modemdb-intermediate_topic557.html"&gt;http://www.rogersaccesslibrary.com/forum/modemdb-intermediate_topic557.html&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8134759511126815909-8324759252407025866?l=rogersaccessblog.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://rogersaccessblog.blogspot.com/feeds/8324759252407025866/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8134759511126815909&amp;postID=8324759252407025866' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8134759511126815909/posts/default/8324759252407025866'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8134759511126815909/posts/default/8324759252407025866'/><link rel='alternate' type='text/html' href='http://rogersaccessblog.blogspot.com/2011/02/new-sample-modemdb.html' title='New Sample: Mode.mdb'/><author><name>Roger's Access Blog</name><uri>http://www.blogger.com/profile/12265719129831415014</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_JYohVQdgpwU/Scu5h2VvtBI/AAAAAAAAAQc/pr3JDoLaqnQ/S220/avi_Roger_1.JPG'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8134759511126815909.post-5088476006463908061</id><published>2011-01-28T13:14:00.000-05:00</published><updated>2011-01-28T13:14:00.260-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='New Samples'/><category scheme='http://www.blogger.com/atom/ns#' term='Tejpal'/><title type='text'>NewSample: Query_GrpSequentialsAndMissing</title><content type='html'>by AD Tejpal&lt;br /&gt;&lt;br /&gt;This sample db demonstrates identification of data blocks as well as missing portions in a sequential series. Three types of source data are covered:&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;(a) Number series.&lt;br /&gt;&lt;br /&gt;(b) Alpha-numeric series.&lt;br /&gt;&lt;br /&gt;(c) Date series.&lt;br /&gt;&lt;br /&gt;Subform on left shows all records, duly highlighting the start of each new block of sequential series. Subform at right depicts group-wise gist (for each PersonID) of sequential blocks as well as missing portions, duly indicating the start and end values for each set. Missing blocks are highlighted in light grey.&lt;br /&gt;&lt;br /&gt;Version: Access 2000 file format. &lt;br /&gt;&amp;nbsp; &lt;br /&gt;You can find the sample here: &lt;a href="http://www.rogersaccesslibrary.com/forum/query-grpsequentialsandmissing_topic556.html"&gt;http://www.rogersaccesslibrary.com/forum/query-grpsequentialsandmissing_topic556.html&lt;/a&gt; &lt;br /&gt;&amp;nbsp; &lt;br /&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8134759511126815909-5088476006463908061?l=rogersaccessblog.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://rogersaccessblog.blogspot.com/feeds/5088476006463908061/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8134759511126815909&amp;postID=5088476006463908061' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8134759511126815909/posts/default/5088476006463908061'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8134759511126815909/posts/default/5088476006463908061'/><link rel='alternate' type='text/html' href='http://rogersaccessblog.blogspot.com/2011/01/newsample-querygrpsequentialsandmissing.html' title='NewSample: Query_GrpSequentialsAndMissing'/><author><name>Roger's Access Blog</name><uri>http://www.blogger.com/profile/12265719129831415014</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_JYohVQdgpwU/Scu5h2VvtBI/AAAAAAAAAQc/pr3JDoLaqnQ/S220/avi_Roger_1.JPG'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8134759511126815909.post-6625962092465625497</id><published>2011-01-18T13:26:00.001-05:00</published><updated>2011-01-18T13:27:15.351-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='New Samples'/><category scheme='http://www.blogger.com/atom/ns#' term='Tejpal'/><title type='text'>New Sample: TableNormalizationByPureSQL</title><content type='html'>by A.D. Tejpal&lt;br /&gt;&lt;br /&gt;This sample db demonstrates pure query based solution for normalization of data held by a non-normalized table (T_Source) and posting the converted contents to destination table (T_Normalized).&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Steps:&lt;br /&gt;&lt;br /&gt;1 - Create the empty destination table T_Normalized with a structure identical to that of table T_Source, but without the non-normalized fields (e.g. Red, Green, Blue, Yellow in this sample).&lt;br /&gt;&lt;br /&gt;2 - Add two new fields to the newly created table T_Normalized. One meant for holding the names of non-normalized fields and the other for holding corresponding values. In the current sample, these two fields are named Color and Stock respectively.&lt;br /&gt;&lt;br /&gt;3 - Create an auxiliary table named T_SourceFieldsConverted having a single field meant for holding names of non-normalized fields. In the current sample, this field is named Color. Populate this table with the names of non-normalized fields (i.e. Red, Green, Blue, Yellow in this sample).&lt;br /&gt;&lt;br /&gt;4 - Execution of append query Q_AppNormalized will populate destination table T_Normalized with normalized data, duly converted from source table T_Source. This query is based upon Cartesian join between tables T_SourceFieldsConverted and T_Source.&lt;br /&gt;&lt;br /&gt;Version: Access 2000 file format. &lt;br /&gt;&amp;nbsp; &lt;br /&gt;You can find the sample here: &lt;a href="http://www.rogersaccesslibrary.com/forum/tablenormalizationbypuresql_topic554.html"&gt;http://www.rogersaccesslibrary.com/forum/tablenormalizationbypuresql_topic554.html&lt;/a&gt; &lt;br /&gt;&amp;nbsp; &lt;br /&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8134759511126815909-6625962092465625497?l=rogersaccessblog.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://rogersaccessblog.blogspot.com/feeds/6625962092465625497/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8134759511126815909&amp;postID=6625962092465625497' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8134759511126815909/posts/default/6625962092465625497'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8134759511126815909/posts/default/6625962092465625497'/><link rel='alternate' type='text/html' href='http://rogersaccessblog.blogspot.com/2011/01/new-sample-tablenormalizationbypuresql.html' title='New Sample: TableNormalizationByPureSQL'/><author><name>Roger's Access Blog</name><uri>http://www.blogger.com/profile/12265719129831415014</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_JYohVQdgpwU/Scu5h2VvtBI/AAAAAAAAAQc/pr3JDoLaqnQ/S220/avi_Roger_1.JPG'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8134759511126815909.post-3065704539256615383</id><published>2011-01-13T07:16:00.010-05:00</published><updated>2011-02-07T07:14:41.104-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Access 101'/><category scheme='http://www.blogger.com/atom/ns#' term='Domain Functions'/><title type='text'>Domain Function Example: Rolling Average in Query</title><content type='html'>&lt;span xmlns=""&gt;&lt;/span&gt;&lt;br /&gt;So far in this series on Domain Functions, I've discussed the general syntax (&lt;a href="http://rogersaccessblog.blogspot.com/2010/12/domain-functions-demystified.html"&gt;Domain Functions Demystified&lt;/a&gt;) and problems involved in building criteria expressions (&lt;a href="http://rogersaccessblog.blogspot.com/2010/12/domain-functions-demystified-criteria.html"&gt;Domain Functions Demystified: Criteria Expressions&lt;/a&gt;). Unfortunately, many of the examples I've given are relatively trivial. So for my next few blog posts, I thought I'd give what I consider truly useful applications of domain functions.&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;Other Examples:&lt;/strong&gt;&lt;br /&gt;&lt;ol&gt;&lt;li&gt;&lt;a href="http://rogersaccessblog.blogspot.com/2010/12/domain-function-example-simulate.html"&gt;Simulate AutoNumber with DMax&lt;/a&gt;&lt;span style="color: blue; text-decoration: underline;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://rogersaccessblog.blogspot.com/2011/01/domain-function-example-running-sum.html"&gt;Running Sum with DSum&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://rogersaccessblog.blogspot.com/2010/12/domain-function-examples-numbered-query.html"&gt;Numbered Query with DCount&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://rogersaccessblog.blogspot.com/2011/01/between-in-query.html"&gt;"Difference Between" with DLookup/DMax&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://begin%20date%20and%20end%20date%20from%20effective%20date/"&gt;Begin Date and End Date from Effective Date&lt;/a&gt; &lt;/li&gt;&lt;/ol&gt;Another value that is difficult to produce in a query, is the Rolling Average for a given number of records. &lt;br /&gt;&lt;br /&gt;For instance, suppose I wanted to display a rolling average for the last 12 weeks for the table below:&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://2.bp.blogspot.com/_JYohVQdgpwU/TS7tpwxmZtI/AAAAAAAAAiE/H62IUH9XDMc/s1600/RollingAverageFigure1.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="400" n4="true" src="http://2.bp.blogspot.com/_JYohVQdgpwU/TS7tpwxmZtI/AAAAAAAAAiE/H62IUH9XDMc/s400/RollingAverageFigure1.jpg" width="360" /&gt;&lt;/a&gt;&lt;/div&gt;Figure1&lt;br /&gt;&lt;br /&gt;For Week 26, I need to display the average for weeks 15-26 (39.85). For Week 25, it would be the average for weeks 14-25 (43.85), and so forth. For weeks with less than 12 in the recordset, it will average only those weeks available. So Week 9 would only average weeks 7-9 (53.67).&lt;br /&gt;&lt;br /&gt;In other words, this:&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://4.bp.blogspot.com/_JYohVQdgpwU/TS7t4AlASXI/AAAAAAAAAiI/rvf32Uy39hM/s1600/RollingAverageFigure2.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="373" n4="true" src="http://4.bp.blogspot.com/_JYohVQdgpwU/TS7t4AlASXI/AAAAAAAAAiI/rvf32Uy39hM/s400/RollingAverageFigure2.jpg" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;Figure 2&lt;br /&gt;&lt;br /&gt;The problem is that SQL does not have positional notation like Excel does. There's no way to simply point to the record above the one you're on -- or the previous 12, for that matter. The only way to do it is to somehow identify the previous records in terms of a Where condition. Since this Where condition must be evaluated for each line, O can do this with a domain aggregate function or a correlated subquery. In this case, two domain functions and two subqueries.&lt;br /&gt;&lt;br /&gt;For either method to work, I must have a unique record ID. The Autonumber field is ideal for this. It doesn't matter if there are gaps in the sequence, but I have to sort on this field, so there cannot be duplicates and they must be in the order I need displayed. In the above sample, ID fits the bill.&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;Domain Function Method (DCount and DAvg)&lt;/strong&gt;&lt;br /&gt;&lt;br /&gt;Domain Aggregate functions are an Access-only method to return statistical information about a specific set of records, whether from a table or query. DCount in particular will return the number of records in a given recordset. DAvg will return the average of a given recordset. Both functions have three arguments: 1) an expression that identifies a field, 2) a string expression that identifies a domain (that is, the table or query), and 3) a Criteria, which is essentially an SQL Where clause without the word WHERE.&lt;br /&gt;&lt;br /&gt;The first step in this process is to create an unbroken sequence number for the records. It must be unbroken so I can subtract 12 from it to average the correct number of weeks. The second step produces the average.&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;Step1: DCount_RollingAverage1:&lt;/strong&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family: Courier New;"&gt;SELECT DCount("ID","Table1","ID &amp;lt;=" &amp;amp; [ID]) AS Sequence, tWeek, tValue&lt;br /&gt;FROM Table1&lt;br /&gt;ORDER BY ID DESC;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;The Order By clause in the query is important. This will sort the query on the ID field. I'll need to have that order to use the criteria argument in the DCount. &lt;br /&gt;&lt;br /&gt;Here's how it works. &lt;br /&gt;&lt;br /&gt;For each record in the query, Access runs the DCount function. The DCount returns the number of records in the domain where the ID in the function is less than or equal to the ID in that record of the query.&lt;br /&gt;&lt;br /&gt;So in the first record, the ID is 1. So the DCount opens the domain (essentially opens the Customers table again) and it sees that there is only 1 record whose ID is less than or equal to 1. So it returns 1.&lt;br /&gt;&lt;br /&gt;Then it processes the second record. The ID of that record is 3, and the DCount function sees that there are only 2 records which have an ID whose value is less than or equal to 2. So it returns 2.&lt;br /&gt;&lt;br /&gt;It is not necessary that the Order By field is an unbroken sequence. As long as that field has unique values and is sorted, it will work.&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/_JYohVQdgpwU/TS7uGs-H4bI/AAAAAAAAAiM/65qSh171w-8/s1600/RollingAverageFigure3.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="400" n4="true" src="http://3.bp.blogspot.com/_JYohVQdgpwU/TS7uGs-H4bI/AAAAAAAAAiM/65qSh171w-8/s400/RollingAverageFigure3.jpg" width="293" /&gt;&lt;/a&gt;&lt;/div&gt;&amp;nbsp;Figure 3&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;Step2: DCount_RollingAverage2:&lt;/strong&gt;&lt;br /&gt;&lt;br /&gt;Now that DCount_RollingAverage1is a recordset with an unbroken sequence, I can use as it as the record source for the query that will create the rolling averages:&lt;br /&gt;&lt;br /&gt;&lt;div style="border-bottom: medium none; border-left: medium none; border-right: medium none; border-top: medium none;"&gt;&lt;span style="font-family: Courier New;"&gt;SELECT Sequence, tWeek, tValue, DAvg("tValue","[DCount_RollingAverage1]",&lt;br /&gt;"Sequence Between " &amp;amp; [Sequence] &amp;amp; " And " &amp;amp; [Sequence]-12) AS [12-Week Rolling Average]&lt;br /&gt;FROM DCount_RollingAverage1;&lt;/span&gt;&lt;/div&gt;&lt;div style="border-bottom: medium none; border-left: medium none; border-right: medium none; border-top: medium none;"&gt;&lt;br /&gt;&lt;/div&gt;For each record in the query, Access runs the DAvg function. The DAvg returns the average for the range of values between the sequence number and the sequence number minus 12.&lt;br /&gt;&lt;br /&gt;So in the first record, the &lt;span style="font-family: Courier New;"&gt;Sequence&lt;/span&gt; is 26. So the DAvg opens the domain (essentially opens Table1 again) and averages weeks 15-26. Then it processes the second record, averaging weeks 14-25 and so forth.&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://4.bp.blogspot.com/_JYohVQdgpwU/TS7uJPkqGFI/AAAAAAAAAiQ/XW19D2ibTbA/s1600/RollingAverageFigure4.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="400" n4="true" src="http://4.bp.blogspot.com/_JYohVQdgpwU/TS7uJPkqGFI/AAAAAAAAAiQ/XW19D2ibTbA/s400/RollingAverageFigure4.jpg" width="397" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div style="border-bottom: medium none; border-left: medium none; border-right: medium none; border-top: medium none;"&gt;Figure 4&lt;/div&gt;&lt;br /&gt;&lt;div style="border-bottom: medium none; border-left: medium none; border-right: medium none; border-top: medium none;"&gt;&lt;strong&gt;Subquery Method&lt;/strong&gt;&lt;/div&gt;&lt;div style="border-bottom: medium none; border-left: medium none; border-right: medium none; border-top: medium none;"&gt;There is no way to combine these two queries into one. To do that, I'd need to use a correlated subquery, which I may discuss at a later date. However, you can find both methods on my website in this sample: &lt;a href="http://www.rogersaccesslibrary.com/forum/rollingaveragesmdb_topic544.html"&gt;RollingAverages.mdb&lt;/a&gt;.&lt;/span&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8134759511126815909-3065704539256615383?l=rogersaccessblog.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://rogersaccessblog.blogspot.com/feeds/3065704539256615383/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8134759511126815909&amp;postID=3065704539256615383' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8134759511126815909/posts/default/3065704539256615383'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8134759511126815909/posts/default/3065704539256615383'/><link rel='alternate' type='text/html' href='http://rogersaccessblog.blogspot.com/2011/01/domain-function-example-rolling-average.html' title='Domain Function Example: Rolling Average in Query'/><author><name>Roger's Access Blog</name><uri>http://www.blogger.com/profile/12265719129831415014</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_JYohVQdgpwU/Scu5h2VvtBI/AAAAAAAAAQc/pr3JDoLaqnQ/S220/avi_Roger_1.JPG'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://2.bp.blogspot.com/_JYohVQdgpwU/TS7tpwxmZtI/AAAAAAAAAiE/H62IUH9XDMc/s72-c/RollingAverageFigure1.jpg' height='72' width='72'/><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8134759511126815909.post-4342067385658724738</id><published>2011-01-10T06:46:00.041-05:00</published><updated>2011-02-07T07:18:23.052-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Access 101'/><category scheme='http://www.blogger.com/atom/ns#' term='Domain Functions'/><title type='text'>Domain Function Example: "Difference Between" in Query</title><content type='html'>&lt;span xmlns=""&gt;&lt;/span&gt;&lt;br /&gt;So far in this series on Domain Functions, I've discussed the general syntax (&lt;a href="http://rogersaccessblog.blogspot.com/2010/12/domain-functions-demystified.html"&gt;Domain Functions Demystified&lt;/a&gt;) and problems involved in building criteria expressions (&lt;a href="http://rogersaccessblog.blogspot.com/2010/12/domain-functions-demystified-criteria.html"&gt;Domain Functions Demystified: Criteria Expressions&lt;/a&gt;). Unfortunately, many of the examples I've given are relatively trivial. So for my next few blog posts, I thought I'd give what I consider truly useful applications of domain functions.&lt;br /&gt;&lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;strong&gt;Other Examples&lt;/strong&gt;:&lt;br /&gt;&lt;ol&gt;&lt;li&gt;&lt;a href="http://rogersaccessblog.blogspot.com/2010/12/domain-function-example-simulate.html"&gt;Simulate AutoNumber with DMax&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://rogersaccessblog.blogspot.com/2011/01/domain-function-example-running-sum.html"&gt;Running Sum with DSum&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://rogersaccessblog.blogspot.com/2010/12/domain-function-examples-numbered-query.html"&gt;Numbered Query with DCount&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://rogersaccessblog.blogspot.com/2011/01/domain-function-example-rolling-average.html"&gt;Rolling Average with DAvg and DCount&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://rogersaccessblog.blogspot.com/2011/02/begin-date-and-end-date-from-effective.html"&gt;Begin Date and End Date from Effective Date&lt;/a&gt; &lt;/li&gt;&lt;/ol&gt;Earlier, I looked at the &lt;a href="http://rogersaccessblog.blogspot.com/2011/01/domain-function-example-running-sum.html"&gt;Running Sum&lt;/a&gt; query. A similar (but opposite) problem is the Difference Between query. Unlike a running sum, which adds the value of a field in a record to the value of the same field in the previous record, the "difference between" query subtracts the value of the previous record from the value of the current record to show the difference. &lt;br /&gt;&lt;br /&gt;&lt;div&gt;&lt;/div&gt;For instance, suppose I wanted to display the difference in days between orders in the table below:&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&amp;nbsp;&lt;a href="http://4.bp.blogspot.com/_JYohVQdgpwU/TTQzXY20TyI/AAAAAAAAAiU/Ui11akaiiYo/s1600/DifferenceBetweenFigure1.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="196" n4="true" src="http://4.bp.blogspot.com/_JYohVQdgpwU/TTQzXY20TyI/AAAAAAAAAiU/Ui11akaiiYo/s400/DifferenceBetweenFigure1.jpg" width="400" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;div style="border-bottom: medium none; border-left: medium none; border-right: medium none; border-top: medium none;"&gt;Figure1: Need to calculate the difference between records.&lt;/div&gt;&lt;br /&gt;&lt;div&gt;&lt;/div&gt;The difference in days between records 1 and 2 is 4, between 2 and 3 is 7, between 3 and 4 is -22, and so forth.&lt;br /&gt;&lt;br /&gt;&lt;div&gt;&lt;/div&gt;The problem is that SQL does not have positional notation like Excel does. There's no way to simply point to the record above the one you're on. The only way to do it is to somehow identify the previous record in terms of a Where condition. Since this Where condition must be evaluated for each line, I can do this with a domain aggregate functions (DMax &amp;amp; DLookup).&lt;br /&gt;&lt;br /&gt;&lt;div&gt;&lt;/div&gt;For this method to work, I must have a unique record ID. The Autonumber field is ideal for this. It doesn't matter if there are gaps in the sequence, but I have to sort on this field, so there cannot be duplicates and they must be in the order I need displayed. In the above sample, OrderDetailsID fits the bill.&lt;br /&gt;&lt;br /&gt;&lt;div&gt;&lt;/div&gt;The technique is similar to creating a &lt;a href="http://www.rogersaccesslibrary.com/forum/topic309.html"&gt;Numbered Query&lt;/a&gt; or a &lt;a href="http://www.rogersaccesslibrary.com/forum/topic279.html"&gt;Running Sum in a query&lt;/a&gt;, but instead of just counting or summing &lt;em&gt;all&lt;/em&gt; the records above the current record, I have to find just the &lt;em&gt;previous&lt;/em&gt; record. This adds an additional complication, which requires an additional domain function.&lt;br /&gt;&lt;br /&gt;&lt;div&gt;&lt;/div&gt;Specifically, I need three steps:&lt;br /&gt;&lt;ol&gt;&lt;li&gt;Use a domain function (DMax)&amp;nbsp;to find the unique&amp;nbsp;identifier the previous row. &lt;/li&gt;&lt;li&gt;Then feed that value in to another domain function (DLookup)&amp;nbsp;that identifies the previous value, &lt;/li&gt;&lt;li&gt;And then subtract the previous value from the current value.&lt;/li&gt;&lt;/ol&gt;One way to show the difference between records is over the whole recordset. Later, I'll look at showing the difference between over groups of records.&lt;br /&gt;&lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;span style="font-size: 12pt;"&gt;&lt;strong&gt;Difference Between - Over All&lt;/strong&gt;&lt;/span&gt;&lt;br /&gt;&lt;a href="http://rogersaccessblog.blogspot.com/2010/12/domain-functions-demystified.html"&gt;Domain Aggregate functions&lt;/a&gt; are an Access-only method to return statistical information about a specific set of records, whether from a table or query. They have three arguments: 1) an expression that identifies a field, 2) a string expression that identifies a domain (that is, the table or query), and 3) a Criteria, which is essentially an SQL Where clause without the word WHERE.&lt;br /&gt;&lt;br /&gt;&lt;div&gt;&lt;/div&gt;Here are the steps:&lt;br /&gt;&lt;ol&gt;&lt;li&gt;I need a DMax function to return the OrderDetailID of the previous record in the table: &lt;div&gt;&lt;span style="font-family: Courier New;"&gt;DMax("OrderDetailID","tblOrderDetails","OrderDetailID &amp;lt; " &amp;amp; [OrderDetailID])&lt;/span&gt;&lt;/div&gt;&lt;/li&gt;&lt;li&gt;Next, I'll feed that to a DLookup function, which will return the date value from the previous row given number of records. &lt;div&gt;&lt;span style="font-family: Courier New;"&gt;DLookUp("OrderDate","tblOrderDetails","OrderDetailID = " &amp;amp; DMax("OrderDetailID","tblOrderDetails","OrderDetailID &amp;lt; " &amp;amp; [OrderDetailID]))&lt;/span&gt;&lt;/div&gt;&lt;/li&gt;&lt;li&gt;&lt;span style="font-family: Courier New;"&gt;&lt;span style="font-family: Arial, Helvetica, sans-serif;"&gt;Lastly, I'll subtract the this value from [OrderDate]of the current record and give the column an alias:&lt;/span&gt;[OrderDate]-DLookUp("OrderDate","tblOrderDetails","OrderDetailID = " &amp;amp; DMax("OrderDetailID","tblOrderDetails","OrderDetailID &amp;lt; " &amp;amp; [OrderDetailID])) AS DaysBetween&lt;/span&gt;&lt;/li&gt;&lt;/ol&gt;&lt;span style="font-size: 12pt;"&gt;The full query, looks like this:&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;span style="font-family: Courier New;"&gt;SELECT tblOrderDetails.OrderDetailID, tblOrderDetails.OrderID, tblOrderDetails.OrderDate, nz([OrderDate]-DLookUp("OrderDate","tblOrderDetails","OrderDetailID = " &amp;amp; nz(DMax("OrderDetailID","tblOrderDetails","OrderDetailID &amp;lt; " &amp;amp; [OrderDetailID]),0)),0) AS DaysBetween &lt;/span&gt;&lt;br /&gt;&lt;div&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;, Courier, monospace;"&gt;FROM tblOrderDetails&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;, Courier, monospace;"&gt;ORDER BY tblOrderDetails.OrderDetailID;&lt;/span&gt;&lt;/div&gt;&lt;/font&gt;&lt;br /&gt;&lt;div&gt;&lt;/div&gt;The two NZ() functions are needed to display a zero on the first line. Otherwise, it would return an ERROR.&lt;br /&gt;&lt;br /&gt;&lt;div&gt;&lt;/div&gt;The Order By clause in the query is important. This will sort the query on the OrderDetailID field. I'll need to have that order to use the criteria argument in the DMax. &lt;br /&gt;&lt;br /&gt;&lt;div&gt;&lt;/div&gt;It is not necessary that the Order By field is an unbroken sequence. As long as that field has unique values and is sorted, it will work.&lt;br /&gt;&lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://4.bp.blogspot.com/_JYohVQdgpwU/TTQ0mqZeN4I/AAAAAAAAAiY/yOku1he8GJM/s1600/DifferenceBetweenFigure2.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="220" n4="true" src="http://4.bp.blogspot.com/_JYohVQdgpwU/TTQ0mqZeN4I/AAAAAAAAAiY/yOku1he8GJM/s400/DifferenceBetweenFigure2.jpg" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;Figure 2: Shows the time difference in days between subsequent records.&lt;br /&gt;&lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;strong&gt;Difference Between - Over Group&lt;/strong&gt;&lt;br /&gt;&lt;strong&gt;&amp;nbsp;&lt;/strong&gt;&lt;span style="font-family: Courier New;"&gt;SELECT tblOrderDetails.OrderDetailID, tblOrderDetails.OrderID, tblOrderDetails.OrderDate, &lt;/span&gt;&lt;br /&gt;&lt;div&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;, Courier, monospace;"&gt;Nz([OrderDate]-DLookUp("OrderDate","tblOrderDetails","OrderDetailID = " &amp;amp; Nz(DMax("OrderDetailID","tblOrderDetails","OrderID = " &amp;amp; [OrderID] &amp;amp; " And OrderDetailID &amp;lt; " &amp;amp; [OrderDetailID]),0)),0) AS DaysBetween&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;, Courier, monospace;"&gt;FROM tblOrderDetails&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;, Courier, monospace;"&gt;ORDER BY tblOrderDetails.OrderDetailID;&lt;/span&gt;&lt;/div&gt;&lt;/font&gt;&lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://4.bp.blogspot.com/_JYohVQdgpwU/TTQ1J-Y6YEI/AAAAAAAAAic/seudKU6CLqE/s1600/DifferenceBetweenFigure3.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="240" n4="true" src="http://4.bp.blogspot.com/_JYohVQdgpwU/TTQ1J-Y6YEI/AAAAAAAAAic/seudKU6CLqE/s400/DifferenceBetweenFigure3.jpg" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;Figure 3: As the OrderID changes, the DaysBetween resets to zero.&lt;br /&gt;&lt;br /&gt;&lt;div&gt;&lt;/div&gt;The only difference between this query and the OverAll query is the addition of &lt;br /&gt;&lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;span style="font-family: Courier New;"&gt;"OrderID = " &amp;amp; [OrderID]&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;span style="font-family: Times New Roman;"&gt;to the "Where" condition of the DMax function.&lt;/span&gt; This sets the value of the first record of each group to zero.&lt;br /&gt;&lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;strong&gt;Subquery Method&lt;/strong&gt;&lt;br /&gt;&lt;br /&gt;&lt;div&gt;&lt;/div&gt;As I said, these can also be done with a correlated subquery, which I may discuss at a later date. However, you can find both methods on my website in this sample: &lt;a href="http://www.rogersaccesslibrary.com/forum/topic445.html"&gt;DaysBetween.mdb&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8134759511126815909-4342067385658724738?l=rogersaccessblog.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://rogersaccessblog.blogspot.com/feeds/4342067385658724738/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8134759511126815909&amp;postID=4342067385658724738' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8134759511126815909/posts/default/4342067385658724738'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8134759511126815909/posts/default/4342067385658724738'/><link rel='alternate' type='text/html' href='http://rogersaccessblog.blogspot.com/2011/01/between-in-query.html' title='Domain Function Example: &amp;quot;Difference Between&amp;quot; in Query'/><author><name>Roger's Access Blog</name><uri>http://www.blogger.com/profile/12265719129831415014</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_JYohVQdgpwU/Scu5h2VvtBI/AAAAAAAAAQc/pr3JDoLaqnQ/S220/avi_Roger_1.JPG'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://4.bp.blogspot.com/_JYohVQdgpwU/TTQzXY20TyI/AAAAAAAAAiU/Ui11akaiiYo/s72-c/DifferenceBetweenFigure1.jpg' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8134759511126815909.post-3399627805354284149</id><published>2011-01-03T06:44:00.011-05:00</published><updated>2011-02-07T07:19:52.472-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Access 101'/><category scheme='http://www.blogger.com/atom/ns#' term='Domain Functions'/><title type='text'>Domain Function Example: Running Sum with DSum</title><content type='html'>&lt;span xmlns=""&gt;&lt;/span&gt;&lt;br /&gt;So far in this series on Domain Functions, I've discussed the general syntax (&lt;a href="http://rogersaccessblog.blogspot.com/2010/12/domain-functions-demystified.html"&gt;Domain Functions Demystified&lt;/a&gt;) and problems involved in building criteria expressions (&lt;a href="http://rogersaccessblog.blogspot.com/2010/12/domain-functions-demystified-criteria.html"&gt;Domain Functions Demystified: Criteria Expressions&lt;/a&gt;). Unfortunately, many of the examples I've given are relatively trivial. So for my next few blog posts, I thought I'd give what I consider truly useful applications of domain functions.&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;&lt;br /&gt;&lt;span style="font-size: large;"&gt;Other Examples&lt;/span&gt;&lt;/strong&gt;&lt;br /&gt;&lt;ol&gt;&lt;li&gt;&lt;a href="http://rogersaccessblog.blogspot.com/2010/12/domain-function-example-simulate.html"&gt;Simulate AutoNumber with DMax&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://rogersaccessblog.blogspot.com/2010/12/domain-function-examples-numbered-query.html"&gt;Numbered Query with DCount&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://rogersaccessblog.blogspot.com/2011/01/between-in-query.html"&gt;Difference Between with DMax&lt;/a&gt; &lt;/li&gt;&lt;li&gt;&lt;a href="http://www.blogger.com/Domain%20Function%20Example:%20Rolling%20Average%20in%20Query"&gt;Rolling Average with DAvg and DCount&lt;/a&gt; &lt;/li&gt;&lt;li&gt;&lt;a href="http://rogersaccessblog.blogspot.com/2011/02/begin-date-and-end-date-from-effective.html"&gt;Begin Date and End Date from Effective Date &lt;/a&gt;&lt;/li&gt;&lt;/ol&gt;&lt;h3&gt;Running Sum&lt;/h3&gt;One thing that is easy to do in a report, but difficult to produce in a query, is a Running Sum. A running sum adds the value of a field in a record to the value of the same field in the previous record. &lt;br /&gt;&lt;br /&gt;In a report, there are two common types of running sums: Over All and Over Group. To create a running over all, I place a textbox control on the details section, setting the Control Source property to the field I want summed and setting the Running Sum property to Over, as in Figure 1. Figure 2 shows the results. To create one over a group (say each Order number), I'd set the Running Sum property to Over Group&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://4.bp.blogspot.com/_JYohVQdgpwU/TSG28n9LjcI/AAAAAAAAAhk/DIs-KVIR_NY/s1600/RunningSumFigure1.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="244" n4="true" src="http://4.bp.blogspot.com/_JYohVQdgpwU/TSG28n9LjcI/AAAAAAAAAhk/DIs-KVIR_NY/s320/RunningSumFigure1.jpg" width="320" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div style="text-align: center;"&gt;Figure 1&lt;/div&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://1.bp.blogspot.com/_JYohVQdgpwU/TSG3G3OGzEI/AAAAAAAAAho/Gi0kqRf82RQ/s1600/RunningSumFigure2.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="256" n4="true" src="http://1.bp.blogspot.com/_JYohVQdgpwU/TSG3G3OGzEI/AAAAAAAAAho/Gi0kqRf82RQ/s400/RunningSumFigure2.jpg" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div style="text-align: center;"&gt;Figure 2&lt;/div&gt;&lt;br /&gt;But it's not so easy in a query. The problem is that SQL does not have positional notation like Excel does. There's no way to simply point to the record above the one you're on. The only way to do it is to somehow identify the previous record in terms of a Where condition. Since this Where condition must be evaluated for each line, I can do this with a domain aggregate function (DSum) is ideal.&lt;br /&gt;&lt;br /&gt;For this method to work, I must have a unique record ID. The Autonumber field is ideal for this. It doesn't matter if there are gaps in the sequence, but I have to sort on this field, so there cannot be duplicates and they must be in the order I need displayed. In the above sample, OrderDetailsID fits the bill.&lt;br /&gt;&lt;br /&gt;There are two different ways I might want to see the running sum: &lt;br /&gt;&lt;ol&gt;&lt;li&gt;&lt;span id="goog_1414398187"&gt;&lt;/span&gt;Over the whole list (see Figure 3)&lt;/li&gt;&lt;li&gt;Over each group, where the running sum resets to zero as the Order ID changes (see Figure 5).&lt;/li&gt;&lt;/ol&gt;&lt;strong&gt;Running Sum Over All&lt;/strong&gt;&lt;br /&gt;&lt;br /&gt;There are two methods to create a running sum: DSum domain aggregate function and a correlated subquery. I'm going to concentrate on the DSum method here.&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;DSum&lt;/strong&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family: Courier New;"&gt;SELECT OrderDetailID, OrderID, ProductID, Price, &lt;br /&gt;DSum("Price","tblOrderDetails","OrderDetailID &amp;lt;=" &amp;amp; [OrderDetailID]) AS RunningSum&lt;br /&gt;FROM tblOrderDetails;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;The DSum function works much the same as the DCount in the &lt;a href="http://rogersaccessblog.blogspot.com/2010/12/domain-function-examples-numbered-query.html"&gt;Numbered Query example&lt;/a&gt;, but instead of counting the records, it sums them. Unfortunately, the DSum does not return a formatted number. &lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://1.bp.blogspot.com/_JYohVQdgpwU/TSG37hWuUsI/AAAAAAAAAhs/VNnbCPVPqDg/s1600/RunningSumFigure3.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="195" n4="true" src="http://1.bp.blogspot.com/_JYohVQdgpwU/TSG37hWuUsI/AAAAAAAAAhs/VNnbCPVPqDg/s400/RunningSumFigure3.jpg" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div style="border-bottom: medium none; border-left: medium none; border-right: medium none; border-top: medium none; text-align: center;"&gt;Figure 3&lt;/div&gt;&lt;div style="border-bottom: medium none; border-left: medium none; border-right: medium none; border-top: medium none;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style="border-bottom: medium none; border-left: medium none; border-right: medium none; border-top: medium none;"&gt;So if you are summing a currency field, you'll have to apply the formatting yourself. To do that, we can modify the DSum function adding the Format function to display the number as currency. Like so:&lt;/div&gt;&lt;div style="border-bottom: medium none; border-left: medium none; border-right: medium none; border-top: medium none;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style="border-bottom: medium none; border-left: medium none; border-right: medium none; border-top: medium none;"&gt;&lt;span style="font-family: Courier New;"&gt;Format(DSum("Price","tblOrderDetails","OrderDetailID &amp;lt;=" &amp;amp; [OrderDetailID]), "Currency") AS RunningSumFormatted&lt;/span&gt;&lt;/div&gt;&lt;div style="border-bottom: medium none; border-left: medium none; border-right: medium none; border-top: medium none;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://4.bp.blogspot.com/_JYohVQdgpwU/TSG4IpZKfuI/AAAAAAAAAhw/2h1sasUUPSc/s1600/RunningSumFigure4.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="171" n4="true" src="http://4.bp.blogspot.com/_JYohVQdgpwU/TSG4IpZKfuI/AAAAAAAAAhw/2h1sasUUPSc/s400/RunningSumFigure4.jpg" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div style="border-bottom: medium none; border-left: medium none; border-right: medium none; border-top: medium none; text-align: center;"&gt;Figure 4&lt;/div&gt;&lt;br /&gt;&lt;strong&gt;Running Sum Over Group&lt;/strong&gt;&lt;br /&gt;&lt;br /&gt;In order to get the running sum for each grouping of OrderID, all I need to do is add another condition to the "Where" argument of the DSum:&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family: Courier New;"&gt;DSum("Price","tblOrderDetails","OrderID = " &amp;amp; [OrderID] &amp;amp; " And OrderDetailID &amp;lt;=" &amp;amp; [OrderDetailID]) AS RunningSum&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;In this case, "OrderID = " &amp;amp; [OrderID]&lt;br /&gt;&lt;br /&gt;The complete SQL statement (including formatting):&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family: Courier New;"&gt;SELECT OrderDetailID, OrderID, ProductID, Price, &lt;br /&gt;Format(DSum("Price","tblOrderDetails","OrderID = " &amp;amp; [OrderID] &amp;amp; " And OrderDetailID &amp;lt;=" &amp;amp; [OrderDetailID]),"Currency") AS RunningSum&lt;br /&gt;FROM tblOrderDetails;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://4.bp.blogspot.com/_JYohVQdgpwU/TSG5ANtVuFI/AAAAAAAAAh0/T9gLrmpJRAQ/s1600/RunningSumFigure5.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="175" n4="true" src="http://4.bp.blogspot.com/_JYohVQdgpwU/TSG5ANtVuFI/AAAAAAAAAh0/T9gLrmpJRAQ/s400/RunningSumFigure5.jpg" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;div style="text-align: center;"&gt;Figure 5&lt;/div&gt;&lt;br /&gt;&lt;strong&gt;Subquery Method&lt;/strong&gt;&lt;br /&gt;&lt;br /&gt;Both types of running sum can also be done with correlated subqueries. I may discuss this at a later date. However, you can find both methods on my website, in this sample: &lt;a href="http://www.rogersaccesslibrary.com/forum/topic279.html"&gt;RunningSumInQuery.mdb&lt;/a&gt;.&lt;br /&gt;&lt;div style="text-align: center;"&gt;.&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8134759511126815909-3399627805354284149?l=rogersaccessblog.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://rogersaccessblog.blogspot.com/feeds/3399627805354284149/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8134759511126815909&amp;postID=3399627805354284149' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8134759511126815909/posts/default/3399627805354284149'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8134759511126815909/posts/default/3399627805354284149'/><link rel='alternate' type='text/html' href='http://rogersaccessblog.blogspot.com/2011/01/domain-function-example-running-sum.html' title='Domain Function Example: Running Sum with DSum'/><author><name>Roger's Access Blog</name><uri>http://www.blogger.com/profile/12265719129831415014</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_JYohVQdgpwU/Scu5h2VvtBI/AAAAAAAAAQc/pr3JDoLaqnQ/S220/avi_Roger_1.JPG'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://4.bp.blogspot.com/_JYohVQdgpwU/TSG28n9LjcI/AAAAAAAAAhk/DIs-KVIR_NY/s72-c/RunningSumFigure1.jpg' height='72' width='72'/><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8134759511126815909.post-5031761724407841312</id><published>2010-12-29T06:57:00.002-05:00</published><updated>2010-12-29T06:59:55.118-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='New Samples'/><category scheme='http://www.blogger.com/atom/ns#' term='Tejpal'/><title type='text'>New Sample: Form_MtoMViaCheckBoxArray</title><content type='html'>by AD Tejpal&lt;br /&gt;&lt;br /&gt;This sample db demonstrates use of check box array for populating a junction table serving many to many relationship between clients and training courses. Data is stored in normalized manner in tables T_Clients, T_Courses and T_ClientCourses. No temporary table is needed.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Two styles of user interface are demonstrated:&lt;br /&gt;&lt;br /&gt;1 - Normal Style (Using Virtual Records Matrix): &lt;br /&gt;-------------------------------------------------&lt;br /&gt;&lt;br /&gt;1.1 - Client particulars are displayed in the parent form while all available course options are displayed in a subform, as a matrix of virtual new records. &lt;br /&gt;&lt;br /&gt;1.2 - As and when the user clicks a check box into selected state against desired course in the subform, the row in question gets converted into a freshly entered actual record in the junction table. &lt;br /&gt;&lt;br /&gt;1.3 - Similarly, if a check box is clicked into a de-selected state, corresponding record in junction table gets deleted. This is accompanied by a virtual new record getting displayed in lieu of the recently deleted actual record (At all times, the subform matrix continues to display all available course options).&lt;br /&gt;&lt;br /&gt;1.4 - Total number of courses opted for current client as well a
