Access SQL Server
Access SQL Server
Access SQL Server
Here is a list of data types in each environment, and how they are different. Some datatypes from
SQL Server were left out (e.g. SQL_VARIANT, TABLE).
w
Access SQL Server SQL Server Definition
Yes/No BIT (Integer: 0 or 1)
Number (Byte) TINYINT (Positive Integer 0 -> 255)
Number (Integer) SMALLINT (Signed Integer -32,768 -> 32,767)
Number (Long
INT (Signed Integer -(2^31) -> (2^31)-1)
Integer)
(no equivalent) BIGINT (Signed Integer -(2^63) -> (2^63)-1)
(Floating precision -1.79E + 308 -> 1.79E +
Number (Single) REAL
308)
(Floating precision -3.40E + 38 -> 3.40E +
Number (Double) FLOAT
38)
(4 decimal places, -(2^63)/10000 -> ((2^63)-
Currency MONEY
1)/10000)
(4 decimal places, -214,748.3648 ->
Currency SMALLMONEY
214,748.3647)
Hyperlink (no equivalent - use VARCHAR())
Decimal DECIMAL (Fixed precision -10^38 + 1 -> 10^38 - 1)
Numeric NUMERIC (Fixed precision -10^38 + 1 -> 10^38 - 1)
(Date+Time 1753-01-01 -> 9999-12-31,
Date/Time DATETIME
accuracy of 3.33 ms)
(Date+Time 1900-01-01 -> 2079-06-06,
Date/Time SMALLDATETIME
accuracy of one minute)
(Fixed-length non-Unicode string to 8,000
Text(n) CHAR(n)
characters)
(Fixed-length Unicode string to 4,000
Text(n) NCHAR(n)
characters)
(Variable-length non-Unicode string to 8,000
Text(n) VARCHAR(n)
characters)
(Variable-length Unicode string to 4,000
Text(n) NVARCHAR(n)
characters)
(Variable-length non-Unicode string to
Memo TEXT
2,147,483,647 characters)
(Variable-length Unicode string to
Memo NTEXT
1,073,741,823 characters)
(Fixed-length binary data up to 8,000
OLE Object BINARY
characters)
(Variable-length binary data up to 8,000
OLE Object VARBINARY
characters)
(Variable-length binary data up to
OLE Object IMAGE
2,147,483,647 characters)
Autonumber (any numeric data type, with IDENTITY
IDENTITY
Autoincrement property)
Some notes on usage of data types:
Switching from Yes/No to BIT
In Access, you could use integers or TRUE/FALSE keywords to determine the value of
the column. In SQL Server, and especially during migration, you should use integer
values only. So here are some sample queries; note that the SQL Server queries will work
Access as well.
-- DETERMINING TRUE
-- Access:
[...] WHERE ynColumn = TRUE
[...] WHERE ynColumn = -1
-- SQL Server:
[...] WHERE ynColumn <> 0
------------------------------
-- DETERMINING FALSE
-- Access:
[...] WHERE ynColumn = FALSE
[...] WHERE ynColumn = 0
-- SQL Server:
[...] WHERE ynColumn = 0
Switching from Currency to MONEY
You will no longer be able to use cute VBA functions like FORMAT to add dollar signs,
thousand separators, and decimal places to your numbers. In fact, in Access, some of this
data is actually stored along with the value. With SQL Server, this extraneous data is not
stored, reducing disk space and making calculations more efficient. While you can apply
this formatting in SQL Server, as explained in Article #2188, it's messy -- and better
handled, IMHO, by the client application. In ASP, you can use built-in functions like
FormatCurrency to apply proper formatting to your money values.
Switching from Hyperlink to VARCHAR()
Like Currency, Access uses internal formatting to make the values stored in the
application clickable. This is partly because Access is a client application, and this
feature makes it easier to use. However, when you're not physically in the application,
you may not want the URL to be clickable (it may just be a display value, or you may
want to wrap alternate text -- or an image -- inside the <a href> tag). In SQL Server, use a
VARCHAR column (likely 1024 or greater, depending on the need) and apply <a href>
tags to it in the client application. Don't expect the database to maintain HTML for you...
this only increases storage size, and hurts performance of searches against that column.
Switching from Date/Time to DATETIME
When passing dates into Access from ASP or an application, you use pound signs (#) for
surrounding dates. SQL Server, on the other hand, uses apostrophes ('). So the following
query conversion would be required:
-- Access:
[...] WHERE dtColumn >= #2001-11-05#
-- SQL Server:
[...] WHERE dtColumn >= '20011105'
MM/DD/YYYY format is strongly discouraged, because of the inevitable confusion
(many such dates can look like DD/MM/YYYY to Canadians or Brits). The only safe
format to use is YYYY-MM-DD in Access, and YYYYMMDD in SQL Server. See
Article #2260 for more information. There is also further information on this later in this
article.
In addition, Access allows you to store date and time independently. SQL Server,
including SQL Server 2005 ("Yukon"), does not allow this (see Article #2206 for more
info). To see if a date equals 2001-11-05 in SQL Server, you would have to convert the
stored value (which includes time) to a date only. Here is how a typical query would have
to change:
-- Access:
[...] WHERE dtColumn = #11/05/2001#
-- SQL Server:
[...] WHERE CONVERT(CHAR(8), dtColumn, 112) = '20011105'
-- if dtColumn has an index, this will be more efficient:
[...] WHERE dtColumn >= '20011105'
AND dtColumn < '20011106'
If you want to retrieve the current date and time, the syntax is slightly different:
-- Access:
SELECT Now()
SELECT Date() & " " & Time()
-- SQL Server:
SELECT GETDATE()
SELECT CURRENT_TIMESTAMP
If you want just the time:
-- Access:
SELECT Time()
-- SQL Server:
SELECT CONVERT(CHAR(8), GETDATE(), 108)
SELECT LTRIM(STUFF(RIGHT(CONVERT(CHAR(19), GETDATE(), 0), 7), 6, 0, '
'))
If you want just today's date, there are other options:
-- Access:
SELECT Date()
-- SQL Server:
SELECT GETDATE() - {fn CURRENT_TIME}
SELECT {fn CURDATE()} -- not friendly with certain languages
SELECT CONVERT(CHAR(8), GETDATE(), 112)
To get tomorrow's date, here is how your queries would look:
-- Access:
SELECT DateAdd("d",1,date())
-- SQL Server:
SELECT CONVERT(CHAR(8), GETDATE()+1, 112)
--or
SELECT DATEADD(DAY, 1, CONVERT(CHAR(8), GETDATE(),
112))
To get the date and time 24 hours from now:
-- Access:
SELECT cstr(DateAdd("d",1,date())) & " " & cstr(time())
-- SQL Server:
SELECT DATEADD(day, 1, GETDATE())
To get the first day of the current month:
-- Access:
SELECT DateAdd("d",1-day(date()),date())
-- SQL Server:
SELECT CONVERT(CHAR(10),GETDATE()+1-
DAY(GETDATE()),101)
To get the number of days in the current month:
-- Access:
SELECT DAY(DATEADD("m", 1, 1-DAY(date()) & date())-1)
-- SQL Server:
SELECT DAY(DATEADD(MONTH, 1, 1-DAY(GETDATE())+GETDATE())-
1)
To get the current millisecond:
-- This is impossible in Access, but just for fun:
SELECT "Pick a number between 1 and 1000" :-)
-- SQL Server:
SELECT DATEPART(millisecond, GETDATE())
To get the current weekday:
-- Access:
SELECT weekdayname(weekday(date()))
-- SQL Server:
SELECT DATENAME(WEEKDAY,
GETDATE())
It would be nice if you could use the same syntax against both data sources, but alas that
is not the case. Try using the following in Access:
SELECT columns FROM table WHERE dateColumn = #20030709#
SELECT columns FROM table WHERE dateColumn = #2003-07-09#
The first should generate an error, the second should work fine. Now check out the
problems when you use the following syntax in SQL Server:
SET DATEFORMAT MDY
SELECT ISDATE('20030713') -- 1
SELECT ISDATE('2003-07-13') -- 1
SET DATEFORMAT DMY
SELECT ISDATE('20030713') -- 1
SELECT ISDATE('2003-07-13') -- 0
Now, there is a way to make YYYY-MM-DD safe in SQL Server, and it involves using
the canonical format for dates:
SET DATEFORMAT MDY
SELECT ISDATE('{d 2003-07-13}') -- 1
SET DATEFORMAT DMY
SELECT ISDATE('{d 2003-07-13}') -- 1
However, I don't believe Access will accept this format, so we're back to square one:
writing different code for each database (I guess we were there already, since Access
requires # delimiters -- but it would be nice if the formats were consistent).
Like switching from Currency to Money, when you present dates in SQL Server, you lose
the convenience of the FORMAT() function, which accepts multiple ways of formatting a
date (e.g. ). For more information on how to overcome this change, see Article #2464 for
a cheat sheet of available formatting options with CONVERT(), and Article #2460 for a
roll-your-own function that mimics the FORMAT() functionality, and then some.
Switching from Autonumber to IDENTITY
Not much difference here, except for how you define the column in DDL (CREATE
TABLE) statements:
-- Access:
CREATE TABLE tablename (id AUTOINCREMENT)
-- SQL Server:
CREATE TABLE tablename (id INT IDENTITY)
Handling Strings
There are many changes with string handling you will have to take into account when
moving from Access to SQL Server. For one, you can no longer use double-quotes (") as
string delimiters and ampersands (&) for string concatenation. So, a query to build a
string would have to change as follows:
-- Access:
SELECT "Foo-" & barColumn FROM TABLE
-- SQL Server:
SELECT 'Foo-' + barColumn FROM TABLE
(Yes, you can enable double-quote characters as string delimiters, but this requires
enabling QUOTED_IDENTIFIERS at each batch, which impacts many other things and
is not guaranteed to be forward compatible.)
Another change is the ability to concatenate NULL values to a string. If you do this in
Access:
SELECT FirstName & ' ' & LastName FROM table
If either FirstName or LastName is NULL, you will still get the portion of the string that
was not NULL. In SQL Server:
SELECT FirstName + ' ' + LastName FROM table
If any of the values is NULL, the whole expression will yield NULL (unless you change
the default setting for CONCAT_NULL_YIELDS_NULL). A common workaround is to
use COALESCE around each potentially NULL value:
SELECT COALESCE(FirstName, '') + ' ' + COALESCE(LastName, ' ') FROM table
(Or, avoiding NULLs in the first place... see Article #2073.)
Built-in CHR() constants in Access change slightly in SQL Server. The CHR() function
is now spelled slightly differently. So, to return a carriage return + linefeed pair:
-- Access:
SELECT CHR(13) & CHR(10)
-- SQL Server:
SELECT CHAR(13) + CHAR(10)
This one is confusing for many people because the CHAR keyword doubles as a function
and a datatype definition.
Another thing to note is that Access can use & or + for string concatenation. SQL Server
uses & for Boolean AND, so you need to use + for all string concatenation in SQL
Server. Also, keep in mind that 'string' + NULL = NULL, so you should always use
COALESCE() on column names / values that might be NULL, in order to avoid setting
the whole result to NULL.
String Functions
There are many VBA-based functions in Access which are used to manipulate strings.
Some of these functions are still supported in SQL Server, and aside from quotes and
concatenation, code will port without difficulty. Others will take a bit more work. Here is
a table of the functions, and they will be followed by examples. Some functions are not
supported on TEXT columns; these differences are described in Article #2061.
There are possibly dozens of other slight syntax changes that may have to be made when moving
from Access to SQL Server. Here are a few of the more significant ones:
WITH TIES
WITH TIES is a common usage with SELECT TOP in SQL Server. This syntax is not
valid in Access (the default behavior for TOP in Access is to use WITH TIES, so in truth,
there is no direct way to use "without TIES" iN Access).
FIRST(), LAST()
FIRST() and LAST() don't have relevance in a relational database that is not based on
physical ordering of a table - a table is, by definition, an unordered set of rows. If you
want the "first" or "last" row in a dataset, you probably have some idea of which row
should be first or last, so use an ORDER BY clause to force it. The order of a SELECT
statement is not guaranteed, and could change from one execution to the next (it's up to
the optimizer, not the physical storage or what you'd like the order to be). The only way
you can ensure a desired ordering of a result set is by using an ORDER BY clause;
absolutely no exceptions. So, instead of using FIRST() or LAST(), when you move your
query to SQL Server, you can use TOP 1 with an ORDER BY (or ORDER BY DESC)
clause, or MIN()/MAX() (with or without a subquery).
IIF(expression, resultIftrue, resultIfFalse)
IIF() is a handy inline switch comparison, which returns one result if the expression is
true, and another result if the expression is false. IIF() is a VBA function, and as such, is
not available in SQL Server. Thankfully, there is a more powerful function in SQL
Server, called CASE. It operates much like SELECT CASE in Visual Basic. Here is an
example query:
-- Access:
SELECT alias = IIF(Column<>0, "Yes", "No")
FROM table
-- SQL Server:
SELECT alias = CASE WHEN Column<>0 THEN 'Yes' Else 'No' END
FROM table
SQL Server's CASE also supports multiple outcomes, for example:
SELECT alias = CASE
WHEN Column='a' THEN 'US'
WHEN Column='b' THEN 'Canada'
ELSE 'Foreign'
END
FROM table
Switch()
Similar to IIF, Switch() can be handled in SQL Server using CASE, e.g.
-- Access:
SELECT Switch(
On=1,'On',
On=0,'Off'
) FROM table
-- SQL Server
SELECT CASE
WHEN On=1 THEN 'On'
WHEN On=0 THEN 'Off'
END FROM table
-- or
SELECT CASE On
WHEN 1 THEN 'On'
WHEN 0 THEN 'Off'
END FROM table
VAL()
The Val() function in Access returns the numeric portion of a string if it appears at the
beginning of the string, otherwise 0, e.g.
Val('5561T5') = 5561
Val('T55615') = 0
Val('556165') = 556165
To mimic this functionality in SQL Server, you will need to do a little more:
DECLARE @val VARCHAR(12)
SET @val = '5561T5'
SELECT CONVERT(INT, LEFT(@val,PATINDEX('%[^0-9]%',@val+' ')-
1))
DISTINCTROW
SQL Server supports DISTINCT but does not support DISTINCTROW.
OBJECTS
When creating tables and other objects, keep the following limitations in mind:
Access uses MAKE TABLE, while both platforms support CREATE TABLE;
Access object names are limited to 64 characters;
SQL Server 7.0+ object names are limited to 128 characters;
SQL Server 6.5 object names were limited to 30 characters and no spaces; and,
Stored queries in Access become Stored Procedures in SQL Server.
For information about the number and size of objects allowed in Access and SQL Server,
see Article #2345.
STORED QUERIES
Stored queries in Access are a way to store query information so that you don't have to type out
ad hoc SQL all the time (and update it throughout your interface everywhere you make a similar
query). Being a non-GUI guy, the easiest way I've found to create a stored query in Access is to
go to Queries, open "Create query in Design View", switch to SQL View, and type in a query,
such as:
PARAMETERS ProductID INTEGER;
SELECT ProductName, Price
FROM Products
WHERE ProductID = [productID]
Be careful not to use any reserved words, like [name], as parameter names, or to give your
parameters the SAME name as the column -- this can easily change the meaning of the query.
Once you have the same schema within SQL Server, when moving to stored procedures, the
basic difference you'll need to know is syntax. The above stored query becomes:
CREATE PROCEDURE MyQuery
@ProductID INT
AS
BEGIN
SELECT ProductName, Price
FROM Products
WHERE ProductID = @productID
END
You can create this stored procedure using this code through QUery Analyzer, or you can go into
the Enterprise Manager GUI, open the database, open the Stored Procedures viewpane, right-
click within that pane and choose New > Stored Procedure. Paste the above code (or a query that
might make a bit more sense given *your* schema), click Check Syntax, and if it all works, click
Apply/OK. Don't forget to set permissions!
Now in both cases, you can call this code from ASP as follows:
<%
productID = 5
set conn = CreateObject("ADODB.Connection")
conn.open "<connection string>"
set rs = conn.execute("EXEC MyQuery " & productID)
do while not rs.eof
' process recordset here
' ...
rs.movenext
loop
%>
See Article #2201 for a quasi-tutorial on writing stored procedures.
FORMS
Yes, Access has pretty little forms that you can create easily with VBA. There is no such thing in
SQL Server; you will either need to develop an application (the most rapid to put together would
probably be an ASP front end), or you could use Access as a front end (employing an Access
Data Project, or ADP).
SECURITY
Access is limited to security in terms of username / password on the database. It also is subject to
Windows security on the file itself (as well as the folder it resides in). Typically, ASP
applications must allow the anonymous Internet guest account (IUSR_<machine_Name>) to
have read / write permissions on file and folder. Username / password access to the database
cannot be controlled with any more granularity.
SQL Server has two authentication modes, and neither are much like Access security at all. You
can use Windows Authentication, which allows you direct access to domain Users and Groups
from within the interface. You can also use Mixed Mode, which allows SQL Server to maintain
usernames and passwords (thereby negating the need for a domain or other Windows user/group
maintenance).
Once you have determined an authentication mode, users have three different levels of access
into the database: login (at the server level), user (at the database level), and object permissions
within each database (for tables, views, stored procedures, etc). Just to add a layer of complexity,
SQL Server makes it easy to "clone" users by defining server-wide roles, and adding users to that
role. This is much like a Group in a Windows domain; in SQL Server, you can use the built-in
definitions (and customize them), or create your own. Alterations to a role's permissions affect
all users that are members of that role.
Microsoft has a thorough whitepaper you should skim through before jumping into SQL Server.
If you're going to deploy your own SQL Server box (as opposed to leasing a dedicated SQL
Server, or a portion of one), by all means read the SQL Server Security FAQ.