Dr.
Thomas E. Hicks
Computer Science
Department
Trinity University

Careful examination of the Table Designer above, will reveal that none of the fields have been selected as an index. Using the mouse, select the Indexes Tab of the Table Designer. (See Below).

The Indexes tab also confirms that Clients.DBF has no indexes. Add the following contents to your Clients.DBF table.

When a database is not indexed, the records will be listed,
browsed, and displayed in the order in which they were added to the database.
In the browse window above, the records are in order from 1 to 7.
Note that the user, entering the data, (1) accidentally placed some blank
spaces before Mouse, Minney, (2) accidentally entered Road Runner's state
as entered Tn instead of TN, and (3) accidentally forgot to
capitalize the first letters of rabbit, roger.
Display Status<--
displays an environmental listing of the database that includes open tables,
work areas, indexes, environmental settings, etc. (Pause)
Processor is Pentium
File search path:
Code page: 1252
Alternate
- off
Fullpath
- on
List status tells us much about the database environment.
Observe the following:
Currently Selected Table:
Code page: 1252
Memo file: C:\CLIENT\CLIENT.FPT
Lock(s):
Exclusive
USE
Print file/device:
Work area
= 1
Margin
= 0
Decimals
= 2
Memowidth
= 50
Typeahead
= 20
Blocksize
= 64
Reprocess
= 0
Refresh
= 0,5 SECONDS
DDE Timeout
= 2000
DDE Safety
= on
Collating sequence: Machine
Compiler code page: 1252
Date format: American
Macro Hot Key =
UDF parameters are passed by: VALUE
Textmerge Options
Delimiters: Left = << Right = >>
Show
ANSI
- off
Heading
- on
Asserts
- off
Help
- on
Blink
- on
Lock
- off
Brstatus
- off
Logerrors
- on
Carry
- off
Mouse
- on
Century
- off
Multilocks
- off
Clear
- on
Near
- on
Color
- on
Null
- off
Compatible
- off
Optimize
- on
Confirm
- off
Print
- off
Console
- on
Readborder
- off
Cursor
- on
Safety
- on
Deleted
- on
Space
- on
Device
- scrn
Status Bar
- on
Echo
- off
Sysmenus
- on
Escape
- on
Talk
- on
Exact
- off
Textmerge
- off
Exclusive
- on
Title
- off
Fields
- off
Unique
- off
Fixed
- off
List Status To File Status NoConsole<--
sends the environmental listing of the database that includes open tables,
work areas, indexes, environmental settings, etc. to a file called Status
without displaying the results in the output window. (No Pause)
List Status To Printer<-- sends the environmental listing of the database that includes open tables, work areas, indexes, environmental settings, etc. to the printer and displays the results in the output window. (No Pause)
List Status To Printer NoConsole<--
sends the environmental listing of the database that includes open tables,
work areas, indexes, environmental settings, etc. to the printer without
displaying the results in the output window. (No Pause)
After we learn to make a create a primary index from the
Name field of the Client.DBF, the query

The Index helps to order output/information from
List,
Display All, Report Form, Label Form, Browse,
and/or
Do Form
III. Make Searching For A Common Key
Extremely Efficient
The third reason we index a database is to make searching
for a common key extremely efficient. Let us once more return to out
unindexed Client.DBF with 150,000 records. We want to print a copy of those
8,000 clients that are from Texas. When the query
After we learn to make a create a primary index from the
State field of the Client.DBF,
Use Client
<-- Open database Client.DBF
A database may have many indexes. An index can be created
from a simple field, as illustrated above. An index can also be created
from a combination of fields and/or variables; we will explore this later.
All of the indexes for database XXXX.DBF will be stored in file XXXX.CDX.
The index file has a .CDX extension and the same name as the corresponding
database table (.DBF). The .CDX file will support hundreds of different
indexes. Each index must have a unique Tag Name. Just as the field names
should be descriptive of the field, the index Tag Name should be descriptive
of the index. The index above was created from the Name Field and the index
Tag Name is Name.
Index On Name Tag Name <-- Create Name Tag (based on Name Field ) within Client.CDX [Temporarily
make Name the Master/Primary Index for the Client.DBF table.]
Browse Field RecNo = RecNo(), Name, Address, City, State, Zip, Phone, Age, Bald, Info <-- Browse

The Name Index is now the primary index in the Client.DBF;
all output/information from List, Display All,
Report Form, Label Form, Browse, and/or
Do Form will be in order by Name.
At first it might appear that the Names are out of order,
but they are not. Character information is sorted in accordance with the
ASCII
(American Standard Code For Information Interchange) collating sequence.
Blanks and case sensitivity are important when searching
character data. Better indexes will be created at the end of this document.
Blank Space = 32
The blanks at the beginning of " Mouse, Minney" have
a low ASCII value (32) thus forcing this record to the top. The lowercase
r at the beginning of "rabbit, roger" has a high ASCII value of 114 thus
forcing this record to the bottom. Things would have been much simpler
had the user been more careful and consistent entering the data.
A = 65, B = 66, C = 67, D = 68, E = 69, ..., Z =
90
a = 97, b = 98, c = 99, D = 100, E = 101, ..., z = 122
0 = 48, 1 = 49, 2 = 50, 3 = 51, 4 = 52, ..., 9 =
57
Let us once more examine the Table Designer of
Client.DBF. From the Fields Tab we can see that there is an ascending index
based on the Name field. (See Below)

From the Indexes Tab of the Table Designer, we can more carefully examine the Index. (See Below)

It is possible to create a new index with the Insert Button. It is possible to remove an existing index with the Delete button. Selecting the Expression ... button enables us to modify/examine the database index. (See Below)

The screen above can be most helpful when creating/examining/altering complex indexes.
Processor is Pentium
List status tells us much about the database environment.
Observe the following:
Currently Selected Table:
Select area: 1, Table in Use: C:\CLIENT\CLIENT.DBF
Alias: CLIENT
Code page: 1252
Structural CDX file:
C:\CLIENT\CLIENT.CDX
Master Index tag:
NAME Machine
Key: NAME
Memo file:
C:\CLIENT\CLIENT.FPT
Lock(s): Exclusive
USE
Default directory:
C:\CLIENT
Bell
- off Deleted
- on
C:\CLIENT\CLIENT.FPT
Browse Field RecNo = RecNo(), Name, Address, City, State, Zip, Phone, Age, Bald, Info <-- Browse

Once again it might appear that the States are out of order, but they are not. Remember that character information is sorted in accordance with the ASCII collating sequence. "TX" < "Tn" [T (value 84) = T (value 84) & X (value 88) < n (value 110)]
The Client.DBF table now has two indexes. The first few lines of List Status may now be seen below. Observe the references to both indexes.
Processor is Pentium
Currently Selected Table:
Select area: 1, Table in Use:
C:\CLIENT\CLIENT.DBF Alias: CLIENT
Code page: 1252
Structural CDX file:
C:\CLIENT\CLIENT.CDX
Index tag: NAME Machine
Key: NAME
Master Index tag: STATE Machine
Key: STATE
Memo file: C:\CLIENT\CLIENT.FPT
Lock(s): Exclusive
USE
Set Order To Name<-- Make the Name Tag the master/primary index. All listings, displays, reports, browse, labels, etc. will be in order by Name.
Set Order To State<--
Make the State Tag the master/primary index. All listings, displays,
reports, browse, labels, etc. will be in order by State.
Use Client
Order
Name<-- Open database Client.DBF
and make the Name Tag the master/primary index.
Use Client
Order
State <-- Open database Client.DBF
and make the State Tag the master/primary index.
Only one index can be the master/primary search index
at a time.
All tag indexes are automatically updated with any and
every
ReIndex
<--
regenerate all index files.
Should the index file, Client.IDX become corrupt, the
ReIndex command can be used to regenerate all index files.

GoTo Top <-- Move the read/write pointer to the logical top of the database. Record # 3 (above)
GoTo Bottom <-- Move the read/write pointer to the logical top of the database. Record # 7 (above)
Set Near On <-- in the event that the search is unsuccessful, move the read/write pointer to the nearest record after where the sought item would be placed in the database table. (if possible) This environmental toggle may be observed in the status listing.
Seek "rabb"
<--
move read/write pointer to record # 5 [Remember, the read/write pointer
points to the current record.]
Seek "Run"
<--
move read/write pointer to record # 2
Seek "M"
<-- move read/write pointer to record # 6
Seek "Mouse, Mic"
<-- move read/write pointer to record # 1
Seek "Woman, Wonder"
<-- move read/write pointer to record # 7
Seek "F"
<--
move read/write pointer to record # 6 [ not found - record after where
"F" would be placed]
Seek "Mouse, Min"
move read/write pointer to record # 2 [ not found
because of leading blanks - record after where "Mouse, Min" would be placed]
Seek "Duck, Huey"<--
move read/write pointer to record # 6 [ not found - record after where
"Duck, Huey" would be placed]
Seek "Clinton, Bill"<--
move read/write pointer to record # 3 [ not found - record after where
"Clinton, Bill" would be placed]
Seek "Duck, Huey"<--
move read/write pointer to record # 6 [ not found - record after where
"Duck, Huey" would be placed]
Seek "xRay, Man"
<-- move read/write pointer to EOF [ not found - record after where
"xRay, Man" would be placed]
Seek "mouse, mickey"
<-- move read/write pointer to 5 [ because it is case sensitive, this
search is unsuccessful - record after where "mouse, mickey" would be placed].
Character information is sorted in accordance with the ASCII (American
Standard Code For Information Interchange) collating sequence.
Blanks and case sensitivity are important when searching
character data. Better indexes can be created!
FirstName = "Tom" <-- creates a character memory variable whose symbolic name shall be FirstName (if it does not already exit and assigns the string "Tom" to that variable.
LastName = 'Hicks' <-- creates a character memory variable whose symbolic name shall be LastName (if it does not already exit and assigns the string "Hicks" to that variable.
NoName = " joHN dOe " <-- creates a character memory variable whose symbolic name shall be NoName (if it does not already exit and assigns the string " joHN dOe " to that variable. Leading and trailing blanks and case are significant.
Display Memory
<-- displays local variables, FoxPro system variables, window definitions,
menu definitions, and pop-up window definitions. The default number
of user variables that may exit at any one time is 1024; this may be increased
to as many as 65,000. (Only the local variables are displayed below).
FIRSTNAME
Pub
C "Tom"
Clear Memory
<-- clears all local variables, FoxPro system variables, window
definitions, menu definitions, and pop-up window definitions.
LASTNAME
Pub
C "Hicks"
NONAME
Pub
C " joHN dOe "
3 variables defined,
41 bytes used
1021 variables available
? FirstName
<-- displays memory variable FirstName - displays Tom
? LastName
<-- displays memory variable LastName - displays Hicks
? "Hello"
<-- displays character string - displays Hello
? M.FirstName
<-- displays memory variable FirstName - displays Tom
? M.LastName
<-- displays memory variable LastName - displays Hicks
NotName = FirstName <-- creates a character memory variable whose symbolic name shall be NoName (if it does not already exit and assigns the contents to the contents of variable FirstName.
Key = 5 <-- creates a numeric memory variable whose symbolic name shall be Key (if it does not already exit and assigns 5 to that variable.
Key = 5.5 <-- creates a numeric memory variable whose symbolic name shall be Key (if it does not already exit and assigns 5.5 to that variable.
Valid = .T. <-- creates a logical memory variable whose symbolic name shall be Valid (if it does not already exit and assigns True to that variable.
Valid = Not .F. <-- creates a logical memory variable whose symbolic name shall be Valid (if it does not already exit and assigns True to that variable.
Christmas = {12/25/98}
<-- creates a logical memory variable whose symbolic name shall be Christmas
(if it does not already exit and assigns 12/25/98 to that variable.
? "Hello" + FirstName
<-- displays HelloTom
? "Hello " + FirstName
<-- displays Hello Tom
? FirstName + LastName
<-- displays TomHicks
? FirstName + ' ' + LastName
<-- displays Tom Hicks
? LastName + ', ' + FirstName
<-- displays Hicks, Tom
The
Upper(var)
function explicitly returns the upper case (all capitals) version of the
passed string argument.
? '[' + Upper(FirstName)+
']' <-- displays [TOM]
? '[' + Upper(LastName )+
']' <-- displays [HICKS]
? '[' + Upper("gOoD TiMes")+
']' <-- displays [GOOD
TIMES]
? '[' + Upper(NoName)+ ']'
<-- displays [ JOHN DOE ]
TotalName = LastName + ', ' + FirstName <-- creates a local memory variable whose symbolic name shall be TotalName (if it does not already exit and assigns the contents to Hicks, Tom
TotalName = Upper(LastName) + ', ' + Upper(FirstName) <-- creates a local memory variable whose symbolic name shall be TotalName (if it does not already exit and assigns the contents to Hicks, Tom The Lower(var) function explicitly returns the lower case (all non-capitals) version of the passed string argument.
? '[' +
Lower(FirstName)+
']' <-- displays [tom]
? '[' +
Lower(LastName
) + ']'
<-- displays [hicks]
? '[' +
Lower("gOoD
TiMes") + ']'
<-- displays [good times]
? '[' +
Lower(NoName)+
']' <-- displays [
john doe ]
The
Proper(var)
function explicitly returns the proper case (all first letters capitals)
version of the passed string argument.
? '[' +
Proper(FirstName)+
']' <-- displays [Tom]
? '[' +
Proper(LastName
) + ']'
<-- displays [Hicks]
? '[' +
Proper("gOoD
TiMes") + ']'
<-- displays [Good Times]
? '[' +
Proper(NoName)+
']' <-- displays [
John Doe ]
The
LTrim(var)
function explicitly returns the passed string argument without any leading
blanks (trim the left side of string)
NoName = "
joHN dOe "
? '[' +
LTrim("
Tom ")+ ']' <--
displays [Tom ]
? '[' +
LTrim("
Hicks ")
+ ']'
<-- displays [Hicks ]
? '[' +
LTrim(NoName)+
']' <-- displays [joHN
dOe ]
? '[' + Proper(LTrim(NoName))+
']' <-- displays [John
Doe ]
The
RTrim(var)
function explicitly returns the passed string argument without any trailing
blanks (trim the right side of string)
NoName = "
joHN dOe "
? '[' +
RTrim("
Tom ")+ ']' <--
displays [ Tom]
? '[' +
RTrim("
Hicks ")
+ ']'
<-- displays [ Hicks]
? '[' +
RTrim(NoName)+
']' <-- displays [
joHN dOe]
? '[' + Proper(RTrim(NoName))+
']' <-- displays [John
Doe ]
The
AllTrim(var)
function explicitly returns the passed string argument without any trailing
blanks (trim the right side of string)
NoName = "
joHN dOe "
? '[' +
AllTrim("
Tom ")+ ']' <--
displays [Tom]
? '[' +
AllTrim("
Hicks ")
+ ']'
<-- displays [Hicks]
? '[' +
AllTrim(NoName)+
']' <-- displays [joHN
dOe]
? '[' +
AllTrim(Proper(NoName))+
']' <-- displays [John
Doe]
? '[' +
AllTrim('
John Doe
") + ']'
<-- displays [John
Doe]
? '[' +
AllTrim(Upper("
hICkS "))
+','+ AllTrim(Upper("
tOm ")) + ']'
<-- displays [Hicks, Tom]
Replace Name With "Hicks, Tom" <-- replaces database field Name with "Hicks, Tom" in only the current record. If the Name field were only 8 characters in size, it would use the first 8 characters of the string "Hicks, Tom" [Name would contain Hicks, T] If the Name field were only 20 characters in size, it would place blanks in the remaining characters at the end.
Replace All Name With " " <-- replaces all of the database Name fields with blank space.
Replace All Name With Upper(Name)
<-- replaces all of the database Name fields with their upper case equivalents.
Replace All Name With Lower(Name)
<-- replaces all of the database Name fields with their lower case equivalents.
Replace All Name With Proper(Name)
<-- replaces all of the database Name fields with their proper case
equivalents.
Replace All Name With Upper(LTrim(Name))
<-- replaces all of the database Name fields with their upper case equivalents
minus any leading blanks.
Replace All Name With Upper(LTrim(Name))
<-- replaces all of the database Name fields with their lower case equivalents
minus any leading blanks.
Replace All Name With Upper(LTrim(Name))
<-- replaces all of the database Name fields with their proper case
equivalents minus any leading blanks.

Use Clint Order To Name<-- Open database table Client.DBF and make the Name Tag the master/primary index. The read/write pointer will be placed at the logical top of the file. All listings, displays, reports, browse, labels, etc. will be in order. (See Above)
FirstName = "Tom"
LastName = 'Hicks'
Name = LastName + ', ' +
FirstName
? Len(FirstName) <-- display the length of character field FirstName. Since a three character field was created and filled with "Tom" in the assignment statement above, a 3 is displayed.
? Len(LastName) <-- display the length of character field FirstName. Since a five character field was created and filled with "Hicks" in the assignment statement above, a 5 is displayed.
? Len('Tom Hicks') <-- display the length of character string; blanks count as characters. A 9 is displayed.
? Len(State)
<--
display the size of the character State field. A 2
is displayed.
? Len(Zip)
<--
display the size of the character Zip field. A 5 is
displayed. In the event that a 3 digit zip code (123) were
entered into the current record, a 5 would be displayed because the remainder
of the field would be blank filled.
? Len(Age) <-- Not Valid - not a character field.

GoTo Top <-- move the read/write pointer to the logical top of the database table - Record # 3
? Name
<-- displays Name Duck, Donald from the open database table.
When a database field has the same name as a memory variable,
the database field has priority. It is often necessary to use the M.
modifier
to specifically identify a memory variable. When two database tables that
have the same field name are both open, it will be necessary to use the
database modifier - Client.Name vs. Employee.Name.
? Client.Name
<-- displays Name Duck, Donald from the open Clients.DBF database
table. [ Uses a database modifier]
? M.Name
<-- displays memory variable Name Hicks, Tom from the database
table. [ Uses a memory variable modifier]

Use Client Order State <-- open database table Client.DBF with State as the master/primary index. The read/write pointer is at the logical top : record 6
Set Near On <-- in the event that the search is unsuccessful, move the read/write pointer to the nearest record after where the sought item would be placed in the database table. (if possible) This environmental toggle may be observed in the status listing.
Seek "TX"
<--
move read/write pointer to record # 1
Seek "OK"
<-- move read/write pointer to record # 1
Seek "A"
<-- move read/write pointer to record # 6
Seek "Z"
<--
move read/write pointer to End of File (EOF)
Seek "fl" <-- move read/write pointer to EOF [ because it is case sensitive, this search is unsuccessful - record after where "fl" would be placed]. Character information is sorted in accordance with the ASCII (American Standard Code For Information Interchange) collating sequence.
List For
State = "TX"
Report Form
PhoneList For State = "TX"
Label Form
Mail For State = "TX"
Display
All For State = "TX"
Set Filter
To State = "TX"
State is now the master/primary index. At least part of
the State index resides in RAM when the database is opened; our database
table with 100,000 records might split this index into several parts. It
is beyond the scope of this document to discuss exactly how the indexes
are designed and accessed. For the sake of discussion, let us concede that
the indexes containing the 2,000 TX records might require as many as 1
or 2 disk accesses to load all of the indexes into memory. With a State
index, each of the queries above
These queries require all 20,000 Client.DBF records to be
examined and possible 1 or 2 Client.CDX records. As you might expect, this
would be almost 50 times faster than processing the queries without an
index. If a query by state is only to be done one time, it is certainly
not worth while to create an index. If a query by state is going to be
done occasionally, a state index should be created.
Index On AllTrim(Upper(Name)) Tag Name <-- Create Name Tag (based on Name Field ) within Client.CDX. In producing the index keys, all leading and trailing blanks will be removed from the Name field and all characters will be converted to upper case capitals.
Browse Field RecNo = RecNo(), Name, Address, City, State, Zip, Phone, Age, Bald, Info <-- Browse would then produce

This new Name index will correct data entry errors that accidentally insert blank spaces at the beginning or end of a the name field. This index will correct data entry errors made by accidentally selecting the wrong case in the name field. As we shall see in FoxProE, this index will make searching by the user more successful. Unfortunately, it does not correct problems that occur when a user enters multiple spaces between the first and last name.
Seek "RABB"
<--
move read/write pointer to record # 5
Seek "M"
<-- move read/write pointer to record # 6
Seek "MOUSE, MIN"
move read/write pointer to record # 4
Seek "MOUSE, MIC"
<-- move read/write pointer to record # 1
Seek "WOMAN, WONDER"
<-- move read/write pointer to record # 7
Seek "F"
<--
move read/write pointer to record # 6 [ not found - record after where
"F" would be placed]
Seek "CLINTON, BILL"<--
move read/write pointer to record # 3 [ not found - record after where
"Clinton, Bill" would be placed]
Seek "DUCK, HUEY"<--
move read/write pointer to record # 6 [ not found - record after where
"Duck, Huey" would be placed]
Seek "Z"
<--
move read/write pointer to the EOF().
Index On AllTrim(Upper(State))
Tag State <-- Create
State Tag (based on State Field ) within Client.CDX. In producing
the index keys, all leading and trailing blanks will be removed from the
State field and all characters will be converted to upper case capitals.
This would be a better State index.
Let us return to the Indexes Tab of Table Designer
for the Client.DBF after entering the alternative indexes from this section.
(See Below)

Selecting the expression ... button enables us to modify/examine the name index. (See Below)



Let us create some good indexes for database Employee.DBF above. Note that the separation of the first name and the last name above is more likely to standardize the user input; it is now impossible for the user to enter multiple blanks between the last and first name; it is impossible for the user to forget the semicolon.
Index On AllTrim(Upper(FirstName))
Tag FirstName
The index pair above is poor. We can have only one master
index. If the LastName were the master index, then all of the Smith's would
be together, but (1) it would not be possible to use the seek command to
find Smith, John as opposed to Smith, Michael (2) it would not be possible
to get labels, reports, or screens to properly sort the employees by their
full name. The compound index below demonstrates how multiple fields
can be used to comprise an index.
Index On AllTrim(Upper(LastName))
Tag LastName
Index On AllTrim(Upper(LastName)) + ', ' + AllTrim(Upper(FirstName)) Tag LastName <-- builds the index, called Name, from the lastname and the firstname fields.
Index On EmployNo Tag No <-- builds the index, called No, from the EmployNo field. Since numeric data can not have leading blanks, there is no need to trim the index. Since numeric data can not have character data , there is no need to upper the index. The people will be in order from the largest employee number to the smallest.
Index On EmployNo Tag No Ascending <-- builds the index, called No, from the EmployNo field. Since numeric data can not have leading blanks, there is no need to trim the index. Since numeric data can not have character data , there is no need to upper the index. The people will be in order from the smallest employee number to the largest.
Index On EmployNo Tag No Descending <-- builds the index, called No, from the EmployNo field. Since numeric data can not have leading blanks, there is no need to trim the index. Since numeric data can not have character data , there is no need to upper the index. The people will be in order from the smallest employee number to the largest.
Index On BirthDate Tag BirthDate <-- builds the index, called BirthDate, from the BirthDate field. Since date data can not have leading blanks, there is no need to trim the index. Since date data can not have character data , there is no need to upper the index. The people will be in order from youngest to oldest.