<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Cypris&#039; lookout &#187; SQL Server</title>
	<atom:link href="http://blog.nkadesign.com/category/programming/database/sql-server/feed/" rel="self" type="application/rss+xml" />
	<link>http://blog.nkadesign.com</link>
	<description>Renaud Bompuis on the interwebs!</description>
	<lastBuildDate>Sat, 14 Jan 2012 06:30:34 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.3.1</generator>
		<item>
		<title>Access: Run-time Error 3155 ODBC insert on a linked table failed</title>
		<link>http://blog.nkadesign.com/2009/access-run-time-error-3155-odbc-insert-on-a-linked-table-failed/</link>
		<comments>http://blog.nkadesign.com/2009/access-run-time-error-3155-odbc-insert-on-a-linked-table-failed/#comments</comments>
		<pubDate>Thu, 11 Jun 2009 08:26:06 +0000</pubDate>
		<dc:creator>Renaud Bompuis</dc:creator>
				<category><![CDATA[Database]]></category>
		<category><![CDATA[MSAccess]]></category>
		<category><![CDATA[Programming]]></category>
		<category><![CDATA[SQL Server]]></category>

		<guid isPermaLink="false">http://blog.nkadesign.com/?p=606</guid>
		<description><![CDATA[I have been spending a lot of time trying to find out why some of the code used to insert new records into a linked SQL Server table would systematically fail with an error: Run-time Error '3155' ODBC--insert on a linked table failed It was driving me mad. I could insert a simple record using [...]]]></description>
			<content:encoded><![CDATA[<p><img src="/wp-content/uploads/access.png" alt="Microsoft Access" title="Microsoft Access" align="left" width="64" height="64" hspace="5" vspace="5" border="0" />
I have been spending a lot of time trying to find out why some of the code used to insert new records into a linked SQL Server table would systematically fail with an error:</p>

<pre><code>Run-time Error '3155' ODBC--insert on a linked table  failed
</code></pre>

<p>It was driving me mad.<br />
I could insert a simple record using <em>SQL Server Management Studio</em>, I could add new records to the table in datasheet mode within Access, but as soon as I tried to insert a record from code, whether using DAO recordset or executing the same SQL INSERT, it would miserably fail.</p>

<p>After a fair bit of investigation and tests, of which you can read the full account on <a href="http://stackoverflow.com/questions/979269/inserting-null-in-an-nvarchar-fails-in-msaccess/">the question I asked on StackOverflow</a>, it turns out that this is a long-standing bug in the ODBC Driver (or Access).</p>

<p><code>Memo</code> fields in Access are usually translated into <code>nvarchar(MAX)</code> in SQL Server by tools like <a href="/2009/ms-access-upsizing-to-sql-server-2008/">SSMA</a>.<br />
Unfortunately, when you link tables having these fields using the <em>SQL Server Client</em> driver, these fields get incorrectly interpreted as <code>string</code>, even though they appear ok from the table design view.<br />
It&#8217;s only if you try to insert something into the field, either text larger than 255 chars or NULL, that you get the error message.</p>

<p>So, the solution, at least in this case, is to revert to the older <em>SQL Server</em> ODBC driver instead, or use <code>varchar()</code> instead of <code>nvarchar()</code>, but if you&#8217;re dealing with Unicode, you have to stick with <code>nvarchar()</code>.</p>

<h3>References</h3>

<ul>
<li>My question <a href="http://stackoverflow.com/questions/979269/inserting-null-in-an-nvarchar-fails-in-msaccess/">Inserting NULL in an nvarchar fails in MSAccess</a> on <a href="http://stackoverflow.com/">StackOverflow</a>.</li>
<li>A reference I found on this issue: <a href="http://social.msdn.microsoft.com/Forums/en-US/sqldataaccess/thread/c6d2466e-ecb5-4a98-963f-ae827dbf8caa">Ms Access linking table with nvarchar(max)</a>.</li>
</ul>
]]></content:encoded>
			<wfw:commentRss>http://blog.nkadesign.com/2009/access-run-time-error-3155-odbc-insert-on-a-linked-table-failed/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Access vs SQL Server: some stats (part 1)</title>
		<link>http://blog.nkadesign.com/2009/access-vs-sql-server-some-stats-part-1/</link>
		<comments>http://blog.nkadesign.com/2009/access-vs-sql-server-some-stats-part-1/#comments</comments>
		<pubDate>Mon, 13 Apr 2009 04:00:53 +0000</pubDate>
		<dc:creator>Renaud Bompuis</dc:creator>
				<category><![CDATA[Database]]></category>
		<category><![CDATA[MSAccess]]></category>
		<category><![CDATA[SQL Server]]></category>

		<guid isPermaLink="false">http://blog.nkadesign.com/?p=527</guid>
		<description><![CDATA[In the perspective of upsizing my current Access 2007 application, I have been trying to understand a bit more about the possible performance impact of various choices of Primary Keys. My problem is that currently, the Access application uses autoincrement numbers as surrogate Primary Keys (PK). Since I will need to synchronise the data over [...]]]></description>
			<content:encoded><![CDATA[<p><img src="/wp-content/uploads/access.png" alt="Microsoft Access" title="Microsoft Access" align="left" width="64" height="64" hspace="5" vspace="5" border="0" />
In the perspective of upsizing my current Access 2007 application, I have been trying to understand a bit more about the possible performance impact of various choices of Primary Keys.</p>

<p>My problem is that currently, the Access application uses autoincrement numbers as surrogate Primary Keys (PK).
Since I will need to synchronise the data over multiple remote sites, including occasionally disconnected clients, I can&#8217;t use the current autoincrement PK and will need to change to GUID.</p>

<p>To see for myself what could be the impact, I made a series of benchmarks.<br />
This first part is fairly simple:</p>

<ul>
<li>Populate a Product table that contains 3 fields: <code>ID</code>, <code>SKU</code> and <code>Designation</code> with 1,000,000 records.</li>
<li>Test natively on SQL Server and Access 2007.</li>
<li>The records are inserted in transactions batches of 1000 records.</li>
<li>I collect the time taken for each of these transactions and plot it.</li>
</ul>

<h3>Test setup</h3>

<p>Nothing much to say about that:</p>

<blockquote>
  <p>All tests are performed on a dedicated <em>Windows Server 2008 x64</em> rack running <em>Access 2007</em> and <em>SQL Server 2008 Standard (SP1) x64</em>.</p>
</blockquote>

<h3>Test database</h3>

<p>In SQL Server, we created a database with two tables <code>ProductGUID</code> and <code>ProductInt</code>:</p>

<p><textarea name="code" class="sql:nogutter" cols="60" rows="10">
CREATE TABLE ProductGUID (
	ID UNIQUEIDENTIFIER DEFAULT NEWSEQUENTIALID(),
	SKU NVARCHAR(32) NOT NULL,
	Description NVARCHAR(255) NULL
);
CREATE CLUSTERED INDEX ProdGUIDix ON ProductGUID(ID);
GO

CREATE TABLE ProductINT (
	ID INT IDENTITY(1,1),
	SKU NVARCHAR(32) NOT NULL,
	Description NVARCHAR(255) NULL
);
CREATE CLUSTERED INDEX ProdINTix ON ProductINT(ID);
GO
</textarea></p>

<p>For the table using a GUID, we use the <code>NewSequentialID()</code> instead of <code>NewID()</code> to create new keys. This is supposed to offer much better performance as the generated GUIDs are guaranteed to be sequential rather than random, resulting in better index performance on insertion.</p>

<p>For the Access version of the tables, we basically use the same definition, except that we used 4 tables:</p>

<ul>
<li>ProductINT: let Jet/ACE autonumbering create the sequential integer Primary Key.</li>
<li>ProductINTRandom: let Jet/ACE autonumbering create the random integer Primary Key.</li>
<li>ProductGUIDRandom: let Jet/ACE use its own internal <code>GenGUID()</code> for the key which generates random GUIDs instead of sequential ones.  </li>
<li>ProdcutGUIDSequential: call the Windows API (<a href="http://www.pinvoke.net/default.aspx/rpcrt4/UuidCreateSequential.html">UuidCreateSequential</a>) to create sequential ID instead.</li>
</ul>

<h3>SQL Server Test code</h3>

<p>Using the SQL Server Management Studio, we performed the following test once for each table (resetting the database in-between):
<textarea name="code" class="sql:nogutter" cols="60" rows="10">
SET NOCOUNT ON;
GO

DECLARE @i INT = 1;
WHILE (@i <= 1000)
BEGIN
	DECLARE @tstart DATETIME2 = SYSDATETIME();
	BEGIN TRAN
		DECLARE @a INT = 1;
		WHILE (@a <= 1000)
		BEGIN
			INSERT INTO ProductGUID (SKU,Description) 
			VALUES ('PROD' + CONVERT(CHAR,@a), 'Product number ' + CONVERT(CHAR,@a));
			SELECT @a = @a + 1;
		END;
	COMMIT TRAN;
	SELECT DATEDIFF(MILLISECOND, @tstart, SYSDATETIME()) AS timespan;
SELECT @i = @i + 1;
END;
GO
</textarea></p>

<p>Basically, we perform 1000 transactions each inserting 1000 records into the table <code>ProductGUID</code> or <code>ProductINT</code>.</p>

<h3>Access 2007 Test code</h3>

<p>To duplicate the same conditions, the following VBA code will perform 1000 transactions each inserting 1000 records.<br />
Note that the recordset is opened in Append mode only.<br />
The importance of this will be discussed in another article.</p>

<p><textarea name="code" class="vb:nogutter" cols="60" rows="10">
' Run this to inset 1,000,000 products in batches of 1000
' In the given table
Public Sub Benchmark(TableName as String, InsertSeqGUID  as Boolean)
    Dim i As Integer
    For i = 1 To 1000
        Insert1000Products TableName, InsertSeqGUID 
    Next i
End Sub

' Insert 1000 products in a table
Public Sub Insert1000Products(TableName as String, InsertSeqGUID as boolean)
    Dim i As Long
    Dim db As DAO.Database
    Dim rs As DAO.Recordset
    Dim ws As DAO.Workspace
    Dim starttime As Long
    Dim timespan As Long
    
    Set ws = DBEngine.Workspaces(0)
    DoEvents
    starttime = GetClock ' Get the current time in ms
    ws.BeginTrans
    Set db = CurrentDb
    Set rs = db.OpenRecordset(TableName, dbOpenDynaset, dbAppendOnly)
    With rs
        For i = 1 To 1000
            .AddNew
                If InsertSeqGUID Then !ID = "{guid {" & CreateStringUUIDSeq() & "}"
                !SKU = "PROD" & i
                !Description = "Product number " & i
            .Update
        Next i
    End With
    ws.CommitTrans
    rs.Close
    timespan = GetClock() - CDbl(starttime)
    Set rs = Nothing
    Set db = Nothing
    ' Print Elapsed time in milliseconds
    Debug.Print timespan
    DoEvents
End Sub
</textarea></p>

<p>We call this code to perform inserts on each of our Access tables:</p>

<ul>
<li>ProductINT table: we just insert data in the <code>ProductINT</code> table, letting Access create autonumber IDs.</li>
<li>ProductINTRandom table: we just insert data in the <code>ProductINTRandom</code> table, letting Access create random autonumber IDs.</li>
<li>ProductGUIDRandom table: we let Access create the Random GUID for the primary key.</li>
<li>ProductGUIDSequential: we use the Windows API to create a sequential ID that we insert ourselves.</li>
</ul>

<h3>Test results</h3>

<p>Without further ado, here are the raw results, showing the number of inserted record per second that we achieve for each test over the growing size of the database (here are only shown tests comapring Sequantial GUID and Autoincrement on SQL Server and Access, see next sections for the other results):</p>

<p><img src="/wp-content/uploads/2009/04/chart04.png" alt="Inserts per second" /></p>

<p>What we clearly see here is that performance when using autoincrement and Sequential GUID stays pretty much constant over the whole test.<br />
That&#8217;s good new as it means that using Sequential GUIDs do not degrade performance over time.</p>

<p>As a side note, in this particular test, Access offers much better raw performance than SQL Server. In more complex scenarios it&#8217;s very likely that Access&#8217; performance would degrade more than SQL Server, but it&#8217;s nice to see that Access isn&#8217;t a sloth.</p>

<h3>Using Sequential GUID vs Autoincrement in Access</h3>

<p>The results show that we do take a performance hit of about 30% when inserting Sequential GUID vs just using autonumbers.<br />
We&#8217;re still getting good results, but that&#8217;s something to keep in mind.</p>

<p>In terms of CPU consumption, here is what we get:</p>

<p><img src="/wp-content/uploads/2009/04/CPUAccess.png" alt="CPU load Access" /></p>

<p>Random PK, whether they are simple integer or GUID do consume substantially more CPU resources.</p>

<h3>Using Sequential GUID vs Identity in SQL Server</h3>

<p>Out-of-the box, SQL Server performs quite well and there is not much difference whether you&#8217;re using Sequential GUIDs or autoincrement PK.</p>

<p>There is however a surprising result: using Sequential GUIDs is actually slightly <em>faster</em> than using autoincrement!</p>

<p>There is obviously an explanation for this but I&#8217;m not sure what it is so please enlighten me :-)</p>

<p>CPU Consumption:</p>

<p><img src="/wp-content/uploads/2009/04/CPUSQLServer.png" alt="CPU load SQL Server" /></p>

<h3>Using Random GUID vs Sequential GUID vs Random Autonumber in Access</h3>

<p>So, what is the impact of choosing a Sequential GUID as opposed to letting Access create its own random GUIDs?</p>

<p><img src="/wp-content/uploads/2009/04/chart05.png" alt="Inserts per second Random GUID vs Sequential GUID in Access" /></p>

<p>It&#8217;s clear that random GUIDs have a substantial performance impact: their randomness basically messes up indexing, resulting in the database engine having to do a lot more work to re-index the data on each insertion.<br />
The good thing is that this degradation is pretty logarithmic so while it degrades over time, the overall performance remains pretty decent.<br />
While GUIDs are larger than Random Integers (16 bytes vs 4 bytes) the actual performance of inserting records whose PK is a random integrer is actually worse than random GUID&#8230;</p>

<h3>Provisional conclusions</h3>

<p>Here we&#8217;ve check the baseline for our performance tests.
In the next article, we&#8217;ll look exclusively at the performance of inserting data from a remote Access 2007 front end using our VBA code.</p>

<p>Having this baseline will allow us to check the performance overhead of using ODBC and letting Jet/ACE manage the dialogue with the SQL Server backend.</p>

<p>Feel free to leave your comments below, especially if you have any resources or information that would be useful.</p>

<h3>Updates</h3>

<ul>
<li>16APR2009: added test of random autonumber as PK in Access.</li>
<li>13APR2009: Original Article</li>
</ul>
]]></content:encoded>
			<wfw:commentRss>http://blog.nkadesign.com/2009/access-vs-sql-server-some-stats-part-1/feed/</wfw:commentRss>
		<slash:comments>15</slash:comments>
		</item>
		<item>
		<title>Sysadmin: SQL server performance madness</title>
		<link>http://blog.nkadesign.com/2009/sysadmin-sql-server-performance-madness/</link>
		<comments>http://blog.nkadesign.com/2009/sysadmin-sql-server-performance-madness/#comments</comments>
		<pubDate>Sun, 12 Apr 2009 11:25:56 +0000</pubDate>
		<dc:creator>Renaud Bompuis</dc:creator>
				<category><![CDATA[Database]]></category>
		<category><![CDATA[Hardware]]></category>
		<category><![CDATA[SQL Server]]></category>
		<category><![CDATA[sysadmin]]></category>

		<guid isPermaLink="false">http://blog.nkadesign.com/?p=550</guid>
		<description><![CDATA[I&#8217;ve just lost 2 days going completely bananas over a performance issue that I could not explain. I&#8217;ve got this Dell R300 rack server that runs Windows Server 2008 that I dedicate to running IIS and SQL Server 2008, mostly for development purposes. In my previous blog entry, I was trying some benchmark to compare [...]]]></description>
			<content:encoded><![CDATA[<p><img src="/wp-content/uploads/windows.png" alt="Technology" title="Technology" align="left" width="64" height="60" hspace="5" vspace="5" border="0" />
I&#8217;ve just lost 2 days going completely bananas over a performance issue that I could not explain.</p>

<p>I&#8217;ve got this <a href="http://www.dell.com/content/products/productdetails.aspx/pedge_r300?c=us&amp;cs=555&amp;l=en&amp;s=biz">Dell R300</a> rack server that runs Windows Server 2008 that I dedicate to running IIS and SQL Server 2008, mostly for development purposes.</p>

<p><img src="/wp-content/uploads/2009/04/pedge_r300_overview1.jpg" alt="Dell PowerEdge R300 Rack servers" /></p>

<p>In my <a href="/2009/access-vs-sql-server-some-stats-part-1/">previous blog entry</a>, I was trying some benchmark to compare the performance of Access and SQL Server using INT and GUID and getting some strange results.
<!--
To get a more accurate baseline to explain the results I was getting from the server, I've tried this little snippet of SQL (the test database is simply a table with a IDENTIY column `ID`, and two text columns `SKU`, and `Description`).  
<textarea name="code" class="sql:nogutter" cols="60" rows="10">
DECLARE @tstart DATETIME2 = SYSDATETIME();
-- BEGIN TRAN
	DECLARE @a INT = 1;
	WHILE (@a <= 1000)
	BEGIN
		INSERT INTO Product (SKU,Description) 
		VALUES ('PROD' + CONVERT(CHAR,@a), 'Product number ' + CONVERT(CHAR,@a));
		SELECT @a = @a + 1;
	END;
-- COMMIT TRAN;
SELECT DATEDIFF(MILLISECOND, @tstart, SYSDATETIME()) AS timespan;
</textarea>

OK, so now for the really weird thing that was driving me crazy.

I have SQL Server 2008 Standard installed on 3 different machines: the server, my desktop and my Macbook Pro.
--></p>

<p>Here are the results I was getting from inserting large amounts of data in SQL Server:</p>

<table border=1>
<thead>
<tr>
  <th>Machine</th>
  <th>Operating System</th>
  <th align="center">Test without Transaction</th>
  <th align="center">Test with Transaction</th>
</tr>
</thead>
<tbody>
<tr>
  <td>MacbookPro</td>
  <td>Windows Server 2008 x64</td>
  <td align="center">324 ms</td>
  <td align="center">22 ms</td>
</tr>
<tr>
  <td>Desktop</td>
  <td>Windows XP</td>
  <td align="center">172 ms</td>
  <td align="center">47 ms</td>
</tr>
<tr>
<td>Server</td>
  <td>Windows Server 2008 x64</td>
  <td align="center"><font color="red">8635 ms!!</font></td>
  <td align="center">27 ms</td>
</tr>
</tbody>
</table>

<p>On the server, not using transactions makes the query run more than 8 seconds, <strong>at least an order of magnitude slower than it should!</strong></p>

<p>I initially thought there was something wrong with my server setup but since I couldn&#8217;t find anything, I just spend the day re-installing the OS and SQL server, applying all patches and updates so the server is basically brand new, nothing else on the box, no other services, basically all the power is left for SQL Server&#8230;</p>

<h3>Despair</h3>

<p>When I saw the results for the first time after spending my Easter Sunday rebuilding the machine I felt dread and despair.<br />
The gods were being unfair, it had to be a hardware issue and it had to be related to either memory or hard disk, although I couldn&#8217;t understand really why but these were the only things that I could see have such an impact on performance.</p>

<p>I started to look in the hardware settings:</p>

<p><img src="/wp-content/uploads/2009/04/screen1.png" alt="Device Manager" /></p>

<p>And then I noticed this in the Policies tab of the <em>Disk Device Properties</em> :</p>

<p><img src="/wp-content/uploads/2009/04/screen2.png" alt="DISK Device Properties" /></p>

<p>Just for the <a href="http://encyclopediadramatica.com/Lulz">lulz</a> of it, I ticked the box, close the properties</p>

<p><img src="/wp-content/uploads/2009/04/screen3.png" alt="Enable advanced performance" /></p>

<p>And then tried my query again:</p>

<table border=1>
<thead>
<tr>
  <th>Machine</th>
  <th>Operating System</th>
  <th align="center">Test without Transaction</th>
  <th align="center">Test with Transaction</th>
</tr>
</thead>
<tbody>
<td>Server</td>
  <td>Windows Server 2008 x64</td>
  <td align="center"><font color="red">254 ms!!</font></td>
  <td align="center">27 ms</td>
</tr>
</tbody>
</table>

<p>A <strong>nearly 35 fold increase in performance!</strong></p>

<h3>Moral of the story</h3>

<p>If you are getting strange and inconsistent performance results from SQL Server, make sure you check that <em>Enable advanced performance</em> option.<br />
Even if you&#8217;re not getting strange results, you may not be aware of the issue, only that some operations may be much slower than they should.</p>

<p>Before taking your machine apart and re-installing everything on it, check your hardware settings, there may be options made available by the manufacturer or the OS that you&#8217;re not aware of&#8230;</p>

<p>Lesson learnt.</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.nkadesign.com/2009/sysadmin-sql-server-performance-madness/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Access: building &#8216;upsizable&#8217; applications.</title>
		<link>http://blog.nkadesign.com/2009/access-building-upsizable-applications/</link>
		<comments>http://blog.nkadesign.com/2009/access-building-upsizable-applications/#comments</comments>
		<pubDate>Wed, 01 Apr 2009 03:39:45 +0000</pubDate>
		<dc:creator>Renaud Bompuis</dc:creator>
				<category><![CDATA[Database]]></category>
		<category><![CDATA[MSAccess]]></category>
		<category><![CDATA[Programming]]></category>
		<category><![CDATA[SQL Server]]></category>

		<guid isPermaLink="false">http://blog.nkadesign.com/?p=433</guid>
		<description><![CDATA[When you start building an Access application, it&#8217;s tempting to just think about today&#8217;s problem and not worry at all about the future. If your application is successful, people will want more out of it and, over time, you&#8217;ll be faced with the task of moving the back-end database to a more robust system like [...]]]></description>
			<content:encoded><![CDATA[<p><img src="/wp-content/uploads/access.png" alt="Microsoft Access" title="Microsoft Access" align="left" width="64" height="64" hspace="5" vspace="5" border="0" />
When you start building an Access application, it&#8217;s tempting to just think about today&#8217;s problem and not worry at all about the future.<br />
If your application is successful, people will want more out of it and, over time, you&#8217;ll be faced with the task of moving the back-end database to a more robust system like SQL Server.</p>

<p>While there are <a href="/2009/ms-access-upsizing-to-sql-server-2008/">tools like SSMA that can help you move an Access database to SQL Server</a>, <strong>a lot of the problems you&#8217;ll encounter can be solved before you even have to think about upsizing</strong>.<br />
Abiding by a few simple rules will cost you nothing when creating your Access application but will save you a lot of headache if -when- the time comes to upsize.</p>

<p>So here are a few things to keep in mind.</p>

<h3>Naming conventions</h3>

<p>Access is pretty liberal about naming conventions and it will let you freely name your tables, columns indexes and queries.
When these get moved to another database you&#8217;ll most probably be faced with having to rename them.<br />
In some cases, you could actually create subtle bugs because something that used to work fine in Access may be tolerated in the new database but be interpreted differently.</p>

<ul>
<li><p><strong>Do not use spaces or special characters</strong> in your data object names.<br />
Stick to characters in the range <code>A</code> through <code>Z</code>,  <code>0</code> to <code>9</code> with maybe underscores <code>_</code> somewhere in between (but not at the start or the end).<br />
Also try to respect casing wherever you reference this name (especially for databases like MySQL which are case-sensitive if the hosted on a Linux platform for instance).<br />
eg:<br />
<code>Customer Order Lines (archive)</code> should be  <code>CustomerOrderLines_Archive</code>.<br />
<code>Query for last Year's Turnover</code> should be <code>QueryLastYearTurnover</code>.<br />
Index <code>ID+OrderDate</code> should become instead <code>ID_OrderDate</code>.</p></li>
<li><p><strong>Do not use keywords that are reserved</strong> or might mean something else whether they are SQL keywords or functions names:<br />
A column called <code>Date</code> could be renamed <code>PurchaseDate</code> for instance.<br />
Similarly, <code>OrderBy</code> could be renamed <code>SortBy</code> or <code>PurchaseBy</code> instead, depending on the context of <em>Order</em>.<br />
Failing to do so may not generate errors but could result in weird and difficult to debug behaviour.</p></li>
<li><p><strong>Do not prefix tables with <code>Sys</code>, <code>USys</code>, <code>MSys</code> or a tilde <code>~</code>.</strong><br />
Access has its own internal system tables starting with these prefixes and it&#8217;s best to stay away from these.<br />
When a table is deleted, Access will often keep it around temporarily and it will have a tilde as its prefix.</p></li>
<li><p><strong>Do not prefix Queries with a tilde <code>~</code>.</strong><br />
Access use the tilde to prefix the hidden queries kept internally as recordsource for controls and forms.</p></li>
</ul>

<h3>Database design</h3>

<ul>
<li><p><strong>Always use Primary keys.</strong><br />
Always have a non-null primary key column in every table.<br />
All my tables have an autonumber column called <code>ID</code>.  Using an automatically generated column ID guarantees that each record in a table can be uniquely identified.<br />
It&#8217;s a painless way to ensure a minimum level of data integrity.</p></li>
<li><p><strong>Do not use complex multivalue columns.</strong><br />
Access 2007 introduced <a href="http://office.microsoft.com/en-us/access/HA012337221033.aspx">complex columns that can record multiple values</a>.<br />
They are in fact fields that return whole recordset objects instead of simple scalar values.  Of course, this being an Access 2007 only feature, it&#8217;s not compatible with any other database.
Just don&#8217;t use it, however tempting and convenient it might be.<br />
Instead use a table to record Many-To-Many relationships between 2 tables or use a simple lookup to record lists of choices in a text field itself if you&#8217;re only dealing with a very limited range of multivalues that do not change.</p></li>
<li><p><strong>Do not use the Hyperlink data type.</strong><br />
Another Access exclusive that isn&#8217;t available in other databases.</p></li>
<li><p><strong>Be careful about field lookups.</strong><br />
When you create Table columns, Access allows you to define lookup values from other tables or lists of values.<br />
If you <em>manually</em> input a list of values to be presented to the user, these won&#8217;t get transferred when upsizing to SQL Server.<br />
To avoid having to maintain these lookup lists all over your app, you could create small tables for them and use them as lookup instead; that way you only need to maintain a single list of lookup values.</p></li>
<li><p><strong>Be careful about your dates.</strong><br />
Access date range is much larger than SQL Server.<br />
This has 2 side-effects:<br />
1) if your software has to deal with dates outside the range, you&#8217;ll end-up with errors.<br />
2) if your users are entering dates manually, they could have made mistakes when entering the year (like 09 instead of 2009).<br />
Ensure that user-entered dates are valid for your application.</p></li>
</ul>

<h3>VBA</h3>

<p>While most of your code will work fine, there are a few traps that will bomb your application or result in  weird errors:</p>

<ul>
<li><p><strong>Always explicitly specify options when opening recordsets or executing SQL.</strong><br />
With SQL Server, the <code>dbSeeChange</code> is mandatory whenever you open a recordset for update.<br />
I recommend using <code>dbFailOnError</code> as well as it will ensure that the changes are rolled back if an error occurs.
<textarea name="code" class="vb:nogutter">
Dim rs as DAO.RecordSet
' Open for read/write
set rs = db.OpenRecordSet("Stock", dbOpenDynaset, dbSeechanges + dbFailOnError)
' Open for read only
set rs = db.OpenRecordSet("Stock", dbOpenSnapshot)
' Direct SQL execution
CurrentDB.Execute "INSERT INTO ...",  dbSeeChanges + dbFailOnError
</textarea></p></li>
<li><p><strong>Get the new autonumbered ID <em>after</em> updating the record.</strong><br />
In Access, autonumbered fields are set as soon as the record is added even if it hasn&#8217;t been saved yet.<br />
That doesn&#8217;t work for SQL Server as autonumbered IDs are only visible after the records have been saved.
<textarea name="code" class="vb:nogutter">
' Works for Access tables only
' We can get the new autonumber ID as soon as the record is inserted
rs.AddNew
mynewid = rs!ID
...
rs.Update

' Works for ODBC and Access tables alike
' We get the new autonumber ID after the record has been updated
rs.AddNew
...
rs.Update
rs.Move 0, rs.LastModified
mynewid = rs!ID
</textarea></p></li>
<li><p><strong>Never rely on the type of your primary key.</strong><br />
This is more of a recommendation but if you use an autonumbered ID as your primary key, don&#8217;t rely in your code or you queries on the fact that it is a <em>long integer</em>.<br />
This can become important if you ever need to upsize to a replicated database and need to transform your number IDs into GUID.<br />
Just use a Variant instead.</p></li>
</ul>

<h3>Parting thoughts</h3>

<p>These simple rules will not solve all your problems but they will certainly reduce the number of issues you&#8217;ll be faced with when upsizing you Access application.<br />
Using a tool like <a href="/2009/ms-access-upsizing-to-sql-server-2008/">SSMA</a> to upsize will then be fairly painless.</p>

<p>If you have other recommendations, please don&#8217;t hesitate to leave them in the comments, I&#8217;ll regularly update this article to included them.</p>

<h3>References</h3>

<ul>
<li>You will find lots of other bits of wisdom on this page: <a href="http://www.granite.ab.ca/access/sqlserverupsizing.htm">My random thoughts on SQL Server Upsizing from Microsoft Access</a> by Tony, from Granite Consulting.</li>
<li>Martin Green&#8217;s Office tips has a series of <a href="http://www.fontstuff.com/siteindex.htm#access">articles on Access to SQL Server migration</a>.</li>
</ul>
]]></content:encoded>
			<wfw:commentRss>http://blog.nkadesign.com/2009/access-building-upsizable-applications/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>

