Most Access data types have clean equivalents in SQL Server. Text becomes VARCHAR, Number becomes INT or DECIMAL, Date/Time becomes DATETIME2. Migration tools handle these automatically and nothing breaks.

But several Access data types do not have clean SQL Server equivalents. These are the ones that cause broken forms, bad data, and confused users after migration. Knowing about them before you start saves hours of troubleshooting afterward.

Yes/No Fields

Access: Stores as 16-bit integer. True = -1, False = 0.

SQL Server: BIT. True = 1, False = 0.

What breaks: Any VBA code that explicitly checks for -1. For example:

If Me.IsActive = -1 Then   ' This works in Access
If Me.IsActive = True Then  ' This works in both

The first line fails after migration because SQL Server returns 1 for True, not -1. The second line works everywhere because VBA evaluates any non-zero value as True.

Also breaks: Queries that use WHERE IsActive = -1. After migration, no records match because the column now contains 1 instead of -1.

Fix: Search your VBA code and queries for any comparison to -1. Replace with True, <> 0, or = 1. Do this before migration so you can test both environments.

Memo Fields (Long Text)

Access: Memo fields store up to about 1GB of text with no practical limit in normal use.

SQL Server: Maps to NVARCHAR(MAX), which stores up to 2GB.

What breaks: The mapping is fine for storage, but several Access features do not work the same way with NVARCHAR(MAX) through ODBC:

Fix: Usually no action needed. If you need to index or search these fields, consider whether they should actually be NVARCHAR(MAX) or if a shorter NVARCHAR(4000) would suffice.

Access: A special multi-part field that stores display text, URL, screen tip, and sub-address in a single field, separated by # characters.

SQL Server: No equivalent. Maps to NVARCHAR, which stores the raw text including the # separators.

What breaks: After migration, the field contains text like Contoso Website#https://contoso.com## instead of a clickable link. Forms that displayed clickable hyperlinks now show raw text. Queries that searched hyperlink fields may not find matches because the search text does not include the # separators.

Fix: Before migration, decide how to handle these fields. Options: - Extract just the URL portion and store it in a plain text field - Keep the multi-part format and parse it in your application code - Split into separate columns (DisplayText, URL)

The cleanest approach is splitting into separate columns. This also makes the data usable by any application, not just Access.

Attachment Fields

Access: A special field type that stores multiple files (documents, images, PDFs) inside a single record. Internally, Access stores these in hidden system tables with a complex relational structure.

SQL Server: No equivalent. There is no way to store multiple files in a single column in SQL Server (or any standard database).

What breaks: Migration tools either skip attachment fields entirely or produce errors. The data is not lost — it is still in the Access file — but it does not transfer to SQL Server.

Fix: Before migration, extract the files from the Attachment fields. You have two options: - Store files on the file system and save the file path in a VARCHAR column - Store files as binary data in a VARBINARY(MAX) column (one file per record, which may require a related table for records with multiple attachments)

The file system approach is generally better. Databases are not efficient file storage systems, and putting large binary files in the database bloats it quickly.

Multi-Value Fields

Access: Allows a single field to store multiple values, displayed as a checkbox list. For example, a “Skills” field might contain “SQL, VBA, Python” as separate selectable values.

SQL Server: No equivalent. This violates first normal form (a field should contain a single atomic value), and server databases enforce this.

What breaks: Migration tools cannot convert multi-value fields. The data is either skipped or concatenated into a single string.

Fix: Restructure into a proper relational design before migration. Create a junction table:

This is more work up front but produces a correct relational design that works in any database.

OLE Object Fields

Access: Stores embedded OLE objects — Word documents, Excel spreadsheets, bitmaps, or any other OLE-compatible content. The data includes OLE headers and metadata, not just the raw file content.

SQL Server: Maps to VARBINARY(MAX), which stores raw binary data.

What breaks: The OLE headers are preserved in the binary data, making the content difficult to use outside of Access. A bitmap stored as an OLE Object is not a standard BMP file — it has OLE wrapper data around it. Applications expecting standard file formats cannot read the content.

OLE Object fields are also large. A simple 50KB image might occupy 200KB as an OLE Object because of the overhead.

Fix: Before migration, extract the actual file content from the OLE wrapper. VBA code can do this by reading the binary data and stripping the OLE headers. For images, consider converting to standard formats (JPEG, PNG) and storing on the file system instead.

AutoNumber

Access: Generates sequential integers starting from 1. Never reuses a number, even if a record is deleted.

SQL Server: INT IDENTITY(1,1). Generates sequential integers but can have gaps (after rollbacks, deletes, or server restarts).

What breaks: Functionally, this mapping works fine. The only issue is that VBA code using DMax("ID", "TableName") + 1 to predict the next ID will sometimes get the wrong answer, because identity values can have gaps.

Also, if you use rs.AddNew followed by reading the ID field, the behavior differs. In Access, the AutoNumber is assigned immediately when you call AddNew. In SQL Server through ODBC, the identity value is not available until after you call rs.Update.

Fix: Use SELECT SCOPE_IDENTITY() after an INSERT to get the newly assigned ID. Replace any code that predicts or manually assigns AutoNumber values.

Date/Time

Access: Stores dates as floating-point numbers. Can represent dates from January 1, 100 to December 31, 9999. Internally flexible about invalid dates.

SQL Server: DATETIME supports January 1, 1753 to December 31, 9999. DATETIME2 supports January 1, 0001 to December 31, 9999.

What breaks: If your Access data contains dates before 1753 (and you are using DATETIME), migration fails for those records. More commonly, Access databases contain invalid date values — empty strings stored in date fields, “00/00/0000”, or other garbage — that Access tolerates but SQL Server rejects.

Fix: Clean your date data before migration. Run queries to find records with invalid dates and either correct them or set them to NULL. If you need dates before 1753, use DATETIME2 instead of DATETIME on the SQL Server side.

The Pre-Migration Checklist

Before running any migration tool:

  1. Identify all Attachment, Multi-Value, OLE Object, and Hyperlink fields
  2. Restructure these into standard field types
  3. Clean date fields of invalid values
  4. Search VBA code for -1 Boolean comparisons
  5. Test the migration on a copy of the database first

Fixing these issues before migration is always easier than debugging them after.