APPENDIX A More SOL- Salutions to Common Problems

Problem 1: Begin a Label Report at Any Label on the Sheet

MosT LABEL RuNs leave unused labels an the last sheet. The following technique will start a label run at a specific label position, so you can use those partially used sheets. The components are a form, a temporary table, and a label report. You'll use the form to identify the position of the first available label. Then, an event procedure will populate the temporary table with blank rows, with one blank row for each missing label. The number of blank rows is determined by the value you enter. Ifyou enter a value of 3 (meaning that the first label is in the third label position), the procedure will add two blank rows. Next, the procedure appends the label data below the blank rows. The report doesn't care that the rows are empty; the positions are still filled in the label report. Each blank row represents a blank label, pushing the actual data down to the first label. This technique doesn't accommodate missing labels between the first and last label.

Example: tblOne, frmOne, and rptOne Solution 1: SELECT, DELETE, and INSERT INTO

Ta create the temporary table:

1. In the Database window, select the table that contains the data you're printing.

2. Press Ctr1 +C to copy the table to the clipboard.

3. Press Ctrl+V topaste a copy ofthe table. In the Paste Table As dialag box, enter a name for the dummy table. (We'll be working with tblOne.) Select the Structure Only option button in the Paste Options section, and dick onOK.

655 AppendixA

4. Open tblOne and delete any primary key. Don't delete the fields; just delete the primary key.

5. Review the Indexed and Required properties for each field and make sure that each is set to No.

6. Save the table.

To create the form:

1. Open a new, unbound form in Design view.

2. Insert a textbox and enter an appropriate label caption.

3. Name the textbox txtBlankRows.

4. Insert two command buttons, side by side.

5. Name them cmdPrint and cmdCancel and set their Caption properties to Print and Cancel, respectively.

6. Click on the Code button on the Form Design toolbar to launch the Visual Basic Editor.

7. In the form's module, enter the following event procedures:

Private Sub crndPrint_Click() Dirn rst As New ADODB.Recordset Dirn bytCounter As Byte Dirn bytBlanks As Byte On Error GoTe errHandler bytBlanks = Forrns!frmOne!txtBlankRows.Value DoCrnd.SetWarnings False DoCrnd. RunSQL "DELETE FROM nameoftemporarytable" Set rst.ActiveConnection = CurrentProject.Connection rst.Open "SELECT * FROM nameoftemporarytable ", , _ adOpenDynarnic, adlockOptirnistic For bytCounter = 2 To bytBlanks rst.AddNew rst.Update Next rst.Close Set rst = Nothing

656 More SQL Salutions to Common Problems

DoCmd.RunSQL "INSERT INTO nameoftemporarytable " & "SELECT * FROM datatable" DoCmd.SetWarnings True DoCmd.OpenReport "nameoflabelreport", acViewPreview Exit Sub errHandler: MsgBox "Please enter a valid label number" DoCmd.SetWarnings True End Sub

Private Sub cmdCancel_Click() DoCmd.Close acForm, "nameofform", acSaveNo End Sub

8. Save the form. The example form is named frmOne.

Because the label report will be unique to your application, we won't build one. The example file uses a label report named rptOne, based on the Customers table in Northwind. Before running the report, insert your label sheets into the printer. Be sure to position the partially used sheet as the first sheet. Then, open the form (frmOne) and enter the position of the first existing label. (For instance, if two labels were missing, you'd enter the value 3, and, if six labels were missing, you'd enter the value 7.) Then, dick on the Print button, and the event procedure prints the report.

Problem 2: Finding the Second-Highest or -Lowest Value in a Group

Querying for the second-highest or second-lowest value in a tableis easy, and it requires two queries. First, you identify the data source and the fields you want to see in the results. Then, you set the sort order, ascending for the highest and descending for the lowest. Then, set the Top Values property to 2 so that the query returns only the highest and second-highest or the lowest and second• lowest values, depending on the sort order. Base the second query on the first query, set the Top Values property to 1, and reverse the sort order. This technique is complicated when you need toreturn the second-highest or second-lowest value in a group, and the solution again requires two queries.

657 AppendixA

Example: qryOneFirstQuery, qryTwoSecondQuery

Solution 2: GROUP BY

To create the first query:

1. Base a Totals query on the data source. Be careful to use only those fields that identify each group. To manually define a Totals query, choose Totals from the View menu in the Query Design toolbar. Specify the Max aggre• gate in the grouping value's total cell, or, in the SQL window, add a GROUP BY clause in the form:

SELECT groupedfield, Max(valuefield) AS name FROM table GROUP BY groupedfield

where groupedfield represents the field(s) by which you're grouping, valuefield is the field with the values you're sorting in order of largest to smallest, name is the alias column name for valuefield, and tableis the data source.

2. Save the query using an appropriate name. Our example query is qryTwoFirstQuery.

3. Base a second query on your data source and add the first query (qryOneFirstQuery) to it. Open the SQL window and use an SQL state• ment in the form

SELECT dsgroupedfield, qrygroupedfield, Max(dsvaluefield) AS name FROM ds LEFT JOIN query ON (dsvaluefield = name) AND (dsgroupedfield = qrygroupedfield) GROUP BY dsgroupedfield, qrygroupedfield HAVING qrygroupedfield Is Null

where dsgroupedfield is the grouped field in the data source, qrygroupedfield is the grouped field in the first query, ds is the data source, and query is the first query. Or, create the query manually by following steps 4 through 9.

4. Create a relationship for the grouped fields and the value fields between the data source and the query. Remember, the value field's name will probably be different in the query.

658 More SQL Salutions to Common Problems

5. Add both grouped fields to the query (from the data source and the query). Also add the value field from the data source.

6. Make the query a Totals query by choosing Totals from the View menu.

7. Change the value field's Totalsaggregate from Group By to Max.

8. Add the criteria Is Null to the query's (qryTwoFirstQuery) group field.

9. Save the query. We saved the example file as qryTwoSecondQuery. To see the results, run qryTwoSecondQuery.

These instructions return a list ofthe second-highest product for each order in the Order Details table. Simply change the Max aggregate to the Min aggregate in both queries.

Problem 3: Adding a Summary Row to a Query

We often see tatals in a form or report, but you can also view tatals in a query. This technique is more limited than the form or report solutions, but it's extremely easy: simply use a Union query to append a single row of aggregate functions to a SELECT query. The number of columns for each SELECT must match.

Example: qryThree Solution 3: Union Query

To create the Union query:

1. Base a query on the data source.

2. Add the appropriate fields to the query.

3. Open the SQL window and add to the existing SELECT statement a UNION clause in the form

UNION SELECT agg(fld1), agg(fld2), FROM ds

where aggis one ofthe aggregate functions,jldxrepresents a field from the original SELECT clause, and ds is the data source.

659 AppendixA

4. Save the query. The example file is named qryThree. The totals row will appear as the first or last row in the query's results, depending on how it sorts.

Each field in the original Select query must be represented in the Union query, either by entering an aggregate function, a delimited string, or a zero• length string (""). For instance, the following Statementdisplays the string "Averages" in the first column, and the result of aggregate functions in the remaining columns:

SELECT OrderiD, UnitPrice, Quantity, Discount FROM [Order Details] UNION SELECT 'Averages', Avg{UnitPrice), Avg(Quantity), Avg( Discount) FROM [Order Details]

lfyou use "" to represent the first column, Jet would sort the averaged row first in the resultset:

SELECT OrderiD, UnitPrice, Quantity, Discount FROM [Order Details] UNION SELECT "", Avg{UnitPrice), Avg{Quantity), Avg(Discount) FROM [Order Details]

Problem 4: Returning Alternating Rows

Returning alternating rows-either oddly or evenly numbered-is a simple task if your table includes an AutoNumber field. The solution uses the MOD operator and sequential values in an AutoNumber field.

Example: qryFourEven, qryFourOdd Solution 4: MOD Operator

To return a table of oddly numbered records, use a Statement in the form

SELECT fldlist FROM tbl WHERE AutoNumberfield Mod 2<>0

660 More SQL Salutions to Common Problems

Toreturn a table of even-numbered records, use a statement in the form

SELECT fldlist FROM tbl WHERE AutoNumberfield Mod 2=0

If no AutoNumber field exists, you can temporarily add one using the ALTER TABLE statement in the following form:

ALTER TABLE tbl ADD COLUMN fldname AUTOINCREMENT

Run one of these preceding Statements and then delete the temporary AutoNumber field using the DROP COLUMN clause in the form

ALTER TABLE table DROP COLUMN fldname

Problem s: Producing a Random Sort

There's no way to create a truly random sort in Access, but you can return a seemingly random set. They won't really be randornly selected, but it will seem that they are, and for most tasks, this will be adequate. The solution requires that you use the Rnd() function to return a random value for each record, and then sort by those values. Because the random values will have no connection to each record, the sorted results will seem to produce a random collection of records. You can then use the TOP predicate to reduce the result set to a specific number ofrecords.

Example: qryFive Solution 5: TOP predicate and the Rnd() function

To produce a random sort, use a statement in the form:

SELECT TOP x fldlist, Rnd(value) AS RanSort FROM table ORDER BY Rnd(value) where x is the number of records you want to return and value is a literal value or a numeric field.

661 AppendixA

The Rnd() function returns a value between 0 and 1, and that value will be the next random value in a sequence, determined by value:

• When value is greater than 0, Rnd() returns the next random value in sequence.

• When value equals 0, Rnd() returns the most recently generated value.

• When value is less than 0, Rnd() returns the same value.

One Iimitation is that Access always resets the seed value to the same value each time you launch. Ifyou need a completely random sort each time, you need to execute the Randomize() function before running the query. You may notice that Jet doesn't appear to sort the records correctly, and that's correct. The reason is that the query actually calls the Rnd() function twice, so the values that you see and the values that Jet sorts by aren't even the same. However, in the context of our solution, it makes no difference. You're not using the random values for anything other than to sort, in an effort to mix up your records in a seemingly random manner. Which set of random values Jet actually sorts by makes no difference to the solution.

Problem 6: Counting Specific Entries

You can use the Count() function to return a number of records using the syntax:

Count(*)

But, used in this way, Count() returns the total number of records in your data source. Counting a specific number of entries requires that you add a WHERE clause.

Example: qrySix Solution 6: Count() and WHERE clause

To count the number of specific entries, use a statement in the form

SELECT Count(*) AS alias FROM table WHERE condition

662 More SQL Salutions to Common Problems

For instance, to return the number of orders from the Order Details table in the Northwind database that have a Unit Price value greater than 20, you'd use the following statement:

SELECT Count(*) AS Total FROM [Order Details] WHERE [Order Details].UnitPrice > 20

Problem 7: Counting Unique Values

A Totals query (or view) will group records by different values. You can include aggregate functions in a Totals query to learn more about these groups: you can total all the values in each group, you can learn the maximum or minimum value in each group, and you can even count the number of records in each group. Returning the number of unique values in a group is a bit different. For instance, let's suppose that you want to group a table by dates and then count the number ofunique items entered for each date. This isn't the same as returning the num• ber of records for each date. A subquery is the easiest solution in this situation.

Example: qrySeven Solution 7: A Subquery

To count the number ofunique items in a group, use a statement in the fol• lowing form:

SELECT grpfield1, Count(grpfield2) AS alias FROM (SELECT grpfield1, grpfield2 FROM table GROUP BY grpfield1, grpfield2) GROUP BY grpfield2

Let's compare the results of this query to a Totals query. Let's suppose that March 1, 2001, has three entries. The entry "red" occurs twice, and the entry "blue" occurs just once. An aggregate Countforthis group (March 1, 2001) will return the value 3 because this group has three records. However, the subquery will return 2 because there are only two unique entries: red and blue.

663 AppendixA

Problem 8: Finding Unique Values

Problem 7 and its solution deal with counting the number of unique values in a group. Sometimes you may need to find each unique value, and that can also be accomplished using a Totals query and the Count aggregate. In this case, the query will return actual entries and not a count of those entries. But, this time, the query will return just one record for each unique entry, and that result will depend on the fields you include in the query.

Example: qryEight Solution 8: A Totals Query and the Count Aggregate

To return unique entries in a group, use a statement in the following form:

SELECT grpfield1, grpfield2, Count(grpfield2) AS alias FROM table GROUP BY grpfield1, grpfield2 HAVING Count(grpfield2)=1

Keep in mind that each field you add to the group changes the group. As such, this query will return different values for different groups. A table that contains three groups based on dates and different values for each group will return a dif• ferent unique value than will a query based on the same table but including only the date field or the data field.

Problem 9: Displaying Optional Data in a List Control

Chapter 5 shows you how to use an SQL statement as the Row Source setting for a list control (list box or combo box). To display data in a list or combo box, you'd use an SQL statement in the form

SELECT DISTINCTROW fldlist FROM table

as the control's Row Source property.

664 More SQL Solutions to Common Problems

Sometimes you may want to display something other than the actual items in a table, and SQL can help here as well. For instance, let's suppose that a table contains a Yes/No field, but, instead of showingYes or No in a column, you want to display some other qualifier, such as Active and Inactive, Old and New, or In and Out. Adding an Iif() function to the SQL statement solves this problem.

Example: frmNine Solution 9: Add an Iif() Function to the SQL Statement

Because the Yes/No field equates to True and False, the Iif(} function easily han• dles this problem. Use an SQL statement as the control's Row Source property, in theform

SELECT fld1, fld2, . . . , IIf( [yesnofld]= True, "truestring", "falsestring") AS alias FROM table where fldl, fld2, ... are the different fields you want to display and yesnofield identifies the Yes/No field for which you want to display optional data. The exam• ple form frmNine displays the ProductiD and ProductName fields from the Products table. In addition, a third column (Status) displays either the string "Discontinued" or "Active," based on that record's Yes/No entry in the Discontinued field. That control's Row Source property is:

SELECT Products.ProductiD, Products.ProductName, IIf([Discontinued]=True,"Discontinued","Active") AS Status FROM Products

Problem 10: Custom Sort for Controls

You learned in Chapter 5 how to sort a recordset using the ORDER BY clause, but you can also use this clause to sort a control's Iist: simply add it to the control's Row Source SQL Statement. Ifyou combine the ORDER BY clause with VBA code, you can offer sort choices to your users. In other words, instead of facing a prede• termined sort order, the user can click a choice and sort the Iist as needed.

665 AppendixA

Example: frmTen Solution 10: VBA and ORDER BY

The only condition that this technique supposes is that a control's list has more than one column. After creating and populating the control, add an option frame and insert an option button for each column in the control. Label the option frame and each option button accordingly. (Each option button should corre• spond to one of the control's columns.) Then, use the following procedure to reset the list control's Row Source property, and make the appropriate adjust• ments to the ORDER BY clause appropriately:

Private Sub frarnecontrol_BeforeUpdate(Cancel As Integer) Dirn strSQL As String Dirn strSort As String Select Case framecontrol Case 1 strSort = fld1 Case 2 strSort = fld2 Case 3 strSort = fld3 End Select strSQL = "SELECT fld1, fld2, " & "IIf( [Discontinued]= True," & '"'"truestring'"'" & "," & "'"'falsestring"'"' & _ & "") AS alias FROM table ORDER BY " strSQL = strSQL & strSort Debug.Print strSQL 1stcontro1.RowSource = strSQL End Sub

This particular procedure assumes that the control has three columns, but that's• not necessary. You can adjust the Select Case statement as needed. The user sim• ply selects the appropriate option button to resort the contents of the control by the selected option. The e:xample form frmTen allows you to sort its list control by each column.

666 APPENDIX B An Introduction to Using ADO and ADOX with SOL-

VIsuAL BASIC FüR APPLICATIONs (VBA) is the programming engine behind Microsoft Office and all its applications, but it isn't the only library available. In fact, a great deal of your programming tasks can be handled more efficiently using SQL. However, not all of SQL's functionality is accessible via Access' SQL window. Same extensions are available only through code, and that's where the data object libraries come in handy.

Microsoft ActiveX Data Objects Library (ADO)

ADO is a library of objects that represent the structure of your database and its data. In other words, ADO gives you easy access to the tables and queries in a database. In addition, you can use ADO to manipulate data. In Access, the Microsoft ActiveX Data Objects (ADO) Library is probably used as much if not more than VBA. In hierarchical terms, ADO is a high-performance interface to OLEDB, a system-level interface. That's about as clear as mud, isn't it? In simpler terms, OLEDB is a technology that allows you to interact with all data, regardless of its location, format, or type. Think of it as a universal data snatcher. A typical client/server setup has a database management system (DBMS) engine, an SQL engine, a transaction manager, a cursor engine, data, business rules, and so on. OLEDB allows all of these components to talk to one another. This magic is made possible by data providers that directly interface between an application and the native data (Access in our case). Next in line is the service provider, which interacts between the application and the data providers, and this is where SQL enters our sights. For our purposes, ADO service providers give us access to SQL extensions that we can't access any other way. And these extensions often make short work of an otherwise laborious task.

667 AppendixB

The ADO Object Model

The top dog in the ADO object model, shown in Figure B-1, is the Connection object. The Connection object creates a connection to a data provider; it's the actuallink to your data. This could mean opening an actual connection to a server or simply identifying the database that contains the data you need. You never need a translator; the Connection object takes care of all that for you. The Command object replaces DAO's torturaus QueryDef objects. The Recordset object is similar to its DAO Counterpart. (There are differences, but the object itselfisn't new.) The Record object is similar, conceptually, to a one-record Recordset object; but it always represents only one row of data. The Stream object represents a stream ofbinary data or text, such as data or email ffies. We'll concentrate on the Connection and Command objects because these are the two you'll be using to access SQL extensions.

Connection

Errors Error

Properties Property

Parameter

Property

Field

Property

Field

Stream

Figure B-1. The ADO 2. 7 object model has Jive objects. (© 2001 Microsoft Corporation. Reprinted with permission from Microsoft Corporation.)

668 An Introducdon to Using ADO andADOX with SQL

Opening a Connection

The first step to using ADO is to establish a connection to a data source using one ofthe following methods:

• Use the Open method of the Connection object to establish a connection to an external source.

• Use the Connection property to establish the current database as the con• nection's data source, which works with a Connection or Command object.

The methods available when using a connection include:

• BeginTrans: Begins a new transaction

• Cancel: Cancels the call to a method

• Glose: Closes a connection

• CommitTrans: Saves and end the current transaction

• Execute: Executes the named query, SQL statement, or stored procedure

• Open: Opens the connection to the data source

• OpenSchema: Returns information about the database schema

• RollbackTrans: Cancels any changes marle within the transaction

To establish a connection using the Open method and the ConnectionString property, use the following form:

cnn.Open connectionstring, userid, password, options where cnn represents a Connection object, connectionstringis astringthat iden• tifies the connection, userid is a string that identifies the user, password is a string that identifies the passward (when the source is passward protected), and options determine whether the connection returns synchronously or asynchronously.

669 AppendixB

The connectionstring argument consists of a series of argument = value com• ponents separated by a colon character. The five valid settings are reviewed in Table B-1.

Table B-1. Settings for the Open Method's connectionstring Argument ARGUMENT DESCRIPTION Provider Specifies the connection's provider (for a list of providers, see Table B-2) File Name Specifies a provider-specific file Remote Provider Specifies a provider when opening a client -side connection (remote data service only) Remote Server Specifies the path name of the server to use when opening a client-side connection (remote data service only) URL Specifies the connection string as an absolute URL, which identifies a resource, such as a file or directory

Table B-2. ADO Providers PROVIDER PROVIDER STRING Access Microsoft.Jet.OLEDB.4.0 SQLServer SQLOLEDB Oracle MSDAORA ODBC MSDASQL Index Server MSIDXS Active Directory Service ADSDSOObject

Armed with this information, you can quickly open a connection to most data sources. For example, let's suppose that you want to open a connection to a local Access database. In this case, your code might resemble:

Dirn cnn As ADODB.Connection Set cnn = New ADODB.Connection cnn.Open "Provider=Microsoft.Jet.OLEDB.4.0;Data Source=C:\Program " & "Files\Microsoft Office\Office\Samples\Northwind.mdb;"

670 An Introduction to Using ADO and ADOX with SQL

This code would open a connection to the sample database, Northwind.mdb, on your local system. Once you're connected, you can use the Execute method to execute most SQL statements in the form

cnn.Execute(SQLstatement, recordcount, textorstored)

where recordcount and textorstored are optional arguments. (The SQL Statement would specify any extensions.) Many of the examples throughout the book use the Connection object and the Execute method to get the job done, often replac• ing laborious VBA code to complete the same task. For more in-depth coverage of ADO's objects, properties, methods, and events, read the ADO Programmer's Guide online at http: I /msdn. microsoft. com /library/default.asp?url=/library/en-us/ado270/htm/mdobjstream.asp.

NOTE Connection strings don't haue to be long and complicated. After assigning a data source name (DSN) to a data source, a connection string may be as simple as:

cnn .Open = "DSN=datasourcename"

The defaults are assumed in this Statement.

It is also possible to connect to a secured Access database. You must provide the path to the MDW file that's used to store the workgroup security information. For example, the following code establishes a connection to a secured copy of the Northwind database, in which yoursystemfile represents the name of your MDW workgroup file:

Dirn cnn As New ADODB.Connection cnn.Open "Provider=Microsoft.Jet.OLEDB.4.0; " & "Data Source=c:\Program Files\Microsoft Office\Office\Samples\" & "Northwind.mdb;" & "Jet OLEDB:System Database=yoursystemjile.mdw;", "admin", ""

The Command object is probably used more frequently in projects (ADP files) than Access MDB files, but both SQL Desktop and Access support the Command object. If an MDB file has the potential to be upsized, the Command object is usually the better choice over the Connection object. One big advantage isthat the Command object accepts parameters. And the Command object has a few more properties, which we've listed in Table B-3, than does the Connection property.

671 AppendixB

Table B-3. Command Object Properties PROPERTY DESCRIPTION Name An optional argument, but we recommend you use it. If the Command object is executing a stored procedure, the Name property must match the name of the stored procedure. Set this property before setting the ActiveConnection property. CommandText Generally, a stored procedure, an SQL Statement, table name, or a URL where data can be found.

CommandType A constant that defines the type of command you're processing. Table B-4lists those constants. ActiveConnection Defines the data source. You can use a Connection object of a simple connection string. Prepared Precompiles ad hoc queries. CommandTimeout Determines how long to wait while executing a command before terminating the action and returning an error.

Table B-4. CommandType Constants

CONSTANT DESCRIPTION adCmdUnspecified Doesn't specify the type of query. adCmdText The CommandText string is a simple SQL command string. adCmdTable The CommandText string is the name of a table. adCmdStoredProc The CommandText string is a stored procedure. adCmdUnknown The default type, forcing ADO to guess. adCmdFile The CommandText string is the name of a file-based recordset. adCmdTableDirect The CommandText string is a table name, only used with Jet 4.0 databases and providers. You may find it unnecessary to be this specific because adCmdTable usually works just fine.

" ... when you use Connection.Execute you are creating and then destroy• ing a virtual Command object to do the actual work. The advantages to using an explicit Command object:

1. Ifyou're going to execute multiple SQL strings, there isamoderate speed boost to creating the object once and just altering its properties, rather than creating and destroying it.

672 · An Introduction to Using ADO andADOX with SQL

2. Ifyou create an explicit Command object, you can set properties that are not available through Connection.Execute. For example, you can use Command.Prepared to compile the Command (useful ifyou're going to execute it more than once), or (ifyou're using SQL Server) specify a value for Command.Properties ('XSZ:') to apply an XSL transformation to the results ofa query. Mike Gunderloy, Lark Group, Inc., (http: I lwww .larkfarm. com)

Listing B-1 shows the typical method for declaring and executing a Command object. This procedure is fairly generic because you can pass the SELECT Statement via the text argument. You could also pass the Command'I'ype setting, making the procedure even more flexible. To populate a recordset, use the procedure shown in Listing B-2.

Listing B-1. Command Object Function CrndObject(text As String) Dirn crnd As ADODB.Cornrnand Set crnd = New ADODB.Cornrnand With crnd Set .ActiveConnection = CurrentProject.Connection .CornrnandType = adCrndText .CornrnandText = text .Execute End With Set crnd = Nothing End Function

Listing B-2. Recordset Object Function CrndObjectRst(text As String) Dirn crnd As ADODB.Cornrnand Dirn rst As ADODB.Recordset Set crnd = New ADODB.Cornrnand Set rst = New ADODB.Recordset With crnd .ActiveConnection = CurrentProject.Connection .CornrnandType = adCrndText .CornrnandText = text Set rst = .Execute End With Set crnd = Nothing End Function

673 AppendixB

The Connection Property

You'll use the Connection property to set or return a reference for the Connection object. Use the form

object.Connection

where objectis either CurrentProject or CodeProject. The CurrentProject object refers to the current MDB or ADP file, and the CodeProject object is a bit more complicated and you probably won't use it as often. It refers to the code database objectwhen you are creating add-ins for Access. The active database is the CurrentProject, and the add-in database is the CodeProject. We're introducing this property to you because it provides a quick method for establishing a con• nection to the current project. Simply use the following syntax:

Dirn cnn As ADODB.Connection Set cnn = CurrentProject.Connection

This shortcut allows you to bypass all the provider and data source infor• mation that's necessary when connecting to an external data source. We use this method throughout the book in most of our procedure examples in which a con• nection is required. It is also possible to shorten the code required to open a recordset using the following code:

Dirn rst as ADODB.Recordset Set rst = New ADODB.Recordset rst.open "Ernployees", CurrentProject.Connection

ADO also permits us to work directly with data. We can use the Add.New method to add a record to an existing recordset. The only condition is that you must call the Update method to actually add the new record to the table, as shownhere:

With rst .AddNew .Fields("FieldOne") "New Text" .Fields("FieldTwo") "New Text" .Fields("FieldThree") = "New Text" .Update End With

674 An Introduction to Using ADO and ADOX with SQL

This notation is very formal, and field names can be referred to using the syntax:

![FieldNarne]

For example, in the preceding example, we could change the field line to read:

!FieldOne = "New Text"

lt is also possible to actually test if the recordset supports the capability to add new records using the Supports property. For example, the preceding State• ment could be enclosed in an IF statement block which tests this property:

IF rst.Supports(adAddNew) Then 'Run the Code Else 'Do sornething End if

We can also use ADO to update existing data. Note that, when you move to another record following the update, the changes are saved. Be careful of this if you are new to ADO. This is unlike DAO, in which the update had tobe explicitly called and used with the Edit method. The Edit method does not exist in ADO.

NOTE Updating data is best performed using SQL Statements (or, if using SQL Server, stored procedures). This example is provided for illustration only.

The main problern you might have when using ADO to update records is to identify the record to update. In the following example, we will (in keeping with the book's topic) use an SQL string to filter the recordset:

Function ADO_Update() Dirn rst As ADODB .Recordset Dirn as String Set rst = New ADODB.Recordset sql = "SELECT * FROM Custorners WHERE CustomeriD = 'AROUT'" rst.Open sql,Currentproject.Connection, adOpenKeyset, adlockOptimistic With rst !CompanyName = "Martin" !ContactName= "Martin Reid"

675 AppendixB

!ContactTitle = "Mr" .Update End With rst.Close End Function

The update could be cancelled by calling the CancelUpdate method in the fol• lowing form. However, if your code has previously called the Update method, then further calls to this method will fall:

rst.CancelUpdate

NOTE adLockOptimistic opens an updateable recordset. Record locks are not placed until an attempt is made to actually saue the update.

Microsoft ADO Extensions for DDL and Security (ADOX)

As we have already stated, some things you cannot do directly with SQL and they can be carried out only via code. ADO is used mostly for data manipulation. To work with database objects more easily, Microsoft provides an additionallibrary named Microsoft ADO Extensions for DDL and Security (ADOX), which allows you to work directly with database schema and security. The Appendixß.mdb file (which can be downloaded from http: I lwww. apress. com) contains a formthat will allow you to run the initial samples shown.

676 An lntroduction to Using ADO and ADOX with SQL

NOTE Before you try any of these examples, ensure that you haue set refer• ences to ADO 2.X and ADOX for DDL and Security. We won't attempt to discuss ADOX in depth because numerous resources are available. One ofthebest resources is ofcourse the Microsoftpage at http://www. msdn. Microsoft. com. Search for ADOX and ADO.

The ADOX Object Model

Figure B-2 shows the library's object model. The Catalog object contains details about the schema of a database, and the catalog acts as a container for all table, procedure, and view collections. In addition, it also holds the collection used to manipulate security, users, and groups. The ability to manipulate objects within the catalog depends to a large extent on the data provider. Some providers do not support changing schema information. For example, we could not use ADOX to create an SQL Server database because properties for existing tables will be read only and users and groups are not supported. We can add objects to the catalog and remove them. We can open the catalog by setting a connection to the CurrentProject, and a new catalog may be created using the Create method. We will briefly look at the Table object (following). However, a full review of these objects is beyond the scope of this section. For the most part, you won't use ADOX with SQL, but perhaps instead of SQL.

677 AppendixB

Catalog

Tables Table

Columns

Indexes Index

Columns Column

Keys Key

Columns Column

Groups Group

Users User

Users User

Groups Group

Procedures

Command

Views View

Command

Figure B-2. The ADOX Catalog object contains tables, procedures, and view collections. (© 2001 Microsoft Corporation. Reprinted with permission from Microsoft Corporation.)

The Tables collection contains all the Table objects for the named catalog. We can add a new table using the Append method, check existing tables using the ltem property, and delete tables using the Delete method. In addition, the Table

678 An Introduction to Using ADO and ADOX with SQL object also provides access to the properties of a table, keys, indexes, and columns. Using the properties and collections of the Table object, we can:

• Identify a table using the Name property.

• Discover a table's type (User, System, or Global Temporary) using the Type property.

• Examine or add items to the Colurnns collection using the Column property.

• Append a column using the Append method.

ADOX offers you another option when creating and redefining database objects. For example, to create a database we could simply execute the following ADOXcode:

Dirn cat As ADOX .Catalog Set cat = New ADOX.Catalog cat.Create "Provider = Microsoft.Jet.OLEDB .4.0;Data Source=" & "C :\Apress\Apress .mdb " Set cat = Nothing

This code fragment will create a new database called Apress.mdb in the fotder C: \Apress. If the database already exists, the code returns error number -2147217897, which can be trapperl using a statement in the form:

IF Err.Number = -2147217897 Then Take some Action

NOTE While writing the above code, I could not get it to work. I spent a considerable amount of time Zooking at references, DLL files, and search• ing the Web for answers. But I simply could not get it to run. lt turns out that I had made a simple spelling mistake in the Create method's Provider string. The moral of the story is don't look for the hard answer first. Check the code, check the spelling, and then look for the hard answer if the sim• ple check doesn't turn up a solution.

We are using the top-level object in the ADOX object model, the Catalog object. Each Catalog object refers to a single database, and the Catalog object contains the Tables, Views, Procedures, Groups and Users collections.

679 AppendixB

The following ADOX codewill add a new table to the collection and create several fields. The following code has been added to the dick event of a button on aform:

Private Sub cbo_addTable_Click() Dirn Cat As ADOX.Catalog Dirn tblCustorner As ADOX.Table Dirn Conn As ADODB.Connection Set Conn = New ADODB.Connection Conn.Open "Provider = Microsoft.Jet.OLEDB.4.0;Data Source" _ & " = F:\Chapter8\Apress.rndb" Set tblCustorner = New ADOX.Table Set Cat = New ADOX.Catalog Set Cat.ActiveConnection = Conn tblCustorner.Narne = "tblCustorner" With tblCustorner.Colurnns .Append "CustorneriD", ad!nteger .Append "CustornerNarne", adVarWChar, 35 .Append "CustAddress", adVarWChar, 35 .Append "CustTown", adVarWChar, 20 .Append "CustZipCode", adVarWChar, 12 End With With Cat.Tables .Append tblCustorner .Refresh End With End Sub

This code uses the Catalog object. Note that we also set a connection to the database named Apress.mdb, so Jet knows to which catalog to add the Table object. First, we identified the newTable object as tblCustomer. Once the tableis created, you can then append columns to the Table object's Columns collection. In the preceding case, we are appending several columns to tblCustomer. Note the data typesthat are used, particularly the Unicode as adVarWchar for the text fields. (Unicode is used with Access 2000 and above.) Table 8-2 below lists the ADOX data types and their Access 2002 equivalent. Finally, we append the table to the catalog and refresh the database to make the table visible. One simple problern remains with the preceding code, however: ifyou execute the example twice, Access will return an error the second time because the table already exists. To deal with this, simply add the line

Cat.Tables.Delete "tblCustorner"

680 An Introduction to Using ADO and ADOX with SQL following the Set Connection statement. If the table does not exist, we will still receive an error. In addition to using ADOX to add new tables to the database, we can also use it to list existing tables. The following procedure willlist all the tables within the current database in a small message box:

Private Sub cboViewTables_Click() Dirn strlisttables as String Dirn Cat as ADOX.Catalog Dirn tbl as ADOX.Table Set Cat = New ADOX.Catalog Cat.ActiveConnection = CurrentProject.AccessConnection ForEach tbl in Cat.Tables strlisttables = strlisttables &vbCrlf &tbl.Narne Next tbl Msgbox strlisttables End Sub

On this occasion, we can use the CurrentProject connection to refer to the appropriate catalog. You may also notice that system tables are included in the message. If you don't need to view the system tables, add the following IF statement after the initial For loop:

If tbl. Type <> "TABLE" Then strlisttables = strlisttables & vbCrlf &tbl.Narne End If

By checking the Type property, we can ensure that only the user tables are listed in the message box.

WARNING You need to be careful of the connection used.lt appears that AccessConnection can't be used to create indexes or primary keys.

Ifyou examine tblCustomer, you'll notice that we haven't defined a primary key. In the following example, we amend the CustomeriD field to act as the table's primary key (make sure that you have the Apress.mdb demonstration file open):

Private Sub Cbolndex_Click() Dirn cat As New ADOX.Catalog Dirn tbl As ADOX.Table Dirn IndNew As New ADOX.Index cat.ActiveConnection = CurrentProject.Connection

681 AppendixB

Set tb1 = cat.Tab1es("tb1Customer") Set IndNew = New ADOX.Index With IndNew Name = "Newlndex" . PrimaryKey = True . Unique = True End With IndNew.Co1umns.Append ("Customer!D") tb1.Indexes.Append IndNew End Sub

The data type of the preceding primary key is set at Number. We can also use ADOX to create an Au toNumher data type, which Access developers often use as a primary key. To do so, we must set the appropriate column property. For example:

.Item ("Co1umn_Name").Properties("Autolncrement") = True

lt is also possible to manipulate the autonumber seed and increment values using this method. For example, we could add the following code fragment to the prior sample:

.properties("Seed") =Clng(15) .properties("Increment") =Clng(15)

This codewill start the autonumber with a seed value of 15, incrementing the autonumber by 15 as records are added. The CLng simply ensures that the value we are using (15) is a long data type as required by this setting. In addition to working with tables, ADOX can also be used to create and amend queries. To work with queries, we can use the Views collection. To add, delete, or amend existing views, we work with the ADO Command object. In this next example, we will use ADOX to create a simple query based on our customer table that we created earlier. The following procedure will create a new query in the ADOX database file that's available for download for this appendix.

Dirn cmd as New ADODB.Command Dirn cat as New ADOX.Cata1og Cat.ActiveConnection=CurrentProject.Connection cmd.CommandText = "Se1ect CustomerName,CustAddress,CustTown From tb1Customer" cat.Views.Append "qryCustomerDetails", cmd

682 An Introduction to Using ADO and ADOX with SQL

The new query will now be available within the Database window, and you can then use it as normal.

NOTE The connection can also point to another Access database. For example changing the connection to

Cat.ActiveConnection = "Provider=Microsoft . Jet .OLEOB . 4.0;" &"Data Source=c:\Folder\Database;"

will permit you to create the query in the database specified by the path.

Deleting a query is just as easy, so be careful. The following code will delete from the database the query qryCustomerDetails we just created:

Dirn cat as New ADOX .Catalog Cat.Activeconnection=CurrentProject.Connection Cat.Views .Delete "qryCustornerOetails"

ADOX can also be used to work with database security. However, many Access developers find that using JET 4 and SQL makes the process of creating users and managing security easier. In this way, we can (for example) use SQL to create a new user account:

CREATE USER username Password pid

where pid is used to uniquely identify the user across workgroups. The equivalent ADOX code to create this user is:

Dirn cat = New ADOX .Catalog cat.ActiveConnection = CurrentProject.Connection cat.users.Append "AineReid ", "Martin" Set cat = Nothing "

Note that, in this case, we do not set a pid value, which ADOX sets automatically.

683 AppendixB

TIP Be very careful when working with security via code but especially when working directly with JET because no warnings are issued and you may find the database locked even to you! Make sure that you read and understand the Access security model before you start messing with it.

This very brief section on ADOX introduces you to a powerful technique that can be used to manipulate your database schema and objects via code. We have given you a mere taste of ADOX, and many areas have not been covered. But what you have seen should encourage you to research and experiment with these powerftll techniques.

684 Index

Symbols viewing OLEDB providers in, 644 @ (at symbol), parameter names starting when it automatically joins tables, 48 with,465 Access 2002 Form wizard, creating an @@ system functions, 425-429 auto form, 469 @@ERROR function, 428, 482-483 Access 2002 Shortcut menu in Diagram @@IDENTITY function, 426-427 window,342 @@ROWCOUNT function, 429 Access XML files, 597 @@SERVERNAME function, 429 AccessConnection object, 449-450 @@VERSION function, 429 Action queries, 8, 134-135, 262, 438-439, 502 A active records, restricting records to in Access application, connecting to SQL views, 378 Desktop, 280 ActiveX PivotTable control, 621-622 Access Data Projects. SeeADPs ADD USER syntax, 249 Access developers moving to SQL Server, ADE file, 399 resources for, 397 creating in Access 2002, 582 Access file export method, vs. ADO, 610 saving a database as, 583 Access forms, upsizing, 508-509 admin account, 245-246 Access objects, upsizing, 496-497, 510 ADO (ActiveX data objects), 667-684 Access queries, building, 8-13 % wildcard, 103 Access Securitywizard, 246-247 as preferred language for data access, Access2002 400 ASPs and, 630-653 executing stored procedures via, backup menu, 364 449-452 CDM (Client Data Manager), 286 object model, 668 creating an ADE file in, 582 opening a connection with, 669-673 creating a query from the Database supplying a value to the Max Records window,645 property, 304-305 creating an XML file using, 595 using to generate an XML file, Database window, 524, 645 608-610 how it uses SQL, 14-28 ADO AddNew method, 674 lookup controls, 126 ADO Command object, 668, 671-673 lookup fields, 122-132 ADO Command object properties, 672 relationships in, 340 ADO Connection object, 28, 643, and security, 297-301 668-670,674-676 SQL cut and pasted into, 439-440 ADO Connection object Execute and SQL Desktop, 280, 285 method,28 SQL Server and, 309, 397-440 ADO Connection object Open method, vs. SQL Server data types, 402-404 669-670 table trigger template, 395 ADO providers, table of, 670 types of Jet SQL users in, 7 ADO Recordset Update query, 138 copying empty, 165-167 upsizing to SQL Server from, 493-539 filtering, 111, 114 using to export a schema, 605-630 populating, 673 using Jet SQL in, 7-34 vs. SQL statement, 134 using namespace created by ADO, ADO Supports property, 675 610 ADO Update method, 67 4-676

685 Index

ADO XML import, tables created follow• ASP script, 635-637 ing, 611 ASPs (Active Server Pages), 630 ADOX (ADO extensions for DDL and and Access 2002, 630-653 security), 676--684 adding a record to a table, 640-647 Catalog object, 677-680 building, 632-633 database security, 683 vs. DAPs for database interaction, 613 object model, 677-678 asterisk (*) wildcard, 98, 103,269, Table object, 678-679 500-501,634,638 using to create and amend queries, attribute-centric XML, 611 682 authentication mode, choosing, 322, 542 ADP file, 399 AutoJoin option, unchecking, 48-49 ADP form properties, 301 AutoNumbers, 217, 682 ADPs (Access Data Projects), 280 replacing with Unique Identifier advantages of, 400 columns, 517 as front end to SQL Server, 398 sequential values in, 660 creating, 523 AVG function, 411 disadvantages of, 401 forms in, 301-306 B Query Builder in, 445 backing up your work, 348-366 saving as ADE files, 582 need for, 349 upsizing,503 what gets backed up, 349-350 aggregate, defined, 169 BACKUP DATABASE ... WITH NOINT aggregate data, defined, 169 statement, 365 aggregate functions, 170-178 backuplocation,356 adding to reports, 17 4 Backup menu, Access 2002, 364 types of, 121, 175 backup plan, 355 aggregates in queries, 176-178, 378 backup schedule, 350 Alias property, changing, 60 BCNF (Boyce-Codd Normal Form), 43 alias es BEGIN TRANSACTION Statement, 252 bound object inheritance of, 88 BEGIN/END keywords, 412 to solve field name conflicts, 237 Bound Column option, 347 using the AS keyword to create, 87 bound form, creating, 468-469 ALL item, adding to a controllist, 239 braces ({}), 420 ALL predicate, 73-74 brackets ([]), 73, 101-102, 201, 408 ALTER DATABASE statement, 242-243 BREAK keyword, 415 ALTERTAHLE error, avoiding, 211-212 browserURL ALTER TABLE statement, 208-210, 661 executing SQL via, 651 CONSTRAINT clause, 205-207 executing stored procedures via, optional keywords with, 210 652-653 altemating rows, returning, 660-661 built-in functions (SQL Server), 19-20, AND operator, 94-95,274 416-429 ANSI standards for SQL, 2-3 apostrophe character ('), 149 c Append queries Cartesian product, 59 stored procedures as, 453-454 Cascade Delete option, 339, 521 vs. Union queries, 235 Cascade Delete Related Records option, application role, 550-556 66--67,156,222 argument passing cascade options, 66-68, 217-224 using ADO, 28 Cascade Update option, 64, 68, 339, 521 using DAO, 27 Cascade Update Related Fields option, AS keyword, 237 55 in T-SQL, 407 Cascade Update Related Records option, using to create an alias, 87 66 ASPfile cascading deletes, 156-157, 161, creating, 632-633 221-222,224 in Internet Explorer, 634 cascading updates, 224

686 Index

CASE keyword, 413 in report Footer section, 171 GASE statements, 413-414,504-505 and WHERE clause, 662-663 CDM (Client Data Manager), 286 counting specific entries, 662-663 CE Edition of SQL Server, 311 counting unique values, 663 CHECK CONSTRAINT, 211 CREATE INDEX statement, 229 check constraints, 211, 348 Create Procerlure (T-SQL keywords), 407 Chr() function, as a delimiter, 82 CREATE TABLE error, avoiding, 207 clause (SQL), defined, 69 CREATE TABLE statement, 200-201, clauses in SELECT Statements, 75-76 203-204 Client Tools Only install option, 319 CONSTRAINT clause, 205-207 dustered indexes, 225, 369 extensions, 204 Column Count option, 347 CREATE TRIGGER statement, 389-390 column headings CREATE UNIQUE INDEX, 230 changing, 87 CREATE USER syntax, 246 limiting, 197-198 CREATEVIEW statement, 371, 374-375, specifying, 197 378,380,385-386 Column Heads option, 347 arguments, 372 column properties, 341,346 syntax, 475 column selects, 264 Criteria cell (query design grid), 9 column update, checking with triggers, Criteria pane (Query Builder), 444 391 crosstab queries, 8 Column Widths option, 347-348 creating, 192-198 CommandType constants (ADO), 672 upsizing, 505-507 comment tags (T-SQL), 407 Crosstab Querywizard, 192 COMMIT TRANSACTION statement, CSS (cascading stylesheets), 589-590 252 CSS files, 589-590 Computer Management Console, 324 current database, listing all tables computer name, choosing, 317 within, 681 concatenation,117-119 current database rotes, 561 and delimiters, 81 current permissions, 563 of literal values and variables, 78-82 custom sort for controls, 665-666 of string variables, 80-81 concatenation operator, plus sign (+) as, D 81 DAO (DataAccess Objects), 25-27 concurrent users, SQL Desktop and, * wildcard, 103 282-283 Execute method, 24-28 connection, opening using ADO, impending demise of, 400 669-673 RunSQL method, 21-24 connection strings, 449, 671 DAO library reference, 26 constraint, defined, 518 DAP section property sheet (data prop- CONSTRAINT clause, 205-207,215,218 erties), 617 constraint error message, 520 DAP toolbox, 614-615 constraints DAP toolbox objects, 615 converting validation rules to, DAPs (DataAccess Pages), 613-626 518-519 with ActiveX PivotTable control, 622 types of, 205 alignment and sizing toolbox, 616 CONTINUE keyword, 415 vs. ASPs for database interaction, control structures (T-SQL), 411-416 613 control's SQL statement, viewing, 16 Field List window, 616 conversion functions (SQL Server), form design environment, 614 421-425 Layoutwizard,623-624 convert function, 421-425 navigation bar, 620-621 to change data output, 424 Relationship wizard, 625 within Access 2002, 425 Shippers DAP Demo in Access 2002, Count aggregate and Totals query, 664 619 Count() function,171-174,177, 662 Suppliers Demo DAP, 618

687 Index

data advanced topics, 215-232 deleting from a single field, 151 introduction to, 199-213 grouping and summarizing, 169-198 setting security using, 242 inserting a single row of, 147-150 debugging tricks, 29-34 modifying, 133-168 debugging T-SQL, 432-434 removingwithDELETE, 150-161 Debug.Print statement, 29-30, 32 returning from multiple tables, 77 default value, input box displaying, 277 summarizing, 170--178 defaults, stored procedure use of, using views to insert, 387-395 463-464 Data Query Language (Jet SQL), 83-132 defaults (fi.eld default values), upsizing, data retrieval, 83-132 521 data source, specifying for a query, 10, DELETE statement, 150-161,259-262 14-17 adding subqueries to, 152-156 data structures, ad-hoc queries on, 481 adding a WHERE clause to, 261 data types avoiding, 157-161 Access 2000 vs. SQL Server, 402-404 removing data with, 150--161 setting for parameters, 267-268 deleting inactive customers, 259 database. See also upsizing issues delimiters backing up, 348-366 apostrophe (') , 149 defined, 35 missing, 81 opening in exclusive mode, 243 pound sign (#), 149 restoring, 361-366 using Chr() function for, 82 Database Diagram wizard, 330--336 DENY Statement, 581 database diagrams, 329-344 Designer window, 342-344 creating without the wizard, 337-338 Desktop engine (SQL Server 2000). See viewed in Access 2002, 335 SQLDesktop database fi.les, 299-301 deterministic functions, 416 database integrity, 354 Developer Edition ofSQL Server, 310 Database Maintenance Plan Wizard, Diagram Designer (Access 2002), 344 351-361 diagram panning, 336 database permissions, 555 Diagram window (SQL), 329,342-343 database properties, 366-367,397-401 DISTINCTpredicate, 74, llO, ll3-ll7, database roles, 547 ll9-120 creating, 559-565 DISTINCTROWpredicate, 74,503-504 current, 561 DML (Data Manipulation Language), 83, fixed,545-546 133-168 vs. server roles, 550 DoLoop,646 database security, and ADOX, 683 DoCmd.SetWarnings statement, 167 database vs. server properties, 397-401 domain aggregate functions, 121 Database window (Access 2002), 524, domain aggregates (in VBA), 175 557,645 DROP COLUMN clause, 210-2ll, 661 Database window (SQL Desktop), 294 DROP CONSTRAlNT clause, 2ll, 219, 232 Data-Defi.nition queries, 242 drag and drop, to create T-SQL state- dateformat ment, 437-439 upsizing and, 494-495 DROP INDEX, 231 using convert function, 423 DROP Statement (Jet), 213 date functions, 417-418 DROP USER syntax, 246, 249 date ranges, SQL Server vs. Access 2002, DSNs (data source names), 630 494 DTS (data transformation services), 525 date value, passing using #, 149 package design, 537 DATEPART function components, 417 using to import Access data into SQL dates, 421-425 Server, 525-538 DAvg() function, 19 DTS Import/Export Wizard, 526-538 DBCC CONCURRENCYVIOLATION, 283 choose a destination screen, 527-528 DCL (Data Control Language), 242 packageexecution,535-536 DDL (Data Definition Language), 199 source/ destination tables, 529

688 Index duplicate indexes, avoiding, 227 FROMclause dynamic subqueries, 265 for external data, 75 JOINin, 195 E Front-end/back-end configuration, element-centric XML, 611 397-398 ELSEblock with BEG IN and END, 412 function syntax, viewing in Object embedding strings, 79 Browser, 429--432 encrypted views, 383 functions Enforce Referential Integrity option, 64 inline, 454--463 Enterprise Edition of SQL Server, referring to in the query window, 459 309-310 SQL Server built-in, 19-20,416--429 Enterprise Manager, 297, 323-328, that accept WHERE clause argu- 521 ments,20 entity integrity, defined, 216 that cannot be used with indexed error handling, 167-168, 482--485 views, 384-385 error severity levels (SQL Server), 484 that do not take parameters, 425 Eval() function, 276, 278 Exclusive mode, opening a database in, G 243 GETDATE() function, 412 execution plan, 442 global defaults, created with Enterprise EXISTS predicate, 263 Manager, 521 external data, using FROM clause for, 75 global temporary tables, 454 GO keyword (T-SQL), 440 F GRANT privilege ON, 249 field GRANT statement, 249, 579-580 adding to Query Design grid, 12 GROUP BY clause, 76, 142, 178-191, 658 defined, 36 group sections, using SQL aggregates in, field cells (query design grid), 9 173-174 First Normal Form (1NF) 37--41 Group and subtotals report, 172 fixed queries grouped data omitting, 17-19 limiting, 181-191 performance hit, 96 vs. summarized data, 169 fixed server roles, 544-545 groups FORXMLAUTO statement, 647 creating,570-572 FORXML clause, 647 creating with GROUP BY, 178-191 foreign key fields, using CONSTRAINT predefined, 248 clause for, 215 groups and users, 558 foreign key value, entering before pri• GUIDs (globally unique identifiers), mary key, 223 517-518 foreign keys, 36, 38, 215, 223, 227 form controls, upsizing queries that ref• H erence, 497-501 hardware requirements for SQL Server Format function (Access), upsizing and, 2000 installation, 312-313 508 HAVING clause, 76, 267 forms combiningwithWHERE, 185-186 in Access Data Projects, 301-306 to limit grouped data, 183-184 creating bound, 468--469 with multiple criteria, 186-191 creating unbound, 469--470 heap, defined, 368 Input Parameter property, 466 HTML file, 641 parameters and, 465--4 73 attached to CSS, 589-590 passing values to Input parameters, in Internet Explorer, 599 466--473 HTML form values, 642 populating with parameter stored HTML generators, 586 procedures,466--473 HTML (Hypertext Markup Language), setting record downloads for, 302 586-591 upsizing,508-509 outputting Employee table as, 632

689 Index

HTML (Hypertext Markup Language), InputBox() function, 276 (continued) INSERT INTO statement, 143-150, 163, table created using, 587-588 167-168 vs. XNIL, 591 adding new data with, 143-150 HTML table, XSL file output formatted bypassing, 166-167 into, 603 insert permissions, 565 HTML table to hold form objects, 640 INSERT statement, 393 HTML tags, 586-587 INSERT stored procedure, 448-449 HTTP query, configuring SQL Server for, installation definitions, 318-319 648-653 installationlog file, creating, 291 installation selection screen options, I 317-318 IDENT_CURRENT() function, 427 installation of SQL Server 2000, 312-323 IF EXISTS statement, 452 Instead Of trigger, 392-395 IF THEN ELSE, 412 integrityrules, 216 Iif() function, 665 Internet Services Console, 325 Import/Export Wizard (DTS). See DTS Is Null, 271-272 Import/Export Wizard ISNULL function, 410-411 IN operator, 153 ItemData property (VBA), 90 IN predicate (PNOT clause), 197-198 index name, returning, 230 J IndexTuningwizard, 370 Jet database, securitywhile using, 541 index wizards, 370 Jet extended properties, 510-511 indexed view, creating using Query Jet indexes, 225-232 Analyzer, 385-387 JetSQL, 3 indexes, 348, 608 Data Manipulation Language, avoiding duplicate, 227 133-168 creating multiple-field, 228 Data Query Language, 83-132 defined, 225 vs. T-SQL, 4-5 deleting, 213 using in Access, 7-34 dropping, 231-232 Jet table properties, vs. SQL Server 2000, explained, 367 295-296 Jet, 225-232 JOIN, addingtoTRANSFORM, 195-196 improving performance using, join line (in Relationships window), 54 367-370 Join Properties dialag box, 53, 58 types of, 369-370 join type, changing, 58 upsizing, 516-518 joins,46-48,54,56,58,77-78 using SQL toset, 229-231 Access automatic, 48 when to use, 226 changing the default, 57 indexing views, 383-395 nested, 78 indextype arguments, 229 self-join, 59-63 INFORMATON_SCHEMA.TABLES view, updatingwith, 142 451 views for hiding complex, 378 inline functions, 454-463 parameter queries upsized to, K 501-502 Kerberos authentication, 543 return data, 457 keywords CSQL) inner joins, 46, 56 defined, 69 default, 56 vs. statements, 84 syntax for, 77 table of common, 71 inputform based on SQL Server view, 375 Input Parameter property (form), 466 L input parameters left outer joins, 46-47, 63 assigning values to, 4 71-473 LIKE operator, 97, 269-270 defined, 452 Iist control, displaying optional data in, form passing values to, 466-4 73 664-665

690 Index

Local Access database, opening a con- nested queries nection to, 670 combining,256-258 logical operators, 406 replacing with a subquery, 254-256 login, creating for user, 551-559 network traffic, reducing with views, 383 lookup column, vs. lookup field, 127 NewiD() function, 517-518 lookup controls in Access, 126 Niladic functions, 425 lookup fields NO INDEX keyword, 227 creating, 126-131 No insert/ amend permissions error, 558 enhancing, 132 NOCOUNT ON/OFE in T-SQL, 407 vs.lookup columns, 127 nondustered indexes, 225, 369 SELECT in, 122-132 nondeterministic functions, 416 lookup list, defined, 126 normalization rules, 36-44 lookup properties, 346-348 NOT EXISTS predicate, 263 LookupWizard, 127-130 NOT NULL, 203 NOT operator, 97 M NULLIF function, 411 Macromedia Ultradev 4, 613 nulls (null values), 92 macros, changing * to % in, avoiding,268-272 500-501 explained, 203 maintenance plan history, 359 in GROUP BY field, 178 Maintenance Plan wizard, 351-361 handling, 171 backup location, 356 specifying, 203 Backup plan, 355 trapping, 464-465 completing, 360 in T-SQL, 410-411 Data optimization, 353 Database integrity, 354 0 Maintenance plan history, 359 object arguments, 251 Report generation, 359 Object Browser, 437-440 Select databases, 352 function tree, 430 Transaction log backup, 357-358 viewing function syntax in, 429-432 many-to-many relationship, 45, 336 object permissions, assigning to master database, 402,451 Windows group, 578 mathematical functions, 425 objects, workingwith, 441-492 mathematical operators, 405 objects (Access), upsized to SQL Server, Max aggregate, 254-255 510 Max Records property, 302-305, 509 objects (SQL), SELECT statements in, Max() expression, 177 108-120 2002. See Access 2002 ODBC,630 Microsoft FrontPage, 613 ODBC linked tables, 399 Microsoft Management Console, OLEDB, 630, 667 324-328 OLEDB connections, using, 643 Min() expression, 177 OLEDB providers mixed mode authentication, 542 available in Office 10, 644 MOD operator, 660 viewing in Access, 644 MSDB database, 402 ON DELETE CASCADE, 218, 222 MSDE 1.0 (in Office 2000), 279,310 On Error Statements, 167, 212 uninstalling, 288 ON UPDATE actions, 218 upgrading from to SQL Desktop, ON UPDATE CASCADE, 218 288-289 one-to-many relationship, 44-45, 160, MSysRelationships system table, 223 336 multiple-field index, creating, 228 operands, 93 multistaterneut functions, 458-463 operators in SQL, 93-97 optional data, displaying in list control, N 664-665 namespace, 602 Or cell (query design grid), 9 nested joins, 78 OR operator, 94-97, 188

691 Index

ORDER BY clause, 70, 75-76, 163, 238, PivotTable, 628 381,665 to calculate a total, 627 in a sort, 119-120 securing, 629 with SQL statement results, 106-108 plus sign (+) andVBA,666 as concatenation operator, 81 Orders view, 380 to mark related data, 59 OSQL,310 pound sign delirniter (#), 149 outer joins, 46-47, 63 predicates, using to limit records OutputToarguments (Access), 631 selected, 73-75 ownership chains, 551 primary key, defined, 36 primary key fields, using CONSTRAINT p clause for, 215 PARAMETER clause, 268 primary keys, 38-41 parameter data type, setting, 267-268 PRINT Statement, 485 parameter expression privilege arguments, 250 defined, 253 programs installed with SQL Server UKE operator in, 269-270 2000,323 parameter names starting with@ (at properties, database vs. server, 397-401 symbol), 465 public role, 550 Parameter queries, 266-278, 634 for prompting the user, 266 Q upsized to inline functions, 501-502 QBE,8 Parameter stored procedure, 466-473 queries parameters adding to Query Design window, as placeholders for input, 452 10-11 default values for, 276-278 aggregatesin,176-178 forms-based values in queries, combining nested, 256-258 499-500 deleting, 683 multiple, 272-276 omittingfixed, 17-19 passing to a report, 499 that are not updateable, 121-122 passing to T-SQL, 452-453 updateable, 120-132 PARAMETERS clause, 272, 275 upsizing and, 497-508 parent table, defined, 54 using ADO to create and amend, 682 parentheses, enclosing a subquery in, whether they are updateable, 122 262 queries that reference form controls, pass-through queries, 240-241, 400 upsizing, 497-501 pass-through query properties, 241 Query Analyzer, 435-437 percentage, converting a value to, 137 creating an indexed view with, percentage (%) wildcard, 103, 105, 385-387 500-501 creating a triggerwith, 387-393 period (.) character, 33-34 split panes, 436 permanent relationships Query Builder, 8, 379, 455, 460-462, 478 creating, 48-63 in an Access Data Project, 445 defined, 49 cutting and pasting SQL, 439-440 displaying, 49-59 designing SQL Server view, 473-481 permission levels of predefined groups, for learningT-SQL syntax, 445 248 launching, 16 permissions, 543, 567 stored procedure template, 446-44 7 assigning, 577 querydatasource, specifying, 10,14-17 assigning to a view or views, 37 4 Query Design grid, 8 current, 563 Query Design window managing, 547 adding a field, 12 using roles to handle all, 566 adding a table or query, 10-11 PNOT clause, 193-195 with an aggregate expression, 255 with IN predicate, 197-198 cells,9 T-SQL and, 198 dragging fields to, 12

692 Index

lines between tables, 11 reporting, summarizing data for, 378 Update query in, 22 ReportMLlanguage,612-613 using, 8-12 reports query results, sorting, 238 adding aggregate functions to, 17 4 query types, 8 generating, 359 question mark (?), as a parameter place• passing parameters to, 499 holder, 499 resolution process (stored procedures), question mark (?) wildcard, 99-100 442 quotation marks, single (') or double ("), restore options, 362-365 79 restoring a database, 361-366 retrieving data, 83-132 R RETURN keyword, 465 RAD (Rapid Application Development), REVOKE privilege ON, 249 281 REVOKE statement, 581-582 RAISERROR, 483-484 right outer join, 47 RAISERROR syntax, 483 Rnd() function, 661-662 random sort, 661-662 role membership, managing, 54 7 Randomize() function, 662 role permissions, 562 RDBMS (Relational Database role users, 567 Management System), 36 roles read-only role, creating, 569 creating, 566-569 record, defined, 36 creating read-only, 569 recordset defined, 543 copying empty, 165-167 types of, 543 filtering, 111, 114 using to handle all permissions, 566 populating, 673 ROLLBACK TRANSACTION statement, recordset solution vs. SQL statement, 252,390 134 rolling back, explained, 252 referential integrity, 55, 64-68 Row Source Property option, 34 7 defined,53,216 Row Source Type option, 34 7 enforcing, 160, 216-225 RunSQLmethod (DAO), using21-24 need for, 216 rules for enforcing, 217 s setting, 222 sa (system administrator) account relation, defined, 36 activating on SQL Desktop, 297-298 relational database, defined, 35 enabling, 297-298 relational database terms, 35-36 login security warning, 298 relational database theory, 35-68 schema relationships, 158-159 defined,36 in Access 2002, 340 using Access 2002 to export, 605-630 creating,44-68,338-342 schema binding, explained, 384 creating and manipulating, 225 SCHEMA BINDING option, 384 creating permanent, 48-63 SCOPE_IDENTITYO function, 427 line representing in Query Design Second Normal Form (2NF), 41-42 window, 11 secured Access database, connecting to, permanently deleted, 49 671 temporary or permanent, 49 security, 541-583 types of, 44 Access2002,297-301 upsizing, 521-522 applying, 242-251 within SQL Server, 348 getting up-to-date information on, Relationships window, 49-59, 158-159 541 adding tables to, 52 SQL Server, 543-546 join line, 54 T-SQL, 579-582 Report Footer section, Count() function using Jet database, 541 in,171 using views for, 374-378 report record source, changing, 568 security node, 552

693 Index

Securitywizard (Access), 246-247 major limitations of, 282-284 Select Case statement, 666 operating systems for running, 281 SELECT INTO statement, 162-168 vs. SQL Server 2000, 287 SELECT keyword, 72 tables, 294-297 Select permissions, adding, 565 upgrading to from MSDE 1.0, 288-289 Select query, 8, 262 where it outperforms Access, 285 SELECT statement, 73-78, 83-120, who should use, 284-285 381-382 SQL grammar, 69-82 clauses, 75-76 SQL keywords in lookup fields, 122-132 defined, 69 to produce XML, 64 7 table of common, 71 in SQL objects, 108-120 SQL operators, 93-97 SELECT statement results SQL pane (Query Builder), 444 limiting, 90-105 SQL Server vs. Access 2002 data types, limiting fields in, 73 402-404 limiting records in, 73-75 SQL Server CE Edition, 311 sorting, 106-107 SQL Server Developer Edition, 310 SELECT stored procedure, creating, 44 7 SQL Server Enterprise Edition, 309-310 self-join, 59-63, 344 SQL Server Group, 326-327 semicolon (;),in SQL statements, 73,211 SQL Server properties, 298 Server Filter property, 509 SQL Server roles, table of, 544 server vs. database properties 397-401 SQL Server security, two-phased server roles, 549 approach, 543 vs. database roles, 550 SQL Server 7.0, uninstalling, 288 fixed,544-545 SQL Server Standard Edition, 310 share-level security, setting, 242-243 SQL Server tables, linking to appli- Show cell (query design grid), 9 cations, 524-538 sort, ORDERBYin, 119-120 SQL Server 2000 Sort cell (query design grid), 9 andAccess 2002,309,397-440 sort order, 107 as a Web database, 399 sorting, random, 661-662 built-in functions, 416-429 Sorting and Grouping properties, 173 components available to install, sorting query results, 238 315-316 sorting the results of a SELECT state- configuring for XML and HTTP ment, 106-108 queries, 648-653 sp_addmessage,484 console root, 325 sp_recompile, 443 custom installation, 312 SQL aggregate functions, 121, 170-178 editions, 309-311 SQL aggregates, using in Group sections, error severity levels, 484 173-174 history of, 308-309 SQL clause, defined, 69 introduction to, 307-370 SQL data types, table of, 202 minimum installation, 312 SQLDDL Object Browser, 429-432, 437-440 advanced topics, 215-232 permitting access to, 575-577 introduction to, 199-213 programs installed with, 323 SQL Desktop, 279-306, 310-311 properties, 298 activating the sa account, 297-298 Query Analyzer, 385-393, 435-437 as a valuable training tool, 285 roles, 544 concurrent users and, 282-283 security structure, 543-546 enhancements to, 286 vs. SQL Desktop, 287 environment, 293-296 Stored Procerlure wizard, 485-492 future of Access and, 280 structure,397-440 installation options, 290-291 typical installation, 311-312 installing, 287-293 upsizing to from Access, 493-539 installing from Office XP, 291-292 views, 371-395 lack of a user interface, 284 and the Web, 585-653

694 Index

SQL Server 2000 Desktop. See SQL stored procedures Desktop action queries upsized to, 502 SQL Server 2000 editions, 309-311 advantages of, 443 SQL Server 2000 installation, 312-323 as Append queries, 453-454 preparing for, 313-314 designing, 446-454 requirements, 312-313 executing via ADO, 449-452 types of, 311 executing via a browser URL, SQL Server 2000 table properties, vs. Jet, 652-653 295-296 execution syntax, 443-444 SQL Server views. See Views (SQL Server) methods to recompile, 442 SQL statement resolution process, 442 constructing within Query Builder, to populate a form, 466-473 445 square brackets in, 408 defined, 69 use of defaults, 463-464 vs. recordset solution, 134 uses for, 442-445 SQL Statement Query Builder. See Query using views, 480 BuHder workingwith, 441-492 SQL statement support, SQL Server vs. string functions, 418-420 Access, 496 T-SQL vs. Access, 418 SQL Statements, 69 using a stored procedure, 420 breaking the lines, 85 string variables, concatenating, 80-81 vs. keywords, 84 strings structure of, 70-78 embedding, 79 viewing in SQL window, 12-13 in quotation marks, 79 when upsized, 510 subdatasheets, 158 SQL (Structured Query Language), 1 subqueries, 663 cut and pasted into Access 2002, adding to DELETE, 152-156 439-440 creating and using, 253-265 dialects of, 3 dynamic, 265 executing via a browser URL, 651 enclosing in parentheses, 262 executing with VBA, 20-28 replacing nested queries with, grammar, 69-82 254-256 how Access uses, 14-28 SUM aggregate, 191 standards, 2-3 summarizing data, 170-178 using toset an index, 229-231 vs. grouping data, 169 vendor-specific extensions, 3 for reporting, 172, 378 verifying syntax of, 409 Summary row, adding to a query, wildcards supported, 97-102 659-660 SQL syntax, verifying, 409 summing a column, 628 SQLDMO (SQL Distributed system databases, 401-402 Management Objects), 583 system DSN, 632 SQL-specific queries, 8, 233-242 system views, in master database, 451 square brackets ([]), 201 System.mdw file, 541 characters enclosed in, 73 in stored procedures, 408 T used as a wildcard, 101-102 Table cell (query design grid), 9 Standard Edition ofSQL Server, 310 Table Design view (SQLDesktop), statement structure (SQL), 70-78. See 294-295 also SQL statements Table Design window within Diagram static data, publishing, 631-640 window,343 stored procedure template in Query Table pane (Query Builder), 444 Builder, 446-447 table properties, 295-296, 345-346 Stored Procedure wizard, 485-492 table relationships. See relationships completing, 490 table scan, defined, 225, 368 selecting the database, 488 table specifications, 200 table and procedure, 489 table structure, modifying, 208-212

695 Index

tables, 36 logical operators, 406 adding to Query Design window, mathematical operators, 405 10-ll Null values in, 410-4ll creating, 200-207 overview of, 407-409 creating and modifying, 199-213 passing parameters to, 452-453 creatingwith SELECT INTO, 162-168 security and, 579-582 deleting, 213 and TRANSFORM and PNOT clauses, listing all within current database, 198 681 tasks in creating, 201 u TBU (Target Benchmark Users), 282-283 unbound form, creating, 305-306, temporary relationships, explained, 49 469-470 temporary tables Unicode, 204 creating,655-657 UNION ALL clause, 234-235, 238 types of, 454 Union queries, 234-240 Third Normal Form (3NF), 42-43 vs. Append queries, 235 timestamps, upsizing, 522-523 creating, 659-660 tool tips, 432 UNION SELECT clause TOP keyword, 477 with "(ALL)", 239 TOP predicate, 74-75, 661 with pseudo column, 240 Top Values property, 657 unique constraints, upsizing and, 495 Totals aggregates, 179-180 Unique Identifier columns, 517 Totals query, 254 unique index, 370 Totals query and Count aggregate, 664 UNIQUE keyword, 230 transactionlog backup options, 357 unique values transactional processing, 251-252 counting, 663 TRANSFORM statement, 192-198 finding, 664 addingJOINto, 195-196 Update query, 138 T-SQL and, 198 executed by DAO RunSQL method, transformation script, 533 24 TransformNode Microsoft extension, in Query Design window, 22 600 update references in a main query, 258 translation log backup location, 358 UPDATE statement, 134-143 triggertemplate,389 avoiding, 143 trigger template parameters, replacing, changing existing data with, 134-143 388 and GROUP BY clause, 142 triggers, 387-395 UPDATEO function (T-SQL), 390 as stored procedures, 387 updateable queries, 120-132 checking column update with, 391 updates, cascading, 224 creating, 392-395 updating creating within the database window, with joins, 142 394 limiting records updated, 139-141 creating from Query Analyzer, of multiple fields, 142 387-393 upgrading from MSDE 1.0 to SQL running, 390 Desktop, 288-289 T-SQL debugger, 432-434 upsizing issues T-SQL statement, drag and drop to cre• of Access Forms, 508-509 ate, 437-439 affects on Access objects, 496-497 T-SQL syntax, learning using Query of dates, 494-495 Builder, 445 of defaults (field default values), 521 T-SQL (Transact SQL), 3, 404-425 ofindexes, 516--518 an add-on to standard SQL, 404 of queries, 497-508 color coding of, 406 oftable relationships, 521-522 control structures, 4ll-416 of timestamps, 522-523 delimiters, 405 of unique constraints, 495 vs. Jet SQL, 4-5 ofvalidation rules, 518-520

696 Index upsizing to SQL Server from Access, virtual directory, setting up, 648 493-539 virtual directory management, 650 things to look for before, 538-539 virtual directory properties, 649 using SQL Server DTS, 525-538 VLDBs (very large databases), 283 upsizing tools, purpose of, 493 Upsizingwizard, 511-518 w properties page, 516 WAITFOR keyword, 412, 415-416 server selection, 513 Web, SQL Server and, 585-653 table transfer, 514 Web database, SQL Server as, 399 tables selected for upsizing, 515 WHERE argument, delimiting, 80 Upsizing wizard report, 403-404, 525 WHERE clause, 70, 75-76, 80, 90-91, user groups, 558 108-109, 165-166 user login, creating, 551-559 adding conditional components to, user permissions, assigning, 577 261 user-defined functions in queries, upsiz- adding to DELETE statement, 261 ing,507-508 Changingwildcards in, 637-638 user-defined roles, 550-551 Count() function, 662-663 user-level security, setting, 244-251 combiningwith HAVING, 185-186 users, creating, 572-579 conditional expression of, 258 functions accepting as arguments, 20 V with Is Null, 271-272 validation rules, converting to con- to limit grouped data, 181-183 straints, 518-520 to limit records updated, 139-142 value, converting to a percentage, 137 with multiple criteria, 186-191 value in a group, finding, 657-659 WHERE criteria, concatenated, 113 value lists, 126, 130-131 WHERE keyword, 257 VBA functions, that upsize to SQL WHERE NOT EXISTS clause, 263 Server, 507 WHILE keyword, 415 VBA ItemData property, 90 WHILE loop, 415 VBA (Visual Basic for Applications), 251, white space (hard returns and extra 667 spaces), 85 domain aggregates, 175 wildcards executing SQL with, 20-28 in DAO andADO code, 103-105 and ORDER BY, 666 for digits (#\), 100 vendor-specific extensions to SQL, 3 SQL-supported, 97-102 views (SQL Server) 371-395 Windows authentication mode, 542 adding records via, 377 Wmdows group advantages of, 373-383 assigning object permissions to, building with Customers table, 4 74 578 closing, 4 79-480 user added to, 574 created using SCHEMA BINDING Windows integrated security, 570,574 option, 384 Windows security setup, 575 creating, 392 Windows user account, 577 designing with Query Builder, WITH DISALLOW NULL clause, 231 473-481 WITH ENCRYPTION clause, 383 encrypted, 383 WITH RECOMPILE option, 442-443 indexing, 383-395 WITHTIES clause (TOP Statement), 477 Input form based on, 375 WITHVIEW_METADATA clause, 378 restricting records to active records, 378 X stored procedures using, 480 XM:L arguments, 647 syntax for, 477 XM:L child elements, 591 using, 381-383,479-481 XM:L document using to insert data, 387-395 saving a file as, 595 using for security, 374-378 well formed, 597 working with, 372-373 XM:L elements, 591

697 Index

XML (eXtensible Markup Language), root element, 592 585,591-605 schema exported as, 604 Access report to, 612 uses for, 594-605 attribute-centric, 611 XMLfile benefits of, 593 creating usingAccess 2002, 595 configuring SQL Server for, in Internet Explorer, 593, 610 648-653 tree view, 592 customer orders via, 652 usingADO to generate, 608-610 element-centric, 611 X/OPEN standard, 2 exportingto,595-599 XPathlanguage,603 the future of data transfer, 591 XSL (eXtensible Stylesheet Language), vs.HTML,591 591 produced using ADO vs. Access file XSL file, formatting output into HTML exportmethods,610 table, 603 raw export, 596 XSLT file, 592

698 Apress Titles Publishing SOON!

ISBN AUTHOR TITLE l-893115-91-7 Birmingham/Perry Software Development on a Leash l-893115-39-9 Chand A Programmer's Guide to ADO.NET in C# l-893115-42-9 Foo/Lee XML Programming Using the Microsoft XML Parser l-893115-55-0 Frenz Visual Basic for Scientists l-59059-009-0 Harris/Macdonald Moving to ASP.NET l-59059-016c3 Hubbard Windows Forms in C# 1-893115-38-0 Lafler Power AOL: A Survival Guide 1-893115-43-7 Stephenson Standard VB: An Enterprise Developer's Reference for VB 6 andVB .NET 1-59059-007-4 Thomsen BuHding Web Services with VB .NET 1-59059-010-4 Thomsen Database Programming with C# 1-59059-011-2 Troelsen COM and .NET Interoperability 1-893115-98-4 Zukowski Learn Java with JBuilder 6

Available at bookstores nationwide or from Springer Verlag New York, Inc. at 1-800-777 -4643; fax 1-212-533-3503. Contact us for more information at sales@apress. com. books for professionals by professionals™

Apress™ About Apress

Apress, located in Berkeley, CA, is an innovative publishing company devoted to meeting the needs of existing and potential programming professionals. Simply put, the "PI.' in Apress stands for the ''Author's Press"'." Apress' unique author-centric approach to publishing grew from conversations between Dan Appleman and Gary Cornell, authors of best-selling, highly regarded computer books. In 1998, they setout to create a publishing company that emphasized quality above all else, a company with books that would be considered the best in their market. Dan and Gary's vision has resulted in over 30 widely acclaimed titles by some of the industry's leading software professionals.

Do You Have What lt Takes to Wr/te for Apress? Apress is rapidly expanding its publishing program. If you can write and refuse to compromise on the quality of your work, if you believe in doing more than rehashing existing documentation, and if you're looking for opportunities and rewards that go far beyond those affered by traditional publishing houses, we want to hear from you!

Consider these innovations that we offer all of our authors:

• Top royaltles with no hidden swltch statements Authors typically only receive half of their normal royalty rate on foreign sales. In contrast, Apress' royalty rate remains the same for both foreign and domestic sales.

• A mechanlsm for authors to obtaln equlty in Apress Unlike the software industry, where stock options are essential to motivate and retain software professionals, the publishing industry has adhered to an outdated compensation model based on royalties alone. In the spirit of most software companies, Apress reserves a significant portion of its equity for authors.

• Serlous treatment of the technlcal revlew process Each Apress book has a technical reviewing team whose remuneration depends in part on the success of the book since they too receive royalties.

Moreover, through a partnership with Springer-Verlag, one of the world's major publishing houses, Apress has significant venture capital behind it. Thus, we have the resources to produce the highest quality books and market them aggressively.

If you fit the model of the Apress author who can write a book that gives the "professional what he or she needs to know"'," then please contact one of our Editorial Directors, Gary Cornell ([email protected]), DanAppleman ([email protected]), Karen Watterson ([email protected]) or Jason Gilmore ([email protected]) for more information.