Designing Queries Manually

Custom applications can build queries programmatically or use prebuilt queries as record sources. Just as with tables, it is likely that you will sometimes build queries manually and sometimes construct them programmatically. A good knowledge of manual query design principles serves as a foundation for learning how to design and run queries programmatically.

This section explores building queries with wizards and reviews using the query Design view. While the wizards are easy to use, they substantially constrain your design options relative to manually building queries on the query design grid in Design view. The review of techniques for using the query design grid covers adding tables, selecting rows, and designating criteria. You will also discover how to construct parameter queries, union queries, and subqueries.

Using the Wizards

The Access query wizards provide a fast and easy way to create queries. This section describes each of the four query wizards.

The Simple Query wizard

The redesign of the Database window in Access 2000 makes accessing the Simple Query wizard somewhat unique. Select Queries in the Database window and double-click the Create Query By Using Wizard icon to open the Simple Query wizard. You can also reach the Simple Query wizard via the more traditional route by selecting Queries and clicking the New button on the Database window toolbar.

The Simple Query wizard enables three kinds of query design. All of its features target beginning developers. However, even you, as an experienced developer, will find you can develop queries just as fast as with the query design grid. The major disadvantage of the Simple Query wizard is that it does not permit the full range of query designs that you can achieve via the query design grid or SQL. Developers who are unfamiliar with SQL and joining rules will find its graphical interface attractive.

NOTE
There are no query wizards for the new Microsoft Access Project (this type of project has an .adp file type as opposed to the traditional .mdb database file type). In addition, the query Design view layout is different for .mdb and .adp files. See the last section in this chapter for an initial consideration of query design with Access Projects. See Chapter 12 for indepth coverage of working with remote databases and the Microsoft Data Engine.

The most basic way to build a query with the Simple Query wizard, shown in Figure 4-1, is to base it on a single table or query. Use the wizard this way to delimit the fields that a query returns. Even this simple application of the wizard adds value since restricting the return set improves query performance.

click to view at full size.

Figure 4-1. The Simple Query wizard on the way to creating a query based on a subset of the fields in the Northwind Categories table.

The real power of the Simple Query wizard comes in its ability to join two or more tables without requiring a query designer to actually create joins between tables. This lets the designer focus on the fields needed in the query and the tables from which they must come without spending any time at all focusing on the proper query design. The wizard is smart enough to generate correct results.

Figure 4-2 shows the Simple Query wizard on the way to developing a query that joins two tables in the Northwind database. All you need to do is select a table and then select fields within the table and move them from the Available Fields list box to the Selected Fields list box. The bottom window shows an excerpt of the result set of joining the CategoryName field from the Categories table and the ProductName field from the Products table.

The query in Figure 4-2 joins the Categories and Products tables on their common CategoryID field. However, at no point do you have to specify this. The Simple Query wizard automatically detects the shared field and joins the two tables on it. The wizard automatically assumes an equi-join that includes only records with matching CategoryID field values in both tables. If a record in one table had a CategoryID value that was not present in a record in the other table, the Simple Query wizard would ignore the record. If your application requires records excluded by an equi-join, then you can edit the query design from the wizard, design the query in Design view, or use SQL statements to construct the query.

click to view at full size.

Figure 4-2. The Simple Query Wizard dialog box and the resulting query's Datasheet view.

The query in Figure 4-2 joins the Categories and Products tables on their common CategoryID field. However, at no point do you have to specify this. The Simple Query wizard automatically detects the shared field and joins the two tables on it. The wizard automatically assumes an equi-join that includes only records with matching CategoryID field values in both tables. If a record in one table had a CategoryID value that was not present in a record in the other table, the Simple Query wizard would ignore the record. If your application requires records excluded by an equi-join, then you can edit the query design from the wizard, design the query in Design view, or use SQL statements to construct the query.

You can also design queries that perform some aggregations with the Simple Query wizard. The wizard automatically detects when an aggregation is possible and does as much as possible to help the query designer. It presents a dialog box like the top one in Figure 4-3 only when numerical aggregations are possible. You can still decline the aggregation route by selecting the Detail option in the second step of the wizard. If you choose the Summary option, then you should also click the Summary Options button to specify which fields to aggregate and which functions to use for the aggregation. The wizard only presents legitimate options. The wizard does occasionally prepare an extra summary field. If this happens, just delete the field in the query design grid.

click to view at full size.

Figure 4-3. The second step in the Simple Query Wizard dialog box allows you to designate the computation of aggregates in a query.

The two bottom windows in Figure 4-3 show the Design and Datasheet views of the query. Notice that the query counts the products in each category. The bottom left window shows the query design grid that computes the result. However, the Simple Query wizard removes the need to know where to place the aggregate function and what selections to make in the Total row. (You need to click the Totals toolbar button to make the Total row appear in the query design grid when designing a query manually.) The wizard relieves you of all this detail.

The Find Duplicates Query wizard

A pair of query wizards helps create queries that perform common tasks. The Find Duplicates Query wizard searches a recordset to determine whether there are two or more records with the same values. It returns the duplicate values for all records with common values for fields specified by the search criteria.

One common reason for wanting to find duplicates is to eliminate the extra copies of records from a record source. However, you cannot convert the resulting query to a delete query since it will remove the original records along with their duplicates. The Access 2000 online help describes a procedure for automatically removing duplicates based on the result from the Find Duplicates Query wizard. To discover the procedure, type "automatically delete duplicates" as the search criterion for the Office Assistant.

Open the specific instructions by choosing the prompt reading "Automatically delete duplicate records from a table."

Our sample for this wizard uses the FamilyMembers table (see the top window in Figure 4-4). The Find Duplicates Query Wizard dialog box in the middle of Figure 4-4 contains a pair of list boxes for choosing fields on which to search for duplicate values. As with the Simple Query wizard, you move fields from the Available Fields list box into the Duplicate-Value Fields list box. In the sample, I chose the Lname field on which to search for duplicates. As the top window in Figure 4-4 shows, all the records in FamilyMembers are duplicates of one of the two values for this field. Step 3 of the wizard enables you to designate other fields that will show in the return set. The sample chooses all the remaining fields.

The bottom window in Figure 4-4 highlights one of the strengths of Access wizards, which is that they simplify complex tasks. The bottom window also shows the query design grid layout created by the wizard. Notice that it contains a SQL subquery. The subquery statement includes an aggregate function as well as GROUP BY and HAVING clauses. It is this subquery in the criterion row of the design grid that detects duplicate values in the source recordset. You can fine-tune the resulting query design by adding or removing fields, including new or replacement source fields, or even by using additional criteria to eliminate some duplicates.

click to view at full size.

Figure 4-4. The Find Duplicates Query wizard. The top window shows the input record source. The middle dialog box shows the selection of a field on which to search for duplicate values. The bottom window shows the resulting query in Design view.

The Find Unmatched Query wizard

The Find Unmatched Query wizard is another feature that supports a common database management chore. This wizard returns records in one table that do not have a match in a corresponding table. It is particularly convenient for managing tables that do not have referential integrity, but that are in a one-to-many relationship. In this situation, you can inadvertently, or purposefully, remove the one or the many side of a relationship. When either the one or the many side is without a match in its corresponding table, the Find Unmatched Query wizard will discover it. You will typically want to remove these records from a record source. Happily, all you have to do is convert the automatically created query to a delete query.

The query in Figure 4-5 checks the FamilyNames table and the FamilyMembers table for unmatched records. The comparison takes place on the Lname field in both tables. The query joins the two tables by forcing all the records from the FamilyNames table into the query, as shown by the directional arrow from the FamilyNames table to the FamilyMembers table. Then, the query searches for null record values for the Lname field in the FamilyMembers table. If the record value for Lname in the FamilyMembers side of the return set is null, the FamilyNames side of the return set has no match in the FamilyMembers table. The proper design of this query requires an understanding of left joins and null values. Studying the design of queries such as this one and queries that return duplicate values can help to sharpen your query design skills if you do not routinely write queries like these.

Figure 4-5. The Find Unmatched Query wizard and its resulting query. The second record source is the FamilyMembers table. The match criterion is the Lname field.

The Crosstab Query wizard

Decision support analysts sometimes find that crosstab queries yield insights about objects and processes. Figure 4-6 shows an input table to the Crosstab Query wizard and resulting return set. I refined the return set's display in both its Design and Datasheet views.

The crosstab query in the bottom datasheet counts FamID by Relation and Lname fields from the top datasheet. The tweak I made in Design view adjusts the order for Relation from ascending to descending. In Datasheet view, I dragged the column labeled Total Of FamID from the second column to the last column. Otherwise, the layout was automatic. There was no need to enter field names and keywords in the query design grid. Nor did I have to write SQL code.

Figure 4-6. A query created by the Access 2000 Crosstab Query wizard. The top datasheet shows the input for the query return set that appears in the bottom datasheet.

Two newer technologies for analysis emerge as challenges to the crosstab queries. First, a pivot table can compute basic crosstab results from a record source, but it can additionally enable the dynamic manipulation of the data after the computation of the crosstab. This permits decision analysts to interact dynamically with their analysis results. Access enables pivot table analysis through its forms and Data Access Pages, but Microsoft Excel is the primary vehicle for performing pivot table analysis within Office. Second, online analytical processing database (OLAP) technology enables dynamic data analysis against potentially very large data sources. This technology requires a remote database server, such as SQL Server 7, and an OLAP consumer for the data. Within the traditional Microsoft Office 2000 components, Excel 2000 is the designated resource for that role.

Using Design View

While wizards enable query design with little or no knowledge of query syntax and design issues, they do not offer great variability relative to the total scope of what queries can accomplish. As is frequently the case with wizards, the design of the query wizards is sometimes more complicated than absolutely necessary to accomplish some tasks. This can complicate minor changes to a query. If you initially design a query yourself, then you are likely to recall the logic and be able to modify it easily later.

Adding tables and queries

You can create your own custom queries by adding tables or other queries to the query design grid. Taking this route will generally remove the requirement that you write SQL code to create and edit your queries. Open the query design grid by double-clicking the Create Query In Design View option in the Database window's Queries objects group. Next, add one or more tables, queries, or a combination of tables and queries to the top of the design grid. Select a table or query and click Add to move a table or query from the Show Table dialog box to the top of the design grid.

After adding a table to the top of the Design view, you need to select fields that will be part of the query in the bottom grid. There are at least three ways to add fields to the query. First, drag and drop them from the record source's field list box to the Field row in the bottom grid. If you drag and drop a field on the left top edge of an already occupied column, Access automatically moves the remaining columns to the right. Second, you can double-click an entry in the field list. This copies the field to the first vacant column in the grid. Third, you can choose a field from the drop-down box in each column's Field row. If you have more than one record source in the top of the grid, then the Field row's drop-down list enumerates fields grouped by record source.

You can use any of the three techniques with a single table to specify a few columns from a table with many columns. Figure 4-7 shows the Design and Datasheet views of a query based on the sample's Products table. This query specifies three fields from the Products table.

The Sort row in the ProductName column of Figure 4-7 is set to Ascending. This row contains a drop-down list with three options: Ascending, Descending, and (Not Sorted). If you choose the last option, the cell appears blank in the grid and the column's data is not sorted when the query is displayed in Datasheet view. Choosing Ascending for the ProductName column causes the records to appear in alphabetical order based on ProductName. If the Sort row for ProductName is blank, the datasheet in the bottom window of Figure 4-7 would appear sorted on the table's primary key, ProductID.

click to view at full size.

Figure 4-7. The top window shows the query design for a manually created query based on the Products table. The bottom window is the resulting datasheet.

It is easy to add more than one record source to a query's design. You can choose an input record source from tables and other queries. It is highly desirable to join record sources (if Access does not automatically join them based on the relationships denoted in the Relationships window). Failing to join the tables will cause the resulting datasheet to show the Cartesian product of the two tables. This will often be a table with an excessively large number of rows.

Figure 4-8 shows the Products and Order Details tables joined on the ProductID field. You can create a default join like this by dragging a field from one table and dropping it on the matching field in another table. This creates a datasheet with one row for each matching field in both tables. If a record appears in just one table, it does not show in the query's datasheet. There are two other types of joins. One adds all the rows from the Products table on the left. The other adds all the rows from the Order Details table on the right. You can choose either of these join types by right-clicking the join line and then choosing the desired join type.

click to view at full size.

Figure 4-8. The top window shows the query design for a manually created query based on both the Products and Order Details tables. The bottom window displays a Zoom dialog box that shows the formula for the calculated field in the query.

The Table row in Figure 4-8 shows four of the columns belonging to the Order Details table. Another column, ProductName, belongs to the Products table. The query design's last field does not show completely in the top window of the figure.

Referencing subdatasheets

One of the innovations introduced with Access 2000 is the ability to reference hierarchically one table or query from within another query. In a sense, it is as if one parent query has a child table or query.

Figure 4-9 displays a subdatasheet within a query datasheet. The parent query is named qryCategories. Notice the + in the query's first column. Clicking any of these, such as the one for the Meat/Poultry category, opens a child datasheet and transforms the + to a -. This +/- sign acts like a toggle switch for opening and closing the subdatasheet linked to any row in the parent query. The child subdatasheet in the figure displays the product fields for one row in its parent's datasheet. More generally, the child subdatasheet references another query or a table linked to the current row. You can concurrently open multiple child subdatasheets. In fact, a query property lets you leave all the child datasheets open by default.

click to view at full size.

Figure 4-9. A parent query, qryCategories, displays its subdatasheet for the Meat/Poultry category.

Five new query properties control the behavior and appearance of subdatasheets. The Subdatasheet Name property points at a table or query with the child data. To access this and the other four properties, open the Query Properties dialog box in the query's Design view. The five new properties are at the bottom of the dialog box. The Subdatasheet Name property box contains a drop-down box that simplifies selecting another query or table as the record source for the subdatasheet. For the query in Figure 4-9, Subdatasheet Name is the table MyProducts. Enter field names for the Link Child Fields and Link Master Fields properties to synchronize the parent query with its child query or table. As with a main/sub form, the field names do not have to be the same, but they must have the same data type. The Subdatasheet Height property controls the height of the subdatasheet within the parent's Datasheet view. Finally, the Subdatasheet Expanded property takes values of Yes or No. The Query Properties dialog box offers a drop-down list for entering either value.

Access 2000 also supports subdatasheets for parent datasheets based on tables as well as queries. However, slightly different mechanisms support subdatasheets for parent queries vs. parent tables. With a parent table, you can designate the relationship between a parent table and its child table or query in the Relationships window. If you do elect to use the Table Properties dialog box, you must open the table in Design view. Then, you must explicitly open the Table Properties dialog box by right-clicking in the top part of the grid and choosing Properties. This makes the same five properties as for queries available for setting relationships between a parent table and its subdatasheet.

Using calculated fields

You can create a calculated field that derives its value from one or more fields in a query. Use simple arithmetic and string operators, built-in functions, or custom functions to express computed fields.

Calculated fields are not directly updateable. This is because Access stores just the expression in a SQL statement, rather than the result for the calculation. You can revise calculated fields by altering the inputs to the expression for the calculation. Although calculated fields are not updateable, your applications can aggregate them across records.

Specifying fields as expressions in a query has several benefits. First, since an expression can display data without actually storing data, using an expression for a field allows a query to show a result without consuming storage space. Second, expressions recompute automatically. This enables them to reflect the most recent data without any special action on the part of a database administrator. Third, there is a rich diversity of ways to include expressions as fields. Learning these ways will heighten your overall understanding of Access.

Arithmetic operators Figure 4-8 shows a calculated field to compute an extended price. The expression for the extended price relies on simple arithmetic operators, such as multiplication and subtraction, to develop its calculated field. Notice that the Table row for the calculated field in the top window of Figure 4-8 is blank. This signifies that the field does not consume any storage space. The calculated field in Figure 4-8 uses the field label to represent a meaningful label for the field in Datasheet view.

Built-in functions Figure 4-10 shows a calculated field that relies on built-in functions. Since the expression builds a string, it can use the Left$ function. This saves memory over the more generic Left function that returns a string in a variant data type.

The expression in Figure 4-10 concatenates three distinct strings. First, it retrieves the first letter of a family member's first name with the Left$ function. Second, it appends the string ". " to add a period and a space after the first letter of a person's name. The expression closes by appending the family member's last name. This calculated field still has the default field label, but the query's Datasheet view shows the column with a different header that is more descriptive. The top right dialog box in Figure 4-10 shows that you can achieve this result by assigning a value to the field's caption. Any entry in the caption property box overrides a field expression as the column heading.

click to view at full size.

Figure 4-10. Three windows reveal the syntax and label conventions for adding a string expression to a query to define its sole field. The bottom window displays the return set from the query.

You can ease the process of using built-in functions and referring to table, query, and form values by using the Expression Builder. If you find it necessary to refine the expression after adding it with the Expression Builder or manually, use the Zoom dialog box. You can do this by right-clicking in the Field row and choosing Zoom. This opens the Zoom dialog box with your expression. Edit the expression as necessary before clicking OK to close the Zoom dialog box and enter your edited expression into the query. The bottom window in Figure 4-8 is a Zoom box that displays a calculated field from a query.

Custom functions In addition to using operators and built-in functions, you can develop your own custom functions to return field values. Figure 4-11 shows a query that links to the dbo_titles table in the SQL Server Pubs database.

This query lists the title and pubdate fields for each record. In addition, it includes a calculated field that calls the custom function daysto2K. The expression passes the pubdate field value to the function. The daysto2K function appears below. By comparing the function name below with the query design in Figure 4-11, you can see that function references in a query are case insensitive.

 Public Function DaysTo2K(InDate As Date) As Integer     DaysTo2K = DateDiff("d", InDate, #1/1/2000#) End Function 

click to view at full size.

Figure 4-11. This query uses a custom function to compute the difference between a field in the query and January 1, 2000

The function accepts the pubdate field value as an argument and returns an integer data type to the query. This enables automatic right alignment. (Failing to specify an integer return causes the function to return a variant to the query, which results in left alignment.) The custom function has a single line. It applies the built-in DateDiff function to compute the difference in days between the pubdate field value and January 1, 2000.

There is a potential downside to applying custom functions in expressions for query fields—particularly with large, remote databases. Your application must pause while a potentially large table moves over the wire from the remote server to your local workstation. In addition, you lose the power of computing your query on the remote server computer. In situations where you absolutely require a custom function, construct a query that extracts the fewest number of records from the remote server and performs all other functions on the remote server. Then, apply your custom function to the small return set from the server. These objections to custom functions are not pertinent to the sample in Figure 411 because the dbo_titles table is very small. Custom functions work well with Jet databases of any size.

Aggregate SQL functions You can compute a value without using expressions. One way to accomplish this is by clicking the Totals button—the button with a sigma (å)—on the Query Design toolbar. Then, drag one or more fields to the query design grid and choose Group By in the Total row. You must have at least one field with a numeric value whose Total row is not set to Group By. You can designate a calculated field as the numeric field. Choose Expression in the Total row. Insert an aggregate SQL function around your calculated field to report a count, sum, or average of the calculated field for each unique combination of fields whose Total row is set to Group By in the record source.

Figure 4-12 shows a query design that sums the extended price by OrderID. The query computes the extended price for each line item before summing and grouping them by OrderID. The Group By keyword appears in the Total row of the OrderID column. The Sum function around the Extended Price expression indicates that the query will compute the sum of the extended price for each order.

click to view at full size.

Figure 4-12. The expression for the calculated field in this query computes the extended price for each line item in every order. The Group By keyword in the Total row forces the aggregate function in the expression to compute the sum of values across line items for each order.

Computing a value from more than one query Statistical results are most interesting when they compare two or more bits of information. While it is useful to know the amount of each order, you may want to know whether the price of each order is above or below the average and by how much. Access does not permit you to compute this kind of outcome in a single query. My sample for this problem requires three queries, and one of these contains a Cartesian product of the other two queries. The first query is the one shown in Figure 412. The second is a query that computes the average price across all orders. This query involves a simple AVG aggregate function on the extended price for the query in Figure 4-12. The top window of Figure 4-13 illustrates the Cartesian product of the query that sums extended prices by order (qryODetailsSummedPrices) and the query that computes the average extended price across orders (qryODAvgPrices). Recall that the way to compute a Cartesian product is to include both queries in the Design view without joining them. The bottom window of Figure 4-13 shows the percent change between each order's total and the average across all orders. If the sum is greater than the average, the percent is positive—otherwise, it is negative.

The formula that computes the average percent change does not show in Figure 4-13. The expression is merely the ratio of two numbers. The numerator is the difference between the order's extended price and the average across all orders. The denominator is the average extended price across all orders. This calculated field has its format property set to Percent with 0 places after the decimal.

 % Delta from Avg: ([Extended Price]-[Avg of Extended Price])/     [Avg of Extended Price] 

click to view at full size.

Figure 4-13. The top window shows a query that computes a Cartesian product between two queries. The expression in the calculated field computes the percent change between the summed extended price per order and the average extended price across all orders.

Working with criteria

Criteria can have a critical impact on the behavior of queries. By specifying criteria with expressions and constants, you can designate which records a query will return. Criteria cause the inclusion or exclusion of records in a select query's return set. The samples in this section highlight simple queries that have one condition. Once you learn how to create these, you can readily extend the knowledge to create compound condition criteria that combine two or more individual criterion conditions with either And or Or operators.

Using a single criterion Figure 4-5 illustrates a simple criterion that uses the key phrase Is Null. Use the Is Null phrase to detect an empty field value in a query. The query's design joins the FamilyNames and FamilyMembers tables on the Lname field. The join type includes all the names from the FamilyNames table whether or not they match a Lname value in the FamilyMembers table. In the FamilyNames table, the Lname field has one of four values. These are Dobson, Hill, Simmons, and Edelstein. The Lname field in the FamilyMembers table has values of Dobson and Hill. Therefore, the query returns both Simmons and Edelstein since these two names are in FamilyNames but not in FamilyMembers. Changing the Is Null phrase to Is Not Null causes the return set to change to Dobson and Hill.

Figure 4-14 illustrates a query design that includes only those records in the FamilyMembers table with Lname field values that contain a lower case s in the fourth position. The InStr function is one of the many powerful built-in operators in Access. It compares one string vs. another in any of several ways. The fourth parameter sets the search type; 0 specifies a binary search. The InStr function returns a number that is the position of the first occurrence of the second string within the first string. For the expression in Figure 4-14, InStr finds the string s at position 4 in the name Dobson. Therefore, setting the criterion to 4 selects records whose Lname field contains an s at position 4.

click to view at full size.

Figure 4-14. This query returns the records in the FamilyMembers table whose Lname field contains an s at position 4.

If you add more names to the FamilyMembers table whose Lname field contains an s at position 4, the query will return those records as well. For example, adding a new record with an Lname value of Samson will cause the query to return Samson along with Dobson.

Notice that the result of the expression in Figure 4-14 does not show in the query's return set because its Show box is unchecked.

Using multiple criteria The query in Figure 4-15 creates a series of sentences. The query dynamically constructs the sentences from the values in fields, but it also excludes the record whose Relation value is me. (The sentences in the query's return set describe the relation of each family member to me (the person). Therefore, the record whose Relation value is me should not be in the query.) This query uses two expressions. One specifies a criterion for including records; the other is a string expression that constructs the sentences using selected field values.

click to view at full size.

Figure 4-15. A query that dynamically constructs sentences.

Using a compound criterion After creating and testing individual criteria for specific fields, you may find it necessary to create compound criteria that combine two or more criteria for specific fields. There are two basic strategies to accomplish this. First, you can combine criteria with an And operator. This design technique causes a query to return rows for records that meet all its criteria concurrently. Second, you can combine multiple criteria with an Or operator. With the Or operator, a query returns rows for records that meet any of its criteria.

Happily, the Access query design grid insulates you from the syntax details of constructing SQL statements. When using the And operator, place all your criteria on a single row of the design grid. If you want to see all customers in a particular city that have ordered more than a minimum sales amount, you might start with a query that sums sales by customer from any city. Then, in a second query that references the first, you might use compound And criteria: one criterion to designate the particular city about which you seek information and a second criterion to set a lower limit so that the query only returns customers with more than a minimum sales amount. Since the task calls for an And operator, both criteria will reside on the same row of the query design grid.

After finding all the customers that meet the criteria in a particular city, you might decide to drill down into the buying patterns of your customers within a city. To develop data on this topic, you might start with a query that joins the Orders and Order Details tables. Your query design grid can include separate pairs of criteria on multiple rows. Placing criteria on different rows within the query design grid combines them with Or logic. The criteria on each row will have a city and customer. The city will be the same for all rows, but the customer criterion will be unique for each row. To group sales alphabetically by customer click the Sort row for the Customer field and choose Ascending.



Programming Microsoft Access 2000
Programming Microsoft Access 2000 (Microsoft Programming Series)
ISBN: 0735605009
EAN: 2147483647
Year: 1998
Pages: 97
Authors: Rick Dobson

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net