BUILDING A CLIENT/SERVER APPLICATION

Up to this point, I've laid the pieces of the Access Project puzzle on the table and clarified what they are. Now it's time to start fitting them together to make a usable, productive application.

Architecturally, an Access Project combines application objects and pointers to server objects through a data link. Within the Access Project (.adp) file are stored the forms, reports, Data Access Pages, macros, and modules. On the server reside the tables, views, database diagrams, and stored procedures. By using the data link, the Access Project causes the server to execute code in stored procedures or to return records from a view or table.

The Access Project retrieves data from the server in one of several ways:

  • By selecting records into a table or view datasheet

  • By retrieving records for use in form or report controls

  • By creating recordset objects from server data through VBA code calling the ActiveX Data Objects (ADO) components

The rest of this chapter uses the sample Project file NorthwindCS.Adp that ships with Access. Locate and open the Project now if you haven't done so already; it's in the Program FilesMicrosoft OfficeOfficeSamples folder. When you first open this Project file, it will look for a matching database (NorthwindCS) on your local MSDE SQL Server installation. If your local SQL Server service isn't started, Access will attempt to start it for you. If you don't have MSDE or a SQL Server Desktop installation on your computer, Access will ask you for the server name, user name, and password needed to log on to a server on your domain. If Access doesn't find the sample database on the server you specify, it will ask whether you want the database to be created (see Figure 25.10).

Figure 25.10. The NorthwindCS Project will create its own SQL Server database if one isn't found.


Note

The script file used by Northwind to create its sample database is stored in the folder Program FilesMicrosoft OfficeOfficeSamples as NorthwindCS.SQL. You can open this script file with Notepad, WordPad, or Word and review the syntax in it if you want to learn more about SQL Server's Data Definition Language (DDL).


Working with Tables

The Access Database window for a Project enables you to design and modify tables on the linked SQL Server database. Simply highlight the Tables icon in the Database window and click New. Unlike working in Jet-based application, you must enter a valid table name in the pop-up Choose Name dialog before you can design the table (see the following section “Naming Conventions for Objects” for information on table naming conventions). Figure 25.11 shows the table design window.

Figure 25.11. Creating and modifying tables is done in this layout.


Enter a column name and data type for each column that you want to create. Press Tab to move around in the design window. For each column, the name and data type are required; the other attributes are optional. Table 25.2 describes the attributes of a table column.

Table 25.2. Column Attributes in SQL Server
AttributeDescription
Column NameThe name for the column, which must follow the rules defined in the following section. A column name must be unique within its table; however, a generally accepted convention for relational database design is that a column name has the same usage and data type throughout a single database.
DatatypeThe data type for the column, using the data types defined in the section “Server Data Types.” The default data type in column design view is char (character).
LengthThe maximum number of characters that can be entered, or the storage size for a data type, depending on the data type. For non-character data types, the value displayed for this attribute is the storage size of the field and can't be changed. For char and nchar data types, the default value for this attribute is 10 characters; for varchar and nvarchar data types, the default is 50.
PrecisionThe total number of digits that can be entered in a numeric data type, without the punctuation. For example, the number 6543.21 has six digits of precision. The default value for this attribute is 0.
ScaleThe maximum number of digits that can be entered to the right of a decimal point for a numeric data type. For example, the number 654.321 has a scale of three. The default value for this attribute is 0.
Allow NullsDetermines whether a column can allow null values. This attribute is selected by default. If you're adding a column to an existing table and clear this check box (to disallow nulls), you must first make certain that the column has no null values.
Default ValueSpecifies a value to be entered into a column when the record is saved and no other value is entered. If a column doesn't allow nulls, no value is entered before the record is saved, and a default value exists, Access places the default value in the column. If a column doesn't allow nulls, no value is entered before the record is saved, and a default value doesn't exist, the record can't be saved. The default value for this attribute is blank.
IdentityThis attribute determines that a column will autonumber from a starting value (the Identity Seed attribute) by adding an increment (the Identity Increment attribute) to each subsequent record. When used with a data type of int and an increment value of 1, this attribute is identical to the AutoNumber column type in Jet-based applications. There can be only one identity type column per table, and the data type must be decimal, int, numeric, smallint, or tinyint, and the Scale attribute must be 0. This attribute is unselected by default.
Identity SeedThe first row in the table receives this seed value in its identity type column, if any. The default value for this attribute is blank, which is translated to the value 1 if an identity column exists and the property isn't set.
IdentityThe second row in the table receives the Identity Seed value plus the Increment Identity Increment value; for each subsequent row added, the prior identity column value is incremented by this attribute's value, or 1 if this attribute is left blank (the default).
Is RowGuidWhereas an identity column generates an ID value unique to the table, a column of type uniqueidentifier stores only GUID (globally unique ID) values that are unique around the world. You can select only the Is RowGuid attribute for a column of type uniqueidentifier. When you select the attribute, Access adds the default expression newid() to the Default Value attribute. SQL Server doesn't automatically populate a uniqueidentifier column; the value must be added via a default value or program code.

Caution

The Access table design window relies on an ADO connection to the SQL server to manipulate the table design. Consequently, the error messages returned when you try to save a table aren't Access-specific and can be frustratingly useless (for example, Errors occurred).


Tip

You will come to dislike the feature that clears the table design Clipboard in a Project when its source table is closed. If you want to copy or cut the properties for a table column or columns to the Clipboard, be sure that you keep the source table design open until you've pasted the information into the destination table's design window.


Tables can also be created and modified from a database diagram. You can open the Relationships diagram in the NorthwindCS sample Project to see an sample database diagram and experiment with table design in this tool.

Naming Conventions for Objects

Objects' names in SQL Server are slightly different from those for Jet-based Access Projects. Generally, the rules for object names in SQL Server are broader than those in Access/Jet, so if you are familiar with the limitations and naming conventions from past Access releases, you will have no trouble adapting to SQL Server's tastes, nor will you have trouble upsizing any existing databases to SQL Server.

In SQL Server, the term identifier refers to any object that can have a name, including tables, columns, views, indexes, constraints, rules, triggers, and stored procedures. Identifiers are either regular or delimited:

  • A regular identifier conforms to the naming conventions described in this section and is used verbatim in table design, stored procedures, or VBA code.

  • A delimited identifier is one that doesn't conform to the SQL Server naming conventions but is allowed in the database as long as every instance is quoted (with double quotes—"") or bracketed (with []).

For example, the object name Order is a reserved word in SQL Server (part of the Order By clause syntax). If you change the table name Orders to Order in the sample database and then try to create a new stored procedure by using the new table name Order, a syntax error results. On the other hand, if you delimit the object name Order in your stored procedure, the code runs as expected. Listing 25.1 shows an example of this.

Listing 25.1. Comparing a Stored Procedure's Regular and Delimited Identifiers
Alter Procedure prcIdentifiers
As
/* This table name is acceptable per SQL Server's naming conventions */
  Select * From Orders
/* This table name is not acceptable and must be delimited */
  Select * From [Order]
  Select * From "Order"
  Return

These object name rules for SQL Server 7.0 names qualify as regular (non-delimited) identifiers:

  • Object names (even if delimited) must be 128 characters or fewer if they are regular identifiers, or 126 characters if they're delimited (to allow two characters for the delimiter as part of the name).

  • The first character must be a letter from A to Z (either case, or their equivalent in a foreign language) or one of these characters—_, @, or #. Other characters and embedded spaces are disallowed.

  • Subsequent characters can be any allowed first character, a decimal number, or the $ character.

  • The identifier can't be a Transact-SQL reserved word (Transact-SQL is the scripting language used in SQL Server stored procedures).

  • The identifier cannot contain embedded spaces.

Keep these considerations in mind when naming objects in your databases:

  • Variables, parameters (arguments), and names in stored procedures use the strings @ and @@ in defined ways, so using this string at the beginning of an object name might be confusing.

  • SQL Server uses the # or ## character strings to begin the name of a temporary table or temporary procedure, so a naming convention that begins object names with this character is discouraged.

Server Data Types

SQL Server column data types are conceptually similar to those in Jet, but the data type names are different, and the characteristics of a SQL Server data type may not perfectly mirror those of the related Jet data type. Table 25.3 describes the SQL Server data types.

Table 25.3. SQL Server Column Data Types
Data typeDescription
binaryFixed-length binary data with an assigned length from 1 to 8,000 bytes.
bitInteger data with a 0, 1, or null value. Bit columns can't be indexed. This data type is used to indicate true/false information in a Project. When using Jet, Access expects to see 0 and -1 for false and true, but in a Project Access correctly translates the value 1 in a bit field to true.
charFixed-length character data with an assigned length from 1 to 8,000 bytes.
datetimeA date and time ranging from 1/1/1753 to 12/31/9999. The time accuracy for this data type is 3.33 milliseconds, or 1/300th of a second, rounded as needed.
decimalA number with a fixed precision and scale that can hold values from (–10^38 – 1) to (10^38 – 1); the maximum precision attribute value for this data type is 28.
floatA floating-point number that can hold values from (–1.79E + 308) to (1.79E + 308).
imageVariable-length binary data whose length can't exceed (2^31 – 1), or about 2 billion bytes. The name of this column type doesn't imply that only image files can be embedded in the column, merely that the column holds a binary image of its contents and isn't aware of what's in the image (in other words, the data can't be searched or sorted).
intA whole number ranging from (–2^31) (about 2 billion) to (2^31 – 1).
moneyCurrency values ranging from (–2^63) to (2^63 – 1). The accuracy for this data type is .0001.
ncharFixed-length Unicode character data with a length from 1 to 4,000 bytes.
ntextVariable-length Unicode data with a maximum length of (2^30 – 1), or about 1 billion, characters.
numericIdentical to decimal, defined earlier in this table.
nvarcharVariable-length Unicode character data with a length from 1 to 4,000 bytes.
realA floating-point number that can hold values from (–3.40E + 38) to (3.40E + 38). A real is the same as a float with a precision value of 24.
smalldatetimeA date and time ranging from 1/1/1900 to 6/6/2079. This data type is accurate to the minute and doesn't store seconds.
smallintAn integer from –32,768 to 32,767.
smallmoneyCurrency values ranging from –214,748.3648 to +214,748.3647. The accuracy for this data type is .0001.
textVariable-length data with a maximum length of (2^31 – 1), or about 2 billion, characters.
timestampA number of type binary(8) that's unique across the database and provides a record identifier that SQL Server uses for concurrency and transaction control. This value doesn't actually correlate to a date or time, but instead indicates the chronological sequence in which data modifications (deletes, inserts, and updates) occur in a database. The value of this column isn't directly useful during application development.
tinyintInteger data from 0 through 255.
uniqueidentifierA GUID, which is a 32-character value in the display format xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx that's unique in the world.
varbinaryFixed-length binary data with an assigned length from 1 to 8,000 bytes.
varcharVariable-length character data with an assigned length from 1 to 8,000 bytes.

Although Access/Jet table columns are now Unicode-enabled by default (meaning that they can contain characters from non-English display languages), you must explicitly use the Unicode-aware data fields in SQL Server if you intend to store non-English data. The SQL Server data types beginning with n (nchar, ntext, and nvarchar) are used for this purpose. However, although you may be tempted to use the Unicode-enabled data types in all cases to enable your application to handle any type of data in the future, be aware that each character stored in the Unicode data types consumes 2 bytes of storage instead of 1, even if the data isn't Unicode. This means that the disk space requirements of your database will grow much faster when Unicode data types are used.

Note

You may already know that Access stores any table data of types Memo and OLE Object outside the table's data pages and refers to the long-value data by using a pointer. This technique involves a “double-jump” to retrieve the data values and has negative performance implications. Similarly, SQL Server stores data of types text, ntext, and image outside their host table, with a similarly negative performance impact. These column data types should be used as sparingly as possible in your Projects.


Table 25.4 shows the SQL Server data types and compares the data type to the nearest Jet type.

Table 25.4. Comparing SQL Server Column Data Types to Access
SQL Server (Project) Data TypeAccess (Using Jet) Data Type
bitYes/No
charN/A
datetimeDate/Time
decimalNumber (Decimal)
floatNumber (Double)
imageOLE Object
intNumber (Long Integer)
int (as identity)AutoNumber
moneyCurrency
ncharN/A
ntextMemo
numericNumber (Decimal)
nvarcharText
realNumber (Single)
smalldatetimeN/A
smallintNumber (Integer)
smallmoneyCurrency
textMemo
timestampN/A
tinyintNumber (Byte)
uniqueidentifierReplication ID (GUID)
User-definedN/A
varbinaryN/A
varcharText

Using Constraints

In SQL Server, constraints provide the functionality that you get from validation rules in a Jet-based application—they test and limit the values that can be entered into a column. The constraint model is quite complex and, therefore, very powerful. Properly used, constraints can save quite a bit of validation coding on your forms.

You can create five types of constraints for a table column.

Check Constraints

A check constraint limits the value that can be entered into a column. Generally, a check constraint takes a column value and compares it to another column value, a literal, or an expression by using this syntax:

[columnname] operator (comparisonvalue_or_expression)

Table 25.5 lists valid SQL Server expression operators.

Table 25.5. Operators Used in Column Check Constraints
OperatorMeaning
Subtract
%Modulo
*Multiply
/Divide
+Add
!<Not less than
!=Not equal to
!>Not greater than
&Bitwise And
^Bitwise Exclusive Or
|Bitwise Or
<Less than
<=Less than or equal to
<>Not equal to
=Equal
>Greater than
>=Greater than or equal to
AllTrue if all of a set of comparisons are true
AndTrue if both Boolean expressions are true
AnyTrue if any one of a set of comparisons is true
BetweenTrue if the operand is within a range
ExistsTrue if a subquery contains any rows
InTrue if the operand is equal to one of a list of expressions
LikeTrue if the operand matches a pattern
NotReverses the value of any other Boolean operator
OrTrue if either Boolean expression is true
SomeTrue if some of a set of comparisons are true
Negative
~Bitwise Not
+Positive

If a check constraint names a single column, it's a column-level constraint and is bound to the named column. A check constraint that references more than one column (even if the same column name is repeated) is bound at the table level.

The expression portion of a check constraint can use wildcard characters that are valid for a SQL Like phrase, including % (match any number of characters), _ (match a single character), and [range] (match a single character in the range if the syntax is [x-x] or in the set if the syntax is [xxxx]). Because the constraint runs on the server, it can utilize Transact-SQL syntax but cannot reference VBA expressions or procedures.

For example, you have a column-level check constraint named CK_BirthDate, from the Employees table in the NorthwindCS sample database, and the constraint references the SQL Server getdate function:

([BirthDate] < getdate())

Another example is a table-level (multi-column) check constraint named CK_Discount, from the Order Details table in the NorthwindCS sample database:

([Discount] >= 0 And [Discount] <= 1)

Here's an example of a column-level check constraint that could be built-in the Order Details table in the NorthwindCS sample database. The constraint allows only state abbreviations for Washington and West Virginia by specifying that the first character of Region is w and the second character is a or v:

([Region] Like '[w][av]')

Here are some basic considerations for check constraints:

  • A single column can have any number of check constraints.

  • A check constraint for a column can reference only the column itself in the test expression, not other columns or tables.

  • If multiple check constraints exist for a single column, they execute in the order they were created.

  • A single table can have any number of check constraints.

  • A table-level check constraint can reference only columns in the same table.

To create a new constraint, follow these steps:

1.
Open a table in Design view and view the Properties dialog. The first page of this dialog includes the constraint information (see Figure 25.12).

Figure 25.12. Constraints are defined in the table's Properties dialog.


Caution

The Properties dialog for a table doesn't have the standard OK, Cancel, and Apply buttons found in other Windows property dialogs. Every change you make in this dialog is permanent, and closing the dialog with the Esc key or the Close box doesn't undo your changes as it would in similar Windows dialogs.


2.
Click the New button. The dialog preassigns a suggested name in the Selected Constraint text box. These names always begin with CK_ and include the table name.

3.
In the Constraint Expression box, type a valid constraint for a column or for the table. Text values in a constraint should always be delimited with single quote ('') marks.

4.
Rename the constraint from the system-suggested name to your own by typing the new name in the Constraint Name text box.

Default Constraints

A default constraint is a value entered into a column if no other value is provided before the record is inserted into the table. Default constraints are entered into the Default Value attribute of the table design window.

A default constraint can contain a constant literal value (delimited with single quote marks or specified with the keyword Null) or an expression that evaluates to the data type of the constraint's column. When using an expression to create a default, any SQL Server functions such as user_name() and getdate() can be used. See the SQL Server Books Online topic “Functions (T-SQL)” for a complete list.

Defaults can be applied to columns of any data type except a timestamp column or any column marked as an identity column.

Note

You may already be familiar with the behavior for the default value attribute in a Jet-based application, which displays the default value in an unsaved record on a datasheet or form. This behavior isn't available in a Project—there's no way to force a table datasheet to display the default value for a column before a new record is saved. The same is true of forms bound to SQL Server data if you rely on the Default Value column attribute. However, you can create a DefaultValue property expression on a form control rather than use a default constraint; the form property will cause the display of the default value even before the record is saved.


Primary Key Constraints

A primary key constraint is a column or multiple columns that, taken together, uniquely identify a single record in a table. Each table can have only one primary key constraint, and generally no table should be without a primary key because the Access Project uses the primary key when updating records in the server table.

Columns that participate in a primary key constraint can't allow Null values, and the table design window creates a unique index for the combination of columns in the constraint.

A primary key constraint is also used in referential integrity, the process of enforcing relationships between records in different but related tables.

To create a primary key constraint, select one or more column definitions in Table Design view; then click the Primary Key toolbar button or select Primary Key from the right-click menu.

Foreign Key Constraints

A foreign key constraint is a column or multiple columns that, taken together, identify a single record in a table for the purpose of relating it to the primary key of a record in a different table. A foreign key constraint maps a primary key in one table to a foreign key in a different table and enforces the relationship between the tables. It can, for example, ensure that no record entered into the foreign key table has a foreign key value that doesn't correspond to a primary key value in the related table.

You create a foreign key constraint in the database diagram designer by defining a relationship, which automatically creates the constraint. To define a relationship, follow these steps:

1.
Open an existing or new database diagram. For a new diagram, add a table to the diagram by selecting Show Table from the View menu and dragging a table name to the design surface.

2.
Select a primary key definition by clicking in the box(es) to the left of one of its columns.

3.
The mouse cursor changes to a pointer, which you drag to the foreign key columns in a different table. The Create Relationship dialog appears. Figure 25.13 shows the Relationships database diagram from the NorthwindCS sample database. The relationship between the Suppliers and Products tables has been deleted and is in the process of being re-created.

Figure 25.13. The Create Relationships dialog is displayed during relationship definition.


4.
Enter a relationship name.

5.
The primary key column or columns is usually defaulted for you in this dialog in the Primary Key Table column. You must select the column(s) that make up the foreign key in the Foreign Key Table column.

After a foreign key constraint is created through a relationship, you can't delete a record from the table in the primary key side of the relationship until all related records (those with a matching foreign key value) are deleted from the table on the foreign key side. Unlike Jet, SQL Server does not provide a property setting that enforces cascading deletions from a parent table into a child table. To create cascading deletions, you must add a delete trigger to the parent table.

Unique Constraints

A unique constraint validates that a value within the specified column is unique in the same table. A primary key, by definition, is also a unique constraint. A table can have multiple unique constraints.

To create a unique constraint, follow these steps:

1.
Open a table in Design view and view the Properties dialog.

2.
On the Indexes/Keys page, click the New button. The dialog pre-assigns a suggested name in the Index Name text box. These names always begin with IX_ and include the table name.

3.
Select one or more column names from the Column Name list.

4.
Change the index name to an appropriate name for the constraint.

5.
Select the Create UNIQUE check box and the Constraint option button.

Figure 25.14 shows this process in the table's Properties dialog.

Figure 25.14. Defining a constraint for a table.


Note

You can use this same page to create indexes as well as constraints. If you want to create a unique index, pay close attention to the Ignore Duplicate Key check box. If this option isn't selected, a bulk insert operation will fail completely if any record being inserted in the transaction attempts to create a non-unique value in the unique index. However, if the option is selected, the bulk insert will succeed for all non-offending records but simply won't add the offending record(s) to the table.


Using Triggers

A trigger is stored procedure code that's run (triggered) by a delete, insert, or update event on a table record. A trigger applies validation rules or maintains data integrity. Triggers can contain most of the Transact-SQL statements allowed in stored procedures. If you are familiar with the event model for an Access form, you can think of a trigger as a BeforeUpdate event procedure.

The code in a trigger can affect rows in other tables, even if this causes other triggers to fire in a sort of chain-reaction cascade. As a result, an insert trigger can be used to provide validation that exceeds the capabilities of a table-level check constraint.

To create a trigger, choose Triggers from the shortcut menu for a table in the Database window. A trigger maintenance dialog appears. To create a new trigger, click New; the trigger design window appears, as shown in Figure 25.15.

Figure 25.15. The design window for a trigger.


In the trigger design window, remove the comment markers around the string "/* Insert, Update, Delete */" and keep one or more of the three values to define the type of trigger; the same trigger can be set to fire on more than one of the three trigger-able events. Enter a name for the trigger on the first line, and then modify the trigger code to achieve the desired result.

Listing 25.2 shows a sample trigger that has been added to the OrderDetails table in the sample database. This code fires whenever a new record is deleted, inserted, or updated in the table. The trigger updates (increments or decrements) the UnitsOnOrder field in the Products table for the product being affected by the order record. Review the code comments in the listing for more details.

Listing 25.2. A Trigger That Increments a Quantity Value in a Related Table
Create Trigger trgdiuOrderDetails
On dbo.[Order Details]
For Delete, Insert, Update
As
  /*
    Increment the on-order quantity in Products
      based on the order quantity in the insert buffer.
    The virtual table 'inserted' contains the records that
      were inserted or updated, if empty then this is a
      delete trigger.
  */
  If Exists(Select * From inserted)
    Update p
      Set p.UnitsOnOrder = p.UnitsOnOrder + i.Quantity
      From inserted i, Products p
      Where p.ProductID = i.ProductID

  /*
    Decrement the on-order quantity in Products
      based on the order quantity in the delete buffer.
    The virtual table 'deleted' contains the records that
      were deleted, if empty then this is an insert trigger.
  */
  If Exists(Select * From deleted)
    Update p
      Set p.UnitsOnOrder = p.UnitsOnOrder - d.Quantity
      From deleted d, Products p
      Where p.ProductID = d.ProductID

Notice in the listing that the name inserted was used to represent the actual record(s) being inserted, because the name refers to a virtual table called the insert buffer. Whenever new records are added to a SQL Server table or are updated (basically, deleted and added), the virtual table inserted is available in your triggers. Similarly, you can reference the delete buffer's virtual table by using the name deleted in an update or delete trigger to refer to the deleted record(s).

Note

When you enter Design view for a new trigger, the first line of the trigger code begins with the Create Trigger statement. When you enter Design view to edit an existing trigger, the first line of trigger code begins with Alter Trigger.


Obviously, triggers can be a powerful tool for Access developers. A single table can have multiple triggers in any combination of the three types: delete, insert, and update. You will find that much of the complex data manipulation that you've been doing with VBA code and form expressions in Jet-based Projects can now be done with table triggers in your Projects.

Because you must change a table record or records to test a trigger, your development may be easier if you write your trigger code in a stored procedure first. You can test and debug the stored procedure by running it from the Database window, and then paste the code into a trigger when the development is completed. However, if you're writing a trigger that relies on the deleted or inserted virtual tables, as shown earlier in this section, you can't test code that refers to those two tables within your stored procedure. You can, however, create dummy tables that represent the deleted or inserted tables for testing purposes. For example, create a table called deleted_test that has the same structure as the table that will receive the trigger, and test your stored procedure against the dummy table. When moving the tested stored procedure code to a trigger, simply remember to rename the references from deleted_test to deleted.

Optimizing Data Access

As with your Jet-based applications, a good database design is critical to the maintenance and performance of the SQL Server databases attached to your Projects. Here are some basic guidelines:

  • Use a standard, normalized database structure— You will probably find that your multiuser applications run much more quickly on SQL Server than they did under a Jet-based model. Consequently, you will probably find yourself cheating for performance less than you used to. In a Jet-based database environment, “cheating” for performance usually means to

    • Denormalize a database (flatten the structure) to produce fewer table joins that each Select statement will process.

    • Break larger tables into several smaller tables so that fewer index pages and records are passed across the network to process a Select statement.

    In a SQL Server model, all processing of table joins, indexed lookups, and record aggregation takes place on the server, thereby minimizing network and workstation burden. Consequently, the database design compromises that you might make to improve performance in your Jet-based applications may actually reduce the performance of your Access Project-based application.

  • Use relationships— Creating relationships between associated tables generally provides the best performance, and data integrity goals are well served by the use of primary and foreign key constraints in related tables. If you are familiar with creating Jet-based relationships in the Access Relationships window, learning to create SQL Server relationships in a database diagram is only a slightly different process.

  • Use stored procedures and triggers— Build as much of the code on the server as is practical. The term “is practical” is relative to your business needs and your development style, but a good rule of thumb is that you should place code on the server that manipulates one or more existing database records. In contrast, you should place code in the application that validates or manipulates unsaved records or otherwise leverages the richness of the VBA language.

    Storing application code on the server has three distinct benefits:

    A single copy of the exists.Assume that you have a Jet-based Access application codewith 100 users, and a validation rule or maintenance routine requires a small change. You must change the code in a master copy of the application and then distribute the updated application to all 100 users. If the code is on the server, however, you change the code in one central place, and all users begin to instantly see the new code, with no workstation-based installation tasks required at all.
    The code is processed on the server.If your code acts against table data, it will run most quickly if it's stored in the database with that same table data. Also, servers tend to have more horsepower than workstations, so even a busy server may well execute code faster than a single workstation with far more limited horsepower.
    The code is closer to the database.Logistically, it makes good sense to have the code for a database travel with that database onto backup devices, be transferred to test machines, or be replicated across an enterprise.

  • Balance your use of indexes. Some developers take the approach that “you can't have too many indexes.” This isn't at all a truism in the SQL Server environment. SQL Server's indexing models are quite well documented, and in many scenarios, an extra index will cost performance rather than add to it. If you have the opportunity, read about indexing in the SQL Server Books Online before you create your first complex Project. As a general rule, however, you will want to create an index on any field commonly used in a join, a Where clause, or an Order By clause.

  • Use the most appropriate data type. Select the simplest storage type for numeric data and the smallest data width for textual data that you can, as dictated by each data column's specific needs. For example, if time accuracy to the minute will suffice and information about seconds isn't required, using a smalldatetime column format over a datetime format will save 4 bytes per record, which can add up to a measurable savings of disk and memory space on a large table.

  • Always put a timestamp field in a table in an Access Project. A timestamp field provides a unique record identifier that's shared between Access and SQL Server to optimize concurrency and transaction control. Not only will the performance of deletes, inserts, and updates improve when a timestamp exists in a table, but the speed of simple tasks such as filling and refreshing a table datasheet or form also is improved.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.147.104.120