Escolar Documentos
Profissional Documentos
Cultura Documentos
What is a Database?
The term database has been applied in a number of different ways,
many specific to the development context in which the word is mentioned. Ignoring the marketing driven terminology for the moment, a
database is best described as simply a collection of related data. The
relationship is defined by some natural or forced affinity between the
items, or records, that make up the collection. Figure 1.1 shows a file of
,.
i
invoices, all related by what they represent and the shared informati4
contained within each invoice.
INVOICE
1098
INVOICE1322
INVOICE1779
figure 1.1
The paper file
system was
emulated in
the first database systems.
Deleting
n
n
unneeded
records
.:g
FRIENDS
Figure I.2
The FRIENDS
f/at file
database
Geurii
Sharon
Rollins Hank
722 Boston Ln
PO Box 89
818 986-2091
Geurii
Wayne
722 Boston Ln
Warhawk Dr
909 221-4456
303 458-8095
Tom
Marion
909 221-4456
Each record in this table contains one entry about each of the peo@e
Yj
that we know. Despite the advantages provided by its simplicity, the fk#
file poses a number of different problems for the developer and user ;,,*t
alike. For example, because all instances of a friend must be con:.;
tained in a single file, the possibility of update anomalies affecting the-_
integrity of the data is great. Notice that two records are needed to ,G
-l.i(
/?.sV
maintain the telephone number for the Geurii family members; two
records are needed because both Tom and Sharon are our friends and
this is the only way of representing both of their names. Modifying the
telephone number that they share requires two discrete operations, one
to modify Toms record and a second to modify Sharons. Due to the
duplication of data, the possibility exists that both entries may not be
updated correctly, or at all, leading to problems with the integrity of
the data. The next time that the phone book database is used to telephone Sharon, we might discover that a disconnected telephone
number has been stored rather than the correct exchange.
A common problem encountered with paper-based files also appears as
a glaring weakness in the flat file. If the records are filed according to
the spelling of the last name, a simple misspelling causes otherwise
matching records to be separated. Tom might be filed correctly as
Geurii but Sharons record could easily be misspelled as Guerii. While
this problem is certainly not limited to the flat file paradigm, the
record-by-record methods that are required when updating data compound this problem. If the update action relies on a search of last
names, only one of the Geurii family records will be updated with their
new address, causing a billing to be sent to the old address and not
received.
Flat file database implementations are usuall>- tightly bound to the
physical storage method used to place the records on disk. Because of
this, the user access methods provided for working with the table
closely match their actual implementation methods. The user perceives
that flat files are being accessed with a record-b>--record approach. The
database access methods are coded in such a ]\a!- that each record is
approached discretely, not recognizing a connection between the
related records.
The flat file database was a logical intermediate solution when the leap
was made from paper-based record keeping to the electronic storage of
data. The problems that plagued paper records followed the data to
disk and the speed at which the technology brought the problems to
the surface made it clear that a new approach was necessary.
Inr
Smelt
Tidepool Company
Crab
Clam
Tuna
Shark
Swordfish
Oyster
Abalone
Clam
repeating
..*::
? CL
I A;/
_ _ _ I n^.
I _i
j
__ -414~1~~-~ ,T
;.$
A domain is the defined set of values from which the data in a column 3
can be drawn. This pool of values describes the entire range of accept- $2
->:;jc*
able data elements. For example, the domain of the column
Employee-ID is the range of valid employee ID numbers. Items not
belonging to that collection of values, such as the employees last name ,::I
or an ID number beyond or below the valid range, fall outside of the
A
,I
domain for that column.
f*
With a domain for each column defined, the user is rewarded with con- 134
sistency in their data. Since the columns make up the attributes, or
I$
description, of each row added to the table, the user knows that each j$
2J
row retrieved will have the same description. Applying this rule also
simplifies data validation. Because you will always apply this rule, busi- $j
q
ness rules can more readily be applied to each data element.
. ;$,;
.3
Each Row
is
Unique
No two rows in a relational table are identical; at least one column
uniquely identifies the contents of each row. The power of this property $4
cannot be underestimated. In a non-relational data collection, it is pos- -5$
sible, and likely probable, that there is a considerable duplication of -1
information. Retrieval operations must then be designed to seek and
f&
compare, record by record, the retrieval request against all of the col- .&j
$
umns in the table in order to satisfy the request. Modification and
manipulation requests are equally at risk; slight differences in a num$
ber of conceptually matching records leads to the possibility that they
::
will not be selected for updating.
The uniqueness of each row is enforced by the presence of a key value.
A more extensive discussion of key values is presented in Chapter 2,
but for now, the definition of a primary key is a column, or a minimal
set of columns, that uniquely describes each row. Two simple rules
apply to the primary key of a relation: First, it cannot be a null. A null
value breaks the rules immediately; it does nothing to describe the row.
Secondly, the primary key must be unique within the table. A duplicate
key value would indicate duplicated data in other fields, destroying the
integrity of the relational operations.
-j
,!
:-:!
,y :x
.&a
;
;I
:
1;
.?
The existence of a key value gives the designers of the data access and
manipulation operations the freedom to build their routines around
fast and simple search methods. With the uniqueness characteristic
guaranteed, the access method can be confident that once it has
located a key value that matches the retrieval request, all of the rows
matching the request have been located and returned to the user.
:
2
.I
.i
1
Ino
(.
-.*_.
Referring back to Figure 1.3, we see that the Vendor column uniquely -,;.
identified each of the rows. With the conversion performed to create . +
the table in Figure 1.4, the database now is at risk of containing dupli-5:.
cate rows. As we will see in Chapter 2, its easy to design a solution to $
meet both criteria.
,_.;
,
By not placing any importance on the order of the columns, the relational table is ensuring that there is no hidden meaning in the order o&j
the data in each row. Without concern for the sequence of the columnq;
the database user is free to retrieve the data in any combination or .i $
order, viewing the data as they see fit, not as enforced by the databas$s
:i<! ;..::
The VENDOR table in Figure 1.5 is shown in two configurations.
Kingfish, Inc.
Tidepool
Coos Bay
OR
Reedsport
OR
9a(
OR
90*3r;;
OR
903 f &$j
El Perro Blvd
Lincoln
Navy
Rockaway
Way
City
Beach
O R
Coos Bay
Kingfish, {nc.
90421
OR
Reedsport
Keiko Avenue
Tidepool
90231
OR
Lincoln
El Perro Blvd
The Crab
OR
Rockaway
9 0 3 3 3
9031
City
Company
Walk
::
The sequence of columns in version two is reversed, but the underlyin$$
data remains the same. Though the columnar sequence has changed, <$
each row still represents the same entity. In your database and code ,. .:i
designs it is important to maintain this condition. Designing data accee
routines that rely on an artificial sequencing of the columns in your
-:
data tables will result in maintenance difficulties prevented by the reEa:j
tional database design.
: ;
(>
.::.*
*a*-*
Data
Integrity
One of the most critical aspects of any data management scheme is the
integrity of the data and how well its internal methods ensure it. The
integrity rules of the relational database model focus on constraining
the values contained within the columns of each table; without the constraints, values in the columns would be free to assume incorrect
values. Simplicity is the key in this regard; there are only two rules that
encompass the whole of the integrity constraints, the entity integrity
rule and the referential integrity rule.
The two relational integrity rules represent business rules, not technical
considerations. The relational database definition requires that the
tools needed to maintain the integrity of the database be an integral
part of the implementation. At the same time, the user should never
need to be concerned with them from any technical standpoint; relational integrity, once set through technical means, should be
transparent to the end user.
Entity integrity
The first rule is simply a repeat of the earlier structure requirement that
each row in a table be unique. The entity integrity rule requires that no
part of the primary key of a table be allowed to accept null values.
Blank data fields do not lend themselves to an adequate description of
the row, so requiring data elements in the column or columns that
make up the key to the row makes perfect sense. Requiring that the key
for each row be unique fulfills the mission of the relational model.
,,_=
.1
Referential Integrity
The rule regarding referential integrity introduces a new topic to the
discussion, the foreign key. Aforeign key is a column or a combinati&$
of columns that serve as the primary key in another table. The reasa i_
exists in the table of focus is to provide a method of linkage betvveea 2
the two tables. The relationship between the tables is often describ+$i
as a parent-child relationship, and, as shown in Figure 1.6, this rela- :$
*I . _
tionship must be maintained at all times.
Kingfish, Inc.
8213
Tidepool
947 I
The
Crab
Devil
Company
Walk
7790
Ray Foods
9022
V E N D O R (Parent)
I30
82 I 3
Smelt
230
947 I
Crab
PRODUCTS
(Child)
Data Manipulation
The third aspect of the relational database model concerns itself tin.=*
the manipulation of data. The model defines two categories of ope *
tions that can be performed using the relations:
a
,.
,,/.
.__
_._
Division
The division operator results in a relation that contains colu
from one table for which there are other matching column
sponding to every row in another table. In other words, a r
going to be divided by another relation with the quotient b
relation. Consider the example shown in Figure 1.7. Dividing the !T
SUPPLIERS
Kingfish, Inc.
33c
Crab
100
Kingfish, Inc.
4T
Tuna
200
The
c22
Crab
300
CRXXI
Crab
100
t:
Kingfish. Inc.
v3
Onion
300
VGXX9
Onion
200
The
BR
Bread Cr
300
Crab
Crab
Walk
Walk
1 Fuo<67
Bread
Crumbs
RECIPE
RESULTS OF DIVISION
.$g
&
The relational database model is the de facto standard for data man:,
agement software and development tools. As a developer, it is criti+S
that you understand how closely your choice of tools follows the
rules of the relational paradigm. By understanding the theory be
the implementation, you are in a much better position to exploit
power of the database. The following two chapters continue to p
the bedrock knowledge that is the mark of the experienced databe;_
developer.
create a.1
proper design pays off when the rubber hits the road and the time corr *
to develop a database application. The application creation process is 1gL-a
simplified, as numerous problems and issues have already been
addressed up front, before the first Begin statement is coded. If you dont
care to spend the time to carefully consider your database up front, you
can simply pay the price in stability, accuracy, and programming effort
needed to make it right on the back end.
28
_-%p.zd
.i
F
20 n PaT-t
I- -The
Relational
Database
First, the design for the database will be mapped through the creation
of entity-relationship diagrams, a standard tool that makes the database members crystal clear and puts the information into a format that
can be quickly converted into relations. Secondly, the process of normalizing the relations described in the ER diagrams will be examined in
detail. Normalization is the process by which the relations you have
designed are tested against the rules of the relational database, and
through multiple design iterations they are manipulated into place.
Finally, the process of mapping the design to a physical data structure
will be explored, setting up your design to be converted to a physical
implementation.
,j
..!,
$
2
52
;
,>
;
S
:i
Entities
In reviewing the ER diagram, it is easy to see that the two most critical ;;i
components of the database model are the entities and the relation-: i -4;;
ships between them. Before applying these diagramming tools,
.::1/
establishing some definitions is in order.
.v
;?,I.
, ;:;s
An entity is any object that the user wants to represent in the databae- i--i
and which she wants to record facts about. Examples of entities are
..3
VENDORS or ITEMS or CDs; any people, places, or things involved wi$$&$
the subject of the database. Entities are composed of attributes, facts
%$1
_-- -.
.-
F _ (_
..->
-_
that describe the object. For example, a VENDOR will have a name,::
street address, and city attributes that serve to make that instance (
VENDOR different from all the rest. Attributes should be simple, disg
Crete pieces of data such as the city or state, atomic in nature, mea&
that they describe only one element of the object.
1.r
Relationsh
ips
the case of two entities, VENDOR and ITEMS, we can identify a rel&?!$
tionship as supplies, as in the VENDOR supplies ITEMS. Thrt:e br
categories, generally described by verbs or prepositions, are general
indicators of the existence of a relationship:
w Existence relationship-a VENDOR has EMPLOYEES
w Functional relationship-an EMPLOYEE fi&Hs ORDERS
n Event relationship-a VENDOR delivers ITEMS
The entity-relationship diagram displayed in Figure 2.1 demonstratd _ _
.i
pronertv of the defined relationships; each has a cardinalin/.
One-to-One I : I
INVOICE
LINE
ITEMS
One-to-Many I :M
Figure 2. I
Cardinality of
relationships
PRoDUCTS
l-----l
Many-to-Many
PARTS
M:M
22
#..
occurrence of a related record in other entity in the relationship. In Figure 2.1, the first example says that for each instance of WAREHOUSE
there is exactly one WAREHOUSE MANAGER.
rip
There are many accepted methods for the graphical representation of an entity-relationship diagram. For purposes of simplicity,
we are going to document the cardinality degree of our relationships with one of two symbols. One-to-one relationships will be
denoted with a single arrowhead (-+). Relationships with multiple
cardinality will be shown with two arrowheads on the relationship line (++).
PROGRAMMER
JO+
/
(Yp,,,
PROJECT Y
-i
Figure 2.2
A manyto-many
relationship
decomposed
TASKS
III
Primary Keys
The singular nature of items in a relational database system is the key
concept that drives the paradigm. By ensuring that each record is
unique in some way, the results of either a query or data manipulation c,;
operation can be guaranteed to be correct by the database system. A
.:
primary key is an attribute value on which the non-redundancy rules
.
are heavily enforced. Though attributes in the remainder of the record r
,t,
24 n
_,
may match similar attributes in other records, in tandem with the key
value, they are unique.
The first step taken in this part of the design process is to identify an
attribute or a minimal set of attributes that can be used to uniquely
identify a record to become the primary key Sometimes it may become
necessary to introduce a new attribute to the relation if a single attribute or a set of attributes (a composite key) cannot be identified. As an
example, in an EMPLOYEE relation, this may take the form of an identification number that is applied to each record. A key may also be
composed from a minimal set of attributes, the smallest number of
attributes that are necessary to uniquely identify a record. Testing to
determine if it is truly the minimal set of attributes involves the
removal of any one of the attributes to determine if it results in a loss
of the uniqueness. Any attribute or attributes that are selected as candih
date keys must meet the requirements of the primary key, uniqueness
and always being non-null.
.,?
-j4
.,$
,g:;
:$iC-3
jj:,$
In the VENDORS entity, the name of the company alone may serve to
uniquely identify each record. An ITEMS table might select the item
number as a candidate key since no two products will share the same
identifying number. On the other hand, if we deal with a number of
divisions of the same vendor, the name alone will not suffice to ensure
uniqueness. In this case we will need to create a composite key of multiple fields. Combining the vendor name and zip code creates a unique
identifier that can be used.
:
;
~
2
:?
1:~
?;
$
,j
*ii
Y
Alternate Keys
The candidate keys that were not selected as the primary keys become
;
:;:,::
alternate keys. These keys will become tools to help implement easier
access to the data for your users, perhaps becoming indexes. The pur.:j
pose of identifying the alternate keys is to provide substitute access
_- 3
~$. t<
paths.
*
Foreign Keys
Aforeign key serves a critical purpose in a relationship. It is an attribute ,Pw2:y
or set of attributes that identifies the parent record. In other words, the $j
foreign key is the attribute that links the child occurrences to the par~~4
2
ent entity occurrence through matching key values. The foreign key is
L.z
:. -j
artificially present in the child entity and is the primary key of the
parent.
i._
/*;1
l
_
_
* r .~*r_s.Bcs)~
_c. eie._*_ ,. __
).
ITEMS
901
Disk
902
CD-ROM
903
Book
904
.
I
.
Disk
200
200
I
I
Figure 2.3 The VENDOR-ID attribute is the primary key in the VENDORS em
und a foreign key in the /EMS entity.
Tip
- .,.~
,x
3
,:;
2
;?
:?
.,-d
26
fart/ -The
Relational
Database
,-.$
: &$f
I
Each attribute in every table should be reviewed with the user to deter-$
mine the constraints necessary for maintaining the integrity of the data.Gg
Acceptable ranges of data as well as format and type are all items that I?$
work together to implement a business rule. The rules will become a
t?
part of the physical database implementation, either directly imple:$
mented through the data structure or handled by the supporting code. 2;
r;
There is a second aspect of the rules that needs to be discussed, the
1:
issue of triggering. Business rules become a part of the field definition ii;
for each relation. Obedience to the rule is tested during specific opera- ;
tions on the database. These actions are called triggering operations.
.-I!
Three database operations trigger the business rules tests:
- .
l Adding data to a database
>*:
m Deleting data from a database
.:I
*.w Updating the data in fields of a database
Triggering operations cause the relational system to validate that the
~.;
database is being changed according to the rules defined for the partic- 3
2~1
ular field. For example, when adding a record to the SERVICE CALL
table, the data being entered for the Area Code field is validated
against the four acceptable codes that were defined. Entering 415
would generate an exception and the RDBMS control mechanisms
would prevent the user from entering this data.
Likewise, a deletion operation should trigger a more complex validation
procedure. Take the example of an INVOICES table that draws the ven- I ji
dors address from the VENDOR table. A business rule applicable to this :J
situation is:
,_-
i~--4-,*n.i_-*-,
Case Stu dY
The best way to apply these conceptual tools is to work our way
:
through an example. To do this well examine a common database LB
application, the order entry system. This business model is easy to .S
understand: A company sells items to customers. The customers
the company and provide their name and address and payment i
mation, which the company retains for future reference. The custd
selects the items that they would like to purchase and orders the
company, in turn, pulls the item from inventory, prepares an in
the offsetting entry to the customers payment, and ships the it
dors ship the items to us that we sell to the customers. The Re
Fish Foods Company will be the company that opens its doors to
exploration.
The Playerl S
In this example, it is relatively easy to pick out the entities. Reme
that the entities are those people, places, or things about which we4
want to record facts in the database. Figure 2.4 collects the ident
entities for Redwood Fish Foods and, as shown, we will be using
standard rectannular remesentation of an entitv in all of the dia
CUSTOMERS
Figure 2.4
The entities of
the Redwood
Fish Foods
Company
/ INVOICES
/ CREDITTERMS
Iii
Part I -The
2 8 H p*l*El__._
Relational Database
*..lrimjXa
1-
. .- _* . _ .-Ic*e,,,s_L
~ eI *,_ _._ss
*. ..gj
hl;.:
. , ::
$
z
Their Attributes
Finding the positive attributes of an object (person, place, or thing) is a .:i
good exercise in any walk of life, but it is an especially important skill
$:
in database design. Remember that the attributes of an entity are the
.?
smallest component that describes the entity. In other words, the attrib-
utes are the individual items that make up each record and will direct& i
translate into the fields of the database records. In reviewing the enti,2;-_-I
ties that you have identified for the database, the next task is to
identify the components of each of them.
Well start the process with the VENDORS entity. Without worrying
about the normalization of the data (that step will arrive soon enough), -:
list all of the attributes you can identify that describe the vendors that .--:I
will inhabit the table. In considering each attribute, you should be sure .j+;
that the element cannot be decomposed further. If it can, it should be X~2:
reduced into two or more attributes. For example, the Address attribute S:!fq
of a vendor entry should not contain the street, city, state, and zip codes ::$J
of the vendor. This attribute can be decomposed further into its individ- .??g
ual elements and should be represented by individual fields for the
.,:i$: .2;
.x-s
street, city, state, and zip code.
.g;
_ 1. ~$
The VENDOR entity is composed of the following attributes:
n Name
n Street
n City
n State
n Zip Code
n Telephone
n Fax
n Contact Last Name
H Contact First Name
This set of attributes adequately describes the vendors in the table. We
must now consider which of the items uniquely describes each entity
The fields that we select will be the candidate keys, and nearly all of
.
$sTPd
.va-ilw. v ;_willnsl-._* . ,
==,~,>a~,_
b-q
30 n
VENDORS
CUSTOMERS
I-
r-----l
CUSTOMERS
t-
PURCHAsf--u
GENERATE--b
ITEMS
r-----l
INVOICE
:2
:;;
-,?
1 Y-2
Figure 2.5
Relationships
between the
entities
Identifying the relationships and the details of each is again perform&j
through an analysis of the existing database, interviews with users, a&$
your own understanding of business practices. Examine the entities on@ 3;
by one and try to determine how many of the other tables are related ,,T.
to it. Often, stating the relationship in sentence form helps to identify .G
the nature of the relationship. In general, a verb or preposition will be
j
the connecting word between the two entities and will readily identify :l;S:i
ii!
a relationship. For example, VENDORS supply ITEMS or CUSTOMERS
-.g
a
generate IhWOlCES.
Once you have established that a relationship exists, one of the primary .I
:,i$
.d
aspects of the affinity that you must identify is which of the flavors of
cardinality you will be implementing. The type of relationship determines the use and extent of foreign keys or linking tables in your
database design.
A one-to-one relationship is one in which a single record in one table is
related to only one record in another table. In addition, a record in the , . :
second table can only be related to a single record in the first table. TIN, :?.%
example shown in Figure 2.6 is a relationship in which each Customer .,
record relates to only one record in the CREDIT TERMS table. Likewise,-S:
each entry in the CREDIT TERMS relation is offset by a single record in , :
the CUSTOMER table.
__
C U
i-
-i
_I
STOMER
b CREDIT TERMS
INVOICE
ITEMS
32 n
VENDORS
7
ITEMS
100
SQO I
Squid
200
SQ02
Cuttlefish
300
Pacific
Coast
CR10
King Cral
400
Maine
Lobster
Crab
Shack
CR50
Blue Cral
OY36
Oyster
.&
,,y :$s&
~<$s
< y
* .;3
? ;:,?
L
,g
Each vendor listed in the VENDORS table could supply one or more of:;<*
the products that we carry in the ITEMS table. Additionally, our crab l
could be purchased from one or more vendors. Due to the amount of -$
redundant data that this relationship requires when implemented with.@
y;,&2-&2
two tables alone, a third table is going to be introduced to the
.ri,<:;*
-d
database.
This third table is called a linking table. The linking table is created byj$$
taking the primary key from each of the two tables involved in the rei@;3
:$g,
tionship and making these the only attributes of the linking table.
: ,:9
VENDORSITEMS
SQO I
Squid
SQ02
Cuttlefish
CR10
King Crab
Maine
CR50
Blue Crab
OY36
Oyster
Lobster
Shack
Figure 2.9 The many-to-many relationship is decomposed and simplified by the addition
ing table.
Item -ID
In looking at this new table you are now wondering how this fu
primary rule of the relation: no redundant data. The reason that t&
table works is that the key for the table is a composite key. The k
composed of both fields together, which serves to make each ret
unique. In examining the new relationships created in Figure 2.9
find something completely different. The many-to-many relations&,&S
now composed of two one-to-many relationships. This makes the dg
base much easier to understand and implement.
Your goal to this point in a project is have a complete ER diagram foryour project. This should include the entities, their attributes, and tl
relationshios that have been identified. The next sten in the orocess
the normalization of the data.
Normalization
Normalization is the process of decomposing relations to ensure rn&<$$
34 n Port I
ii
--The
Relational Database
~;L-;
normalized relation is one that most closely matches the guidelines &
the relational model and exhibits correctness, consistency, stability, iL_non-redundancy.
I
A table is said to be in normal form when its structure and data meet.:;;
the requirements of one of the stages of normalization. There are
.:
stages labeled first through fifth normal form, Boyce/Codd normal
form, and domain-key normal form. For purposes of our discussion,
will limit our detailed discussion to the first through third normal
forms, as this level of normalization is sufficient for the vast majority
database programs. We will also step away from the Redwood Fish :Tg
example to explore other relations.
; .gy2-i
.;*$< 1!
First Norm ral Form (INF)
To be in first normal form, a relation must have no repeating groups ti
multi-valued attributes. The class and grade fields in Figure 2.10 are.&
good example of a non-relational design.
GRADE
i
Figure 2. IO
Decomposition
to first normal
form (I NF)
._i*i) .? . -_. . . . i
^ _.
Chapter
I (,
based upon the Student ID being the primary key in the STUDENT&
table and a foreign key L the GRADES table.
,.g
,J
The benefits of a table being in first normal form are easy to enume&$
ate. First, you are now working with simpler data structures. Second&$
the tables are now in a state from which further normalization can .;,*
occur, and finally, these structures are much easier to move from the-:
:a
logical data model to a physical one.
. $4
Second Normal Form (ZNF)
Y$
Second normal form further refines the database structures. To be in i,
2NF a table must in 1NF and all of the attributes must be fully depeaz
ent upon the whole primary key. Every non-key attribute must be fu@
dependent upon the primary key. The TRANSCRIPT relation in Figur&#
2.11 is probably not in 2NE
,,;.~ 1
r-
I
STUDENTID
Figure 2.12
Decomposition
to second normal form
WW
I
1
L--
I---
1
STUDENT NAME
STREET
CITY
STATE
I
ZIP
-~~~~~_I
STUDENT ID
cLAss
rGRADE
I
~--
>I ->
; rf
-5
.:,
.I:
G-a
tl
:
Each record in the TRANSCRIPT relation remains unique based on the $
primary key values and the relationship is set by the values in the Stu-:i
dent-ID fields in both TRANSCRIPT and STUDENTS. The update
,.
advantages are now apparent; changes to the student record will only. :z
$
involve a single operation, leaving far less opportunity for error.
Tl?ird NomJOI Form
Third normal form is achieved by first massaging the relation into setond normal form. In 3NF, each non-key attribute must now be fully
dependent upon the whole primary key 3NF introduces the concept of
transitive dependency. This type of dependency indicates a functional
dependency between two or more non-key attributes. The FRESHNESS
relation in Figure 2.13 has a transitive dependency in the Hours
attribute.
FRESHNESS
Coos Bay
200
Portland
300
Seattle
Denver
Los Angeles
Portland
!:f
c.;;
.-?
,-{
,,:i::
.pi
yh v
I)
.,
Chapter
Database Design
^*lrvdtl
VENDOR-DISTANCE
Coos Bay
Denver
Portland
Los Angeles
Seattle
Portland
FRESHNESS-INDEX
Coos Bay
Denver
Portland
Los Angeles
Seattle
Portland
i:
The values contained in the Hours field are dependent on the Origin, -.
and Destination fields but not the Vendor-ID value. Origin and Destil
tion are non-key attributes of the FRESHNESS relation. Third normat!:
form removes these transitive dependencies to a child relation in wl$
:..:,j
the attribute is fully dependent upon the primary key.
The FRESHNESS relation has been decomposed into two new relatio
VENDOR DISTANCE and FRESHNESS-INDEX. Now, each of the attrig
utes in the new relations is fully dependent on the primary key of the
table. Why go to this much trouble? This normalization exercise
.
removes update anomalies such as the deletion anomaly surrounding
the removal of vendor 100. Because of the transitive dependency we 1,
lose the freshness index between Coos Bay and Denver that is useful f$ I
7
other vendors in that location.
38 n
Boyce/Cod
/;_e
.__
xI--sa-*
the point at which YOU must artificially manipulate data in your apd,,
-).y;
cation in order to make the relations fit together.
~:*:g.pcq
2
Back to the Redwood Fish Foods Company and the entities that were
identified during the earlier processes. The normalization process
through first normal form and second normal form is usually intuit@.j
to an experienced database developer. Rather than a formal process c: .~
examining dependencies and repeating groups, experience will tell th&
developer when the attributes of a relation dictate that it is appropriaa
-.a-ax:
to decompose the relation into multiple tables.
Lets review one of the entities to determine if it meets the require1;:_
.:*
ments of the normalization process. Earlier we decided on the
following attributes for the VENDORS entity:
w Name
n Street
n City
W State
n Zip Code
w Telephone
n Fax
n Contact Last Name
w Contact First Name
This structure works on a limited basis. Figure 2.14 has placed the
..-a-SE*
attribute list in table format and filled it with some representative da&?
VENDORS
Notice the entries for Deep Sea Foods; this repeating group breaks the
first normal form rules. A primary key field needs to be added because
40
Deep Sea
2 I2 Flounder
Seattle
WA
99999
(9991999.9999
OR
99999
(999)999-9999
OR
99999
(999)999-9999
99999
(999)999-9999
Foods
Deep Sea
67 Captan
Bligh Cannon
Beach
Foods
Deep Sea
3934 Maple
Florence
Foods
PKlflC Coast
Rockaway O
I2 Semde
Deep Sea Foods has informed us of a change in their sales ranks. They$$
have broken out their product lines into Finned and Non-finned and :y
assigned a salesman to each, assigning them to cover all of the su
locations. This causes a new issue within the VENDORS table; the
mans data is going to be repeated and the phone number, fax, and ~:f
;r,
name are not directly dependent on the primary key of the VEND0Rs ~.ii
.7
table. This tells us that we need to decompose the table further.
;
The new rable structures are displayed in Figure 2.16:
VENDORS
,+
.:_J
,
SALESMAN
Cunningham
S298
Richie
s300
Krista
Shea
S299
Linda
Bates
s574
Martin
Blank
;r
The final step in all of this work is to create the tables that will make
up the database. The entities and their attributes will directly map to a
table structure and, if the design and normalization steps were care;F&%
fully applied, you should end up with a stable, simple, optimal
database. There are two steps remaining in the process that lead up tO:
the use of Delphi to create the data structures: determining the data 1
types of the structure of the table and creating the field and file nam
Fields
Each attribute that you have identified will become a field in the tabkg
In creating a field in a table, some new aspects of the attribute must ti
considered. The name, data type, and size of the field are the most Q ---.
ical elements of any field. Turn your attention to the name first. It m
be an identifier that fully describes the contents of the field so that it -1:
will be clear and easy to work with within your application. In a
.
42 n
Part I --The
Relational
Database
*i
-y.
.:j,+1
.;,-;z
IL?;
.;3Jf
Iz
~.1:_jl
Once the field is named, the data type must be set. The database tool Ti
with which the relation will be implemented will determine the set d &
choices. Delphi supports a wide variety of data types, all of which are -$
shown in Figure 2.17. Choosing the appropriate type may take some :;
consideration of the final use of the data. A primary consideration is ..a
the differentiation between an Alphanumeric and a Numeric data nq-&-.:
.
Alpha
Character
Number
Float
Money
Number
Date
Date
Short
Number
Memo
Memo
Binary
Memo
Formatted Memo
Memo
OLE
OLE
Graphic
Binary
Long
Number
Time
Character
DateTime
Character
Boo1
Booi
Autolnc
Number
Bytes
Memo
BCD
N/A
Summa1Y
This chapter covered the process of designing the relations that wi$
make up a database. As stated many pages ago when this chapter
began, when performed conscientiously and correctly the design ph
will result in a stable, consistent database. A good database will mi
the user interface much easier to design and implement, as the pro-i3
grammer wont be required to cover for any flaws in the underlyi
database structure.
Looking Forward
The next chapter introduces you to the SQL language, the primary .I
method of manipulating data in a relational database. Though the 1
Delphi SQL implementation is limited in scope, the knowledge of th@,
query aspects alone will make your database development much mf
productive.
DQL, Structured Query Language, is the linguafranca of the relational database environment. SQL is the driving force behind nearly
every database product available to developers and end users today
.i
Depending on the audience for which the product is designed, the SgL , ;.G
7
may be hidden behind the scenes, doing its work through a point and
,:
.^:
click interface rather than the typing of a long SQL statement. One of
the major benefits of SQL is its portability between vendors. Though ;
there are numerous, vendor-specific extensions and dialects that color $
the language, the core concepts and principles of SQL remain constant.
g
6.
B
&
This chapter presents the concepts of SQL in a reference fashion, focusing on Borlands Local SQL implementation. Upon completion of this
chapter you will be well versed in this implementation of SQL and be
prepared to work in multi-tier environments with other SQL dialects.
.?
;
SQL is an evolving standard language that closely parallels the evolution of the relational database model. Early implementations were
developed in the 1970s and an ANSI committee developed a first Stanf
dard around 1986. The most recent standard from this group was
!
published in 1992 and most vendors have settled on support for the
::$
ANSI92 SQL standard. SQL does not ensure adherence to the relational $
:.
46
.,:J3~2
The structure of the language is based on the relational concept of all :I 2
data being in table form rather than a flat file. Files have a specific
j
structure and order while tables are unordered sets of items. Languages.:;
that address files must explicitly rely on the structure and the sequence
of the file itself and thus become inextricably linked to the data structure. One of the most important precepts of the relational model is the
separation of the logical and the physical implementation of the database. The SQL language is not directly connected to the underlying
storage mechanism and is therefore not affected by the data structure
or the storage mechanisms.
Note The first thing to learn about SQL is how to say it! There are
two camps, old guys and young bucks, warring over this topic
every day. Many in the older camp will pronounce the acronym
sequel, causing great consternation among the young bucks.
The youth movement insists that its pronounced IBM style, one
letter at a time, as in es-queue-et. Im not getting in the middle
of this one. Pronounce it however you feel comfortable.
Language n
47
item. This methodology works fine until the store manager comes in
one night and rearranges the layout of the store. Suddenly, our friend
finds that his instructions no longer work and he needs to develop a
new set of instructions to respond to the changed conditions.
The procedural clerk tires of this exercise and decides to become a
declarative agent. A declarative method requires only that we reiterate
our request to the clerk. Being smarter now, he has inside structural
knowledge of the store and he knows precisely where an item is and
how many he has. He also understands the rules by which the manager
runs the store. There are no duplicated item storage spots; each item
exists in precisely one spot and is addressed by its name, not its storage
location.
Leuming SQL
To effectively learn SQL, there are a couple of items that must become
ingrained to prevent a procedural-thinking block. The first is that SQL
is set based. Just like in mathematics, a set is a non-sequenced collection of items of the same type. All SQL statements act upon and result
in sets of rows, or records. This is a primary feature of the relational
database; all data in the database is presented to the SQL language
processor in the form of tables and any action upon them results in a
new table.
The structure of these tables, being composed of rows and columns, is
the second item to remember. Though the data appears to the SQL programmer in the form of a matrix, the order and sequence of the rows or
columns of the data set are irrelevant. SQL will address the columns by
their names, not their position within the table, making the order of the
columns unimportant. SQL processors also know that once they have
located an item that meets the criteria of a statement, there is not
another one like it. This is driven by the non-redundancy rules of the
relational paradigm.
SQL. Subsets
The SQL language is subdivided into three subsets that represent separate, functionally related tasks:
n
48
Locd SQL
There are a number of items that are specific to Local SQL that should
be mentioned prior to discussing the statement syntax. The sections
that follow highlight the syntax rules that are specific to the formulation of the SQL statements using the Local SQL implementation. When
developing in a multi-tier environment, refer to the other product documentation to determine its specific requirements.
fib/e Names
Dates used as a part of a SQL statement must conform to U.S. date format, MM/DD/YY or MM/DD/YYYY. All dates must be enclosed in
quotes to prevent confusion with arithmetic expressions. If the year is
Chapter 3- Structured
Q luery
Language W
Comments
:i
1:
J,.
!;.
VendorName
FROM Vendors
I oins
Another common SELECT statement task is to select rows from multiple tables; this process is called a join. The SELECT statement allows
you to use a comma-separated list of table names in the FROM clause
to specify the table or tables that the statement should act against:
SELECT * FROM Vendors, Items
The resulting union contains every column and every row from both of
the tables. The statement given as an example points out the importance of carefully considering your goals for the query. Such a blanket
query as this may generate results that are far removed from what you
were expecting. You would be much better off limiting the scope of
your result set by adding additional clauses to the statement.
Join types are closely related to their mathematical counterparts and
are named accordingly The joins supported through Local SQL are:
H
n
n
W
n
:: ;;
Equi-join
Inner
Outer
Cartesian
Union
Heterogeneous
Equi-join
The purpose of an equi-join is to denormalize tables that were originally normalized into separate relations. Through the normalization
process, new relations were formed from a single original and both
contain one or more columns in common. It is through these common
values that the equi-join rejoins the tables into a single result set.
The WHERE clause of the SELECT statement determines on which columns the relations will be joined, the rows being selected when the
values in columns of the WHERE clause are equal. The values used in
the comparison expression can be single columns or concatenated
_, pG.33
GG
<:,P,
.._A
i
.?
.:.<I$
x,j
52
,.,,
i
.
iL
**
Inner Join
,rJg
Y;:
::
The result set from this join will contain only rows that meet the equality conditions of the join expression.
Outer Join
in outer join generates a new relation from two relations that have one
or more columns in common. The resulting data set does not exclude
rows from the source table-the table in the FROM clause-when they
do not match a row in the joining relation. This concept may be best
illustrated with an example. Suppose that we have VENDORS and
ITEMS tables and we perform the following outer join on the tables:
SELECT * FROM Vendors
OUTER JOIN Items ON Vendors:Vend-ID = Items:Vend-ID
The base relations and the resulting data set are shown in Figure 3.1:
ITEMS
VENDORS
Crab
200
Oyster
300
CrabwalkSuppliers
loo
RESULT SET
100
Crab
ID0
The resulting relation contains the columns from both of the tables. In
the third row, where there was no match between VENDORS and
ITEMS, an outer join will fill the columns with null values.
.(.
.I:.
,_
*_ia
very Lung
This is representative of a left outer join where all of the rows from t&$
relation on the left of the expression are included in the result set. TiW{
modifier BIGHT will create exactly the opposite result; all rows from ;;i
the table on the right of the expression will be included. A full outer ,i:
join wiI1 contain all rows from both of the tables with the expression
:qa
null filled in as appropriate.
I
,;:jS
Cartesian Join
r 3i/
! I
.?
$!
.j
T:
,?;
~sTf:,.q
~g
Union Join
The union join is used when you want to add the rows of one relation ;:
to the end of another relation of similar structure. The two relations
must be very close in structure, including data type, for the union joti,
to be successful. The BDE will attempt to do some data conversions
during the process, but up-front work will pay off if this type of join is :
,$., y
your goal.
I. .g
Heterogenous
join
A heterogenous join is a join in which the tables involved are from two
different databases. A Local SQL requirement is the databases involved
in the join must be accessible through the BDE.
from Vendors
will result in a new relation composed of these two columns. The SQL
,;i!
processor will evaluate each combination of columns for uniqueness,
and redundant rows will be removed from the final result set. You are -i.::
probably questioning the need for this clause, given the relational rules i,$
,>j$
that we have discussed in the previous two chapters. In general, the
database should take care of this for us. If the column list that we a&~&-~;
includes the primary key, then we can be assured that a unique data set ;.
will result. However, many times we will want to retrieve a subset of .zs:Tjg
54
the table that does not necessarily include the primary key, as shown in
the above SELECT statement. The DISTINCT keyword acts upon the
non-repeating values rule and ferrets out the duplicate rows.
Limiting the Result Set
Most of the time, you will not want to retrieve an entire data set into
another, new data set. More often, the SELECT query that you formulate will be based upon a set of criteria. The SQL WHERE clause is the
keyword that filters the resulting data set based on a Boolean evaluation of each record. The optional clause results in a subset of the base
table being selected.
SELECT <column list> FROM <table
WHERE < predicates >
list>
The WHERE clause uses a defined set of predicates, or logical expressions, to define the filtering conditions. The following predicates are
supported:
Comparison-Compares two values
BETWEEN-Compares a value to a range of values
EXISTS-Compares a value to a lookup list
IN-Determines if a value exists in a list of values or a table
LIKE-Compares one value with another
IS NULL-Compares a value with Null
SOME/ANY/ALL-Performs a quantified comparison
Comparison
FROM Vendors
= '80437'
Language
%$$$
.:;,
You can add the logical operators AND or OR to your WHERE clause t@:5
create more complex comparison statements. The SELECT statement: -$
,<a3
SELECT * FROM Vendors
WHERE ((VendorCity
= 'Coos Bay') OR (VendorCity
= 'Portland'))
This statement will retrieve a result set containing all vendors that are
not located in Coos Bay.
Tip The CAST function can be used in SQL operations that require
like data types such as the comparison predicates. The CAST
function converts a specified value into a different data type.
Using this function can allow you to create a comparison
between two operators such as a number and a string. For
example,
SELECT * FROM Biolife
WHERE CAST("Species
No" as Char(6)) >= '90200'
.,
i
^i3
BETWEEN
-i
-?i
>::
I$
~5
5:
i
<*.
56 n
ITEMS
Figure 3.2 The VENDORS and ITEMS relations are related through the Vendor-/D field, making
an
EXISTS
The subquery uses a WHERE predicate that includes one or more fields
from the outer query and, presumably, additional filtering conditions.
The SQL statement:
SELECT * FROM Vendors V
WHERE EXISTS
performs the subquery (shown in italics) once for each row in the table
of the outer query. If the conditions in the subquery are met and a
TRUE is returned, the current row in the outer query is returned in the
result set.
57
The IN predicate allows you to select the result set based on the comparison of a column value with a specified set of values. The IN
predicate works on the same principle as the Pascal IN Boolean operator: If value X is a part of set S, a TRUE value is returned. The IN
predicate works the same way. If a column value matches an item in a
set that is defined in the body of the query, then a TRUE is returned
and the row containing the column value is retrieved as a part of the
result set. The following SQL statement will retrieve only those rows in
ITEMS that have an item description that is included in the set
(Crab,Shad) :
SELECT * FROM Items
WHERE IT-ITEM-DESC IN
('Crab','Shad')
58
Selections for the result set using the LIKE predicate are based on the
similarity between a column value and a comparison value. The fuzzy
comparison is implemented through a set of substitution characters and
allows you to match anything from the first letter of a column all the
way out to the entire length of the value.
The wildcard substitution character % represents any number of characters in a comparison. A row is retrieved in a query in which a WHERE
comparison using the LIKE predicate matches any portion of the comparison value not corresponding to the wildcard character.
SELECT * FROM Vendors
WHERE VS-VENDORNAME
LIKE 'C%'
Running this SQL statement against the VENDORS table will retrieve
rows that contain Crabwalk Suppliers and Conglomerated Foods
but not Blue Moon Foods.
If the substitution is limited to a single character, you can use the substitution character _ at any position within the comparison value.
SELECT * FROM Items
WHERE IT-ITEMNUMBER LIKE lo-
This SQL statement will retrieve any Item Number that falls between
100 and 109, performing the substitution on the last character in
the string. As with the wildcard substitution character, the single-character substitution token can appear in the beginning, middle,
or end of the comparison value. For example, the comparison value in
the SQL statement could be modified to be U 0 . This would return a
result set that included the first ten values in-e&h hundreds set
(101..109, 201..209) but would not include any above those values
such as 110 or 233.
The keyword ESCAPE is a modifier to the substitution characters used
in a LIKE predicate. You will use ESCAPE when the characters O/6 or
- appear as a part of the data in the column values. An escape character designates a symbol which indicates to the LIKE predicate that the
(yo,, or ,, character immediately following is to be taken as a literal
value. For example, @ is the escape character in the comparison value
%lOO@%%. The retrieval selections will be filtered for values that are
like 100%. The SQL syntax for this statement is:
SELECT * FROM Inventory
WHERE InStock LIKE %lOO@%%
ESCAPE @
IS NULL
The subquery may retrieve multiple rows but it can only contain one
column. The column selected must be of the same data type as that to
which it is being compared in the outer query You may also cast the
values if necessary to make them comparable. The ALL predicate
requires that every simple comparison between the column value and
the column values in the subquery evaluate to TRUE.
SELECT * FROM Items I
WHERE (I.Item-Cost > ALL
(SELECT WholesaleCost
FROM Wholesale))
This SQL statement modifies the original query to retrieve rows from
ITEMS in which the Item Cost is greater than every row in the
WHOLESALE relation.
Put-t I - T h e Relationd D a t a b a s e
Predicate
Summary
The predicates supported for the WHERE keyword determine the filtering of the query and any subqueries. If you review these in terms of the
relational database discussion in earlier chapters, you can see that the
SQL statements all work on the surety of the design. There can be only
a true or false response to any query of the database; all gray areas
have been removed through careful design. This confidence makes all
of your up-front work worthwhile.
The ORDER BY Chuse
The ORDER BY clause is used in a SQL SELECT statement to order the
retrieved rows in the result set. The order is determined by the values
in a comma-separated list of one or more columns.
SELECT * FROM Vendors
WHERE VS-VENDORCITY = 'Seattle'
ORDER BY VS-VENDORNAME
This statement will retrieve all vendors located in Seattle and then will
order the output by the value contained in the column
VS-Vendorname. By default, this will be in ascending order. You may
control the listing direction by adding either the ASC or DESC modifier
to the ORDER BY colunm list.
SELECT * FROM Vendors
WHERE VS-VENDORCITY = 'Seattle'
ORDER BY VS-VENDORNAME DESC
Tip
IT-DESCRIPTION
DESC
6 1
Item-Cost ) As Average
Figure 3.3 shows the original relation and the rows that are retrieved
from this query
563
563
I
I
I
I
I Smelt
563
I Smelt
I Devil Ray
I Smelt
666
IDevil Ray
921
I Oyster
666
I
I
I
I
I
IOysteC
I Smelt
I
I
8.77
5.77
3.07
9.88
.99
3.56
I .os
8.77
I
I
Average.
I
I
Figure 3.3 SQL results using an aggregate function and the GROUP BY clause.
H
H
W
H
In its simplest form, the AVG (average) function will compute the average of all values in a column.
SELECT AVG( ITEM-COST ) FROM Sales
will result in a single row result set. When the GROUP BY clause is used,
an average is computed for each group determined by the column list.
COUNT
will return a count equal to the total number of rows in the Items table.
You can modify this function by adding the DISTINCT modifier to the
statement:
SELECT COUNT(
This will result in the function ignoring duplicate column values in its
total.
MAX
The MAX function returns the maximum value encountered in all of the
rows of the identified relation.
SELECT MAX{ ITEM-COST ) From Items
When this SQL statement is processed against the Sales table in Figure
3.3, the row returned will contain 9.88. By modifying the SELECT
statement with a GROUP BY clause, the maximum cost of each of the
item types can also be returned.
MIN
The MIN function returns the minimum value of a column in all of the
rows selected. It works identically to the MAX function.
One item not mentioned to this point is that you can combine these
functions in a single statement to produce a set of useful statistics from
a single query The following statement will produce a result set that
shows the average cost for each item and the highest and lowest costs
as well.
SELECT ITEM-DESCRIPTION, AVG( ITEM-COST
MAX( ITEM-COST ), MIN( ITEM-COST )
FROM Items
GROUP BY ITEM-DESCRIPTION
),
SUM
FROM
Sales
61
Database
The WHERE clause limits the data to be aggregated, using columns that
are not a part of the aggregate functions as the criteria for selection.
The HAVING clause filters the rows after aggregation, using the columns that are part of the aggregation functions to limit the result set.
FROM table-name
predicates]
You can use the DELETE SQL statement to delete one or more rows
from a relation. When used without the WHERE clause, all rows in a
table will be deleted. The WHERE clause is used in conjunction with
selection predicates to limit the rows that are deleted. The WHERE
predicates supported match those detailed in the SELECT section in
previous pages.
The statement:
DELETE
FROM
Sales
will result in all rows in the relation Sales being selected for deletion.
Be cautious when issuing such a blanket command. More common will
be a DELETE statement that utilizes a WHERE clause to control the
deletion selection. The following example deletes rows from the Sales
relation based upon the value of the SaleDate column.
DELETE FROM Sales
WHERE SaleDate <= 10/15/98
65
You can use the SQL INSERT statement to add new rows of data to a
relation. The syntax for this DML statement changes a bit. The keyword
FROM is replaced by INTO before declaring the target relation name.
You may include a comma-separated list of column names that is surrounded by parentheses to the statement to explicitly define the data
targets. If you do not include the column list, as in the following
statement:
INSERT INTO Sales
VALUES (1231,Smytheson,ll/l5/60,455.68)
the data contained in the VALUES clause is inserted into the columns of
the relation on a positional basis, as the columns appear in the relation
definition. Without the column list, you must supply the identical nurnber of values as columns in the relation definition.
Specifying a column list gives your SQL statement much more control
over the insertion process. When a column list is included, the VALUES
clause now corresponds one to one with the column list, inserting the
values into the same enumerated slot.
INSERT INTO SALES
( ITEM-NUMBER, ITEM-DESCRIPTION)
VALUES ('833','Lobster')
In this statement, 833 will be entered for the Item-Number value and
Lobster for the Item-Description. Any columns not included in the
column list will have a NIL inserted.
66 n
= 0.10
a
n
H
n
CREATETABLE
ALTERTABLE
DROP TABLE
CREATEINDEX
DROP INDEX
The DDL language set is more concise than the Data Manipulation Language but no less important. In fact, outside of database tools that take
all of their direction through SQL statements, these statements are little
used by programmers. Many programmers, not surprisingly, prefer to
use interactive tools to create and manage their data objects. After this
67
review, you might see these commands in a new light and realize
opportunities for expanding your application development skill set.
precwon
optlona
$4
..z
:;
The PRIMARY KEY keyword defines a primary index for the relation
composed of the items contained in the column list, in this case
LastName plus FirstName.
The type of table that is created is defined by the filename extension
that is a part of the table name. .DB will build a Paradox table; .DBF
builds dBASE tables. If the extension is omitted in the table name, the
default driver setting in the BDE is referenced for the type of table to
build.
Chapter
3-Structured
Query
Language n 69
You
When adding a column, the column definition must follow the same
structure described in the CREATE TABLE section. The statement below
drops the column FirstName from the STUDENT relation and replaces
it with a new column named FirstInitial.
ALTER TABLE 'student.db'
DROP FirstName,
ADD FirstInitial
CHAR (1)
TABLE
table-name
70 n
creates a Paradox index built with the LastName and FirstInitial columns of the STUDENTS table. The keyword DESC is used to sort the
index in descending order.
INDEX
table-name.index
name 1 PRIMARY
You can use the DROP INDEX SQL statement to delete a primary or secondary index from an existing table. The notation used to perform this
drop is different according to the table type and the type of index you
are working with. To delete a Paradox primary key, the SQL statement
will read:
DROP
INDEX
Students.PRIMARY
INDEX
student.gpa
Summary
SQL is the point at which careful relational design pays off. As you have
seen with both the DML and DDL statements, the primary rule of relational databases, non-redundancy, creates an environment in which
there can be only one correct set of responses to any query The depth
of the SQL constructs supported by Local SQL makes this language
within a language, the Delphi development environment, an important
skill to obtain. We will discuss these statements again when the components that use them are explored.
This is the last chapter on the theoretical aspects of database development. You have reviewed the theory behind the relational database
paradigm and, hopefully, have gained some new insight into the importance of following this model in your development efforts. You have the
tools and the knowledge to effectively design a relational system; now
we will explore the tools that Delphi provides to do so.
Looking
Forward
The next chapter will explore the Borland Database Engine. This is the
database behind Delphi and it sometimes works in unexpected ways.
We are going to delve into the engine itself and the API through which
many commands can be entered.
. .-z
*,c;j
-.
-,j
76
complex calls into their application. This chapter will seek to develop
this understanding while leaving the API discussion to later chapters.
We will examine the architecture of the BDE first to surface the efficacy
of the driver-based design. The drivers and the system of aliases critical
to utilizing the engine follow, rounding out the engine discussion in
abstract. Putting it to work is next on the agenda, so the utilities including the Database Desktop and the Database Explorer are examined along
with a brief discussion of distribution needs of projects using the BDE.
Architect we,
The Borland Database Engine utilizes a driver-based architecture. Discrete software drivers provide the specific support and translation
layers needed by the specific database product. The engine then uses
these drivers to provide a standard set of functions among a disparate
variety of database products. Drivers can be specific to a single database, such as the Paradox driver, or a single driver that can support a
range of similar database structures such as the dBASE/FoxPro family
The fundamental task of the driver is to implement the specific
low-level commands required by the file system of the specific database. In doing so, it serves as a translator between the underlying file
structure and the high-level structures and commands needed by the
BDE. The database engine, in turn, then provides a transparent interface to the Delphi components that support database access. It is this
type of architecture that makes it possible to utilize a single component
to access the wide variety of database structures that are available to
the developer. Though the architecture seems cumbersome and
unwieldy, it is well implemented and highly optimized, giving Delphi its
world-renowned flexibility and multi-tier capabilities.
Figure 4.1 is an architectural diagram of the BDE layers. Each layer is
responsible for performing a specific function as the database call traverses the engine. A Delphi database access path through the BDE
follows these steps:
The application calls a VCL component.
2. The component passes the call to the BDE.
3. The BDE in turn calls the specific database driver.
4. The driver enables direct access to the data tables, performing the specific actions requested.
I.
t
~
i
Two-Tier
Utilities n n
Multi-Tier
CONNECTION
COMPONENT
Figure 4. I
Borland Database Engine
architecture
tabase Drivers
The Borland Database Engine ships with a collection of database drivers, allowing access to a wide variety of data sources. The standard
BDE drivers include those for Paradox, dBASE, Microsoft Access, ASCII
text, and a new file specific to FoxPro. Each driver is implemented as a , -i
DLL and each is similar in function; they encapsulate the specific
78 n
Figure 4.2
79 1
The BDE Administrator interface is split into two panes. The left-hand
pane displays a hierarchical listing of the applicable objects. This pane
contains two tabs, the Databases tab that shows the aliases of all avail.$L-4
able database objects and the Configuration tab that provides access to
;&I
the configuration of the database drivers and the BDE options. When
an item is selected in the left-hand pane, a Definition tab will be shorn, .1
in the right-hand pane containing the options applicable to the selected 2
object.
;
4
2
<
:
:
i&i
l
:
;
4. Click Object1 Apply to save the configuration. The alias will be saved :
with a default name such as Standardl. You will most likely want to -?
change this to something more meaningful to your application.
Manager
Maosoft
MIcrosoft
Access Driver
Access Driver
r.m
r.m
Figure 4.3
ODBC Dato
Source Admin-
istrator dialog
To establish the sample MEDINFO data source, the following steps were
taken:
I. Select the System DSN tab. Creating a System data source gives your
application multi-user access to the data source. Click Add to start the
process.
2. The first dialog asks that you select a driver for the data source. For
the sample, the Microsoft Access Driver was selected. Click Finish.
82
Engine/CONFIGFILEOl
INIT
n Date Formats
n Time Formats
n Number Formats
The system INIT settings are the initialization options used when starting an application. All of this information is stored in the registry
Figure 4.5 is a table showing the parameters accessible through the
INIT branch.
AUTO ODBC
q -
DATA REPOSITORY
DEFAULT DRIVER
LANGDRlVER
LOCAL SHARE
shared between BDE
applications at the same time.
LOW MEMORY USAGE
LIMIT
MAXBUFSIZE
The
that tne
I The maximu
MAXFILEHANDLES
MEMSIZE
MlNBUFSiZE
cache.
MTS POOLING
SHAREDMEMLOCATION
SHAREDMEMSIZE
SQLQRYMODE
SYSFIAGS
Do not modify.
VERSION
] Do not modify
Figure 4.5 The IN/T options accessible through the BDE Administrator
Darte Formats
The options in the FORMATS 1DATE branch of the configuration option,
modify the handling of data strings in BDE applications. The parame- . ~li
ters surfaced here, shown in Figure 4.6, affect the way in which string ,:
values are converted into date values.
84
FOURDIGITYEAR
When True,
digits.
LEADINGZEROD
LEADINGZEROM
MODE
SEPARATOR
YEARBIASED
figure 4.6 The date format options accessible through the BDE Administrator
Time
Formats
The parameters accessible through the FORMATS 1TIME branch determine how string values are converted into time values acceptable to the
BDE. The parameters are listed in Figure 4.7.
AMSTRlNG
MILSECONDS
PMSTRlNG
SECONDS
TWELVEHOUR
Figure 4.7 The time format options accessible through the BDE
Administrator
Chapter 4-The
Database Utilities n
85
Numeric Formats
Much as the TIME and DATE format parameters control the string conversion into time and date values, the NUMBER parameters determine
how string values are converted into numeric values. Figure 4.8 lists
the parameters accessible.
number
of
e maximum
decimal places in the string converted
Figure 4.8 The number format options accessible through the BDE Administrator
Creating Tables
One of the most common uses for the Database Desktop is creating
tables for use in your Delphi projects. The Database Desktop can create
a wide variety of table types. Depending on the type of table that you
choose to build, a varying array of capabilities will be available for the
modification of the table. Aside from naming the columns and defining
the constraining sizes and data types, you may also find the ability to:
H Specify a table language to control sort order and the available character set.
n Assign columns for indexes.
w Use (borrow) the structure of another table to create a new one.
Figure 4.9
The Create
Table dialog
2. When the file type is selected, you will be presented with the Create
Paradox 7 Table dialog as shown in Figure 4.10. This is where all
memaining design work on the table will occur.
Create
! all
3. Figure 4.11 contains the schema for the REGISTER table that we are
creating. It describes the field name, data type and size, and domain
constraints for each column in the table.
SOCIAL
IRI-I-V
II
Ml
IM
1S T R E E T
STATE
ZIP
4. Begin by entering the name of the first field, Social Security Num.
Tab to the Type column and select a data type. Pressing the space bar
will drop down a list of possible data types supported for the database
type. For this field, use the Alpha data type. Tab again and enter 11
for the field size. Finally, this field is the primary key for the table so
88
this must be indicated in the Key column. Press any key to mark this
as the primary key. Your design should appear as shown below.
Figure 4. I2
5, You will now set the domain constraints for this column. The only constraint for this field is that it is required, being the primary key To
indicate this in the Paradox .VAL (validity check) file, click on the
Required Field check box. Add the Last Name and First Name fields in
this same way.
6. Add the Date of Birth column to the table. The validity check value for
this column requires a little more work, The constraint requirements
for this field state that the child being registered must be five years old
or older on the first day of school. For this exercise we will establish
the first day of school as September 1, 1999. Therefore, to be old
enough, a childs birthday must be prior to or equal to August 31,
1994. Implementing this constraint in a Paradox table utilizes a Maximum value validity check. For this school year all children should have
been born no later than August 31, 1994. This constraint is shown in
Figure 4.13.
7. To simplify the data entry process, another validity check that can be
added is a default value. For this database, the vast majority of entries
will come from a single town and state. When the column City is
entered, add the value Evergreen (minus the quotes) to the Default
value field. Perform the same operation for the State Column, entering
CO as the default value. When an empty row is added to the database, these values will have already been entered.
8. Finally, add the Zip column. As a further verification of the students
district status, the zip code should be validated. A narrow range of values, sometimes as low as one, can be used to validate an entry For the
Zip column, the highest and lowest value that should be allowed in
this field is 80439. Setting this validity check is simply a matter of
entering this value in both the Maximum value and Minimum value
fields of the validity check.
9. Save the table with an appropriate name in the work directory for
your application. Remember that an alias will be needed to locate the
table for use in Delphi.
Using the Database Desktop, you can immediately test the results of
your modifications. Open the RFGISTER table and click on the Edit
Data button to open the table for data entry Experiment with an entry
to see the errors that are generated when incorrect values are entered.
The messages till appear in the desktops status bar. The validity
checks you created will prevent poor quality data from being added to
the database. These same validity checks will raise exceptions in your
Delphi code, making it much easier to trap errors in the data input by
your users.
Another benefit of using the Database Desktop for table creation is the
ability to add Picture clauses to each column, helping you to format the
data in the most presentable manner. For example, you can modify the
presentation of the column that contains the Social Security number so
that entries appear in the accepted format of ###-##-####. To add
this picture string using the desktop:
I. Select File 1Open 1Table from the menu and navigate to the alias
under which you saved the REGISTER table. Open the table.
2. Click on the Restructure button, opening the Restructure Paradox
table dialog. This will contain the current structure of the REGISTER
table. Alternately, you can get to this point by selecting Tools 1Utilities 1Restructure and then selecting the table.
3. Select the Social Security Num column. Click on the Assist button
below the Picture field. This opens a dialog that helps you to format
the picture appropriately or allows you to experiment with different
formatting strings. Figure 4.14 is the Picture Assistance dialog. In the
Picture field, the string *3{#)-*2{#)-*4(#}
represents the standardized format for a Social Security number.
Figure 4. I4
The Picture
Assistance
dialog
The chart in Figure 4.15 shows the recognized picture string characters.
These characters are substitutes for the data that will be entered into
the field. The formatting is positional, meaning the character that is
input in a particular location is formatted according to the picture
string character found in that spot. Any characters other than those
Ar*
9 1
shown in the chart &at are added m he string will be considered constants and will appear b be final dg , as the dashes do in the
example.
allowed
in this
position.
&
@
I
to uppercase*
to lowercase.
Figure
:.
92 n
builder knows which files from the BDE can be distributed per the
license granted by Inprise. You cannot use your Delphi installation disk
to install the BDE on a client machine. During the development of the
installation script, the installation program will determine if you want
to perform a complete or partial redistribution of the BDE.
Borlands reasons for this requirement are sound, even beyond simple
protection of their property Careless installation of the Borland Database Engine can easily cause other programs to fail. Remember the
earlier mention of the number of applications that utilize the BDE for
their core database functionality An installation of a later numbered
version of the BDE can overwrite or otherwise destroy references to the
other installations of the engine, effectively destroying the other applications database access. Use of a certified installation program
prevents these entries from intersecting. Inprise now also includes a
.CAB file for distribution with ActiveX controls.
The only BDE utility that can be distributed with an end user application is the BDE Administrator, and it is included with a certified
distribution as outlined above. None of the other utilities, including the
Database Desktop and the Database Explorer, are redistributable.
Database Explorer
The Database Explorer is a utility shipped with Delphi that incorporates
a hierarchical database browser with data editing capabilities. The utility is also the gateway to the creation and manipulation of data
dictionaries for Delphi datasets. A data dictionary is a special database
that contains the attribute sets for field components. The tool ships in
three different flavors with two different names, depending on the version of Delphi in use. The Desktop and Developer versions of Delphi
receive the Database Explorer while the Client/Server version is
shipped with the SQL Explorer. Their capabilities differ commensurately with the database access capabilities offered with each version.
The Database Explorer that accompanies the Desktop edition accesses
local databases only, while the Developer edition gains access to the
Local Interbase Server and ODBC-compliant databases. In addition, the
data dictionary is enabled. The Client/Server version of Delphi ships
with the SQL Explorer, giving the developer extended capabilities wit&
access to a full range of SQL databases, both local and remote.
Chapter I-The
n 93
:.
T F&we
4.16
; The Database
T
DBDEMOS
&--#i DddtDD
II
.H
Figure 4. I7
The hierarchical view of the
Explorers
Oatabases tab
The detail for the REGISTER table, built earlier in this chapter, has
been expanded. The number and names of the nodes that appear will
differ for each of the supported table types. The Fields node in the
example is expanded to show all of the fields that were defined for the
table. Selecting the field narrows the information down to the field definition. Information also available for the Paradox table includes the
Indices, Validity Checks, Referential Constraints, Security Specifications, and Family Members. Perhaps most importantly, the data that the
table contains can be modified and examined.
The Database Explorer can also be used to establish a new alias. The
methodology is the same as that used for the BDE Administrator.
Data
Dictionary
The ability to create and utilize a data dictionary is a distinct advantage
of the BDE/Delphi development tool. A data dictionary is a database
that is used to store attribute sets for field components. Attribute sets
determine the field type, properties, and the data-aware component
that should be generated whenever a TField object is dragged to a form
from the Fields Editor. A data dictionary containing an attribute set
means that you will only need to set properties once to have them
propagated throughout your project.
rhapter
95
This tool displays its use when you have several columns in a database
that share common formatting properties. An extended attribute set
can be set once for such things as the alignment, picture string, and
minimum and maximum values, and then be associated with multiple
fields in your application. If a change is necessary it can be made once
through the Explorer and it will then flow to all of the components that
use it at once.
Attribute sets can be changed both through the Database Explorer and
the Fields Editor. The editor will be discussed in conjunction with the
components it services in a later chapter; right now we will focus on
creating an attribute set through the Explorer. The first thing to be
done is to create a new dictionary Start the Database Explorer, either
from the Start menu or from within Delphi, using the Tools menu.
Select the Dictionary tab in the left-hand pane.
Bight-click on the Dictionary entry and select New. Alternately, you
may select New from the Object menu. The dialog shown in Figure
4.18 will be displayed.
Elgure 4.18
lnfirmation fir
I- 0 new dictio$wfyin the
4Create a New
$XcGonafy
I. .
3.
r...
Enter the data shown in the example dialog. The Description field is
optional. Click OK to save the new dictionary.
You can create multiple dictionaries as needed to support your development needs. Only one dictionary is displayed at a time; the Database
Explorer does not extend its hierarchical display to this feature. To
work with alternate dictionaries, select Dictionary 1Select from the
menu and choose the dictionary name from the drop-down box.
Establishing the attribute sets that make up the contents of the new dictionary is the next step. Attribute sets can be created manually or
imported from databases or SQL constraint sets. Before establishing an
attribute set, it is helpful to view an exceptional example provided by
Borland. This dictionary is linked to the examples provided with the
Delphi package so they must be installed prior to exploring this item.
Select Dictionary1 Select from the menu and choose the Borland
Data Dictionary; click OK to open it. Expanding the Databases node
informs you that this dictionary is used for the DBBDEMOS database,
the demonstration tables included with Delphi. Theres not much of
interest under that node but plenty to look at by expanding the Attribute Sets node. You will discover a number of attribute sets defined.
Expand the VendorNo node to find two nodes that follow: Referencing
Fields and Referencing Attribute Sets. Select the VendorNo field itself to
open the attribute definition shown in Figure 4.19.
_-.-.-
Transliterate
--.-----{---I~~~.
I___ _I ___._ ---.-_-----I
Edit Mask
Displ~ofmat
EditFormd
____I_~-
Figure 4. I9
The Definition
tab for setting
attributes
MinValue
MaxValue
Garency
PWiSiOtl
DiS&Vdues
BlobTYpe
IVN ooo0
_____
DefaultExpressim
customconst1aint
_~____._
Enoc Strii
Based On
I
I
ilOO
;9999
/False115
---.
__.._...
I
_____
i - (VendorNo
< 99%)
- - - > 1000)
- - and (VendotNo
%ndof No has to be between 1000 and 9399
,<nOne)
TFieldClass
TControlClass
Alignment
DisplayLabel
Database Engine
97
DisplayWidth
ReadOnly
Required
True/False.
This determines whether or not a field can be displayed in a
Visible
EditMask
DisplayFormat
EditFormat
MinValue
MaxValue
Currency
a data-aware control.
DisplayValues
DefaultExpression
This specifies a default SQL value for the field when it is left
or graphic control.
Error String
objects
98 n
The entry for the field VendorNo contains some interesting examples of
the level of control that can be exercised through the data dictionary.
Note first the DisplayFormat. The entry contains the value VN 0000,
which translates into a literal string of 70-J followed by the vendor
number that you want to assign. It is important to remember that this
differs from the EditFormat entry, especially when you are entering
data. When a new value is entered for the vendor number, the VN
will not appear until after the value is accepted.
The value acceptable for this field is constrained by the
CustomConstraint SQL string. This field contains any valid SQL string
that you wish to use to validate or otherwise constrain the data for this
field. Values entered that violate this constraint will cause an exception
to be raised. When the exception occurs, the error message contained
in the Error String field is displayed, overriding the default exception
message.
Below the entry for the VendorNo field are two more nodes. Expanding
the Referencing Fields node demonstrates the usefulness of the data
dictionary Notice that two fields in entirely separate tables reference
the extended attributes set for the vendor number. By setting the
parameters for a field once, we are able to reuse the attribute set for
any similar field. Whether the data item is the same, as is the case with
the vendor number, or different data items with similar formatting, a
single entry in the data dictionary can format multiple fields. When the
settings need to be modified, expanding the range of allowable values
for the vendor number for example, the modification is made at a single point rather than at each field in the database. This saves enormous
time and makes for much cleaner code.
The REGISTER database is a comfortable place to establish our first
attribute set. The attributes that are going to be set will be used for verification of the Date of Birth field (renamed DOB for simplicitys sake).
After reopening the dictionary DemoDictionary created earlier in this
chapter, follow these initial steps to establish a new attribute set:
I. Expand the nodes of the dictionary by clicking on the plus sign. You
should have two empty nodes: Databases and Attribute Sets.
2. Right-click on the Attribute Sets node and select New from the context menu. A new attribute set called EXTFIELDl will be opened.
3. While the set name is highlighted, rename it DOBField.
4. Three items in the attribute set are going to be modified for this field.
Figure 4.21 is the right-hand pane of the Database Explorer showing
the completed items.
-_~-__.-
Aliint
DisplayLaM
Displawtih
Readonly
Rmuired
Vile
Transliterate
E&Mask
Disph$FC4mat
ECUFormd
Minvdue
Math/due
I
_
_
1 mmmm dd, yyyy
-.___ --...--~.---.-----l.-.-
I
!
Currency
PIecision
DisplayValues
BkbTVpe
DefatAtEwpression
Figure 4.2 I
The Definition
tab for setting
uttributes
-____-
customcQrlstraint
ConstraintErrwhiessage
Based On
I <none>
<= 08i31/94]
Figure 4.22
Database
Breadboard
dialog
Figure 4.23
The Associate
Attributes
dialog
IO 1
3.
The properties in the Object Inspector that relate to the attribute set
properties will now be filled with the parameters from the data
dictionary
4. Close the Fields Editor and compile the project.
Test the project by entering various dates in the DOB field. The error
message defined earlier will appear for each date that fails outside of
the range. You will also discover that the validity checks set in earlier
sections are in place. For example, the row will not be accepted without
a Social Security number.
One critical caveat must be remembered when updating your attribute
sets. The changes will not automatically be associated with the TField
objects. You must associate the new attribute sets with the objects and
recompile your project before the changes will appear in the executable-a small price to pay for the flexibility offered through a data
dictionary
The Dictionary Menu
The Dictionary option available on the main menu provides access to a
number of different functions pertaining to the management of data
dictionaries. Each of the selections is detailed in the following
paragraphs.
Select
As discussed and utilized earlier, the Select option is used to select an
existing data dictionary from the Select a Dictionary dialog.
Register
New
;:
,._
-.,
Delete
The Delete option is a destructive command that will delete the specified data dictionary file. The deletion is performed through the Delete a
Dictionary dialog box.
Import from Dutabase
The Import from Database option allows you to import table schema
information from an existing database into a new or updated data dictionary The dialog box that appears, Import Database, is used to
determine the column attributes that you want to import into the data
dictionary as attribute sets.
Import from File
Similar to the previous command, the Import from File option imports
table schema information. This command works with information contained in a flat file rather than a data-dictionary file format. The flat
file format that it recognizes is the Borland Dictionary Export file extension (.BDX). This type of file is created using the Export to File
command from the same menu.
Export to File
This command exports a data dictionary to a flat file format. As
explained above, the flat file can be imported using the Import from
File command.
.i
Chapter AThe Bodand Database Engine and the Database Utilities n 103
Before performing this action, you should be very familiar with the SQL
database and the types of constraints that it supports. This information
will determine where you will find the different types of constraints
after importing them.
Looking Forward
The next chapters will finally get into the meat and potatoes of Delphi
database programming. The data access components will be examined
first since they are the ones that actually link the program to the data
files. Then the data controls are put to work, creating the outstanding
interface that Delphi is famous for.
^.i
*: _(i
The TDataSet object (Latin name, TDataSet) is the core unit that .:*
enables access to tables through the BDE or other database access !$
points. A TDataSet object is the ancestor class from which all oth&#
dataset objects are derived and models such real-life items as a ta
a database or a query that presents a subset of this data. The clas
defines a set of fields, properties, methods, and events that are in
ited by all of the descendants. You cannot utilize the TDataSet ob
directly because many of the properties and methods are virtual ~$2
abstract. A virtual method is one that is defined in the base clas
can be overridden in descendant classes. An abstract method in
class provides a function prototype but no implementation; descen,i
dants are required to provide the implementation. This object modei &
perfect for implementing these database components: Each is built F4$$
the cask of accessing data; they just use different methods of accom-l*
plishing the job.
There are three classes that are direct descendants from TDataSet.
TBDEDataSet enables the TDataSet class to work with the BDE to
enable database access (TDataSet itself is database engine indepen
ent). The TDBDataSet component is a descendant of the BDE-eni
TDataSet that adds additional session and database features. I~Jx&~
were to develop your own data access components, you would m
likely create a descendant from the TDBDataSet class. The last cl&$
also a direct descendant of TDataSet, TClientDataSet. This class
designed lo implement multi-tier database applications using di
uted databases.
Dataset
St
ates
Before we talk about what can be done with a dataset, it is
to understand under what condition we are addressing the table. The
state of the dataset is a read-only flag that determines what actions u
and cannot occur at any specific moment. The table in Figure 5.1 SU$$
marizes the DossibIe states that are communicated bv the tables.
;?4
dslnactive
Inactive
tab&
Browse
&Edit
Edit
Dslnsert
insert
DsSetKey
SetKey
DsCalcFields
Calcfields
::
5 are
recessed and prevents changes to
DsCurValue
CurValue
DsNewValue
NewValue
DsOIdValue
OldValue
Dsfilter
Filter
Figure 5. I
Possible
states of a TDataSet
descendant
p*-i
Each of the state values shown breaks down into one of two basic
$5
conditions-the table is either open or closed. A closed dataset object
does not expose any of the data contained within to either browsing or :f
modification. In order to perform these operations, the dataset must be $$
opened. Delphi has two methods of performing this function. You can :i:k$
set the Active property of the dataset object to True, either at design
time or programmatically at run time:
Vendors.Active
:=
true;
Open;
Many operations, such as setting the current index, also require that
the dataset be closed and reopened for the effects to be seen. Delphi
provides two methods for performing this task as well. The Active property of the dataset can be set to False at design or run time:
Vendors.Active
:=
False;
When the dataset state falls in the open category, the developer must
be aware of two factors: what actions can be performed against the
database and how the dataset arrived at the current state. For example,
a dataset object linked to a data-aware control can be displaying the
data but the user may find that he is unable to edit the current record.
The developer should be aware that the default state of a dataset,
Browse, will allow browsing of the data values but no modifications.
The state of the dataset will have to be changed to Edit before any
changes can be applied. Depending on the dataset component used,
certain combinations of properties can be set to automatically set the
state to Edit upon accessing the table. Awareness of these conditions is
important in your design considerations and in testing your application
prior to rollout.
Key Properties
The Object Inspector displays the wide range of properties published
for the Table component and the chart in Figure 5.2 provides a brief
definition of each of them. Many of these will be utilized when we
arrive at the first sample application, but there are a number of properties whose values are not self-explanatory or immediately put to use.
Active
False
AutoCalcFields
True
CachedUpdates
False
Constraints
for
this table.
DatabaseName
Defaultlndex
belongs.
True
False
current
for
unless ..:
FieldDefs
Filter
condition.
Filtered
False
i
,jl:
.34.
for the
.;
table.
FilterOptions
IndexDefs
IndexFieldNames
fo be
IndexName
secondary
I.
MasterFields
that
. 2
*F
j :z
property.
The name of the DataSource
MasterSource
component
-:t
,
relationship.
Name
Table I
ObjectView
False
ReadOnly
1 False
of the
table.
SessionName
StoreDefs
table.
the
TableName
TableType
I 1
ttDefault
this
..
(ri
5
the
table.
Tag
UpdateMode
UpWhere
All
UpdateObject
2
_; 0
dataset.
component properties
Aura Cc&Fields
This table property controls the firing of the OnCalcFields event that .!$
updates the calculated fields in a row. If the property is set to True, cat%j
culated fields will be updated under the following conditions:
;
n The table is opened.
n The state of the table is set to dsEdit.
n ii column is modified through a grid or other data-aware control
and the focus changes.
n A row is retrieved from the database.
Depending on the complexity of the calculation and the number of columns with which it works, auto-calculation may slow data entry or
retrieval considerably. For this reason, it might be desirable to set this
property to False during periods of intense data entry When set to
False, the event will not be fired and the columns will not be updated.
$
,. :g.-a
! sQ
(4
_,::
.,-*-s
r,iys
Tip
.~;.i
.:
..f
CachedUpdates
:=
True;
When caching is turned on, the user will work with a local copy of the
data held in memory All updates will be applied to the in-memory data
and written to the source table in a single transaction. Disabling cached
updates without writing the data back to the table will result in a loss
of data as the updates will be discarded without notification.
Constraints
2
,ii
1 .;
.$
i
.7*
_ ?&
.I4;I
:,s
,+j
,.-+
__
^...
/_
.._
i_.*
_.s~u.pI~-~~~~
.:
Filter
--
Filtering is the method by which some data from the table is exclude from view by displaying only those rows that meet specific criteria. ::
These criteria can be set through the Filter property of the Table
ponent. The property contains a string that defines the filter criteria.
For example, if we want to limit the displayed data to those custome __
who owe more than $10,000, a filter such as the following will only-g
,. 4.<
display records that meet the condition:
I
TotalDue > 10,000
This condition can be set either at design or run time. In addition to--this string, the values in the Filtered and FilterOptions properties ati ,_r
how the filtering occurs. The Filtered property is a Boolean value th@$
determines if the filter string is ignored or acted upon. When set to -.::
False, the filtering is ignored and the complete dataset is displayed.4
ting the property value to True will enable the filter string in the Fi&
property. The FilterOptions property is a set of two separate prop&j
that further control the filtering process. The first, foCaseInsensit
makes the comparison between the literal in the Filter property
column value case-insensitive. When foNoPartialCompare is set.tu
False, strings in the Filter property that end in an asterisk signify a
tial match with the asterisk acting as a wild card. If the value of thk,,> _~
item is set to True, the asterisk is taken literally.
lndexFie/dNames
This orooertv allows vou an alternate method of defining an index for r:;j
the I:able. The value entered in this property is the column (
that you want to use to index a table. Entering a value in this propeM
overrides the value set in IndexName and vice versa.
I
MasterFields
:.$
-.?
tj;/*
Figure 5.3
The main window ofthe
Field Link
Designer
/ *
The Field Link Designer has done some quick background work and has
already determined the salient data for you when the program starts.
The indexed columns from the detail table have been placed in the
drop-down box; you simply choose the linking field in the detail table
by selecting it from your list of choices. In the example, the column
CustNo has been selected; it then appears in the Detail Fields box.
Select this field and move your attention to the Master Fields edit box.
To create the linkage, the value in the detail field must match the value
in the master field. Select the CustNo field in the Master Fields box and
click on the Add button. The join will now be displayed in the Joined
Fields box. Clicking on OK finishes the process.
SessionName
The Borland Database Engine uses a Session object to wrap all of the
database connection, drivers, cursor, queries, etc., under a single name
and separate them from other applications use of the engine. By
defaulr, every database application creates an object called Session and
the BDE maintains a list of all of these objects called Sessions. The
property SessionName allows you to associate a new session name with
n
-
this table. You will create new session objects only under exceptional
circumstances, such as concurrent queries against the same database,
for two reasons. First, the default Session object can manage a wide
range of conditions and needs and rarely needs to be overridden on
low-tier applications. Secondly, the complexity of managing multiple
sessions can quickly overwhelm an application and its developer.
Table Type
TX-T-ASCII table
b/e Events
The events for dataset objects are somewhat different from those for
other visual components. The events are triggered by movement and
modification within the underlying data rather than the changes to
control itself. The Table component events are:
H AfterCancel
n AfterClose
n AfterDelete
n AfterEdit
W AfterInsert
n AfterOpen
W AfterPost
W AfterScroll
Chupter 5-b
-_l*-__l
De
-_I
@hi
BeforeCancel
n BeforeClose
W BeforeDelete
H BeforeEdit
H BeforeInsert
n BeforeOpen
H BeforePost
w BeforeScroll
w OnCalcFields
w OnDeleteError
n OnEditError
n OnFilterRecord
w OnNewRecord
n OnPostError
w OnUpdateError
w On UpdateRecord
The before and after events for TDataSet descendants surround a
corresponding interaction with the dataset. For example, we have
BeforeCancel and AfterCancel events. The event that will trigger both
of these events is a request to cancel changes to a row. The
BeforeCancel event is fired after the cancellation message is sent but
before it is executed. The AfterCancel event is fired after the request
has been executed. By utilizing the before and after events, you can
wrap and control nearly every transaction against your database.
.g-4
( A :
-i
,;*
;.;
i ii
CIose Events
These events fire when a message to close the dataset has been issued ,.X:
and immediately after the dataset has been closed.
cfj
.,$.$
,;c;
,/
Delete Events
The delete events fire when the message to delete a record is issued
and immediately after the record is deleted.
Edit Events
This set of events fires immediately before the application enters edit
mode for the current record. The AfterEdit event fires after the
of the record begins.
3
.*
Insert Events
These events fire before the application enters Insert mode and after a
new record is inserted.
Open Events
The BeforeOpen event fires when a request to open a dataset is
received and before the dataset is opened. The AfterOpen event occurs
after the dataset has been opened to the application and prior to any
data access.
Post Events
After changes have been made to a record, the modifications must be
posted to the dataset. The BeforePost event is fired before the update
request is executed. AfterPost is triggered after the changes have been
posted to the dataset but before the dataset is returned to the Browse
state.
Scroll Events
These events fire before the application scrolls from one record to
another and after the scroll action has occurred.
OnCalcFields
The OnCalcFields event fires when an application recalculates calculated fields within a dataset. This action occurs, in the default mode,
when a dataset is opened, the dataset is put into Edit mode, the cursor
moves from one column or field to another, and when a record is
retrieved from a database.
OnDeleteError
This event fires when your application fails at an attempt to delete a
record. Your event handler can issue corrective instructions and then a
retry attempt for the delete action.
OnEciitError
-__
.i
.__*-
-._
_n_((-~qs
OnFilterRecord
?sj
The OnFilterRecord event fires each time a new record becomes the
1-j
current record and filtering is enabled. This event handler is included -
so that you can set a filter condition that cannot be set in the Filter
..$
property due to its complexity. Each record is tested against this code %&
q
determine if the data is visible to the application.
4
OnNewRecord
This event is fired when your application adds a new record to the
dataset.
OnPostError
OnUpdateError
~7
Key Properties
The property list for the Query component is detailed in Figure 5.4 and
as you will notice, it looks remarkably similar to the property set of the
Table. This, of course, is because both the Table and Query components .!
1
descend from the TDataSet class and inherit its properties, methods,
Tools
.i *
,b.
and events. The changes to tllis property list reflect the differences ia
the components approach to da ta access.
Active
False
AutoCalcFields
True
CachedUpdates
False
Constrained
False
statement that
This contains the
record-level
.y:j
II
:
-I
.?j
the mhlc
IQ uery I
The
FilterOptions
Name
ObjectView
RequestLive
values
to be used in the
False
SessionName
SQL
Tag
UniDirectional
0
False
capabilities
Constrained property value determines if, when using a live result set,
rows can be inserted or modified that would violate the parameters of
the WHERE statement that built the result set. For example, if the origi- .?
nal result set was created using the following SQL statement:
SELECT * FROM Vendors WHERE State = 'CO'
"CO".
DataSource
The ParamCheck property determines if the parameter list for the SQL
statement is cleared and rebuilt when the SQL property is modified
during execution of the application. The SQL property is easily modified during execution to run different SQL statements. If the statement
fort II-The
.^,
There are two types of result sets that can be obtained through the use z.~
of a Query component: read-only and live. A read-only result set con- Gi.-:
tains the results of the SQL statement but cannot be modified by the
--.A
application. If the value of the RequestLive property is True, a live
i
result set open to modification is returned when possible. The user CSQ+~
modify the contents of the result set and have those modifications -:
propagated to the underlying dataset.
I.
.,g!
SQL
c$;+a
.:;:
The SQL property is the heart of the Query component. This string IiW:$
contains the text of the SQL statement that is executed to provide the !:
result set to the application. This property can be set at design time or
built dynamically at run time. The BDE and the underlying capabilities
of the target database determine the syntax supported.
UniDirectional
The BDE supports bidirectional cursors, meaning that both forward and .i
backward navigation are allowed regardless of the rules of the undetly-$2
ina database. If the value of the UniDirectional oropertv is False,
. __;.e
bidirectional cursors are returned in the result set. Setting this value to ;:i
True will result in a unidirectional cursor that allows only forward
:;i 8.,
navigation.
.z ,
,.
I,i
Chapter 5-Data
.-
-^- ;I-.
^.
.s..
_.*%a*
:-
DataSet
1 Enabled
Name
Tag
1 True
DataSource
10
\i
component properties
Auto Edit
When the value of this property is set to True, the associated dataset
automatically enters the Edit state when a connected data-aware con- :!j
trol is updated. When it is set to False, the application must explicitly 2
change the state of the dataset to Edit through a call to its Edit metk#
-;
before the user can update the data.
The convenience of defaulting to the Edit state is a good choice for i-3
most programs but should be considered carefully for more critical
applications. You may want to exercise more control over exactly wlI&
updates can be performed by either having the user explicitly turn on :;$
the edit functionality or having the program review the conditions
-*2
under which the update is being performed.
:*
_...i$>
DataSet
122
#.,%. _
_
Enabled
This property is the switch that controls the visibility of the data contained in the dataset. When this property value is True, the DataSourc$f
is connected to the dataset and any controls that reference the
:
DataSource will display current data. (This is provided that the Table;::
etc., is in an active state). A value of False will serve to disconnect the 2
data-aware controls from the dataset.
I:
<:
The value of the property can be modified at run time in order to tern-%
porarily disconnect the controls from the dataset, although this is not --?
recommended. The preferred method of performing this operation is ..jf!: .t
c;
through the DisableControls method of the dataset.
. :;
7
Tip
,;g
_, .A
., .-(
A<
.,
7
X.
. .i.
--;:
OnUpdateData
Whenever the data content in the current record of the dataset is about?!!
to be updated, the OnUpdateData event is fired. This handler will pro- :;I
vide your application with the opportunity to update non-data-aware .,--TZ-$+
components and keep them synchronized. For example, when data ina
field is modified and the user tries to move on to the next record, the 1.
Post method of the dataset is called. Prior to actually changing the
record, the OnUpdateData event is called.
,.
The best way to understand the interaction between the database corn--$
ponents is to put them to work. A simple project that uses the
minimum number of components will allow us to explore the
DataSource and DataSet components without the interference of interface issues. The first project will zero in on the DataSource componen&,:j:
On its own, it is singularly unspectacular and cannot function in a vat- .k
.,;sj?;
uum without the support of other components. Remember that its
purpose is to provide a conduit between the interface components and :
the database. The Datasrc project will examine the control that it provides to your application. All of the projects described in these pages
are on the CD-ROM in the Chapter5 subdirectory.
matter where you place these components. They are non-visual and
will not appear on the form at run time.
3. Click on the Table component and set the properties as follows:
H DatabaseName = DBDEMOS
. 3.. 21
W TableName = CUSTOMER.DB
,-I,
4. Select the DataSource component and set the value of the DataSet : .;s
property to Table 1.
5. From the Data Controls tab, select the DBGrid component and drop it
onto the form. The DBGrid control provides a table-like display of the
contents of a dataset. Select the DataSource property and change it to
DataSourcel. You will notice that all of the database components
6.
7.
I.
are aware of what other components are a part of the project and their dg;
names will appear in the drop-down lists for properties such as this.
Select the Table component again. We are going to turn on the spigot
and start the data flowing to the application. Change the Active prop,jj!
erty to True. When you do, data from the CUSTOMER table will be
_ f$
displayed in the DBGrid control.
~~3
One last step for neatness sake. Drop a BitBtn control on the form and
.i
_
A.?$
modify its Kind property to bkclose.
ihs,$A
You can run the application at this point. It does not do anything specd:
tacular but you should be able to navigate the dataset with the arrow
4
i
keys and browse all of the data. Click on Close to continue.
; ;-.* #
Recall that the state of the dataset determines what actions can occur
::$
against or with the dataset. Using the simple database application that
..&j
we have constructed there is an excellent opportunity to learn more
-.::
about the complexity of the state sequence. The Datasrc application
will be modified in the following steps to pop up a message box each
time the state changes in the database.
Select the Events tab for the DataSource component and add the code
shown in Listing 5.1 to the OnStateChange event handler.
procedure TForml.DataSourcelStateChange(Sender:
TObject);
var
tempstr
: string;
begin
tempstr := 'I;
case Tablel.State of
dsInactive
: tempstr := 'Inactive';
dsBrowse
: tempstr := 'Browse';
dsEdit
: tempstr := 'Edit';
dsInsert
: tempstr := 'Insert';
dsSetKey
: tempstr := 'SetKey';
end;
messagedlg('Current
state changed to '+tempstr,
mtInformation,
[mbOk], 0);
End;
Listing 5. I Modifying the OnStateChange event handler
2.
As shown in Figure 5.6, your application should notify you of every single state change.
- * *
Pfl Box 54
Notice that the dataset is originally in Browse mode and will automatitally switch to Edit mode when a change is made to the data. You may
discover that the state changes more often than you ever suspected.
i _.,_ .~ *a _Y -slr_*_.*lil-__/,__llI_~
4. Unlike the Table component that starts the data flowing to the app,,,
tion as soon as it is made active, Query sends data to the applicatiu@$$
only as a result of a successful query. The SQL statement must be
:.:;>
entered into the SQL property prior to activating the component. Th&,;$
easiest way to enter code at design time is to invoke the String List :?$
Editor by double-clicking on the SQL property value column. The fi
statement that will be used is a simple SELECT statement that will
retrieve all of the rows from the MASTER.DBF table. Enter the follovjx;,, i
ing code into the String List Editor:
.>B
,<$
SELECT * FROM
5.
:
:y
MASTER.DBF
Set the Active property of the Query component to True. If the SQL
i
statement was correctly formatted, the grid should fill with data. If a a!
problem is found with the statement, an exception will be raised and :$
the Query component will not be activated. Save the project after c
piling it.
>q
A simple query but useful in learning to utilize the components. The. L&
muscle behind the Query component is the complexity that is allows
in the SQL statement. Local SQL supports a wide variety of construe
for building static statement but it also has another advantage-it SI
ports the placement of replaceable parameters into the clauses of a i., Jstatement. This allows you to build a statement into the Query co
nent into which your users can place values to manipulate the
statement at run time.
MASTER.DBF has a column containing the risk assessment of each of
the stocks that we are tracking. Each of the instruments is rated as low,
medium, or high risk. To add functionality to the Wall Street Watch
application, we will use the Query component to allow the user to categorize the stocks displayed by risk.
:.p
-8
, .,
,I
i.*,.Aq
A SQL statement can be parameterized so that the values in the param- fi:.
eters can be modified at either design time or run time. The parameters ::
replace persistent values in a SQL statement. One very useful place to ii
utilize parameterization is in the WHERE clause in which the selection,. 3:s2
criteria is based on the values found in specific columns. Parameters I f?J
are represented in a SQL statement by their label and are always pre- :$j
2 I>,2
ceded by a colon.
-.d
53
The SQL statement in the Query component will be modified with a :T$
parameter to represent one of the operands in the WHERE clause. The-:$
new statement will read as follows:
-I 2
SELECT * FROM
master.dbf
WHERE
risk
:RISKCODE
'-3
RISKCODE will contain a value that will be compared against the column Risk when the SQL statement is executed.
When the SQL statement is modified, it will share the parameter values 8
with the Params property of the Query component. Modifying this
property at run time is another dynamic method of adding new parameters to a SQL statement that enables you to build the statement on the 8
fly.
Listing 5.2 shows the code used to modify the RISKCODE parameter
value at run time.
procedure TForml.ButtonlClick(Sender:
begin
with Query1 do
begin
Close;
Unprepare;
Params[O].AsString
TObject);
:= Radiogroupl.Items[Radiogroupl.ItemIndex];
Prepare;
Open;
end;
End;
Listing 5.2 Modifying the RISKCODE
parameter
When using parameters in a SQL statement, there are a number of preparatory and cleanup steps that must be executed. Notice that the first
8
thing that occurs is that the Query controls Close method is used to
close the dataset. Modifications to the SQL or Param properties must be
executed on a closed dataset. Next is the statement Unprepare. This
8
method works in conjunction with the Prepare method to set up the
environment and bind the values to the parameters prior to executing
the SQL statement. The Unprepare method cleans up after the SQL
8
statement and releases resources back to the application.
The value that we want to use in the WHERE clause of the SQL statement is set in the line that reads:
Params[O].AsString
:=
Radiogroupl.Items[Radiogroupl.ItemIndex];
Remember that the Params property is an array with each of the parameters in the statement indexed to their position. Therefore, the first and
only parameter in our statement resides at subscript 0 in the Params
list.
I
128 q
_,._
HIGH
Figure 5.7
The Wall
Street Wutch
project
Try selecting the different risk values. For each one, a query will be executed and a new dataset displayed on the grid. (The Risk column is on
the far right-hand side of the table and may not be immediately visible
for you to verify your results.)
I 29
,. ,.
Start a project and create a form that is similar to that shown in Figure
5.8. You are going to use two Tables, two DataSources, and two
DBCrids. Save the project.
2. Prior to building your application you will have decided which table
will be the master, or controlling, table in a relationship. Navigation in
this table determines the contents of rhe detail table when linked in
this manner. The Table1 and DataSource components will link to the
master dataset in this relationship. Their properties are the same as
those used up to now. Point the DataSource control to Table1 as the
DataSet property. Table1 will use DBDEMOS as the Database property and fill TableName with CUSTOMER.DB.
Activate the dataset.
3. Select DataSource and set the DataSet value to Table2. Select the
Table2 component and again, set the DataBase property to
DBDEMOS. The TableName property for the detail table will point to
ORDERSDB.
4. The detail component is used to set up the relationship at the application level. Select the MasterSource property and select the DataSource
control linked to the master table, DataSourcel. Select the
MasterFields property and click on the ellipsis button to invoke the
Field Link Designer. (This was referenced earlier in this chapter. Please
refer back to those paragraphs for usage information.) The primary
key for the ORDERS table is the default index displayed in the
designer but this will not work as a linking field. Click on the Available
Indexes drop-down box and select a secondary index, CustNo. Click
on the CustNo field in both the Detail Fields and Master Fields lists
and click Add. Select OK to finalize the relationship.
Activate the Table2 component. Review the orders shown in the detail
grid. They are now limited to those orders matching the customer
number of the currently selected record in the master table. Compile
and execute the application. As you navigate the master table, notice
how the contents of the detail dataset change. The miracle of the relational database is revealed!
Filtering is a dataset-based method of limiting the rows that are displayed. Afilter is a constructed statement that compares a value to a
column value. Only those rows that match these criteria become available to the application. The OrderForm application can be modified to
utilize the filtering properties of a table to improve the usability. A filter
will be built to utilize the contents of the State column to limit the size
of the dataset geographically
1.
Figure 5.9
Addrng an edit
box and button
to the
OrderForm
project
2. Add the following code to the OnClick handler procedure for Buttonl,
+ I"';
One last thought prior to moving on to the remaining components: If your application plans include a requirement for
scalability, you may want to use Query components for all table
access rather than simple Table components. This will make the
task of scaling up much easier to accomplish with fewer changes
to the application.
Active
False
to be executed.
AutoCalcFields
True
OnCalcFields
CachedUpdates
False
DatabaseName
stored
procedure
belongs.
Filter
filtering
Filtered
event is fired.
condition.
False
FilterOptions
StoredProc
ObjectView
False
reasons.
ParamBindMode
PbByName
Params
SessionName
StoredProcName
stored
procedure.
13
Overload
kq
: v$
r
1::
rilr-l
Param BindMode
MAPCJB
MapBrowser porl
Translaior blipgrade
Figure 5.
Figure
5, I I
The Project
Manager
Manager moin
mum
screen
Two different operations are being handled by stored procedures running on the InterBase server. When the Programmers dataset is scrolled
from row to row, a procedure is used to extract the programmers current project assignments and display them in the Assignments grid.
Secondly, a procedure is used to insert new rows into EMPLOYEEPROJECT To begin building this project, start with a new application.
I. Caption the form Project Manager and save the project. Name the
unit file stprocuZpas and the project stproc2,dpr.
2. Add three DBCrid components to the form: DBGridl in the top left,
DBGrid2 in the top right, and DBGrid3 in the lower left-hand corner. So that the following instructions will have some measure of
clarity, add the labeling as shown. We will refer to the grids by their
closest label in the instructions.
Chapter
135
emp-no,
first-name,
last - name
FROM
employee
Figure 5. I2
Modifyrng the
Project Managerproject
.,,
, .. .~....,. . >.. . > .
. . . _.
.:.*--:a>
._ ._.I.
.
-,.
5. To the Current Projects grid, add both a DataSource and Query component. The grids DataSource property should be set to
DataSource2. The DataSet property of DataSource should be
pointed to Querya. The Query components
components DatabaseName
DatabaseName property
property
will be IBLocal and the following SQL statement will be entered for
the SQL property:
SELECT
proj-id,
proj-name
FROM
project
6. Activate this query to fill the dataset with the results from the SQL
statement.
I36 n
__,. ,.-
7.
. *%;--
TObject;
Field:
9. Add a BitBtn control and set the Kind property to bkclose. Compile
and save the application. Execute the application and scroll through
the Programmers list. As the employee ID changes in the current row,
the GET- EMP- PROJ procedure is fired and returns all of the projects
assigned to that specific ID number.
IO. Add a Button component and caption it Add Project. The user will
click on this button to assign new projects to a programmer. For this
we are going to use another stored procedure from the IBLocal database, ADD-EMP-PROJ. This procedure does not return a value, so
rather than using a Query control we are going to use the StoredProc
component. Place a StoredProc control on the form and set the
DatabaseName property to IBLocal. Since the procedures are a part
of the metadata of the database, clicking on the StoredProcName
property will display a list of all of the registered procedures. Select
GET-EMP-PROJ. Double-click on the Params property. The parameters for the selected procedure are displayed and their properties
surfaced for editing. By clicking on each parameter you can determine
what you are working with. Dont make any changes. Close the editor
to return to the Object Inspector for StoredProcl.
1%
$2
I I. The code for adding a project record is going to be placed in the but- y$
tons OnClick event handler. GET-EMP-PROJ
has two parameters thag 1s
are going to be gathered from the EMPLOYEE and PROJECT tables. >.?I
Enter the following code for the event handler:
r;
procedure
TForml.ButtonlClick(Sender:
TObject);
begin
StoredProcl.Prepare;
StoredProcI.ParamByName('PROJ~ID').AsString
:=
QueryZ.FieldByName('PROJ-ID').AsString;
StoreProcl.ParamByName('EMP-NO').AsInteger
:=
Queryl.FieldByName('EMP-NO').AsInteger;
StoredProcl.ExecProc;
StoredProcl.Unprepare;
end;
12.
Compile the program and save it. Execute the application and select a 2,
programmer and a project. Click the Add Project button and the ,:;
ADD-EMP-PROJ
procedure will take care of inserting the new row. :
When you select the employee record again, the new project will have.
$2
been added to the Assignments list.
fj..
138 n
AliasName
Connected
that djT
False
DatabaseName
The name of the BDE driver for the
DriverName
Excluwe
False
from
.z,
even if
are closed.
connecting
to a database.
.;I
connection is
. sz4-r
j.._ :;:
- !,
. ..
, ~ _(_I
-.:.lt:
.L; p
:y3
<?j
,-A
SessionName
Default
The name
the database
Tag
Translsolation
tiReadCommitted
connection.
component properties
i
AliasName
.t.,
I40 n
/
_,,,i
_.
The data in the Params property is not secure. If security considerations are high for the application and the database, it is not
recommended that the login prompt be bypassed in this manner.
An alternative to this method is to utilize the OnLogin handler to
provide the login information from a more secure source.
Params
This property is similar to the Params property in other components we !*Q
-.;.$,: ~ i
have examined. It is a collection of string values that specify certain
characteristics of the database connection. The Params property can
used to override the values contained in the BDE alias definition. The
parameters that can be set are determined by the alias and driver type:
specified.
Trans/so/ution
Transaction isolation is a specification of the interaction characteristics
between simultaneous transactions on the same tables. When transactions occur against the same table at the same time, the value in the
TransIsolation property determines how much of the other transactio
changes are visible to your applications transaction. There are three
settings for this property:
tiDirtyRead-Permits
the reading of committed, or permanent, changes to the database. At this point the other transaction has
physically changed the data. This is the default value for this propew
!
.: /_/
--tj
3
:&
tiRepeatableRead-This
level of isolation ensures that once your
,:-?
applications transaction has read the row, its view of that row will not I.
change. Subsequent changes to the row by other transactions are not
$
, _:4i
visible.
.
Isolation levels are supported to different degrees by the variety of
database servers. If the target server does not support the requested
isolation level, the BDE will use the next highest level of isolation.
14 1
Figure 5. I#
The Datobose
Editor
142 n
Pa
I-The
Delphi
Database
Tools
5.
6.
$
i:
/_ f?f
:-*2
I1
procedure
TForml.FormCreate(Sender:
begin
Databasel.Params.Add(PATH=C:\Program
Shared\Data');
TOBject);
Files\Common
Files\Borland
:~ ~,~ j~3
,<..<:>A &:z
.;j
_*m----Lem.
n,ll,..
.I
t&&?$
.~,
Tablel.Open;
end;
3.
Active
False
the
session is active.
AutoSessionName
False
session
True
even if the
dataset is closed.
Name
NetFileDir
Session I
network
control
file.
I:i
i
.,tq
.t~-s
.{
:
.;
:
.&
144 n
PrivateDir
stored.
it is
True
Fieure 5. I5 T h e TSession
This DroDertv
combonent
DroDerties
Auto
.
.$
..*
unique session name is automatically generated. This property is
.ii
designed to ensure that multithreaded applications always have unique 3;
session names regardless of r-he number of sessions created. When this .:!
$
property is True, SessionName cannot be set. Also, a Session compo..j.
nent cannot be added to a form or data module that already contains a
Session component that has the AutoSessionName property set to True::.+!!
NetFileDir
aif
TSession Events
The Session component exposes two events, OnPassword and
OnStartup. The OnPassword event is triggered when an attempt is
made by the application to open a Paradox table and the BDE reports
.1,,
insufficient access rights. The event handler for this event must
to this anomaly. OnStartup is triggered when a session is first a
Any actions that need to occur during this process can be executed
through the handler for this event.
,$
f?hj
,i9
4
+operties
There are a number of new properties introduced with this component.
The comolete list of oublished properties is shown in Figure 5.16.
..;
AbortOnKeyViol
True
operation is
table.
:
,: 2
. ;:
.?
AbortOnProblem
True
operation is
the target
written
table.
ChangedTableName
CommitCount
before a
commit
ooeration occurs.
Destination
KeyViolTableName
Mappings
contains
the
column-to-column mapping
instructions
batAppend
Name
Batchplove I
ProblemTableName
problem
Determines
RecordCount
the
maximum
number
of
Source
batch
operation.
This property
Tag
Transliterate
is executed.
True
IS
to
AbortOnKeyViol
This Boolean property determines what actions are taken when a key
violation or integrity exception occurs. A key violation occurs when a
row is inserted or appended or so modified that an integrity error is
generated against the primary key. Recall that the highest order rule of
the primary key is that it cannot be duplicated. An attempt to break this
rule generates a key violation.
_j .~.
___
.i_
Chapter
;-ricrii-ir-.-i-,(-.
S-Dota
.--
Access
.,l
.8:;(
_,.,
.,.,<t-;s
+*
$
i-g,;
The Boolean value in this property determines whether or not the batch
operation is cancelled immediately when a data type problem is
encountered. A type exception is a condition in which data from the
source table must be truncated in order to successfully fit in the target
column it is mapped to. When the AbortOnProblem property is True,
the batch operation is cancelled immediately. Set the property to False
to have the problem fields truncated to fit into the target columns.
The ProblemTableName property contains the name of a table to which
copies of the original, non-truncated records are copied when a problem is encountered.
ChangedTableName
Implicit through its name, the Mappings property contains the column
mapping instructions from the source table to the target table. The
BatchMove default action is to match columns based upon their position, column zero going to column zero, etc. The Mappings property
overrides this default.
;j
.:
;1
2-%I
j
t
:<
.;
;$
* -i
d
.: f,
~-;
__fb$
3
.?f
148
The string list used to define the mapping cannot be partially done or
unexpected results may occur. To define a mapping from a column to a
same-named column in the target table, use the column name alone. To
map from two dissimilar named columns, use a complete equation in
the form Target-Column-Name = Source-Column-Name. Columns that are not defined in the mapping will be Null filled,
necessitating the complete mapping definition in most cases.
.I
,.u
.::$
,i.i
:.J
,;.;
::li
Mod e
The BatchMove operations consist of five defined modes. The Mode
property contains the selected mode of operation. The modes are as
follows:
batAppend-This is the default mode. Rows from the source table
will be appended to an existing target table.
:;J
.I ..+*
:3z$-2
z*(. .rg
-.i
-~, :;
-,:,
_:
;I
Putting
the BatchMove
Component
to
.- :g_
Work
The BatchMove method and component are enormously useful addit-ions to your programming repertoire. For all of the additional
functionality that the component offers, it is relatively simple to imple- : $
ment. The Copy Machine project that is built in the following steps will :?
experiment with the functions in two segments. The first will simply
:
demonstrate the component and its copy mode. After this has been
.
<$j$3
done, we will work with some of the error handling abilities of
. 3.63
BatchMove.
;iii
.j
,_
I. Start a new project and place two DataSource, Table, and Grid components on the form similar to the form shown in Figure 5.17. Add a
BatchMove component, a BitBtn, and a Button to round out the form.
g4, .
T, k :
150 n Pm-t
BatchMove
grid.
procedure
TForml.ButtonClick(Sender:
begin
BatchMovel.Execute;
Table2.Active
:= True;
end;
7.
-.:
,.$I
..
TObject);
Compile and execute the program. Click once on the Copy button to
copy all of the rows from COUNTRY.DB to COPIES.DB.
-2
3
,:
3.
?9
.k
1 sf
'Key violations');
Table2.Close;
:= BatchMovel.KeyViolTableName;
TableZ.TableName
TableZ.Open;
DBGrid2.Color := clAqua;
end;
end;
4.
Compile and execute the application. When the key violations occur,
you will be notified by a message dialog. The key violations table will
be displayed in the second grid, changing its background color to
announce the change.
.
I .,."
-. 'c
"i
+,
$J
$$y
3$3
-a
&.:;
:
1
:a
Jj
There are also caveats involved in the use of cached data that the
developer needs to be aware of when making the design decision to
implement this strategy First, it must be remembered that since the
li ,;$,G
user is working on a dataset that is local and viewed only by them,
changes that they are making to their dataset are not seen by other
.I
concurrent users until they are committed. This issue also appears in
.
reverse; the local dataset is not refreshed when other users update the
$
remote source dataset, thereby rendering the local dataset obsolete.
Additional consideration must be given to the committing of modifica- ::i
11.a*
tions in parent-child relationships due to referential integrity
q?$$
constraints.
$j: :Cached updates are enabled through the aforementioned property
,,
being set to True. When cached updates are enabled, a read-only query ,_,.
gathers enough data as needed to display and places these records into
local memory. The local dataset is updated by periodic fetches from
-:~
the remote dataset as needed by the user scrolling through the data. All ,$
updates are posted to the cached data until complete and then the
-:
entire cache of modifications is written back to the remote dataset in a
.:
$*
single transaction.
i
IBLocal
Figure 5. I
The
UpdateSQI
Editor
The options default on the side of including all fields and indexes tvhen
invoked but you can pare them down to the desired selections. Click on
the Generate SQL button to automate the creation of the SQL that will
be used for the update process. This statement may not be complete
but provides a good framework to start from. Clicking on OK saves the
SQL statement to the property rhat was selected, the default sratement
going to the ModifySQL property This process is repeated for each of
the types of operations that will be handled. The ensuing code is executed during the OnRecordUpdate event.
)I
Ila..m*~*\
of the nested table data. The nested table then has most of the functionality of a single table component recognizing the nested nature of
the data.
;$fJ
a3
;,-
etermines
component properties
Putting this control to work requires an in-depth knowledge of the Oracle data structure that you are addressing. The DataSetField property of
the component is the determinant of the field that contains the nested
data. A DataSource component is directed at the NestedTable control to
pass the result set to the display controls of the application.
Summary
This chapter has covered all of the components that are available to the
developer on the Data Access tab of the VCL. These controls serve the
purpose of connecting to the datasets in various ways, from straight
table display with the Table component to complex multiple join result
sets from the Query control. A minimum of one of these controls, and
more likely two or more, is necessary in every database application
developed using Delphi and the Borland Database Engine.
Looking Forward
The next chapter is going to provide the same coverage to the data controls. These components provide the visual interface components that
the user sees and interacts with in using the database application.
, ( -**3
.$3
2
Y
-?$
.q
25
elphis data-aware components are the other half of the RDAD picture introduced in Chapter 5 where we put the data access components
to work. The data-aware controls are TControl descendants that have
been modified to display and manipulate the contents of associated
-
dataset fields. Delphi includes a wide variety of tools to display every- !
thing from a single row of data that represents an individual record to
DBGrid control.
Whatever your need, Delphi includes a control on the Data Controls tab
to support every kind of data accessible through the Borland Database
Engine. Different from the components on the Data Access tab which
perform their tasks in the background never seen by the applications
users, the data-aware components are the interface building blocks for
the program. Each of the controls is a visual component, and the range
of properties, events, and methods is vastly expanded because of this.
.I
I56
.F_ ..
A.__
Common Properties
Before diving into an examination of the individual components, the
common elements of all of the data-aware controls will receive our
attention. The table in Figure 6.1 summarizes the common published
properties that carry the same contextual meaning between all of the
components. The majority of these are familiar to experienced Delphi
developers because they have the same usage and purpose when used
with the non-data interface controls. Additionall!; there is a small subset of properties that is unique to the data-aware versions of these
controls.
__t
Determnes
parent
IS
anchored to Its
Top, Bottom.
Left. Rreht
-4
BiDrMode
how a control
bdLeftToRrgh:
the BiDlrectional
determines the
-t
True
beveled, three-drmenslonal
appearance
Cursor
crDefault
IS
drsplayed
DataSource
component.
m
DragCursor
crDrag
DragKind
DkDrag
DragMode
DmManual
Enabled
True
This set of properties controls the attribut&.j
of text displayed by the control.
Font
Height
HelpContext
Hint
ImeMode
ImDontCare
ImeName
language
characters.
Name
ParentBiDiMode
True
ParentColor
False
ParentCtl3D
True
ParentFont
True
ParentShowHint
True
PopupMenu
. .!
ReadOnly
False
ShowHint
False
TabOrder
Tag
Visible
Width
True
the
components
1
.:;
,.j
Key Properties
The key properties shared among the data-aware controls are those
that define the database interface and manipulative abilities of the
component. Without these properties, the components would have abil- :.J$
ities on the same level as the controls from which thev are subclassed.
The value in the DataField property specifies the field in the dataset
from which the control derives and to which it sends its data. The
TField objects associated with the dataset determined through the
DataSource property determine the possible values accepted by the
component. The property may refer to a field from the table referenced
or to a persistent lookup or calculated field defined through the Fields
Editor.
DataSource
This property enables or blocks the users ability to modify the underlying dataset. When ReadOnly is set to True, the data-aware controls will
Chapter 6-Delphis
i,.
Data-Aware Components
^I e-*__ls_18/
I!$@
.q
2-*
display the data from the dataset but will not allow it to enter the Edit z
state.
4
The DBGrid component is a workhorse among the data-aware controls. ;
Using a DBGrid, your application can display or manipulate a dataset in
a familiar table-like form. The data is displayed and can be edited in an 1:
easily discernable rows-and-columns format. The columns represent the
field definitions from the dataset with each row representing an indi:
vidual record.
.
I,
-:
4
:4i
The columns in the grid are dynamic and can be added, deleted, and
1
rearranged at design or run time. The fact that must always be remem- j
bered is that the controlling properties for these columns do not belong 1-i
to the grid itself but rather to the field objects contained in the dataset
*
iQ
that the grid is composed of. Dynamic grid columns, for example, exist --::?J
only so long as the field upon which they are based exists. When the
I.$/:
dataset is closed, the column is destroyed.
i
Because the field objects determine the makeup of the DBGrid, the con- . .,$
struction of the grid enjoys a great measure of flexibility. The same
display grid in an application can display a local database table at one
:;
moment, then be completely redrawn seconds later to display the
g
results of a query. The new dataset and its properties will immediately
,.jreplace all of the properties of the former table. This change is as simple as pointing the DBGrid to a different DataSource control.
.:*,-.:.($.
Key Properties
There are a number of properties that control the display elements of
the DBGrid component, some of which will make their only appearance
in this context.
:i
r
-:>
m
Alignment
taLeftJustify
ButtonStyle
cbsAuto
Color
if
clWlndow
the number of ,
if the
column is connected to a lookup table or
has values in its PickList
property. Action
DropDownRows
property.
Expanded
False
FieldName
Font
ImeMode
imDontCare
ImeName
Method
Method
Editor.
A string list that determines the
PickList
values that
PopupMenu
popup me
False
in the
that
for the
.._. I. _jI
,,. _ _
-i
Visible
Chapter 6--Delphis
_
.*,,
Data-Aware Components W 16
(weeL_(_ -
True
column is!-;
column.
dgEditing
dgAlwaysShowEditor
dgTitles
be used
at the top
of the column.
dglndicator
dgColumnResize
dgColLins
dgRowLines
dgTabs
dgRowSelect
dgAlwaysShowSelection
are @ored.
dgConfirmDelete
control.
dgCancelOnExit
dgMultiSelect
I62 n
Part II-The
DBGrid Events
The events exposed by the DBGrid focus on the movement of the cursof -:
within the grid and the presentation of the grid by the application.
-$
: .s$,;
~>
OnCeKlick
This event fires when the mouse click is released in a cell of the grid.
-$
.-i
..
T>
OnColEnter
The OnColEnter event fires when the focus shifts into a new cell in the .:.I
grid including navigation by keyboard or mouse click. OnColExit events .*:;
fire when the focus is in the process of leaving the cell.
OnColumnMoved
This event fires when dragging with the mouse moves a column.
OnColumnMoved occurs just after the column has been moved.
OnDrawColumnCell
The OnEditButtonClick
event fires when the ellipses button on a
drop-down or pick list column is pressed.
OnTitleClick
This event is fired when the user clicks on the title of a column.
We will put the DBGrid component to work in a moment after we discuss another control that is often found in close proximity, the
DBNavigator. We have seen simple usage examples in the previous
chapters, so we will attempt something a little more ambitious with
DBGrid in this chapter.
:h
,,i
- 2
.+
Chapter
6--Delphis
Data-Aware
Components m
--- .~*s.nm,I*~
transaction. The toolbar takes the familiar shape of a VCR button set,
giving the user visual clues as to the actions of the navigation buttons.
Using the button set the user is able to skip to the first or last record iti
a dataset, move to the next or previous record, insert new rows, de&&j
existing rows, post changes, or cancel changes. The buttons that are *:$$
visible to the user can be controlled through the manipulation of the 4
property settings. The DBNavigator component is especially useful
j
when used in conjunction with a DBGrid control to make navigation I
and editing fast and simple.
,3:*<
+operties
The majority of the properties for the DBNavigator control are listed &$
among the common properties. There are two specifically that are of :{
interest to the developer.
ConfirmDelete
The ConfirmDelete property is provided to the application developer to-2
help prevent the careless deletion of rows of data. When ConfirmDeleM
is True, a message box is displayed to the user asking for confirmation
of the pending deletion. If the user chooses the OK button, the deletion
action commences; otherwise it is cancelled. If ConfirmDelete is set to .J.-pj
False, no message box is displayed and the deletion is immediately
.i,: s1
a
2
allowed.
, .?
Tip
;i
.:
VisibleButtons
This property controls which buttons are visible to the user on the
-t
DBNavigator and can be set at design time or run time. VisibleButtons
-;$
is a set of TNavigateBtn objects and removing one from the set has the ..:,.:I
effect of hiding the button. The buttons available for the VisibleButtons /,:y
set and their associated actions are:
First
Prior
Next
:;$g
-,I,;:$<: .x
::;
1;G.
.e
!&
.qj
::*$
IQ
;:+q
pj
DBNavigator Events
. ~,1:xJ
Two events are native to the DBNavigator class, both allowing you to ;J
trap the push of one of its buttons and initiate any special processing
$)
-y.2
that you want to implement.
24
BeforeAction
his Duta-Awure
Components I 165
,,
The two tables used throughout the program are going to be linl red L1.in -=
manner that was not in Borlands original design. We want to set up a
master-detail relationship with the VENUESDB table being the master.
In order to accomplish this, a new index needs to be added to the
EVENTS.DB table.
I. Start the Database Desktop application and select Tools 1 Utilities (
Restructure from the menu. At the bottom of the Select File dialog,
set the Alias to DBDEMOS. Select the EVENTS table from the files
listed by double-clicking on the filename.
2. The Restructure Paradox dialog will display the schema for the
EVENTS table. In the Table Properties drop-down box, select Secondary Indexes, You will find that one index, Date-Time, has already
been created for the table. Click on the Define button.
3. The Define Secondary Index dialog will be displayed as shown in Figure 6.4. Select the VenueNo field and click on the Add arrow button.
The field name will be displayed in the Indexed Fields list and none of
the options at the bottom of the dialog will be modified. It is very
important that the Maintained check box remain checked. Maintained
indexes are kept up to date automatically by the BDE and should be
your standard choice unless the overhead requirements are too severe.
Click on OK to define the index. You will be asked for a name for the
new index; name it VenueIdx.
4. Click Save on the Restructure dialog to save your changes to the file.
With the new index defined, the tables have been prepared for use in
the sample application. Thinking about the design of the program and
Pa
other typical Delphi database applications leads to the need to discuss Se?4
an important tool that falls outside of either of the VCL component
I:$
tabs, the Data Module. The Data Module is a non-visual container fom :.g
that serves one very specific purpose: It provides a common access
i:
point to the dataset objects from all of the forms in a project. The alter- si
native to using the Data Module approach would be to place multiple ,~;,lIh,r.Sti
sets of dataset objects on each of the forms of an application. All of the A:, *
objects would required individual configuration settings, and the appli- .c$
cation would be required to provide the code necessary to maintain the ~~5
relationships, record pointers, etc., in the transference between forms. .-:,;
>:,t -:
The Data Module has uses beyond simply sharing database tables. Any Gi
g,C.. ;
Delphi object, with the exception of those that descend from TControl, ::l
can be placed on a data module and become available to all of the
$3
forms in the program. This carte blanche should be used wisely with
:$
consideration for the actions of the components themselves prior to .) :
placing such things as your main menu in the module.
/ -ji
Adding a data module to an application is done through menu selections. Select File 1New and pick the Data Module from the New Q&
the repository A small container form is added to the application with :
three properties:
n Name
H OldCreateOrder
,.
n Tag
>
OldCreateOrder is the only unfamiliar property surfaced. Its purpose is ,-,
to control the firing of the modules OnCreate and OnDestroy events for .$:i
backward compatibility.
i
3%
For all of its benefits, there are also times in which it is not appropriate, .j
to use the data module. The first consideration is a situation that does ,z$
not require the sharing of components between forms. The overhead ot,:$
adding the module is wasted in this situation; place the dataset objects Y.7
directly on the form that needs them. Also place the datasets directly
1:
when a form has a completely unique view of the data not used in any
other place in the application such as report generation tables and special views.
tab
The Venue Manager will use a data module so that the datasets that
are going to work with can be set up once and shared among all oft
forms used in the demonstration. Clear naming rules for the dataset .
objects become much more important in this type of situation because-:
of the new distance between the datasets and their interface controls, 2::
,.s
I.
Chapter
6-Delphis
Data-Aware
Components
__
n I&%
..v.-&
:.
-iStart a new Delphi application and add a Data Module to the project. i :$$
Place a Table and DataSource component on the data module form. A!_. . 1
Name the Table control VENUE-TBL and set the DatabaseName prop- :i$
.-p$
erty to DBDEMOS. The TableName property should be set to
yqd
i 4
VENUES.DB. Activate the table by setting the Active property to
True. Save the project; the form unit will be named VenMgrU.pas,
.~f
the data module unit will be dModU.pas, and the project will be
.t
VenMgr.dpr.
I_ $4gs
tents of the data module visible to the other forms in an application
you must create an association between the objects. Select Form1
-I!#
and then File 1Use Unit from the menu. Choose the dModU unit and g$
click OK. Alternately, you could simply type the Uses declaration in the
Implementation section of your forms unit.
3. Add a DBNavigator component to the form. If you want to position
this control so that the resizing of the form will not affect it, use the
.
Align property to lock the position. Select alTop as the value for the
property; the navigator will move to the top of the form and size itself
to match the client area. To associate the control with a dataset object,
set the DataSource property to DataModulel.VENUE-SRC. To
familiarize your users with each buttons function, set the ShowHint
property to True. This will provide fly-over hints to the user as they
_
move their mouse across the navigator bar.
4. Now add a DBGrid control to the form. This component also offers an ,>..g2
-,,j
alignment property; we will set the Align property for the grid to
.$j
:
alTop as well. It will fit itself in just below the navigator and will
receive the same benefits of using Align as discussed for the navigator.:
Point the DataSource property to DataModulel.VENUE-SRC
to
make the table connection.
Execute the application and use the navigator to move around in the
table. Note that the movements controlled by the navigator are row
and table centered. Movement between the fields and columns is
driven by the cursor control keys.
Before adding the detail end of the relationship there are a couple of
items that need attention on the Venues grid. The last two columns in
the VENUES.DB table are of type BLOB and MEMO, data types that do
not lend themselves to display in a DBGrid. For the time being, we will
hide these columns to give the table a neater appearance. Additionally,
we will unlink the table from the top of the form and adjust its width to
match the displayed columns.
;:i
i.
;
;:
-1
,4
.wa
II-The
Select DBGridl and set the Align property to alNone. This will let
you move the table down a bit from the DBNavigator bar.
Right-click on the DBGrid and invoke the Columns Editor from the
.:j
context menu. The Columns Editor is similar to the Fields editor that
:
we have worked with previously. The first action that needs to occur is .i.lj
to add the columns that we want to manipulate to the list. Click on the -i
Add All Fields button to perform this transfer. You will notice that the
underlying properties of the columns are displayed in the Object
Inspector. Select the Remarks column and ser its Visible property to
False. The column will disappear from the DBGrid but remain unaffected within the dataset. Do the same thing for the Venue-Map
column.
3. Before making the modifications suggested in the next step, the cre;
ation order of the forms should be reviewed. Select Project 1Optiom
from the menu. As it stands, Form1 will be created before
DataModulel. Since we are going to make use of the forms OnCreate: :
handler and it requires knowledge of the internals of the datasets, w&j
need to have the data module created first. To move DataModule up i L
in the order, select it and drag the list entry above Form1 and release:?
ygj
it. Select OK to save the changes.
4 . After hiding the columns, the table is left in a rather odd-looking grid
:+.cs;j*:_
with excessive white space on the right-hand side. If we add a little
code to the project, the DBGrid component can size itself to accommW.i.I,. : 1..
date the columns that are displayed, regardless of changes to that
:$I
.$
number. Add the following code to the forms OnCreate handler:
.:j
procedure TForml.FormCreate(Sender:
TObject);
var
tablewidth : integer;
: integer;
begin
tablewidth := 0;
with OBGridl do
begin
for x := 0 to (Columns.Count - 1) do
begin
tablewidth := tablewidth + Columns[x].Width;
end;
(Add column widths + scroll bar width}
ClientWidth := tablewidth + 18;
end;
end;
Chapter 6--Delphis
One note of caution before adding this code to a production application; this code would have to be highly modified if there were a risk
that the ClientWidth of the DBGrid component would be greater than
the ClientWidth property of the form.
The next step in the project is to add the detail table to the form. Setting the relationship between the two tables will be done in the same
fashion that has been used in previous projects. When the new table
has been added, some additional programming will enable the
DBNavigator to be shared between them.
Add a second Table and DataSource component to the data module.
Name the table EVENT-TBL and set the DatabaseName to
DBDEMOS and the TableName to EVENTS.DB. Select the
DataSource control and point the Dataset property to EVENT-TBL.
Name the DataSource EVENT-SRC.
2. Add another DBGrid to the form and set the DataSource property to
DataModulel.EVENT-SRC. Similar to the Venues table, this grid
contains two rows that will not display well. Using the Columns Editor, change the Visible property for the Event-Description and
Event-Photo columns to False. You may also want to apply the grid
sizing procedure to this table as well if appearances are important.
3. Establish the relationship between the two tables with VENUE-TBL
being the master table in the connection. We are seeking to display al .l
of the events assigned to each venue. The new index that was established a few pages back is the one that will be used to establish the
relationship.
4. The last modification we will make to this project will share the navigator bar between the two tables, rather than adding another
DBNavigator to the form. Rather than coding separate event handler
procedures to implement this, we are going to write a single procedure and then call it when the OnEnter event of the DBGrids is fired.
Add the following procedure to the VenMgrU unit:
I.
procedure
TForml.ChangeDataSource(Sender:
TObject);
in
beg
i f Sender = DBGridl then
DBNavigatorl.DataSource
:=
DBGridl.DataSource
e lse
DBNavigatorl.DataSource
:= DBGridZ.DataSource;
end
70
5, Select either of the Grid components and click on the OnEnter event in
the Object Inspector. Select the ChangeDataSource procedure for this
event. Be sure to add this to the other grid as well.
I
i
1
*: __
__\,\,
Y a:? ,iud:lcrum
1400
:\
,:,
E Gutlltrma Fald
Figure
i
6.5
The Venue
Manager
For each venue selected, the Events grid should only display the events
scheduled for that venue. Selecting either of the grids will transfer the
focus of the navigator to that table. You can use rhose control buttons
to move around freely and manipulate the data regardless of the table
chosen.
Tip
L
/
Its a graphical world out there, and Delphis darabase supporr for a
graphics object is second to none. Nearly all database products supported by the BDE support the inclusion of bitmapped graphics in BLOB
{Binary Large OBject) data fields within the table. (BLOBS are not
limited to being containers for graphics but this is one of their most
common uses.) If a field type is supported by the BDE you can count _, _,,
Delphi offering a component to work with this type of data. For graph-.:?;a
its, that control is the DBImage component, a data-aware version of t&&3$
TImage class.
&z
When the DBImage control is enabled, the BLOB graphics images are :t+%
;!+j
:$
captured from the dataset and then stored internally in the Windows
DIB format. Device-independent bitmaps are an internal data format -.
that is sent to the computers display adapter driver for translation into. :
a screen display. Because of the size of images and the time needed to -g
process them to and from memory, BLOB fields are cached locally wheti.
rows are retrieved from a dataset. This improves the applications per--.-,
formance when a user scrolls through the rows of a table and the
.%!
*;$$
images are retrieved from memory rather than a server.
*.
Key Pr
The properties that will receive our attention here are those that are
unique requirements of the image orientation of the control.
AutoDisplay
:
a
.-i
.?
!
I.
:
=
Ti
:
Center
^
Part I/-The
(_ .-..- q
D
::#j
<j
;&&j&
._ s;j
Setting this property to True will center the image within the DBImage ,.z
control. When this property is False, the upper-left corner of the image /.i?
;-2i
is displayed in the upper-left corner of the DBImage container.
-i::
..hif.$-Zai
QuickDraw
f -1j-4;
When QuickDraw is False, a custom color palette is used when drawing ..$
the image into the control. This gives a very high quality picture at the : ;$
expense of processing speed. If the property is set to True, no palette is .;%
used and the image is displayed much faster. The tradeoff for process- -2%
ing speed is lower image quality
manner. The Center property controls whether or not the image is
automatically centered within the boundaries of the control.
Stretch
i
&3,.
.- :
The name of this property is misleading. If the Stretch property is True,,:<2
the BLOB image stretches or shrinks to fit within the boundaries of the .,gi
DBImage component. The DBImage component does not change size
fit to the image. This propertys actions can cause distortion of the
image because the width and height of the image are independent of 32
;:, +j
one another.
$
*g :
; s
:~
-i-*.3*.
----~
Key Pr operties
There are a number of properties of interest published by the DBMemo
component. Mostly centered on the display characteristics of the text
exhibited by the component, properties are also available to help with
the applications performance.
1
.-j 4
:
::
:.$I,
Alignment
1.
-.i
AutoDisplay
MaxLength
\?:j
You will use the MaxLength property to limit the number of characters ,$22
that can be entered through the DBMemo control. When the value of
I
the property is set to 0, the default value, there are no limits in place. if
a maximum length is entered, the underlying memo field is safe; no
:
text is truncated or lost. The limitation is meaningful only within the
scope of the control itself.
ScrollSars
The text displayed in the DBMemo control is not constrained by the
size of the component. Because of this it can easily be hidden from
view off to the side or past the bottom of the editing region of the control. The ScrollBars property determines what, or if, any scroll bars are
displayed along with the text. The developer has the option of adding
single scroll bars to either the bottom or right side of the control or
adding both simultaneously. The scroll bars are not automatic and are
displayed at all times, even if they are not needed to display the text.
Wantlabs
.?
?
<
:
-
I 76
I. Adjust the width of DBGrid 1 to approximately the size of the three displayed columns and the scroll bar and move it to the left as shown in
the picture. Add a DBImage component to the form, placing it to the
right of DBGridl.
2. Adjust the size of the conrrol so that its height measurement is the
same as that of the Venue grid. Set the width so that it fills out the
space comfortably Depending on the size of your form, the image
should be roughly rectangular. Set the DataSource property to
DataModulel,VJZNUE-SRC and the DataField property to
Venue-Map. Use the Stretch property to have the different-sized
images display in the DBImage control by setting the property to
True. We are not going to worry about the distortion of the image in
this application, though it should not be discounted for others.
*.n=1
,*. *
*.
Chapter 6-Delphis
Data-Aware Components n
. --a--
3 . Add a DBMemo
I.
Add a new form to the project. Name this form EnlargedF and add?%
DBImage control. Set the Align property to alclient so that it fills a$-:
:
of the available area inside of the form. Save the new unit as
EnlargU.pas.
data module so we need to add that unit to the new form. Select the ,:$j
EnlargedF form and then select File 1Use Unit and the dModU unit $8
to include the code into the forms unit.
3. Select the DBImage control and set the DataSource property to
.
DataModulel.VENUE-SRC.
This value is now available since the
.-j
CL,
data modules unit was added to the Uses clause. Set the DataField
property to Venue-Map and the Stretch property to True. The
,:$
a
graphics that we are using are not of the highest resolution but they
.-;
give us a good opportunity to experiment.
4 . One last thing must be done on the new form. When the image form is A<
closed, the resources should be released to the system. Add the follow- ;.
:>
ing code to the forms OnClose handler:
_ .:P>*
procedure TEnlargedF.FormClose(Sender:
TCloseAction);
begin
EnlargedF.Release;
end;
.',' .(
$
_^"X41
>a*;
% .?
".9.<
=c,;
.8
5f;1
L
178 n Part
application starts. Select Project) Options from the menu and move
the EnlargedF form from the Auto-create list to the Available list.
6. Select the VenMgrU unit and modify the Uses line under Implementa,
tion to read:
uses
dmodu,
enlargeu;
,$
72
f
I-.
7. The application is going to allow the user to click on the small image L.:
and receive an enlarged view of the same picture that would let them ;:
view the seating sections seating better. Press F12 to switch from the ,z
unit editor to the form. Double-click on the DBImage control so that
C2
2
you can modify the OnClick event handler and enter the following
:r
.a Xi
,d
code:
;:
procedure TForml.DBImagelClick(Sender:
TObject);
begin
EnlargedF := TEnlargedF.,Create(Application);
EnlargedF.Show;
.-+!
8. Compile and execute your application. Click on the image displayed .w+:d
from the database and you will be treated to the sight of a much
~4
larger version, Though the image quality causes these to pixelate
,,a
;:zjj .<
when displayed, you are able to see the idea in action.
;;
The use of the data module is what enables us to create this multiple :!
form application so easily. Both forms are accessing a single dataset .%$
object so the record pointer is always synchronized without us having Ji.<
to write the complex code needed to manage independent table
accesses.
.iA _. I_;
/ _ . , .
. . --
179
$ .;
.;.,
?
+
,>YJ%c
7%:;
operties
DBText is a simple control to implement and use with just a few properties that need attention when placed on your form.
Alignment
The Alignment property specifies the text alignment within the boundaries of the control, the choices being Left-justified, Right-justified, and
Centered.
Autosize
The amount of text contained in a database field can vary in size, and
the AutoSize property determines how the DBText component supports
this attribute. When the property is set to False, the size of the control
is fixed in both height and width. If the text to be displayed exceeds
these measurements either through length or typeface, the display is
truncated at the boundaries. Setting AutoSize to True allows the control to stretch horizontally to accommodate the data.
;
.;
. ,. .
.i:;
.I
,$$;
:.:Yt,i,$
-
When used in conjunction with the WordWrap property, the control can*.
also expand vertically to fit the text. Some thought at design time is
necessary to ensure that this wildly expanding and contracting component does not interfere with other controls in the vicinity.
Transparent
WordWrap serves the same purpose for this control as it did with the
DBMemo component. Setting this property to True will insert soft
returns into the displayed text so that there is no truncation of lines
longer than the width of the control. To be effective, this property
needs to be used in conjunction with the AutoSize property.
There are no events of note for the DBText control. We will put the
.i
component through its paces in a few pages, after we have a look at the
DBEdit control.
180 n
Key Properties
The properties to be discussed in the context of the DBEdit control are
focused mainly on the formatting of the data for input or display
purposes.
AutoSelect
To aid the user in quickly replacing the text displayed in a DBEdit control, the AutoSelect property can be set to automatically select all text
when the control receives focus. If the property is set to the default
value of True, all of the text is automatically selected when the user
clicks on the field or tabs into the field. The contents of the field are
then replaced as soon as the first editing keystrokes are received by the
control. When the property is False, the user must double-click on the
text in order to select it.
AutoSize
The AutoSize property for the DBEdit component differs slightly from
similarly named properties seen previously. When the property is True,
the height of the control can resize automatically 10 accommodate
changes in the text size. The changes can be driven by items such as th
font or border style of the control. When the value of the property is
False, the height is fixed and changes in conditions are ignored.
C h a rrCase
$3
7 4$7
-3 -54
_.-
-.
Chapter
6-Delphis
Data-Aware
Components
.f
.t
_.
DBCheckBox Component
The DBCheckBox component is a data-aware check box control used ;.i$j
for input and representation of Boolean or other logical fields in a
-4
dataset. Unique to this control is that it does not strictly require that
.i
,
,:
:i
-&;:
3
:;p
;.,y:;
E*:
:a.$
j -$
/+l7
:Tj
182
component are allowed: Checked, Unchecked, and Grayed. The gra
state is a non-valued state, applying no data to the database.
Caption
**.. =, _
Properties
DBRichEdit surfaces a number of properties, some that are very familiar and others focused on the unique tasks of this component.
Alignment
.>,
, :4
:
..i9
:$i
1:
I 84
Ll-I1ma
they may become annoyed at the flashing of the scroll bars. If this is
the case, you might want to consider setting this property to False.
HideSelection
As discussed earlier, the Tab key is a standard navigation tool in Windows. If your application requires that the DBRichEdit control accept
tabs for formatting purposes, set the WantTabs property to True.
.:!!$!
- : I<,$
:,,::j
,3
:k
.~C
.,:;
_,..?r
I
?
: ;:.$
l 1
-. c-
OnProtectChange
OnResizeRequest
_. ;A$:*
:,.$
This event fires when the edit control attempts to resize to accommo- et>
date expanding or contracting text. The application then has the
::r
opportunity to adjust or retain the edit window size and manipulate-a
necessary user interface elements to handle the text.
OnSaveClipboard
When the user changes the text that is selected, the OnSelectionChange
event is fired. The application is able to effect the modification or inter-i!
rupt it altogether if necessary.
_-.c;
A
OnChange
I86 H P a r t II--The D e l p h i D a t a b a s e T o o l s
,,,
__.\
,.
Figure 6.8
The completed
Event Scheduling form of
the Venue
Manager
project
. , ., .
Associate rhe data module with the new form so that it has access to
the shared data tables. Under the Implementation section of the
EditEventU unit, enter the following line:
Uses
dmodu:
Switch your attention to the main form for the application, Forml.
Add the EditEventU unit to the Uses statement for this application,
either by typing the code in by hand or selecting File 1Use Unit from
the menu.
.Add a Button control to the form somewhere in the vicinity of the
Events grid. Caption the button Edit Events and then double-click on
it to access the OnClick event handler. The application uses this button
to pop up the event editing form, so add the following code to the
procedure:
procedure TForml.ButtonlClick(Sender:
TObject);
begin
EditEventF :=
TEditEventF.Create(Application);
EditEventF.Show;
end;
Test the button to ensure that your form will pop up when needed. If
everything is working as planned, switch your attention back to the
edit form and add the following line to the forms OnClose event
handler:
procedure TEditEventF.FormClose(Sender:
TCloseActlon);
begin
EditEventF.Release;
End;
TObject;
var
Action:
1
/
7, Add a Label control just below the venue name and set the Caption
nronertv to Event Number. Increase its font size to 10. Immediateh,,
beliw this label the Event Number field is going to be added. To meTi
the business rules for this field, we want the field to remain under the
control of the program and not the user. Again we can use a DBText to
display the field in a read-only format that will prevent tampering. Add
the component and set the DaraSource property to
DataModulel.EVENT-SRC and DataField to EventNo. For the sake
of appearance, change the Alignment property to taGenter.
8. Add four more labels distributed as shown in Figure 6.8 for the
remaining edit fields in the table. Caption them as Event Name,
Event Date, Event Time, and Ticket Price. Set the font size to 10
KO match the other label.
9. Add four DBEdit components to the form, distributing them below the
labels that were just added. Set the DataSource on all of them to
DataModulel.EVENT-SRC.
The properties of the controls from left
to right are:
II DBEditI
q DataField = Event-Name
0 Width = 150
II DBEdit2
n DataField = Event-Date
II DBEdit3
q DataField = Event-Time
II DBEdit4
II DataField = Ticket-price
IO. Add another label, setting the font attributes to match the others and
the caption to Brochure Description. Below this label add a
DBRichEdit control. Set the DataSource property to DataModule l.EVENT-SRC and the DaraField property 10 Event-Description. Size the component as appropriate for the data. The
applications values are set to a Height of 135 and a Width of 240.
I I. To the right of this pair of controls, place another label with a font size
of 10 and set the caption to read Publicity Photo. Below this we
want to display a picture so add a DBImage control, setting the
DataSource to DataModulel.EVENT-SRC and the DataField property to Event-Photo. A good size for the images contained in the
database is to set the Height at 135 and the Width at 200.
12. The last component to add to this form for now is a DBNavigator. Set
the Align property to alBottom so that the control will hug the bottom
Components W I89
.w-
of the form. Compile and execute the application to ensure that everything is working as planned. Notice that because we have the
relationship between the Venues and Events set up in the data module,
when you switch to the edit form you are only able to access those
events for the currently selected venue.
The event handlers and methods of the components are used to implement the integrity rules in this application. It would be common for the
developer to set many business rule items at the table level, close to the
data. The integrity constraints such as date or time conflicts discussed
earlier would be best handled through code additions to the event handlers of the specific field. The date, for example, could be tested against
the other elements in the same column to determine if a match occurs.
If so, an error message would be displayed explaining the conflict and
the focus returned to the date field. A good place to put this code
would be in the OnExit handler. After a date is entered and the user
tabs over to the next field, the event would be triggered.
To explore some of the capabilities of the DBRichEdit control, well set
up a way in which the contents of the field can be modified. This
means adding a couple of additional components to the Event Scheduling form.
I. Add a FontDialog and a PopupMenu component to the form. Both of
the controls are non-visual so their placement is not important. Select
the DBRichEdit control and set the PopupMenu property to
PopupMenul.
2. Double-click on the popup control to start the Menu Designer and add
a single entry that is captioned Font. Double-click on the Font entry
so that its OnClick handler can be modified. Add the following code to
the procedure:
procedure
TEditEventF.FontlClick(Sender:
begin
if FontDialogl.Execute
then
begin
DBRichEditl.SelAttributes.Color
:=
FontDialogl.Font.Color;
DBR ichEditl.SelAt tributes.Size :=
FontD ialogl.Font.Size;
DBR ichEditl.SelAt tributes.Style :=
FontD ialogl.Font.Style;
end;
End ;
TObject);
190 n
^- ..cx.s;_,
*- % ir*maCI-i
__
The SelAttributes property refers to the text that is selected by the user, ,,
If you want to affect all of the text in the control you should address
-:.x.$_ 1<Y
the Font properties of the DBRichEditl control directly.
-$ I
3. Compile and execute the modified program. Select some of the memo ,I[!
text by using the keyboard or the mouse and then right-click on the
iz
DBRichEdit control. This will pop up the menu, which can certainly be 2
:sj
a good deal more detailed, from which you can start the Font dialog.
.;j
Set the properties the way that you want them to appear and click
,9
OK. If you press the Cancel button, the code will bypass implementing %
i --i
the property changes.
._ 2,;
:
i,
;I
t
The list can be built at design time using the Strings Editor or the collection of strings can be dynamically constructed at run time, giving the l.:,
application the opportunity for the list to always contain an up-to-date
.,!-$
d
set of choices. The DBListBox will highlight an element of the list if
<52$a
there is a match with the contents of the associated data field. If the
value contained in the field does not match any element of the list,
nothing is highlighted.
Key Properties
The properties of the DBListBox are mainly composed of the commonly
inherited set of values. There is a common pair of properties, however,
that often is misunderstood in the context of this control. The
DataSource and DataField properties do not refer to a field that supplies the items for the item list, but instead the DataField property
determines the receiver for a choice from the list of strings. The string
list is defined at design time or modified from some other external
source at run time.
lntegrdfeight
can be set at design time, there is a risk that it will cut off a string at
half or more of its height, rendering it unreadable. When True, the
property will cause the height of the component to be a multiple of the
ItemHeight property value.
-4
:.
i
%
The elements contained in the list box are defined through the Items
property Items is a collection of strings that can be defined at design
time through the Strings Editor. This tool allows the developer to add,
rearrange, or remove strings from the list without having to manage
the string list through Delphi code. At run time, if you choose to take
that route, the application can make use of the Add, Delete, Insert,
Exchange, and Move methods to perform the same operations. Managing the list takes a bit more work within the application, requiring
that the program track index values. A closely related property,
Itemindex, returns the number of the currently selected string.
-;
I:
:
!
Items
Style
The Style property can be used to specify whether the DBListBox contains strings or graphical images. Owner-draw list boxes are used to
display items other than the standard string collection. Owner-draw
boxes require that the application supply the code to draw the list.
DBListBox Events
Two events differ from the user-action oriented events of the other
interface controls and both are connected to owner-draw style
DBListBox controls. OnDrawItem is fired when a new item needs to be
drawn in the list. OnMeasureItem supports this capability for variablesized owner-draw lists by measuring and returning the dimensions of
the object to be drawn.
-1.
.>!
192 n
the control. Also different from the simpler DBListBox is the action @.-.
the control when used to display the current contents of a data fie&@#
the field value is displayed regardless of its membership in the strin&d
list.
Key PIl
operties
DropDownCount
Setting the Sorted property to True can alphabetize the item list in thedrop-down portion of the control. Any new kerns that are added to 9
list will be inserted in the correct sequence as long as the value of&@(g
pJS
property remains True. Once the list has been sorted, it cannot be
returned to its original configuration through the change of the vale __
to False.
Style
Similar to the Style property used with DBListBox, this property deter*:
mines how the DBComboBox will display the items. The control
supports the same owner-draw options as DBListBox that we have d#&~~
cussed. The default style for the component is csDropDown, which
Chapter
B-Delphis
Data-Aware
Components
n I93:gj
,
I..
creates a drop-down list of string items and allows the user to modify
the value shown in the edit box.
::g
The cssimple value creates an edit box with a fixed list below. This
4q%
style still allows the user to modify the contents of the field.
csDropDownList is much closer to the actions of DBListBox. An edit box
:@4
is displayed on the control but the only way to modify the contents of a .
field is to select one of the items in the list. The edit box will only display text if the value of the field matches one of the items in the string
list.
~8ComboBox Events
OnChange
The OnChange event fires immediately after a user modifies the text in
the edit box of the control or selects an item from the list. You might
use this to query the user and determine if they want to add a new item.
to the list, allowing the application to modify itself to new usage.
OnDropDown
operties
The lookup controls take a little study to integrate properly into your
:
application, and the key to understanding them is to be knowledgeable ,*!
about the settings for the properties.
KeyField
;. . x
The KeyField property identifies the field from the source listed in the
ListSource property that must match the value of the field specified in
DataField. This links the ListSource dataset to the DataSource. The
fields do not need to share a name but must have the same values.
-~:
--:z
.?V
The field specified in KeyField is not the field listed in the list box
region of the control. That data is derived from the ListField property.
-;
.A$
;7;
.,;,;
-. -a
ListField
, .&;,cSg
t:
The ListFieldIndex property is used when the ListField property specifies multiple fields to specify the field to use for incremental searches.
Specifically for the DBLookupComboBox,
the value in ListFieldIndex
determines the field that appears in the edit region of the control.
This property builds flexibility into the design of the elements for your
lookup controls. When multiple fields are used to build the item list, it
is not necessary to place the most important element in the first po&
t-ion. You can sequence your elements in the order that makes the most
sense for the appearance of the application and specify the proper field
through this property
3
4::
.&a.2
.:
,
._I,
:?
.,:g.
ListSource
Chapter 6-Delphis
_
Data-Aware Components n
S.~
@$
Lookup
Lookup fields must be mentioned in any thorough discussion of this
ji
brace of components. These field types are read-only fields that display $
values at run time based on search criteria specified by the application. ?!$f
;&
The simplest example is a lookup field that is passed the name of an
.,G
existing field to search on, a field value to search for, and a completely j.
different field in a lookup dataset whose value will be displayed. A brief&$!
sample application will help to demonstrate how these fields work.
i!J
Two tables are needed for this exercise, ORDSHIP and DESTDIST.
,..!*
(Both can be found on the CD-ROM under the Chapter6 directory.) The, :-$i
ORDSHIP relation represents customer orders on which we need to
+,
compute the postage. The total of the shipping cost is a formula that
.351
involves the distance to the customers city and the value of the merchandise. Lookup fields will be added to the table definition to pull the. g,
customer city and the distance to that city. A computed field will then
i8z
use this information to return the shipping cost. When you copy these .,:$
i
tables from the CD-ROM, place them in the same directory as the
Borland sample files so that they will fall under the DBDEMOS Alias. Gij
Start a new application and place two DataSource and two Table corn- .;$
,:
ponents on the form. Set the TableName property of Table1 to
ORDSHII?DB and on Table2 to DESTDISKDB. The DatabaseName
,i
properties for both controls will be set to DBDEMOS. Point
DataSource to Table1 and DataSource to Table2. Add a DBGrid
and DBNavigator to the form, pointing both to DataSourcel. Save
the program, naming the unit lookeru.pas and the project
looker.dpr.
Select Table1 and invoke the Fields Editor (right-click and select from\ _ _
the context menu or double-click on the Table component). Add a new
i
field to the table. The first field to be added to the ORDSHIP table will
be called Destination and will look up the destination city from the
-;T3
DESTDIST table based on a match with the zip code entered for the
order. The Component field will fill itself in with the field name. Set
the data type to Float and be sure that the Lookup radio button is
1
selected. Selecting Lookup will enable the Dataset and Key Fields edit
boxes. Set the Dataset property to Table2. Key Fields and Lookup
Keys are the columns in each of the two datasets that will be compared when seeking a match. Set both of these to the Zip field. The
Result Field is the data that will be returned from a match. For this
-.
field, select City. The completed form should appear as shown in the
example in Figure 6.10.
Figure 6. IO
Defining 0 new
field
3.
Add anorher new field for a second lookup field. This one will be
called MilesTo and will represent the number of shipping miles
between the factory and the recipient. The link between the two tables
will be the Zip code again and for this column the Result Field will be
Distance.
The last new column will be a calculated field of type Float. Name this
field ShippingCost. After the field has been created, edit the
DisplayFormat property and set it to a mask of ##O.OO.
With the calculated field in the table, we need to establish the formula
for calculating the shipping costs for the furniture. Close the Fields
Editor and select Tablel. Double-click the OnCalcFields event and add
the following code to the handler:
procedure TForml.TablelCalcF~elds(DataSet:
TDataSet);
begin
TablelShippingCost.AsFloat
:=
(TablelMilesTo.AsFloat
* 0.15) +
(TablelCost.AsFloat
* 0.02);
end;
6.
Figure 6, I I
The completed
form for the
Looker project
As you add new entries, a match is made on the zip code that you
enter. If there is a matching entry in the DESTDIST table, the lookup
fields will be filled with the appropriate information. The entry being
added shows the result of a non-matching entry in the Zip field.
As you spend more time working with them, numerous uses for lookup
fields will be discovered. As with all persistent fields they will remain
defined and prepared for the applications use. Remember that the key
fields, those columns on which the match is made, do not have to be
similarly named; the only requirement is that they share the same values. Also, the columns do not need to be displayed as we did in the
sample application, They can be hidden within the program and used
in other calculations.
putting
to Work
After completing the sample project above you have a good working
knowledge of how lookup fields work and the concept behind them;
based on some matching column between two dataset objects, other
columns from that row can be displayed. The two data-aware lookup
controls work on the same principle with one great advantage: They
are able to modify the contents of the underlying dataset, This lets you
look up the entries for a field from another dataset and build consistent
data into the column.
The Look2 project is a modification of the Looker application just completed. In this admittedly contrived example the user will select more
of their order information from lookup objects, including a
DBLookupListBox and a DBLookupComboBox. To begin this project,
open the Looker project, select File 1Save As, and save your work as
Lbok2.
I. The new layout for Form1 is shown in Figure 6.12. Use this as a guide
for adding and moving the components that are discussed.
Figure 6. I2
The completed
form for the
look2
project
9387
80439
Rechm
.
Ill:
n
n
n
n
2.
3.
4.
5.
6.
The DBGrid has been left on the project so that the effects of the modi:;;;;
fications can be monitored on the table. Take it out of the tab order so
.,g
that it is not available for input. Also, all of the columns in the grid are
hidden with the exception of CustNo, Zip, Item, and Cost.
Add a third DataSource and Table pair to the form. Point the
DataSource to Table3 and select the new table. Set the
DatabaseName to DBDEMOS and the TableName to INVENTRY.DR.
->, .,
Select Table1 and invoke the Fields Editor so that we can add a new
2
lookup field. Name this field ItemCost and set the data type to
.
Float. The lookup dataset is going to be Table3, the Key Fields are
zT
both Item,and the Result Field will be Price. This lookup field will
*.
return the price when an item is selected from another of the controls. *!i
Standard DBEdit boxes are used to represent the Customer, Destina-I
_r.
tion, Cost, and Shipping fields. All of the assigned fields are from
DataSource and they are, in the order listed above, CustNo, Destina- 123
tion, ItemCost, and ShippingCost.
Add a DBLookupListBox in the Zip Code position. The lookup box will
return all of the values from another dataset column and allow the
user to choose only one of those for the value of the underlying field:-:+
Different from the lookup fields, the lookup controls will automatical&
assign the value selected to a dataset field. The DataSource and
DataField properties of this control determine the dataset and field
.I,
that the selected value will be written to. Set these values to
DataSource and Zip, respectively. The values that are displayed in the ,:,*~
list are drawn from the DESTDIST table. Set the ListSource property to
.g
DataSource and the List-Field and KeyField to Zip. The word contrived was used earlier as this example assumes that the only zip codes
the company will ship to are contained in the DESTDIST table. Perhaps a more realistic example would have been to ship only to those
customers who have an assigned CustNo. Oh well, perhaps in the set\
ond edition.
The next component to add will be the DBLookupComboBox.
This
control provides the same services as the DBLookupListBox but in a
more convenient package. The combo box rolls up when not needed,
giving the application a cleaner interface, and the selection tools work :i
incrementally. When selecting an entry from the list, as the user types .;
letters the selection changes to the element that most closely matches i
that letter. Assign DataSource to the DataSource property and
5
Item to the DataField. The ListSource is going to be set to Table3
and the KeyField and ListField both set to Item. This example of
usage is a bit closer to reality. The order clerk can only select those
items that are in our inventory table. The entries in the order table
will now have consistent spelling and capitalization, making SQL and
.?&j
other queries much more effective.
7. As long as the Item is going to be pulled from the lookup set, we
might as well get the price that matches it. This is easy enough to do
by using the ItemCost field created in step 3. The data that appears in , , t:
the edit box will have been looked up in response to the item that is
,,W
. >+4%F
selected in the DBLookupComboBox. We would like to retain this
,<?&
$$
number as a part of the ORDSHIP database so we need a method to
write the value shown here into the new row. To do this, use the
OnExit event handler of the combo box so that when the user tabs out i.=
of there, the data is written. Add the following code to the handler:
d-a
-:.,.&.,,
procedure TForml.DBLookupComboBoxlExit(Sender:
begin
TablelCost.AsFloat
:= TablelItemCost.AsFloat;
end;
TObject);
+3
.x
.
9. Compile and execute the program. As you utilize the new components,
experiment with them to note the differences between the two so that
you can make the best design decision for your own applications.
I::
200 n
3
_ .- eIxIe~_ $*:
.:? a
Key Pr -0perties
Columns
The radio controls in Delphi do not need to be designed vertically on1
they have the capability of spreading over multiple columns horizontally as well. The Columns property specifies the number of columns
for the DBRadioGroup control. The maximum number of columns is 16, i
Items
The Items property contains the string list that describes each of the i
buttons in the radio group. For each item in the list, a button will be
created. In the absence of a list in the Values property, the elements of
the Items string list are the data values that will be written to a field
specified in the DataField property
i
;
-
.,:
i
Value
.i
The Value property contains the content of the current data field. If a
i
different radio button is selected, the value of this property will change.,<::
This property can be queried to retain a value before a change is made. $
&
2;^;
Values
,.i&
1
In some cases your application will require that the value that is writ- ~~~2
ten to the data field is different from the caption of the button. This
-i
might be for simplification or clarity purposes. When this is the case,
the string list that defines the actual data values to be written is
..\
..
:-.
a..&:..<f,;5
n
,. %I. .
$5
n
i1:i
The Table setting for the DatabaseName will be DBDEMOS and the
TableName is RHPSDB. Point the DataSource to Table1 and point all
of the remaining controls to DataSourcel. Activate the table.
2. Select the first radio button control and set the Caption property to
WHO. This component is going to control what values get entered
into the WHO field of the database. Set the DataField property to
WHO and then double-click on the Items property, starting the String
Editor. Add the following strings to the list, each on its own line: Riff
Raff, Columbia, and Magenta.
3. Select the second DBRadioCroup component on the form and set the
caption on it to read WHAT. Set the DataField property ro WHAT and
duplicate the string list from the previous control in the Items property. In this group the value written to the underlying dataset field is
going to come from the Values property rather than the Items list.
Double-click on the Values property to get to the string editor and add
the following elements to the list: Handyman, Entertainer, and
Housekeeper.
n,z.
,.
,::
n
n
n
n
m
202 n
*
Allow
The setting in the AllowDelete property determines whether or not the :!g
current record can be deleted from the dataset by pressing Ctrl-Delete. ;d
When False, no deletion can occur through the DBCtrlGrid, either
,>
through the aforementioned key combination or through the DoKey
-$
method passed with the applicable parameters. If the property is True, ..$?
the default value, the row can be deleted by the key combination or usi.<$
$2
of the DoKey method.
The DoKey method matches a keystroke combination to a method to
perform specific actions. The action is specified by the Key parameters
and supports the activities shown in Figure 6.14. A number of these
-::
actions are specific to navigating within the DBCtrlGrid component,
which can become complicated when the interface has been separated ;
j
into columns and rows.
gkNull
Do
nothing
GkEditMode
gkPriorTab
gkNextTab
gkLeft
gkRight
gkUp
gkDown
gkScrollUp
gkScrollDown
gkPageUp
property
number of records up
in the
I
gkPageDown
gkHome
gkEnd
gklnsert
Insert a new row in the dataset above the current row. Set
EditMode
to True.
.?2
24
gmPPend
to
True.
gkDele !te
t
gkcancel
Set
the
modifications that
Allowlnsert
<.f> $1
co/count
The DBCtrlGrid is capable of subdividing the panels into columns, creating smaller panels. Each of the smaller panels continues to represent .i:
a single row in the dataset. The new panels will fit themselves within
:-f
the original panel, constrained by the Width property of the
component.
Orientation
The Orientation property determines the order in which the rows of the
dataset are displayed by DBCtrlGrid. If the property is set to goVertical,
the panels will be ordered in rows and the grid will display a vertical
scroll bar. When the property is set to goHorizontal, the panels are
arranged in columns with a horizontal scroll bar displayed.
Pane/Height
.j
-4
;_
,..i
SelectedColor
Changing the color of the panel can highlight the panel that contains
the currently selected row. The SelectedColor property contains the current color value and should be modified to change the default shade.
ShowFocus
206 n
UpArrow
DownArrow
gkDown
PageUp
gkPageUp
PageDown
gkPageDown
Home
gkHome
End
gkEnd
Return
gkEditMode
F2
gkEditMode
Insert
gwPPend
Ctrl + Insert
gklnsert
Ctrl + Delete
gkDelete
Escape
gkCancel
gkNull
method
2.
,..
Figure 6. I6
The Tickets
main form
The top panel of the grid is the one on which all of your layout should
occur so that the desired behavior of the control grid is retained. The
striped panels represent the placement of the other rows of the database and are shown for your design reference.
6. As shown, add the appropriate components to the top panel of the
grid. There are some limitations to the DBCtrlGrid; foremost, the following components cannot be used on the grid:
208 n
/s#_pn-
i,
._ __ . ,- ---=--*-
DBGrid
DBNavigator
DBListBox
DBRadioGroup
DBLookupList
DBRichEdit
All of the fields, with the exception of the DBImage, are DBText controls so that they cannot be modified.
7. Set the SelectedColor property to clWhite so that the current record
will really stand out on the screen.
8. Finally, this application will use the OnKeyPress event handler to emulate the touch screen. We will trap for the B (bar) key in the handler
and simply pop up a message. Add the following code to the event
handler procedure:
procedure TForml.DBCtrlGridlKeyPress(Sender: TObject;
var
Char);
begin
if UpCase(Key) = 'B' then
MessageDlg('Ticket
Master down for maintenance.',
mtError,[mbOk],O);
end;
Key:
Chuoter
q 209
x__---_--I-.
------
-----
R 00 100 PM
Memorial Stadiu
figure 6.1
7
The
final
components of
the Venue
Manager
application
Use all of the different navigation options including the Up arrow, the
Down arrow, and the PgUp and PgDown keys so that you can become
familiar with their actions.
Summary
This chapter has covered all of the data-aware controls and, hopefully,
provided the usage information needed to put them to effective use in
your applications. The projects that were built in the chapter have demonstrated the interplay between the data access controls and the
interface components. The key to successfully designing effective
Delphi database applications is to understand the interplay between
these separate but interlocked groups of components. The interface
components, those data-aware controls we have just reviewed, provide
the windows through which the user sees their data. The management
of that data is centered in the data access controls we explored in the
previous chapter. Recognizing this difference in task assignments gives
the developer direction when deciding where a specific job is to be performed in a program.
Looking Forward
..;/ z
I.
*.
,.*
.&
: I%
The next chapter rounds out the application development topics by cl&:$
cussing reporting. This item is often left to last in the application des
and development process but this sometimes has a detrimental effect .j
on the overall quality of the program.
N
early every application generates hard copy output of some sort,
and database applications are often designed with the primary goal of
producing analytical reports from the collected volumes of data.
Designing and producing printed reports that satisfy the users information requirements ask that the developer come to the task with a
wide-ranging knowledge base. From the users you will receive a list of
output needs to be satisfied by the application, and your job will be to
map the output desired to the data structures that have been designed
to collect the raw facts that are to be analyzed.
Before discussing the tools that Delphi offers to satisfy your printing
and reporting needs, we will examine report structures themselves. We
will apply working names to different types of reports and examine
their structure and any special requirements surrounding their production. Once a decision is made about the format and layout of the
report, the methodology of producing it becomes the focus. You have a
choice to make in your report programming: to manually code the
instructions needed to produce the report, to utilize the QReport components to assemble the output, or to utilize a third-party tool that
gives your users the flexibility to create ad hoc reporting as their needs
grow. We will explore each of these options to give you a feel for the
.;.
always used it. Your task is to comprehend what the reports are presenting and explain, if possible, how the report can be deleted and
replaced with another information source. Tell the users that multiple
reports are redundant, determine if two or more reports can be rolled
into a single output, inform them that the output of a certain report is
meaningless in the current or future environment. Since you are doing
this in the initial analysis phase of your project, you are also in a position to explain that some simple summary reports can easily be
replaced by an online query that provides dynamic information at their
fingertips, when needed. With their agreement, an interactive reporting
module can be built into the program rather than added later, and the
user can pull data from the application rather than having it pushed
at them.
During the report analysis phase, you are seeking to answer three
questions:
I. In analyzing the data that is to be collected, can all of the users information requests be satisfied?
2. If the application is replacing an existing system, what is the utility of
the current reports? Can they be combined or deleted for greater
efficiency?
3. Is hard copy output really the best way to answer a question?
The answer to the first query may send you back to the design table
with your data structures, adding or deleting some facts that had previously seemed important. The second and third questions open up more
fertile design possibilities. Not only will the reports come under scrutiny for efficiencies and usefulness, but the application that wraps them
will also be considered. Your analysis may point to pieces of the program that would benefit from being reworked.
Once a decision has been reached as to elimination, combination, simplification, etc., in the reporting scheme, analyzing the reports
themselves takes a different eye. Your initial query will be to frequency.
How often does this information get produced? Is it produced infrequently, in a manner that suggests an ad hoc query would be an
improvement, or does the report get printed so often that the usefulness of previous editions still exists while the daily copies go into the
recycling pile? Examine the distribution of the report to determine who
receives it and why Again, examine the recycling bin in the users area
for clues. Efficacy is the last consideration of the report. In reviewing
each report or information request, consider the following points:
,.
,.
.,
a- I__*~___*~~svi~~
I=-~pl~~L-.I-._.-sr*.
L- .._L ^.O
2 15
.l -/_ . ^$,% wmr_*P
report that shows cost increases.7 It would be much easier to understand a rapid increase in costs than to assimilate a reduction in the rate
of cost decreases.
Repot%
Formats
The formatting of reports can be generalized into four recognizable categories: columnar reports, forms, production labels, and cross-tab
reports. You may discover more exotic forms in your work but well use
these as a baseline for discussion.
Columnar reports are easily the most familiar form to you and your
users. These are traditional multiple-row listings of data usually reporting all or an important subset of the users data. The rows are tabulated
and grouped to produce information that is important to the user. A
regional listing of all of the firms clients would be a good example of
this type of report.
Forms are ubiquitous in the business world but many users and developers fail to consider them as reports. A form usually reports one
record at a time and formats it in a highly readable layout that conforms to a preprinted form. The form is also commonly used to report
master-detail information. An invoice, for example, shows the client
address and credit information as well as all of the services performed
for the reporting period.
Production labels are not limited to mailing information but also
include package and warehousing information. Labels can often serve
multiple reporting purposes. A label may initially serve as a pick ticket
for a customer order that later translates, when stuck on a shipping
form, as the basis for an invoice.
Cross-tabulation reports will be most familiar to spreadsheet users. The
ability to arrange data so that columns and rows meet and produce
information at the intersection is one of the strengths of the electronic
spreadsheet. Users will often discover new trends and uses for data
when it is presented in this form.
Being familiar with the different forms that information can take when
output to paper can make your advice of much greater value to your
users. Listening to the needs of your applications users will enable you
to steer them towards the most beneficial format for their reports.
Showing a user how a report that they have used for years can be
transformed into an important competitive tool by selecting the appropriate format can often make or break their positive impression of your
application.
4 .; 9
-3<.
~c
.iG
,$
3
!
;;
i .:
_ 133, j;;;* I
..t*
SetAbortProc
StartDoc
StartPage
:$
:
.+$1 .
f
j
4;G
Aborted
Canvas
Capabilities
Copies
Fonts
-d.-::
,,.I
%sGt
Handle
Orientation
PageHeight
PageNumber
PageWidth
Printer-Index
Printers
Printing
Title
The key property of the TPrinter class is Canvas. The canvas of the
printer is utilized in the same fashion as the canvas of a form. Text and
graphical output is directed to the printer canvas from which it is converted to printer commands and sent to the device. To utilize Canvas and
begin a print job, the developer calls the printers BeginDoc method,
opening the canvas surface to receive output. All of the Canvas related
methods are then available to the program to format the output as
j
;&Z
,.:z
,;.
(.
._
__
__
desired. When a page of output has been laid out on the canvas, the
printers EndDoc method is used to send the canvas output image to the
printer. The Abort method can be called to discard a print job once trans.
ferred to the print manager. The NewPage method alternately will send
output to the printer and start working on a new canvas page. The met&
ods used to format the output on the canvas differ in their abilities. To
present the methods in context, their presentation will be in order of
declining precision with regard to the placement of text on the canvas.
Before continuing the discussion of the specifics of text placement,
important differences in usage of the Canvas of a print device and that of
a video device should be noted. Output location coordinates for printed
,::
output lie along a plane that is much more dynamic than that of a video
_
output device. Changing the resolution for the applications carefully
:,?
designed printed output can happen as quickly as the user can switch
from the network laser printer to her local line printer or fax software. X .:+:
and Y on the laser printer plane will not fall in the same spot on lower
.;:z
resolution print devices such as the line printer. Hard-coded output coor- ;3J$
dinates will produce poor output and no scroll bars will appear on the
document to allow the user to access the missing data as they would on e:;.
screen form. Windows eases the task of managing the dynamic environ- C
ment by returning the information needed about the capabilities of the
device through its device context. Specific information such as pixel mea- ,gj
::;i
surements can be retrieved through a call to the API function
GetDeviceCaps. A specific example of calling the function with a device
context and an enumerated constant will be demonstrated in an upcom- -::
ing example.
3
Z
The best way to begin our exploration of manual print control is
through some simple, non-database examples that build up to the pro
duction of a database report printed strictly through the Delphi code.
The first example will print text lines to the printer canvas using the
canvas TextOut method. TextOut requires three parameters when
called: the X and Y pixel coordinates for placing the text string and the
text string itself. The code below demonstrates a simple procedure to
send text to the printer.
implementation
uses Printers;
{$R *.DFM}
procedure
TForml.ButtonlClick(Sender:
begin
Printer.BeginDoc;
TObject);
The power of the TextOut method lies in its ability to precisely place
the output at a desired location on the document through the X and Y
coordinates. Consider the placement of a centered report title. To provide the proper starting location to the TextOut method, you must do
some simple calculations with the call or prior to it. Centering text on
the printed page is demonstrated in this snippet:
:<
.;gi++
-is
$
$2
implementation
uses Printers;
{$R
*.DFM}
procedure
TForml.ButtonlClick(Sender:
var
Outline
: string;
centerpoint
: integer;
TOb jet
begin
Printer.BeginDoc;
outline := 'Text 1 ine to be centered.';
.':I
centerpoint := (Pr inter.PageWidth div 2) ..$
* (Length(outline) -$
(Pr inter.Canvas.TextWidth('A')
div 2));
Printer.Canvas.Tex tOut(centerpoint.
10. outline);
Printer.EndDoc;
end;
!-Ty
, %
c
:
..
tc
.k
.r
/l-The
Delphi
Database Tools
Printer.Handle,
LOGPIXELSY
);
LOGPIXELSY will return an integer value that represents the number of ,~$
pixels along an inch of the display height. This value is then utilized in .,@
an algorithmic computation of the line placement. The snippet below
.>i
demonstrates the sequence of calls needed to determine the height of . ;
the text lines.
. *.
{ - Get vertical pixels per inch - }
VPixelsPerInch
:=
GetDeviceCaps(Printer.Handle,
LOGSP IXELSY);
g
i
+ VLineSpacing;
e.s j
',
.-.._.
i
The total line height is measured as the sum of the number of pixels in
VLineSpacing plus the number of pixels returned from the TextHeight .
call. Note that the divisor value, 10, used in the computation of
VLineSpacing is a completely arbitrary value and is to be adjusted as
necessary for the specific application. As with the TextWidth call used
earlier, the Canvas method TextHeight takes into account the metrics of
the font currently in use, an important factor in printing to various
printers. Querying the PageHeight property of the canvas provides the
total pixel height of the page. The number returned is the total against
which the accumulated printed lines are measured to determine when
a page break is required. The TPrinter object provides a method,
NewPage, that forces the canvas to send its current output to the
printer and to begin printing on a new canvas page. This method will
handle the canvas itself, resetting its Pen position to 0,O and incrementing the page number. You must reset all of the print variables, such as
LinesPrinted, within your application.
.s:
:;
I$. (_i<
1-*,b$
)_
f,i
$
.?
.;;
..
Columnar
Reports
The columnar report, a simple listing of rows, is a good place to start
building reports. We will use that format to demonstrate the report creation process through simple coding in a procedure. The key to this
approach to reporting is speed, not the number of features on the
report. You can make this report as simple or as complicated as you
desire. The format that we will use in laying out the report is shown
below.
l/l/99
PAGE:
I
DIVERS
SUPPLY
CO.
CUSTOMER
LJST
CustNo
Company
Country
1221
US
I 23 I
Unisco
Bahamas
1351
Sight
I354
Cayman
Diver
Divers World Unlimited
Cyprus
British
West
Indies
222 n
,I*smIL-L
._
II
_. _
I/).
.I
private
: integer;
VPixelsPerInch
VLineSpacing
: integer;
: integer;
LineHeight
LinesPrinted
: integer;
: integer;
CharWidth
: integer;
LinesPerPage
procedure PrintHeading;
procedure
PrintTimeStamp;
Making the variables global to the unit eases the need for parameter
passing in this example. Production needs may differ.
3. Enter the code shown in Listing 7.1 to the buttons OnClick handler.
procedure
var
NextCol
TForml.ButtonlClick(Sender:
TObject);
: integer;
begin
***************k~***********************~~~~~~~~~~~~
1
I
{ DETERMINE THE NUMBER OF LINES PER PAGE
I
*******X**********X******k*************X~~~~~~~~~~~~
1
I
VPixelsPerInch
:=
GetDeviceCaps(Printer.Handle,
LOGPIXELSY);
VLineSpacing
:= VPixelsPerInch div 10;
LineHeight := Printer.Canvas.TextHeight('A')
+ VLineSpacing;
LinesPerPage := Printer.PageHeight div LineHeight;
LinesPrinted := 0;
Printer.BeginDoc;
********************X***t***************~~~~~~~~~~~~
I
I
( Center Report Title - Add 2 lines to linecount for
1
{ 2 more lines for spacing
I ****X************X**t**X***Xf***********~~~~~~~~~~~~
1
Printer.Canvas.TextOut((Printer.PageWidth
div
2)
(Printer.Canvas.TextWidth('A')
*
(Length('DIVERS SUPPLY CO. CUSTOMER LIST'))),
LinesPrinted
LinesPrinted
begin
if LinesPrinted
<
Printer.PageHeight
co. c :us1
then
begin
NextCol := 10;
Printer.Canvas.TextOut(
NextCol,
LinesPrinted,
Tablel.FieldByName('CUSTNO').AsString);
NextCol
:= NextCol +
(Tablel.FieldByName('CUSTNO').DisplayWidth
Printer.Canvas.TextWidth('A'));
Printer.Canvas.TextOut(
NextCol,
LinesPrinted,
Tablel.FieldByName('COMPANY').AsString);
NextCol
:= NextCol +
(Tablel.FieldByName('COMPANY').OisplayWidth
Printer.Canvas.TextWidth('A'));
Printer.Canvas.TextOut(
NextCol,
LinesPrinted,
Tablel.FieldByName('COUNTRY').AsString);
Tablel.Next;
:= LinesPrinted
LinesPrinted
end
else
begin
Printer.NewPage;
LinesPrinted := 0;
PrintTimeStamp;
PrintHeading;
end;
end;
+ LineHeight;
224
,'
TObject);
begin
i
*************************t*******t******~~~~~~~~~~~~
i
I
LOGPIXELSY);
:=
GetDeviceCaps(Printer.Handle,
VPixelsPerInch
VLineSpacing
:= VPixelsPerInch div 10;
LineHeight := Printer.Canvas.TextHeight('A')
+ VLineSpacing;
LinesPerPage := Printer.PageHeight div LineHeight;
I ***X*******k****t***********************~~~~~~~~~~~~
{ PRINT A COLUMNAR REPORT WITH HEADING
Chapter 7-Reporting
. . .-
and Printing m
li^r_sB
( *****************************k****************~*~**~~***
jL. .a
1
1
{ ****XX*********************************~************
I
{ Center Report Title - Add 2 lines to linecount for
)
{ 2 more lines for spacing
**********************x*********************~**~****~**
I
I
Printer.Canvas.TextOut((Printer.PageWidth
div
2)
(Printer.Canvas.TextWidth('A')
*
(Length('DIVERS SUPPLY CO. CUSTOMER LIST'))),
LineHeiaht * 2. 'DIVERS SUPPLY CO. CUSTOMER
LIST');
LinesPrinted := LinesPrinted + (LineHeight * 2);
LinesPrinted
:= LinesPrinted + (LineHeight * 2);
I **********X****k*****************************~***~~*****
( Print Header for Columns
I ********f*XX****************************~~~~~~*~~~~~
PrintHeading;
I *X***X****XX*********kX*X******************~**~*****
{ Spin through all rows in the table and extract the
( specific columns to be printed
I tXX***t*****X*******~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Tablel.First;
While not Tablel.EOF do
begin
if LinesPrinted
< Printer.PageHeight then
begin
NextCol := 10;
Printer.Canvas.TextOut(
NextCol,
LinesPrinted,
Tablel.FieldByName('CUSTNO').AsString);
NextCol
:= NextCol +
(Tablel.FieldByName('CUSTNO').DisplayWidth
Printer.Canvas.TextWidth('A'));
Printer.Canvas.TextOut(
NextCol,
LinesPrinted,
Tablel.FieldByName('COMPANY').AsString);
NextCol
:= NextCol f
(Tablel.FieldByName('COMPANY').DisplayWidth
p
*I
226 n
1.~., . .
+ LineHeight;
Printer.EndDoc;
end;
Procedure TForml.PrintHeading;
X*******C*****X**X**i*X****X****t****tX*~~~~~~~
1
i
{ Prints the column headings for the report when }
/ a new page is started
'r
i
i ********r***************************~********x*
var
Spdclng
:
integer;
Printer.Canvas.Textout(
Spacing, LinesPrinted, Tablel.FieldByName('CUSTNO').DisplayLabel);
Spacing :- Spacing + (
Tablel.FieldByName('COMPANY
Spacing := Spacing + (
Tablel.FieldByName('COMPANY').DisplayWidth
Printer.Canvas.TextWidth('A'));
Printer.Canvas.Textout(
Spacing, LinesPrinted,
Tablel.FieldByName('COUNTRY
end;
end.
Listing 7.2 The PrintHeader procedure
The column headings for our report will be underlined and set in bold.
To accomplish this we use the line:
Printer.Canvas.Font.Style := [fsBold, fsunderline];
to set the style. The style values are then released in the last line of the
procedure. The spacing of each column is determined by utilizing the
DisplayWidth property of each of the fields of the table. This property
contains the number of spaces needed to properly display the contents
of the field so its a good number to utilize when laying out your
columns.
Another procedure that must be added handles the need for time and
date stamping on each page of the report. The code below lists the
PrintTimeStamp procedure.
228 n
_./ j -= . __
Component-Based Reports
Delphi supports component-based report building by including the
QReport components on the same-named tab in the VCL. These components are provided by a third-party developer, QuSoft, and are well
integrated because the entire set of components is written in Delphi.
The component set gives the developer complete control over the page
layout, all printer settings, and output. The next set of reports that we
will examine will be built using these components.
Well begin by building a form report. A form is usually based around
reporting the data of a single record and is formatted in a highly structured way so that the person receiving the report can quickly assimilate
the information. The report that the next project will create is going to
take the form one step further and create a master-detail report in the
form of an invoice.
QuickReport IS a banded report generator. This means that the report
form is built up using bands, sections that contain the elements of your
report and act according to their type. For example, a Detail band will
automatically recognize the necessity to repeat itself when the data it is
reporting contains multiple items such as with a database table. Before
jumping into the fray with the Invoice report, lets re-create the simple
customer listing that was coded in the last example to get a feel for the
component-based process.
I. Start a new Delphi project. Add a Table component and set the
DatabaseName property to DBDEMOS and the TableName property
to CUSTOMER.DB. Activate the dataset. Add a QuickRep component
to the form and set the DataSet property to Tablel. Expand the form
as large as your monitor allows while not covering the Object
-
:
:
230
, I -, =.
H Part I/-The
2.
3.
4.
5.
6.
7.
8.
9.
Inspector. In turn, move the QuickRep control so that you can have
the maximum viewing area available. Save the project. Name the unit
CustlistU.pas and the project CustList.dpr.
The first band that we will add is a Detail band. This is the workhorse
of the report, containing our listings. Double-click on the QRBand button to place the control on the form.
Next, we need to add the data fields to the band. Double-click on the
:I
QRDBText button three times to place the field components on the
,_
band. Select all three and set the DataSet property to Tablel. Dese
lect the group and, as appropriate to the layout of the report, set the
DataField property of the fields to CustNo, Company, and Country,
Right-click on an open area of the QuickRep control to invoke the con-
text menu and select Preview. The preview function lets you see the
WYSIWYG layout of your report. At this point you should have a complete customer list. If not, examine the settings of all of the controls on -the report to ensure that all of the properties are set as needed.
The spacing on the report seems a little wide for our purposes. The
1::
size of each line is controlled by the height of the Detail band. Move ,,-z
the field components to the top of the band and then select the band
itself. Grab the handle at the bottom center of the frame and pull it
upward to tighten up the spacing. Use the preview function to examine your changes and modify as necessary to suit your tastes.
Select the QuickRep form again. Click on the Bands property to
expand it and set the HasTitle property to True. Notice that when you
do, a new band is added to the report with its type set to Title. This is
an alternate method to add new bands to the report. A band of type
..:
Title will print once on the first page of the report. This is different
:.
from a page header that will print on all pages.
Double-click on the QRLabel button to add this control to the Title
band. Set the caption for this component to DIVERS SUPPLY CO.
CUSTOMER LIST Set the Alignment property to tacenter and the
AlignToBand property to True. These settings will have the effect of
centering the reports title in the band.
The last thing that we need to add is the time and date and the page
number. Because we want to have this information appear on every
:
page of the report, we are going to use a PageHeader band. Add a new ,.
band to the report, using either method, and set the BandType property to rbPageHeader. Did you notice that as bands are added to the I
QuickRep form they position themselves correctly?
Add a QRSysData component to the form. These controls obtain system information to add to your report. Align the control to the upper
(_.___
Chapter T-Reporting
and Printing n 23 I
.._, ...e*__ilf_L, -.s(x*-
Figure 7.3
The final form
design of the
project
232 n
1131
1351
1354
!3513
I380
1384
1 5 3
153
1551
1560
563
524
545
Figure 7.4
The final
report
12. All of this work has been done in the design mode of our project. Td
activate the report in the project is a simple matter using the met
of the QuickRep component. Since the size of the QuickRep contr&$
has no bearing on the way that it prints, size it down a little so th&$
some of the underlying form is visible and add two button controls
the form. Caption one Preview and the other Print.
:-a
1 3 . Double-click on the Preview button and add the following line to the:,+
event handler:
a:5;
3
QuickRepl.Preview;
.2
:,L...
d
.:
.$
,_ $
One additional note is in order about the bands. These components a&$
container objects, meaning that they own the other components place&,!
within them. Once you place a field or other object, you will not be
:
able to simply drag it to a different band. Also, remember that when ki
container object is deleted, it takes all controls and settings with it. : Y$i
This completes the customer list project. Save your work.
Form Reports
Form reports use more of a free-form layout when presenting data to tk
user. Rather than list the data rows, the data is presented in different
areas of the page for clarity and readability purposes. A good example4
this type of report is an invoice. The top of an invoice will usually CO&
Below that, the invoiced items are listed and summarized, usually ending :
with an amount owed. The next project creates a form report using the
same fundamentals learned in the last segment.
Start a new application, adding two Table and two DataSource components to the form. On the first Table, set the DatabaseName property i.
to DBDEMOS, the TableName to CUSTOMER.DB, and the Active
;I>
property to True. The second Tables properties should be set as
DatabaseName to DBDEMOS, TableName to ORDERS.DB, and
Active to True. Point DataSource to Table1 and DataSource to
.,. ,.:,;
Table2. Save the unit as InvoiceU.pas and the project as
Invoice.dpr.
. ~z.1r
2. The form that we are creating requires data from both tables so we
-:-.- . .
must establish the master-detail relationship between the tables.
Select Table2 and set the MasterSource property to DataSourcel.
Click on the MasterFields property to invoke the Field Link Designer.
Using this tool sets the relationship between the two tables on the
?i,*
CustNo field.
.:
3. Drop a QuickRep component on the form and stretch it as necessary SO .$; 1
that you can easily work with the whole page width.
4 . Add a Detail band to the report. Set the DataSet property to Tablel.
:
Drop a QRDBText control on the band and set the DataSet property to 1.Bz
Table1 and the DataField value to Company. If you preview the
report at this point you will find that the report consists of a list of
:&.-4*s
company names.
Expand the size of the Detail band. Drop two more QRDBText components on the band. Move one to the left of the Company field and set
the DataSet to Table1 and the DataField to CustNo. The other will
go directly below the Company field; set the DataSet to Table1 and
the DataField to Addrl.
The next line takes a bit of fancy footwork to handle. The final line of
the address block is traditionally filled with three items: City, State, and _:_
Zip Code. A comma usually separates the City and State entries and a
_
space separates the State and Zip Code. This leaves us with a difficuh
situation in placing the punctuation because it needs to float with the
varying size of the City field. If the City field is placed, followed by a
5
Label with the comma as its caption, followed by the State field, we
would wind up with varying amounts of space between these elements,
depending upon the size of the City string. Hardly a professional
appearance. A further problem presents itself with the particular
dataset that we are working with because the CUSTOMERS table contains international clients and many do not have an entry in the State
_,
^.._ . ,
or Zip Code fields, If we solve the first issue alone, the comma will still
be floating out there without a reason for being. A Zen dilemma that is
easily solved using a little ingenuity and the QRExpr component. This
control accepts expressions that determine its output, and by carefully
constructing a formula to handle this line we can solve both of our
issues.
6. Drop a QRExpr component on the band and position it below the
AddrI field. Click on the Expression field; this will invoke the Expression Wizard shown in Figure 7.5. The Expression Wizard will help you
to build a number of different types of computed expressions for use
in your reports.
Figure 7.5
The
Expression
Wizard
'+Tablel.Zip)
If the State field is empty, an empty string is appended to the City field.
If there is a value, a comma is prepended to the string composed of the
235
State and Zip fields, Because the comma is not in a fixed position, it is
free to float with the varying size of the City field.
7. Add the remaining fields and band as shown in Figure 7.6 to round
out the top of the invoice.
figure 7.6,
Adding the IF
function to the
form
The invoice date will come from a QRSysData control and the heading
should be in a PageHeader band so that it prints on each invoice. To
center the title, set the Alignment property to taCenter and the
AlignToBand property to True. Finally, change the Frame values for the
Detail bands Draw properties all to True to box in the information.
8. With the master portion of the invoice completed, its time to add the
detail items that make up the bulk of the document. To add these
items we will use a QRSubDetail component. When you drop this item
on the report it becomes a new band on the document that links in
additional datasets. This band will point to the second dataset, Table2,
by setting the Dataset property.
9. Add three QRDBText components to the Sub Detail band. The Dataset
for all of these components is going to be Tablea. The DataFields to
be selected, in this order, are OrderNo, ItemsTotal, and
AmountPaid. Preview the report again to see that the list of orders
follows each header.
IO. One additional field will round out each detail line. Add a computed
field using a QRExpr control. We are going to subtract the AmountPaid
field from the ItemsTotal field to determine the amount owed on each
line item. In the Expression property, enter the following statement:
Table2.ItemsTotal
- TableZ.AmountPaid
236 n
Par II-The
I_~
_;
._ -------w
sub-detail columns. To link this band to the Sub Detail band, set the
HeaderBand property of QRSubDetaill to the Sub Detail band. Add
four QRLabel components to the new band, captioning them Order
Number, Items Total, Amount Paid, and Amount Owed. Position each over the appropriate column in the Sub Detail band. Now
might be a good time to save the project and preview the report. Its
looking more impressive, isnt it?
I 2. To summarize the Sub Detail records we are going to add a Group
Footer band. Follow the same steps to add a band to the report, this
time setting the BandType property to rbGroupFooter and setting
the Sub Detail bands Footer Band property to the name of the newly
added band.
13. Were going to summarize the columns using three QRExpr components to be placed in the Group Footer band. When using these
controls in a footer band you must set the master property to the
name of the Sub Detail band, in this case QRSubDetaill. For the
first column, the expression statement is SUM(TableZ.ItemsTotal)
and the second is SUM(Table2.AmountPaid).
These components
will size themselves to fit the expression statement and will appear
quite large but they can be overlapped safely on the band for alignment purposes. The final column expression will display the difference
between the two total columns using the expression
SUM(Table2.ItemsTotal) - SUM(Table2.AmountPaid).
After
aligning these controls under the appropriate columns, set the Mask
property for all of them to $##,##O.OO to format the output in currency form. To ensure that each invoice total starts from zero, set the
ResetAfterPrint property to True.
14. Finally, we want each invoice to print on a separate page. To do this,
well utilize the AfterPrint handler of the Group Footer band. Add the
line QuickRepl.NewPage; to the procedure and youre finished
with the Invoice report. To see your handiwork, add a Preview button
that calls the reports Preview method and run the application.
Though we added all of the labels and lines necessary to produce the
actual customer document to this report, this will not always be
needed. You may find that your application is required to produce output on preprinted forms. The quadrille pattern on the QuickRep
component and the variety of page sizes should make this easy to
complete.
Chapter 7-Reportir
__..+-.-
and P I*inting n
_r =
Label Reports
Mailing or product labels are another common task for report develop,3
ers. Labels are unique in their printing requirements because there are :
generally multiple instances of the label form on a single page. As
other designs, the QReport components handle the label task with
. rg..d?
- 4.:
aplomb. After the form report project, this will give you a little
breather. The labels that will be designed are product labels such as
those used in a warehouse to pick products from the shelves. The
..
report we are creating as an example is designed to print on Avery@
i
8164 shipping labels. The dimensions for each label is 3.33 (3 l/3) by
4 inches, two across by three deep.
I. Start a new application and add a Table and QuickRep component to
.y
the form. Select the Table and set the DatabaseName property to
>:,<T
DBDEMOS and the TableName to PARTS.DB. Save the project with
the unit named Labelwpas and the project called label.
.;
2. The form will need to be slightly modified for this report. Set the
.~53
~~
QuickReps DataSet property to Table1 and Bands 1HasDetail to True,::
Expand the Page property to access the sub-properties. To accommo- d
date the sheet labels that we are going to print, we need to adjust the G
dimensions of the document. First, set the number of the Columns
:
property to 2. To make the gutter between the label pairs, set the
i;;*
ColumnSpace property to .1875. Next, the margins require some
minor adjustment. The top and bottom margins will remain at ..500
13
inches. Set the LeftMargin and RightMargin properties to .125.
3. The Detail band is now the correct width and only needs the height
w
adjusted. Expand the Size property and set the Height value to 3.333 -5;. >,g
inches. When you view the report, the detail band should now be
.A>I
correctly dimensioned to match the label type. To make the individual
labels easier to see during the development process, set the frames
,2Draw options to True so that a solid box will be drawn around each
detail band.
4. Add the QRLabel and QRDBText components as shown in Figure 7.7.
PartNo
bescription
Figure 7.7
Example of
label
a
Because the labels are intended to perform the pick ticket function, d
part number and description fields have been enlarged to enable th
viewing from a distance.
5 . Save and preview the report. Mult iple labels will be prepared for ~CH#
printer, one per row in the PARTS. DB table. Sheets of labels will prints
as shown in Figure 7.8.
-
900
1313
Dive kayak
1I
Regulator System
I
- -
Figure 7.8
Example of a
sheet oflabels
912
1314
COST
$504 00
msi
$124 10
I.li.
*.
_l., _1 ajl .I i
239
Now that you have suffered through manually creating these labels, its
only fair to tell you that a Quick Report form to print labels exists in the
Object Repository. This is set up for three across labels but it is easily
modified to fit any label configuration that you choose. (While we are
in the confessional, there are also forms for QuickReport List and master-detail reports as well.)
Cross-Tab Reports
The last type of report that we are going to create is a cross-tab report.
This style of report is an analytical tool that summarizes on different
planes the numbers that intersect. Placing this process in context
requires that we set the scene with a typical report of sales information.
The first application that we are going to produce uses a Query component instead of the Table controls that we have used as the source, The
information will be in the form of a grouped report in which the header
band repeats when a controlling value changes.
I. Start a new application and place a Query component, a Button, and a
QuickRep con t ro1 on the form. Caption the Button Preview and place
in the OnClick handle a call to the QuickReps Preview method. Select
the Query component and set the DatabaseName value to DBDEMOS.
In the SQL property, add the following statement:
SELECT OrderNo, PartNo, Description, Qty, Company
FROM Orders 0, Items I, Parts P, Customer C
WHERE (O.OrderNo = I.OrderNo) and (I.PartNo = P.PartNo)
(O.CustNo = C.CustNo)
ORDER BY OrderNo
and
Figure 7.9
Designing the
project
Figure 7. IO
The final
report
__ , l_i__ _L;~
_n
This report groups each orders specific items, changing the data each
time the Order Number changes. This type of report is difficult and
time consuming to analyze when trying to spot trends in the data. Suppose that we want to determine what our most popular items are. We
.J
could analyze the sales report, making a tick mark under each item that <=G
we carry to determine what gets ordered the most but this might be a
2
little too much work to make it worthwhile. What would be ideal is to _yteq
have the data summarized on another report that displays a number of +I:
items across the report and directly below that displays the number of
times that they were ordered. This is the purpose of a cross-tab style
1
report. The running total of each item can be accumulated simply
i~$
, $2
through a query Count function and then rather than listing them as
_.%
normal, we can turn the list on its side to create a more readable set of. 4
data. To give you an idea of what we are trying to accomplish, the sum- 3
mary report to be created by the next application is going to be similar %F;, x. .A
to that shown in Figure 7.11.
-.?%d
I-- ---
figure 7. I I
output ofsummary report, a
cross-tab style
report
Creating this application requires all of the skills you have developed so :;+
far and some you have not. New to this report is the programmatic cre- -<!4
SF
ation of report elements. Like any Delphi component, the QReport
lj.: j
classes can be instantiated in code within your application without the
,,-~
visual control being placed on a form.
I.
Start a new application, adding Query, Button, and QuickRep components to the form. Select the Query component first and set the
DatabaseName property to DBDEMOS. Add the following code to the
SQL property and then set the Active property to True:
:>.**
-:@&
,i-*
;:
242 n
Before compiling this code, there are a couple of steps needed to set up
some items such as the arrays and the field descriptions. The arrays for
the unit are declared in the Public declaration area as follows:
TempMtrx
ItemsArray
OrdersArray
end;
: array[1..5,1..2]
of string;
: array[0..5] of TQRLabel;
: array[0..5] of TQRLabel;
The previous item occurs al run rime when the Preview button is
clicked so that the array is dynamically filled each time the report is
run. There is a block of code that must occur before the report is processed and that is to define and declare the elements within the
ITEMS and ORDERS arrays. Both of these arrays are of type QRLabel.
We are going to use these as the display elements on the report band,
so after declaring them it is also necessary to set a group of property
values prior to the data being assigned to the Caption property of
each. For this exercise, the most convenient place to have these
actions occur is when the form is created, so we want to add the following code to Formls OnCreate handler:
procedure
var
ColWidth
TForml.FormCreate(Sender:
: integer;
TObject);
j :
1 .j
-
!:.?
:!
Chapter 7-Reporting
_^n.
,.
I
: integer;
begin
{Divide the space up evenly for the number of
ColWidth := Trunc( QRBandl.Width / 6 );
{initialize the array elements}
for I := 0 to 5 do
begin
ItemsArray[I]
:=
TQRLabel.Create(Forml);
OrdersArray[I]
:= TQRLabel.Create(Forml);
with ItemsArray[I] do
begin
Parent
:= QRBandl;
AlignToBand := False;
AutoSize
:= False;
AutoStretch
:= False;
Color
:= clWhite;
:= 64;
Top
if I = 0 then
begin
Left := 0;
Width := ColWidth + 6;
end
else
begin
Left := ColWidth * I + 6;
Width := ColWidth - 6;
end;
OrdersArray[I].Parent
:=
ItemsArray[I].Parent;
OrdersArray[I].AlignToBand
:=
ItemsArray[I].AlignToBand;
OrdersArray[I].AutoSize
:=
ItemsArray[I].AutoSize;
OrdersArray[I].Color
:=
ItemsArray[I].Color;
OrdersArray[I].Top
:=
ItemsArray[I].Top + ItemsArray
OrdersArray[I].Left
:=
ItemsArray[I].Left;
OrdersArray[I].Width
:=
ItemsArray[I].Width;
244 n
*I*B,/as
- .i
5.
6.
TObject);
This report is very dynamic in nature and the data changes require
that we wait until the last minute before laying out the actual values.
The Detail bands BeforePrint handler is used to load the arrays of
QRLabel elements:
procedure
TForml.QRBandlBeforePrint(Sender:
PrintBand: Boolean);
var
I
: integer;
begin
{Use the 0 subscript for labels}
ItemsArray[O].Caption := 'ITEM DESC';
OrdersArray[O].Caption
:=
'ORDERS';
{Load the Arrays
for I := 1 to 5
begin
ItemsArray[I].
OrdersArray[I]
end;
end;
7.
TQRCustomBand;
with values}
do
Caption := TempMtrx[I, I];
.Caption := TempMtrx[I,Z];
var
Chapter 7--Reporting
and Printing n 2 4 5
I /a% .,* e-v
Figure 7. I2
Designing the
summary
report
If all of the elements have been added, compile the project and execute
the application. Nothing much will happen until the Preview button is
pushed, which generates the cross-tab.
The cross-tab report is a special-purpose tool to pull out when summarization needs go beyond simply tallying the columns. It is a lot of work
in the QuickReport environment but the results are worth it.
246 n Part
make the most of this opportunity though; it may take the users may
take some time to understand the intricacies of your data and the
report tool.
There is a cost associated with integrating most report engines into
your application. When your users press the Print button, they expect
instantaneous response to their request. The overhead of loading the
engine will generally take quite a bit longer than an internal solution,
so you are going to need to take extra care to manage this expectation
with your users. The installation of the application is also going to
expand a bit as well when the engines DLLs are included with your distribution disks.
Summary
I
lnclucled
n
n
n
in This Part:
$4
aving read this far on the topic of database programming with the
a
Delphi tools, you have correctly surmised that there is a lot more to the
process than meets the eye. Good database programs are made in the
planning stages as much as they are in the final execution. We have
:I
extensively covered the thinking that goes into a successful database
,:
design, judged as one that is efficient, mindful of the integrity of the
users data, and does not have to resort to pained contortions of logic
and code to meet the users information needs. The coverage of the
Delphi tools in the previous chapters has been referential in nature,
focusing almost exclusively on the component of the moment. This chapter will take a different approach and discuss the tools in terms of solving
specific tasks and the methods that Delphi offers for doing so.
There are two goals in writing this chapter. One is to share with you a
number of examples of usage that you can integrate into an application
to improve it on some level. Secondly, in exposing some techniques and
demonstrating how to perform them using Delphi, it is hoped that you
will see a glimmer of an idea for further development on your projects.
For example, to kick things off, well discover that A is for Access. The
PC data world at one time was dominated by .DBF formatted data files
and as a programmer you learned the ins and outs of the file structure.
Well, things are much different these days as Microsofts Access enjoys -: .J
a good deal of success and the file format shows up more and more at
.$
your clients locations. Are the Delphi development tools up to the ta& ~. f
of working with these structures? Lets find out.
.-2
TheAccess Database
Whether by default or choice, Access 97 is one of the most popular
)!,(.I
database products on the market today Improvements in recent years -:3
have made the user interface much more friendly to the casual and
:.;;i
non-technical business user, making it an inviting end-user and devel- .a&!
3
opment platform. Because of this, most developers will eventually run j
::;f:
-IAT$
into a situation in which their data source is going to be in an Access
database format. Rather than convert the data unnecessarily, it makes $$
more sense to understand the unique aspects of the format and learn t+
integrate the data source with the Delphi tools, deriving the best from
i -..
both worlds.
::
3
,-
The Open Database Connectivity standard was developed over a number of years to provide a method of communication between different ~.$?
SQL databases. The goal of the standardization was to create a utopia
where any product that communicates through SQL would be able to
J,
speak to any other product that uses SQL. This WIould be beneficial to
p;
developers, allowing them to develop an application in any selected
&g
tool and then be able to manipulate data from any SQL source. As SQL
products were developed, the companies that created the tools added
their own extensions and syntactical differences to the standard SQL
language, making it difficult at best to communicate between one product and another.
A consortium of companies decided to find a way of maintaining the
unique aspects of their SQL implementations and continue communieating with each other. The group, known as the SQL Access Group,
defined a common base SQL implementation that would be shared
among all of the participating products. These companies developed
the Common Language Interface (CLI) standard and committed their
products to supporting it.
Microsoft improved upon this idea and developed its own standard
called ODBC, an acronym for Open Database Connectivity. Access provides the basic driver and administrator program to translate ODBC
SQL to Microsoft SQL Server SQL. When you connect to a remote
-1.;
I
.;
I.i.:I
-,:i
q
-...a
.,$
Access database through an ODBC driver, your commands are translated through the driver from a local products implementation of SQL
statements to those needed to manipulate the Access database. Figure
8.1 shows the Microsoft ODBC architecture.
8,
??
Delphi 32 Application
(or any SQL application)
1 ODBC
Driver
I
/ ODBC Driver
/
ODBC
Driver
Figure 8. I
The Microsof?
ODBC
architecture
;
.$-;
:$
252 n
When you start the ODBC Data Source Administrator you will find
yourself looking at the User DSN {Data Source Name) tab as shown in
Figure 8.2.
rrgurc: 0.L
database
You must add a connection to the database that you want to access in
your project. Click on Add and you are prompted to select a driver for
the data source you are building. Select Microsoft Access Driver
(*.MDB). The resulting Create New Data Source dialog is shown in
Figure 8.3.
.d.._>*~/l:.~~
253
.<% _
Figure 8.4
ODBC
M icrosofi
Access 97
Setup dialog
The advanced options available behind the Advanced button will allow
you to define login information or modify the configuration settings.
We will leave these as is for now.
i
Adding the Data Source Name to the BDE configuration file is the next
step in the integration. Start the Database Explorer and you will be able
to locate the DSN that was just defined. To tie this to a specific database file, select the object from the Databases tab and click on the
Database Name definition region. You can type the fully qualified filename into this region or select the ellipsis button and browse to find
the file. Save your updates by clicking on the Apply button and exit the
Explorer.
Separating Access from the dBASE and Paradox databases that we commonly encounter as Delphi developers is the way that Access stores its
tables. All objects that make up an Access database are encapsulated
within the .MDB file. When you work with this file, you must address
this database first before you can directly address the tables. Fortunately, Delphi makes the unique aspects of the MDB transparent to your
application through its design.
The Alias will take care of addressing the database file, exposing the
tables and query files to a Delphi application. A quick example can
demonstrate the transparency offered by the BDE. This simple,
one-form application will start by accessing a Table and a Query
I
Start a new application and add the components shown in Figure 8.5.
,,,LlXW
10/31/139?
Figure 8.5
The AccessX
main form
2.
Select the first DataSource and point it to Table1 and then point the
DBGridl DataSource property to DataSourcel. Select the
DatabaseName property and select MEDINFO, exposing the tables
contained within so you can address them individually as though they
were stand-alone tables. Set the TableName property ro VISITSEG, an
Access table. Set this table to an Active state, and the data will flow
into the grid, You will norice that both the Fields and Columns editor
function the same as they do with the Paradox or dBASE tables.
3. How about the Access query? The query is a SQL-based tool that
returns a dataset using the SELECT clause. With a dataset being
returned, it can be treated as though it is a simple table. Follow the
instructions in the previous step for the second set of components but
select the VISITSEG Query as the TableName. When the table is activated, you will notice that the mouse pointer changes to the SQL
hourglass as the SQL statement is executed and the dataset returned.
The disadvantage in accessing the query files in this manner is that
you are unable to manipulate the SQL statement itself.
4. Does the Delphi Query component offer the same function when working with an Access database? Replace the Table2 control with a Query
component. Set the DatabaseName property to MEDINFO and add
the following statements to the SQL property:
SELECT vs-account-num,
vs visit num,
Vsradmitrdate,
#4>*-I.xx=
_ ;
. _~ II_S
255
vs disch date
FROM visi;seg
-
Set the Query to an Active state. The BDE and the Access driver will
take care of translating the Local SQL statements to the SQL used by
Access to return the same dataset that we were just working with.
The Access database tables and queries are fully shared and both types
of applications can access them simultaneously, If you can, open the
MEDINFO database in both Access 97 and your Delphi application on a
shared basis. Access reports and forms are not compatible with Delphi
but it is not likely that you would attempt to use them anyway,
The driver-based design of the BDE makes the option of using Access
data in a Delphi application a reality, As you have seen, there are no
major impediments to using these data sources that should dissuade
you from selecting Delphi as the development tool of choice. The
advantage of the Delphi application is that it will be a stand-alone executable, not requiring the Access application behind it for support.
Ir(_li.w-
, x I~~_..e.+^*I_~~-.
.w_p.~s.~l->~
A<
._ /
The parameters passed to this procedure are called keys. The array SU$
scripts match the columns used in the index currently in use for the
:i
table. For example, assume that the table is indexed on a single field.,:;
The FindKey method would take an array with a single element as i@:-parameter. If the index was a composite index, each key is comma
arated and any value not explicitly declared is assumed to be NIL.
.;
FindNearest-This brother of the FindKey method searches for the-l;
row that most closely matches the array of key values. This could be--,$$
the record that matches exactly or the next highest row if an exact :q$
, .-g4
match cannot be found.
GotoKey-The GotoKey method works in conjunction with the Se
or EditKey functions to move the record pointer to a matching row
the table. Unlike the FindKey method, the key that specifies the vti
to be matched is specified separately from this method. If a match ,~ ~
achieved, the record pointer is moved to the matching record and &i
TRUE value is returned. If no match is located, a FALSE is returned82..!
:
the record pointer stays on the current record.
GotoNearest-The behavior of this method matches that of
FindNearest. If no match is found to the key values specified by Se!
this method moves the record pointer to the next highest row.
SetKey-The SetKey method is used to set the key value for a datasc
prior to performing a search with the GotoKey method. This method&$
the first step in a two-step search process that matches the functional&#
of the single-step FindKey method. When SetKey is called, it places @,I
table into the dsSetKey state, setting up a search key buffer. Values a~$$
added to this buffer prior to calling the method used to move the cur- -$
sor. EditKey is closely related to this function, maintaining the curre@f
contents of the search buffer so that they can be modified rather thart,Yz
.: *;?
replaced.
%..a4$
all Me the Seeker
Lets examine the coding needed to implement these methods in an
application. Figure 8.6 shows the Sear&X program that can be 1-a .*r
on the companion CD-ROM in the Chapter8 directory.
-9<d
It uses the DBDEMOS database and the COUNTRYDB table from that
database. The table is indexed on the Name field, representing the
name of the country. All of the search methods default to using the
tables primary index for searching. If you want to use an alternate
index for the search, you must set the tables IndexName property to
the name of the index.
FindKey is the simplest method to utilize since it is self-contained. The
code behind the OnClick handler of the first button is simple:
procedure TForml.ButtonlClick(Sender:
TObject);
begin
if not Tablel.FindKey([Editl.Text])
then
MessageDlg(Editl.Text+'
not found', mtInformation,
[mbOkl ,O) ;
end;
The text to search for is provided from the Edit1 edit box and passed as
the only element of the search values array FindKey takes care of
everything else. If no match is found in the table, an appropriate message is returned to the user but the record pointer remains on the first
record.
FindNearest acts in a similar fashion when the button is pushed, with
one exception-it moves the record pointer to a value just beyond the
search value if a match cannot be found. No value is returned so the
code for this procedure is even simpler:
procedure TForml.Button2Click(Sender:
begin
Tablel.FindNearest([Editl.Text]);
end;
TObject);
Figure 8.7 shows the results of a search for the country Korea. Because
that country does not exist in the table, the pointer is positioned
between Jamaica and Mexico.
Figure 8.7
Searching
COUNTRY
the
table
As you can see from the code involved, the GotoKey and GotoNearest
methods are the more complicated of the search methods. They have
rhe edge in the flexibility department, allowing you to use EditKey to
modify the key values, but that edge is slight. The code needed to
implement the same simple search is:
procedure TForml.Button3Click(Sender:
TObject);
begin
with Table1 do
begin
SetKey;
Fields[O].AsString
:= Editl.Text;
if not GotoKey then
MessageDlg(Editl,Text+'
not found', mtInformatlon,
lImbOk], 0) ;
end;
end;
I
The search methods provided through the Table components are simple
and effective. You have enormous flexibility both in the way that you
want the search handled and in the source for the search value as well.
?!
the index and the second is a semicolon-delimited list of fields to
include in the index. The last parameter is Options, a collection of 3.,
attributes and restrictions applicable to the index. These options
include the following:
w ixPrimary--Represents the primary index for a dataset.
n ixunique-The index contains no duplicate values.
,;
n ixDescending-The index sorts the records in descending order.
n ixCaseInsensitive-The
index records are sorted without regard ted
p,*the case of the characters.
w ixExpression-The rows are sorted using dBASE key expressions,-:J.$r<.
Be sure to note which options are appropriate for the table type &a$:3
you are indexing. The use of incorrect options will result in an excep-j
i
tion being raised, (i.e., ixExpression with a Paradox table).
To add an index using the AddIndex method, the lines will read as
follows:
Tablel.Active := false;
Tablel.Exclusive
:= True;
Tablel.Active := true;
Tablel.AddIndex('ITEMIDX',
'Item',
i 6
_. -.:
I
I -i
.;
[ixoescending]);
This code block modifies the ORDSHIEDB table, adding a new index i
based on the Item column. Note that the table must be in Exclusive ;,,.$
mode before making this kind of modification. Also, note that the
needs to be opened exclusively for this operation to be carried out.
sure to return to Shared mode operations if your application is
multi-user.
t
Noting much happened after the last series of instructions. The table is
still in the same order as it was before the new index was built. The *;
reason for this is that the index being used to display the records has T-3
not been changed. To select the new index, we can use the Tables
IndexName property. This property specifies a secondary index for a .j
table. If the value is empty, the primary index for the table is used to --j
display the records. To change the table to using the new index, add ,:I>
-7:.
the following line:
Tablel.IndexName
:=
'ITEMIDX';
If the table is displayed, in a grid perhaps, the new sort order will be .i.:-;
.d,
immediately apparent.
260 n Par
xI ._ ., _,-),. l
An alternative method for specifying the index for a table is to use the
IndexFieldNames property. The propertys value is a semicolon-delimited list of column names. If the table is a Paradox or dBASE relation,
the columns must already have been indexed for this process to work
correctly. SQL tables are more dynamic and do not require that the
index already exist. When you use this property, you immediately override the IndexName property and vice versa; the two are mutually
exclusive.
3;:
j~:;iiy
Since any table can have a plethora of index possibilities, how can you
application determine what you have to work with? The IndexDefs
property is an array of index items that can be queried to determine if
the column that you want to work with has been indexed. The array
elements are composed of the semicolon-separated column name
entries used to define the indexes. The number of elements in the
.
IndexDefs array is maintained through its Count property. With this
number in hand, an application can walk through each element and
compare the column names to find the one that it wants to use. For
example, to find the ITEMIDX index, the application will search for & :-$g
,.:&I_ $ ;;b
index based on the Item column:
for x := 0 to (Tablel.IndexDefs.Count
- 1) do
begin
if Tablel.IndexDefs[x].Fields
= 'Item' then
begin
Tablel.IndexName
:= Tablel.IndexDefs[x].Name;
end;
end;
To ensure that you are working with the most complete array, issue a
call to the IndexDefs Update method prior to using the array. This will
refresh the collection after any changes may have occurred.
.q
-2
c"i .":
:
"A5
.i
:i
.T.jj$
;
,i
j.
ni
!,
:
,-:.
oeas.m--
:.$
I. Start a new application and drop a Table component on the form.
,
2. The first order of business is to assign a Database alias to the
DatabaseName property. This is the database in which the new table
i
will be a member. Use DBDEMOS for this example by selecting it for
.Zc&f
the DatabaseName propertys value.
3. The table must have a name, which will be assigned through the
:!
TableName property Call this table CDCOLL. If the table is going to .:y
be of a database type other than Paradox or dBASE, you must includi Z
the appropriate file extension in the string you pass as the table name.%
4 . All tables must have a table type that defines the database structure of 2
the object. If the database is going to be something other than ASCII, - 2
Paradox, or dBASE, set the TableType property to ttDefault. Otherwise-,,~l
select the appropriate type from the drop-down list. For the sample
Zi
that we are building, set this property to ttDBase.
5 . The fields for the table need to be defined. Double-click on the
FieldDefs property to call up the collection editor. The process being
_,
described here will need to be repeated for each field in the table. The
schema that we will use to define the CDCOLL table is shown in Fig1,,
z-3
ure 8.8. Click the Add New button and add the data shown for each
field.
When you have completed the definition of the table you will use the
Create Table menu option. Right-click on the Table component to
-7
-,iz
invoke the context menu and select Create Table. A physical representation of the table will be created in the background if everything
has been defined correctly.
7. The table definition is complete at this point but one more item should
exist with nearly all of your tables, an index. We will define a primary
key for this table to keep it sorted and to meet the relational standard.
Double-click on the IndexDefs property to start the collection editor
again. Click the Add New button to define a new index. Name the
index TITLE and the Fields property Title. Click on the Options property to see the properties listed below it. Set the ixPrimary and
ixunique properties to True. Close the collection editor and right-click
on the Table again. This time, use the Update Table Definition command to modify the table definition.
8. Test your table by adding a DataSource and a DBGrid to the form. Set
the Active property of the Table to True and execute the application.
Try entering a few CDs, making sure that the index is working by
inserting them randomly and making sure that they insert into the correct spot.
:i:4
;a
,\: s4
4
:z
1
.
.,.:-i
TObject);
,+ij
-1
"7:->
263
'Title I [ixprimary,
ixunique]);
Tablel;
P art I II-The
Well-Rounded Application
,.- 1(
_...
.
i
(
,!
:(~#,;,
,I
There are three different methods for loading the SQL string list at run 3
time: adding strings to the SQL property directly, deriving the strings .$
from a file, and copying the strings from another string list object. We. ..ii
will examine this last option first. A string list is an object derived froftvj
the TStrings base class and it represents a list of character strings. The ,:
SQL property of the Query component is of type TStrings. String lists 1
can be copied from one TString object to another just as any similar j II
data type can be copied. From the TStrings base class, the SQL props -
inherits a method called Assign whose purpose is to copy the value of .5Yi.
iJ
one TStrings object to another.
Figure 8.9 shows a demonstration program from the CD-ROM called
SQLBBX.DPR. The SQL Breadboard is a good learning tool that could
-:c
6
not be simpler to build. The user can enter SQL SELECT sta tements
.$
into the Memos edit region and with a click of a button they are executed and the results shown in the grid below. The Run SQL buttons
.?
OnClick handler encapsulates all of the code for the program. The lines .$
!,.%J
read as follows:
procedure
TForml.buttonlClick(Sender:
begin
Queryl.Close;
Queryl.SQL.Assign(Memol.Lines);
Queryl.Open;
end;
TObject);
;$
,,
,I -6
.'
, -4
.j
..:$
-;
.?
* 3
i
I?
With a bit of additional coding to keep the users out of trouble, this
approach can provide a simple free-form query interface for an
application.
;:*.9
:<:-aj.pi
-*
. ,. :,<,t:;
The first line adds a parameter definition to the Params list and the second assigns the value from the list box control to it. With the parameter
issue addressed, the rest of the code block is straightforward:
with Query1 do
begin
Close;
Params.CreateParam(ftString, 'WhatState', ptInput);
Params.ParamByName('WhatState'.AsString
:=
ListBoxl.Items[ListBoxl.ItemIndex];
SQL.LoadFromFile(
'D:\BPROJECTS\CHAPTER8\SQLPARAMS.TXTi);
Prepare;
Open;
end;
* FROM country');
a;.
#&
.": Ev
n
end;
This statement will work the same way as the original statement, ignoring all of the white space when the sentence is parsed. By building the
statement piece by piece, it is a simple operation to substitute one pan
for another. Lets assume that we want to allow the user to select all of
the fields from the COUNTRY table, as demonstrated in the two examples, but we want to give them the option of using a WHERE clause in
the statement to apply some selection criteria.
i,
Adding an Edit box control to the application allows the user to type a
clause for the SQL statement without having to worry about structurinz
the rest of the sentence. Modifying the SQL statement is as simple as
adding another block, in the case of the WHERE clause:
with Query1 do
begin
Close
SQL.Clear;
SQL.Add('SELECT
');
SQL.Add('* ');
SQL.Add('FROM
');
SQL.Add('country');
SQL.Add(Editl.Text);
Open;
end;
Taking a look at the rest of the statement, the design side of your brain
is probably buzzing with ideas for the ultimate SQL Builder that is
based around this method. How about CheckListBox controls that list
all of the fields available or push buttons for the equals, greater than,
or less than symbols?
DBChart Component
The DBChart control is another of Delphis hidden gems that is available to lend additional features to your application. The class is derived
from the TChart class and is extended to utilize a dataset for the source
of its data series. Using the columns of a dataset for its source, the
chart becomes a dynamic display that is updated as the dataset is modified. You have a wide variety of chart type options to choose from and
all of the parameters of the display can be controlled within an application. Through interface controls that you provide, the user can spend
:G
3
39
.!{
?I
21
hours changing the look and feel of their charts rather than playing
solitaire.
:;*>4
.:
All of the above-mentioned things are true, if you can solve the
-$
design-time interface! Like QuickReport, this set of controls (TChart, ,y;$
TDBChart, and TQRChart) is provided to Delphi from an outside ven- .$$j
dor and it takes a little getting used to in order to become proficient ;K.I+$$
its use. Charting also requires the knowledge of a new set of terminol- f:
ogy that applies to the properties and methods. Figure 8.11 is a table of?:;-:
selected key properties sufficient to get you started with the charting -g
tools.
sible
BufferedDisplay
True
True
ChartBounds
ChartHeight
ChartRect
ChartWidth
Legend
MaxPointsPerPage
i.;
Monochrome
Series
SeriesList
View3d
..
Figure 8. Ii?
The completed
DBChart
project
Well go step by step to build this first chart before applying some of the
different options,
I. Start a new application and add a DataSource, a Query, and a DBGrld
to the form. Create the chain pointing the DBGrid to the DataSource
and the DataSource to the Query.
2 . Set the Querys DatabaseName property to DBDEMOS. The SQL
statement used to build the dataset is:
SELECT Count(*) Customers,
WHERE State <> "
GROUP BY State
ORDER BY State
State
FROM
Customer
Add this to the SQL property and activate the query to ensure that it is
functioning correctly.
3. Add a DBChart component to the form and set the Align property to
alclient. There are TWO avenues to use when approaching the properties of the chart, though they both lead to the same place, the Chart
Editor. The easiest route to get to the form shown in Figure 8.13 is to
right-click on the DBChart.
and Techniques II
27
4, Click Add in the Series tab. A series is the set of data elements to be
charted. Multiple series are used on a chart when comparing one set
of figures against another. The first thing that you are asked to select
is the representation, or type of chart to build. Select the Horizontal
Bar chart from the TeeChart gallery and click OK.
!
f
!
I
/
The selection of a graph type is determined by the message that you are
trying to convey and the data that is involved. A bar chart is excellent
for conveying a sense of comparative amounts but would not tell the
viewer how much of a whole each bar represents. We selected a horizontal bar chart simply to make the labels describing the states more
legible. On the other hand, a pie chart quickly tells the viewer how
much of the whole picture is made up by each segment. Line charts
show a connected series of points, representing changes in a single data
element over a period. The bar chart is selected for this data because
we want to quickly ascertain where the bulk of our orders come from.
5. Double-click on the new series or select the Series tab to edit the
source for Seriesl. Select the Data Source tab. Since were going to
use a dataset object as the source for the series, select Dataset from
the drop-down menu.
6. The tab is now filled with choices that are appropriate to the data
source that youve selected. Start by setting the newly displayed
Dataset property to Queryl. The next three choices determine the
way that the information will appear on the graph. The Labels property is used to draw labels for each of the bars in the graph, Select the
State column for those values. The Bar property determines what
value each bar is representing; for this well use the Customer column
that contains the counts of customers by state.
7. Click Close and your chart will be generated. If any changes are made
in the dataset, the chart will be automatically redrawn as long as the
AutoRefresh property is True.
8. Lets view the data in a different format to see if the information is as
clearly disseminated as it was with the bar chart. Start the Chart Editor and select the Chart tab. Series1 should be selected already so click
Change. Youll see the TeeChart gallery again. Select the pie chart
from the available choices. Notice that the types of charts have been
limited by the original dataset decisions. Click OK to make the choice
and your chart will be redrawn as a pie chart. When the data is viewed
in this format, it is not as clear what you are viewing and it doesnt
have the quick impact of the bar chart. Change the chart type back to
Horizontal Bar chart.
9. The title of the chart is somewhat misleading when the default value
is used. Click on the Titles tab, and in the edit region name the chart
Customer Distribution by State. Click Close to complete the
chart and view the newly painted control.
This completes the bar chart for this project. An alternative to building
the report by hand is to use the TeeChart Wizard. This is accessed by
selecting File ) New from the Delphi menu and then selecting the Business tab of the repository. The wizard will start by asking you what type
of chart you want to create, giving you a choice between the Database
Chart and the Non Database Chart. Selecting the Database chart then
asks you to select a table to use as the data source. Select the fields you
want included on the chart and then the type of chart to display them
on. A new form that contains the dataset and the DBChart is generated
for you, completing the process.
273
r*lr.~~
;-x
*/l~~~~~~,.~~~_^_/_~,.~~~~~~~~~~~~r)~.~li-*~~~.~y~~idl~^ii
.li__ccuIeL-mIIIII*-----~
v..
.-
.$
.x
-:
*,&Atj
-1:9
-3
-T:$
.$
.*.!. *
:zFy3-,
:I)r
_., ,
;.&&.&+
.S\
Q$4
it
API Architecture
API Functions
277
API Architecture
The BDE API architecture unites the two types of database systems that
have developed through the years, supporting both through an interface unique to the BDE. PC-based database systems have developed
along the model of ISAM (Indexed Sequential Access Method) database
systems. The ISAM model is a navigable file system in which directions
such as Previous and Next have meaning to the file structure.
SQL-based systems found on servers do not, in theory support such
navigation. As we discussed in earlier chapters, SQL databases are set
oriented with no specific order to the records.
The BDE takes the best of both of these worlds and connects to either
type through its common interface. The cursor-based engine extends
the most powerful features of both types of databases into the other.
The navigational features supported, for example, are drawn from the
ISAM design. SQL tables not having a directional property can now be
navigated through a common set of functions while providing the rich
capabilities of the SQL command set to the mix. ISAM databases, in
return, can then benefit from the set-oriented query capabilities inherent with SQL databases.
The common functionality supported by the API, using a native BDE
driver or an ODBC driver, encompasses the following:
Opening and closing of databases
Getting and setting the properties of BDE objects
Accessing and manipulating the data stored in each type of database
Database definition, such as creating tables and indexes
Operations that are performed across database systems, such as
copying and joining tables
_ilx.iml279*>
_S,~W
.*o__i._
._
dbiREADWRITE,
dbiOPENSHARED,
This sample function call opens the DBDEMOS database, assuming that
it has been properly configured within the BDE. The second parameter,
STANDARD in this case, determines the type of database that is being
accessed. A database can be opened in either Read/Write or Read only
modes and in Shared or Exclusive mode. These choices are set through
the settings found in the third and fourth parameters of this statement.
The remaining parameters are optional with the exception of the crucial database handle found in the last slot.
DbiInit must be called prior to calling this function and
DbiOpenDatabase must return a satisfactory result value prior to
attempting any table access. The success of any BDE function is determined by querying the return value of the function. This result is
returned as a type DBIResult. This data object will tell your application
if an error was encountered; a result of DBIERR-NONE,
no errors,
means that the function call was a success.
When an error occurs, the BDE will place much more detailed information about the error onto the BDE error stack. These error contexts can
be examined further through a set of error handling functions built into
the BDE. These are:
280 n
DbiGetErrorEntry
DbiGetErrorString
n DbiGetErrorContext
n
DbiGetErrorInfo
This directory should be one that is not used by any other programs
and must be write accessible, a consideration if this application is
installed over a network.
0,
dbiREADWRITE,
1 refer you to Appendix A again for the data type and descriptions of
each of the parameters in this extensive list. The sample function
<I-I-xlm(iiielixl--.---
. ,r
I.,l^ . .
demonstrates one of the great design advantages of the Borland Database Engine. Being a driver-based tool, the same function is used to
open all types of supported files including text, dBASE, Paradox, and
SQL files, regardless of their underlying architecture.
__
11
j
-.%:
CursorProps)
CursorProps.iRecBfSize
Sizeof(BYTE))
Be sure to verify this step by checking the size of the allocated buffer
space. If the buffer has not been established, further steps in this process will be unsuccessful.
Step Eight: Position the record point on the appropriate record
This step introduces some new vernacular to the developers vocabulary
in the use of the word crack. Cracks are the imaginary lines that separate each record in a database. Using this terminology allows you to
f
envision the record pointer being positioned before the first record in a
table or after the last. You can also place it between two records, committed to neither. Use the DbiSetToBegin function to set the pointer to
the crack before the first record:
DbiSetToBegin(hCur)
Crack semantics also lets you use a single method to access all records
in the table by using DbiGetNextRecord.
Step Nine: Retrieve the desired record from the cursor
Finally, after hours of toil we are coming closer to achieving our goal of
retrieving data from the database. Because were sitting on the crack,
we can use the DbiGetNextRecord function to retrieve the structure:
j
DbiGetNextRecord(hTb1,
dbiREADLOCK,
RecBuffer,
nil)
If a record buffer is passed in the parameter list, the data for the record
is placed in the buffer. When NIL is passed, instead of a pointer to a
record buffer, no data is retrieved and the positioning on the record is
the only result.
Step Ten: Get the desired fields from the record
The object of our desire is in sight now. This step will retrieve the data
from the record and place it into local variables for use by the application. The DbiGetField function retrieves the contents of the specified
field from the record buffer:
3
* .g$4:f
::$
:,
.>?
i
.i
{
{
{
(
Upon closer review of the steps involved, you might reconsider the
amount of effort involved against the eventual payoff. Listing 9.1 is a
template program, provided by Borland, that has been converted from
.;
,;+f
I. ;$
:.,s
.
c'"?
?
*
(:
Tii
1
BDE API Template
Copyright 1998 Borland Corporation
1
1
I
1
interface
uses
Windows, Messages,
Dialogs,
StdCtrls, BDE;
SysUtils,
Classes,
type
TForml = class(TForm)
Buttonl: TButton;
Labell: TLabel;
procedure ButtonlClick(Sender:
private
{ Private declarations }
public
{ Public declarations )
end;
Graphics,
TObject);
var
Forml: TForml;
implementation
($R *.DFM)
procedure VerifyDBI( inResult.Code : DBIResult);
var
ErrorMsg
: DBIMsg;
Controls,
Forms,
284 n
) + ': ' +
TObject);
,// handle to the database
// handle to the cursor
// cursor properties
// pointer to the record
i/ variable for field data
/,/ is the field blank
_,..,.%_.
cursor - }
( handle to database }
( table name (ext optional
{ driver type - not needed
if ext used )
{ index name
{ index tag
{ index ident fier }
{ open mode }
{ shared or e X elusive use 1
{ translation mode - almost
always xltFIELD }
{ determines if cursor is
unidirectional
}
{ optional parameter }
{ handle to the cursor }
allocation
failure')
:= FloatToStr(CustNum);
end;
hCur ));
hDb ));
. I-li,.ea.ea
Rather than using the API directly to build your database applications, :$
the more likely scenario is one in which you will combine the ease of T
development that comes with the visual components and the depth
d
available through the API. Using the API to perform selective tasks
allows you to accomplish some items that cannot be performed throu;
the visual components. The first of these is packing a dBASE table.
:
P a c k i n g dB.A S E Tables
The deletion of records in dBASE tables is a deceiving process. When ,-i
you request a deletion from a table, the record is not deleted; the row .$
:$
is merely marked for deletion. The advantage of this methodology is
that you have the ability to instantly recover deleted records. The dis- --.
advantage is that over time these marked records continue to take up
space in your table, slowing down your table processing. The solution r
to rhis problem is a process called packing. The Packing procedure
.s
removes the deleted rows and reclaims the space. In addition, with the ,li
correct parameter settings, all associated indexes are rebuilt in the
.\?
,.
+g
same operation.
,+:*zJ
The code shown in Listing 9.2 demonstrates a call to the API function 1-i
DbiPackTable. The table at which this procedure is directed must be
jq
opened in Exclusive mode, which might require that you close and
; .$
jj
reopen the table. Also be sure to follow good programming practice
and return all objects to their original state.
procedure dbasePack(InTable
: TTable);
var
: boolean
bHoldExc1
bHoldStat
: boolean
ResCode
: DBIResu 1t;
: DBIMSG;
EMessage
begin
{ - Hold current state - )
with InTable do
begin
bHo ldExc1 := Exclusive;
bHo 1dStat := Active;
I - table must be open/exclusive to pack - }
Close;
Exclusive := true;
Open;
-^
c
288 n
.r.rrressrerrr~s
end;
ResCode
:=
DbiPackTable(InTable.DBHandle,
InTable.Handle,
nil,
nil,
True);
);
example, must be copied into the descriptor table as a nullstrinp. To arcnmnlish this. we must first oerform the conversion
throigh the use of the StrPCopy procedure.
procedure ParadoxPack(InTable : TTable);
var
bHoldExc1
bHoldStat
ResCode
EMessage
PdoxTableDesc
:
:
:
:
:
boolean;
boolean;
DBIResult;
DBIMSG;
CRTblDesc;
begin
ResCode
:=
DBIERR-NONE;
- 1
:=
DbiDoRestructure(InTable.DBHandle,
1,
BPdoxTableDesc,
nil,
nil,
nil,
false);
!
/,
begin
DbiGetErrorString(
ResCode,
ShowMessage( EMessage );
end;
( - Restore
with InTable
begin
Close;
Exclusive
Active
end;
EMessage
);
state of table - 1
do
:= bHoldExc1;
:= bHoldStat;
end;
TObject);
begin
StrPCopy( szTableName,
Tablel.TableName
);
);
-' 9
,%*I
2::.
1
@ValidityBuf,
API Functions
To conclude this chapter, the following lists categorize the API functions
by functional groups. Scan this list when making the decision on how
best to accomplish a task in your application.
cursor Functions
These functions either perform an action on a cursor, such as positioning, linking, or creating and closing a cursor, or return information
about a cursor.
DbiActivateEilter
DbiAddFilter
DbiApplyDelayedUpdates
DbiBeginDelayedUpdates
DbiBeginLinkMode
DbiCloneCursor
293
DbiCloseCursor
DbiCompareBookMarks
DbiDeactivateFiLter
DbiDropFilter
DbiEndDelayedUpdates
DbiEndLinkMode
DbiExtractKey
DbiForceRecordReread
DbiForceReread
DbiFormFullName
DbiGetBookMark
DbiGetCursorForTable
DbiGetCursorProps
DbiGetExactRecordCount
DbiGetFieldDescs
DbiGetLinkStatus
DbiGetNextRecord
DbiGetPriorRecord
DbiGetProp
DbiGetRecord
DbiGetRecordCount
DbiGetRecordForKey
DbiGetRelativeRecord
DbiGetSeqNo
DbiLinkDetail
DbiLinkDetailToExp
DbiMakePermanent
DbiOpenTable
DbiResetRange
DbiSaveChanges
DbiSetFieldMap
DbiSetProp
DbiSetRange
DbiSetToBegin
DbiSetToBookMark
294 n
,-vIcbBe-
i_, .~w--wz..-m-.e-
DbiSetToCursor
DbiSetToEnd
DbiSetToKey
DbiSetToRecordNo
DbiSetToSeqNo
DbiUnlinkDetail
Data Access functions
These functions perform data access operations on tables.
DbiAppendRecord
DbiDeleteRecord
DbiFreeBlob
DbiGetBlob
DbiGetBlobHeading
DbiGetBlobSize
DbiGetField
DbiGetFieldDescs
DbiGetFieIdTypeDesc
DbiInitRecord
DbiInsertRecord
DbiModifyRecord
DbiOpenBlob
DbiPutBlob
DbiPutField
DbiReadBlock
DbiSaveChanges
DbiSetFieldMap
DbiTruncateBlob
DbiUndeleteRecord
DbiVerifyField
DbiWriteBlock
Database Functions
The BDE functions listed here perform database-level tasks or return
information about a database as a whole.
DbiCloseDatabase
DbiGetDatabaseDesc
DbiGetDirectory
DbiOpenDatabase
DbiOpenDatabaseList
DbiOpenFileList
DbiOpenIndexList
DbiOpenTableList
DbiSetDirectory
Date/Time/Number
Functions
These functions encode and decode date and time objects and set or
retrieve the current numeric or date and time formats for a session.
DbiBcdFromFloat
DbiBcdToFloat
DbiDateDecode
DbiDateEncode
DbiGetDateFormat
DbiGetNumberFormat
DbiGetTimeFormat
DbiSetDateFormat
DbiSetNumberFormat
DbiSetTimeFormat
DbiTimeDecode
DbiTimeEncode
DbiTimeStampDecode
DbiTimeStampEncode
Environmental Functions
The BDE environment functions return information pertaining to the
application environment. This includes the supported table, field, and
index types or available types. In addition, tasks such as loading a specific driver or initializing the engine fall into this category.
DbiAddAlias
DbiAddDriver
DbiAnsiToNative
DbiDeleteAlias
DbiDeleteDriver
DbiDllExit
DbiExit
DbiGetClientInfo
DbiGetDriverDesc
DbiGetLdName
DbiGetLdObj
DbiGetNetUserName
DbiGetProp
DbiGetSysConfig
DbiGetSysInfo
DbiGetSysVersion
DbiInit
DbiLoadDriver
DbiNativeToAnsi
DbiOpenCfgInfoList
DbiOpenDriverList
DbiOpenFieldTypesList
DbiOpenFunctionArgList
DbiOpenFunctionList
DbiOpenIndexTypesList
DbiOpenLdList
DbiOpenTableList
DbiOpenTableTypesList
DbiOpenUserList
DbiSetProp
Error functions
Error handling for the BDE is performed using these functions.
DbiGetErrorContext
DbiGetErrorEntry
DbiGetErrorInfo
DbiGetErrorString
Functions
298 n
___..
Query Functions
The listed functions perform query-related tasks.
DbiGetProp
DbiQAlloc
DbiQExec
DbiQExecDirect
DbiQExecProcDirect
DbiQFree
DbiQGetBaseDescs
DbiQInstantiateAnswer
DbiQPrepare
DbiQPrepareProc
DbiQSetParams
DbiQSetProcParams
DbiSetProp
DbiValidateProp
Session Functions
These functions return information about a session or perform session-related tasks.
DbiAddPassword
DbiCheckRefresh
DbiCloseSession
DbiDropPassword
DbiGetCallBack
DbiGetCurrSession
DbiGetDateFormat
DbiGetNumberFormat
DbiGetSesInfo
DbiGetTimeFormat
DbiRegisterCallBack
DbiSetCurrSession
DbiSetDateFormat
DbiSetNumberFormat
DbiSetPrivateDir
x_*.*%*.
-...
-.,
--_
l(
DbiSetTimeFormat
DbiStartSession
Functions
k.j
;&fj
This list of functions performs tasks related to the tables. Some will
return information about a table such as lock status, a list of referenti*!!
integrity links, or the indexes open on the table. Functions that perfor&%
>-2%
table-wide tasks such as copying and deleting are also included.
:>g
/ $j
DbiBatchMove
,j : :
41il
DbiCopyTable
3ji
DbiCreateInMemTable
) :.,.-,g.;i
DbiCreateTable
DbiCreateTempTable
,^T
DbiDeleteTable
.;
g
DbiDoRestructure
.+2b
_
DbiEmptyTable
.:;:,~:$
DbiGetTableOpenCount
5:
DbiGetTableTrpeDesc
DbiIsTableLocked
DbiIsTableShared
DbiMakePermanent
DbiOpenFamilyList
DbiOpenFieldList
DbiOpenIndexList
DbiOpenLockList
DbiOpenRintList
DbiOpenSecurityList
DbiOpenTable
DbiPackTable
DbiQInstantiateAnswer
DbiRegenIndexes
DbiRenameTable
DbiSaveChanges
DbiSortTable
Transdction
Functions
These functions begin and end or return information about a
transaction.
DbiBeginTran
DbiEndTran
DbiGetTranInfo
Summary
The API of the Borland Database Engine exposes a raft of useful functions for the programmer willing to learn the complexities of working
with the interface. Careful review of the architectural requirements
described in this chapter and the function listings in Appendix A will
provide the programmer with a number of opportunities to expand thl
functionality of their products. In addition to directly improving the
programmers repertoire, a secondary benefit is accrued through a
much deeper understanding of the underlying structure of the Delphi
development tool itself through interaction with its native interface.
Function Reference
Every Borland Database Engine API function, whether it is surfaced in
the visual components or not, is documented in the following pages.
Note that the majority of these functions will use structures and data
types defined in the BDE.INT file. The function reference should be
cross-referenced with that file for further information about the data
structures. The structure of these calls also drives home the point that
this engine was originally designed by and for C programmers. Dont
let this sway your determination to utilize the API to its fullest. Explore
the functionality and see if there is anything here that will improve
your programs.
Documentation Format
Each entry in this reference will appear in the following format. The
following entry shows an annotated example:
The function declaration is given in shorthand. This shows the calling
sequence of the parameters.
DbiActivateFilter
( Kursor,
Milter )
Each of the parameters is documented in a table that follows the function declaration.
Parameter
D a t a Tvbe
Descrivtion
hCursor
hDBlCur
hFilter
hDBlFilter
The functions all return a code indicating whether they were successful
or not. If there was an error in the transaction, a code will be returned
here.
RETURNS : DBIResult
DbiAcqPertistTableLock
( hDb, szTableName,
szDriverType
hDb
hDBlDb
Database
handle.
szTableName
PChar
szDriverType
PChar
RETURNS: DBIResult
DbiAcqPersistTableLock
acquires an exclusive, persistent table lock that
prevents other users from accessing the table or creating one of the
same name.
DbiAcqTableLock
( hcursor, eLockType )
hCursor
hDBlCur
eLockType
DBILockType
RETURNS: DBIResult
hDBlCur
hFilter
hDBlFilter
RETURNS: DBIResult
szAliasName, SzDriverType,
hDBlCfg
szParams,
&Persist )
szAliasName
PChar
szDriverType
PChar
szParams
PChar
bPersist
Boolean
RETURNS: DBIResult
This function adds a new alias to the BDE configuration file. You must
call DbiInit prior to a call to DbiAddAlias.
DbiAddDriver ( hCfg, szDriverName,
hCfg
hDBlCfg
szParams, &Persist )
The configuration file to be used. This
parameter must be set to NIL.
szDriverName
PChar
szParams
PChar
bPersist
Boolean
RETURNS: DBIResult
DbiAddDriver adds a driver to the BDE configuration file. with all
parameters set to the default values unless overridden by the szParams
list. DbiInit must be called prior to calling this function.
DbiAddFilter ( hCursor, iC/ientData,
Filter, hFi/ter )
hCursor
hDBlCur
iClientData
UINT32
Not used-must be 0.
iPriority
UINTI6
N o t u s e d - m u s t b e I.
bCanAbort
Boolean
Not
pCANExpr
A pointer to a CANExpr
pCanExpr
used-must
be
False.
structure. This
pfGENFilter
hFilter
phDBlFilter
as
-%-*i*
ij
RETURNS: DBIResult
DbiAddFilter adds a filter to a table. The CANExpr structure must be
defined and filled prior to calling this function.
DbiAddlndex ( hDb, hCursor,
szKeyViolName )
szTub/eName,
szDriverType,
IdxDesc,
hDb
hDBlDb
hCursor
hDBlCur
and szDriverType
szDriverType
PChar
IdxDesc
plDXDesc
szTableName
(I DXDesc).
szKeyViolName
PChar
RETURNS: DBIResult
DbiAddIndex
DbiAddPassword
szPassword
RETURNS: DBIResult
DbiAddPassword provides users with access to operations on a previ
ously encrypted table. DbiCreateTable and DbiDoRestructure can be
used to add or remove encryption.
DbiAnsiToNative
LdObj
( LdObj, NativeStr,
Pointer
NativeStr
PChar
The
translations
string.
AnsiStr
PChar
iLen
UINTl6
bDataLoss
Boolean
305
DBIResult
( hcursor, RecorciSuf)
hCursor
hDBlCur
RecordBuf
Byte
RETURNS:
DBIResult
( hcursor,
UpdCmd )
hCursor
hDBlCur
UpdCmd
DBlDelayedUpdCmd
RETURNS:
DBIResult
DbiApplyDelayedUpdates
commits or rolls back any changes made to
cached data when cached updates mode is active.
DbiBatchMove ( SrcTbiDesc, hSrcCur, DstTbiDesc, hDstCur,
ebatMode, ifidcount, SrcFidMap, szindexName, szindexTagName,
iindexid, szKeyvio/Name, szProbiemsName, szchangedhlame,
ProbRecs, IKeyvRecs, IChangeciRecs, bAbortOnFirstProb,
bAbortOnFirstKeyvioi, IRecsToMove, brransiiterate )
SrcTblDesc
pBATTblDesc
hSrcCur
hDBlCur
structure
DstTblDesc
pBATTblDesc
hDstCur
hDBlCur
table
structure.
dBATMode
Mode of operation
iFldCount
UINTl6
SrcFldMap
PWORD
szlndexName
PChar
ebatMode
PChar
ilndexld
UINTI6
The
szKeyViolName
PChar
index
szProblemsName
PChar
szChangedName
PChar
IProbRecs
UINT32
IKeyvRecs
Violations
table.
table.
UINT32
Changed
bAbortOnFirstProb
number.
UINT32
IChangedRecs
identification
table.
Boolean
Boolean
pUINT32
Boolean
iVal
pDFLOAT
iprecision
UINTI6
iPlaces
UINTI6
pFMTBcd
Bed
RETURNS : DBIResult
( Ekd, iVal )
pFMTBcd
A pointer to a FMTBcd
pDFLOAT
data.
307
RETURNS : DBIResult
DbiBcdToFloat
DbiBeginDelayedUpdates
( hCursor
phDBlCur
hCursor
RETURNS: DBIResult
The function DbiBeginDelayedUpdates is used to activate the cached
update mode.
DbiBeginLinkMode
( phcursor
phDBlCur
phCursor
)
For input. this specifies the orIgInal cursor. The
new cursor is returned on output.
RETURNS : DBIResult
Used after a call to DbiLinkDetail,
between two tables.
DbiBeginTran
hDb
hDBlDb
eXlL
eXI LType
phXact
phDBlXact
The
transaction
level.
handle
RETURNS: DBIResult
DbiBeginIran starts a transaction on a given database. The actions that
occur upon issuance of this command vary from setl-er to server.
DbiCheckRefresh
No
parameters
RETURNS : DBIResult
The function DbiCheckRefresh is used to scan for remote updates to
tables for all cursors in the context of the current session. If changes
are noted, the cursors are updated.
DbiCIoneCursor
hCurSrc
( hCurSrc,
hDBlCur
bReadOnly, bUniDirectional,
The source cursor handle.
phCurNew
308
-~*-d~~*~.-*-
Boolean
bUniDirectional
Boolean
phCurNew
phDBlCur
cursor.
RETURNS: DBIResult
The function DbiCloneCursor serves to create a new cursor that is a
copy of the source cursor. The cloned cursor inherits certain properties
of the source:
n
Current index
w Range
H Translate mode
n
Share mode
Position
w Field maps
n
Filters
( hCursor )
hCursor
hDBlCur
RETURNS: DBIResult
DbiCloseCursor closes the specified cursor.
DbiCloseDatabase
hDb
( hDb )
hDBlDb
RETURNS: DBIResult
DbiCLoseDatabase
cursors.
( hXlt )
hXlt
hDBlXlt
RETURNS: DBIResult
The function DbiCloseFieldXlt closes a field translation object.
DbiCloselndex
hCursor
hDBlCur
szlndexName
PChar
ilndexld
UINTl6
RETURNS: DBIResult
The function DbiCloseIndex
DbiCloseSession
( hSes )
hDBlSes
hSes
RETURNS: DBIResult
DbiCloseSession closes the session and frees all resources associated
with it including database handles, cursor, table-level locks, and
record-level locks.
DbiCompareSookMarks
pCmp5kmkResult )
hCur
( hCur, p5ookMark
I, pBookMark2,
hDBlCur
PByte
pBookMark2
PByte
Pointer
pCmpBkmkResult
pCMPBkMkRslt
pBookMark
result.
RESULTS: DBIResult
The function DbiCompareBookMarks
is used to compare the relative
positions of two bookmarks set on the same cursor.
hCursor
hDBlCur
PKeyl
PByte
WY2
PByte
iFields
UINTI 6
value.
The number of fields to be included
when a composite key is used. iFields
and iLen
UINTl6
piResult
pINTI
result.
RETURNS: DBIResult
szSrcDriverType,
hDb
hDBlDb
bOverWrite
Boolean
szSrcTableName
PChar
szSrcDriverType
PChar
PszDestTableName
PChar
RETURNS: DBIResult
The function DbiCopyTable copies tables of the same driver type from a
source table to a destination table.
DbiCreatelnMemTable
hDb
hDBlDb
szName
PChar
iFields
UINT
pfldDesc
pFLDDesc
hCursor
phDBlCur
I6
(FLDDesc)
structures.
3 1 1
RETURNS: DBIResult
DbiCreateInMemTable
creates a temporary table in memory. The data
types and capabilities supported for in-memory tables are very limited.
DbiCreateTable ( hDb, bOverWrite, crT6lDsc )
hDb
hDBlDb
bOverWrite
Boolean
crTblDsc
pCRTbIDesc
RESULTS: DBIResult
The function DbiCreateTable
by the handle.
hDBlDb
pcrTblDsc
pCRTblDesc
the
working
The
table
directory.)
descriptor
structure
(CRTblDesc).
phCursor
phDBlCur
RETURNS : DBIResult
DbiCreateTempTable creates a temporary table that is deleted when the
cursor is closed.
DbiDateDecode ( dateD, piMon, piDay, piYear )
dateD
piMon
DBIDATE
The
pUlNT
I6
encoded
decoded
piDay
pUlNT
I6
pINTI
component.
day
component.
RETURNS: DBIResult
month
piYear
date.
year
component.
iIm
,~ .*
it.
?,
3 12 n
.*erz_*
lJINT16
iDay
UINTl6
iYear
INTl6
pdateD
pDBlDATE
RETURNS: DBIResult
DbiDateEncode creates a data type DBIDATE from individual date
components.
DbiDeactivateFilter ( Kursor: hDB/Cur; hfilter: hDBlfi/ter )
hCursor
hDBlCur
hFilter
hDBlFilter
RETURNS: DBIResult
DbiDeactivateFilter turns off a specified filter so that it will not affect
the records displayed from a cursor.
DbiDeleteAlias
hCfg
( hCfg, szAliasName
hDBlCfg
)
The configuration file to be used. This
parameter must be NIL.
szAliasName
PChar
RETURNS: DBIResult
DbiDeleteAlias deletes an alias from the BDE configuration file.
DbiDeleteDriver ( hCfg, szDriverName, &Save )
hCfg
hDBlCfg
szDriverName
PChar
bSave
Boolean
3 13
RETURNS: DBIResult
hDBlDb
hCursor
hDBlCur
szTableName
PChar
szDriverType
PChar
szlndexName
PChar
szlndexTagName
PChar
ilndexld
UINTl6
.CDX
indexes.)
RETURNS : DBIResult
hDBlCur
pRecBuf
Byte
record.
RETURNS: DBIResult
hDb
hDBlDb
szTableName
PChar
szDriverType
PChar
RETURNS : DBIResult
No
parameters
RETURNS: DBIResult
DbiDllExit is used to prepare the BDE to be disconnected from within a
DLL only. The function is called immediately prior to calling DbiExit
within the DLL.
DbiDoRestructure ( hDb, iTb/DescCount, pTb/Desc, szSaveAs,
szKeyviolName,
szProb/emsName, bAnalyzeOnly )
hDb
hDBiDb
iTblDescCount
UINTl6
pTblDesc
pCRTblDesc
The CRTblDesc
PChar
PChar
szProblemsName
PChar
bAnalyzeOnly
Boolean
Not
currently
used.
RETURNS: DBIResult
The function DbiDoRestructure has a wide range of uses. It performs
modifications to tables such as modifying field types or sizes, adding or
deleting fields, changing indexes and passwords, or packing Paradox
tables.
DbiDropFilter ( hCursor, hfilter )
hCursor
hDBlCur
hFilter
hDBlFilter
RETURNS: DBIResult
The function DbiDropFilter
associated resources.
DbiDropPassword ( szPassword
szPassword
PChar
)
The password to be dropped. If NIL, all
passwords for the session are dropped.
RETURNS: DBIResult
DbiDropPassword
DbiEmptyTable
hDb
hDBlDb
hCursor
hDBlCur
szTableName
PChar
szDriverType
PChar
RETURNS: DBIResult
DbiEmptyTable
DbiEndDelayedUpdates
phCursor
( phCursor )
phDBlCur
RETURNS: DBIResult
The function DbiEndDelayedUpdates removes the cursor from cached
updates mode and returns a new cursor handle.
DbiEndLinkMode
( phcursor )
phCursor
phDBlCur
RETURNS: DBIResult
DbiEndLinkMode takes the cursor out of Link mode and returns a new
cursor handle.
Dbifndnon ( hDb, hXact, eEnd )
hDb
hDBlDb
hXact
hDBIXact
The
eEnd
eXEnd
transaction
handle.
RETURNS: DBIResult
The function DbiEndTran
SQL server table.
DbiExit
No
parameters
DBIResult
hCursor
hDBlCur
pRecBuf
Byte
pKeyBuf
RETURNS:
Byte
DBIResult
The function DbiExtractKey gets the key value for the current record
from either the cursor or the record buffer.
DbiForceRecordReread
( Kursor, pRec5uf)
hCursor
hDBlCur
pRecBuf
Byte
RETURNS:
DBIResult
RETURNS:
( Kursor )
hDBlCur
DBIResult
The function DbiForceReread refreshes all data for the cursor. The
refreshed data will include any remotely updated rows.
DbiFormFullName
( hDb, szTab/eName,
szDriverType,
database
szFul/Name
hDb
hDBlDb
The
handle.
szTableName
PChar
szDriverType
PChar
szFullName
PChar
RETURNS:
DBIResult
DbiFormFullName
supplies a fully qualified filename based on the settings for the working directory known to the BDE.
DbifreeBlob
hCursor
hDBlCur
pRecBuf
we
iField
UINTl6
RETURNS: DBIResult
DbiFreeBlob releases a BLOB handle that was opened through a call to
DbiOpenBlob.
DbiGetSlob
hCursor
hDBlCur
pRecBuf
Byte
iField
UINT16
handle.
iOffSet
UINT32
iLen
UINT32
field.
pDest
Byte
iRead
pUINT32
RETURNS: DBIResult
The function DbiGetBlob retrieves the BLOB data from the field specified. This function is used with Paradox tables only and is needed to
support the feature of storing a minimal amount of BLOB data in the
tabIe itself.
DbiGetBlobHeading
pDest )
hCursor
hDBlCur
iField
UINTI6
pRecBuf
Byte
--.i 3r--aTvl*I*
a 18 ..i n
Purt l/l-The
Well-Rounded Application
pDest
Byte
heading.
RETURNS: DBIResult
DbiGetBlobHeading provides information about the specified BLOB
field in the record buffer.
DbiGetSlobSize
( Kursor,
hCursor
hDBlCur
pRecBuf
Byte
iField
UINTI6
piSize
pUINT32
RETURNS: DBIResult
DbiGetBlobSize retrieves the size, in bytes, of the specified BLOB data
from the record buffer.
DbiGetSookMark
( hCur, pBookMark
hCur
hDBlCur
pBookMark
Byte
RETURNS : DBIResult
The funcrion DbiGetBookMark saves the current row position in the
provided cursor to the pBookMark buffer defined by the application.
DbiGetCallBack
piCbSufLen,
PPCbM PPWJ 1
hCursor
hDBlCur
ecbType
CBType
piClientData
pUINT32
piCbBufLen
pUlNT
ppCbBuf
ppVOlD
wfcb
ppfDBlCallBack
I6
callback
RETURNS : DBIResult
function.
( pClient/nfo
pclientlnfo
pCLlENTlnfo
RETURNS : DBIResult
DbiGetClientInfo
retrieves system-level information about the application that can determine if other sessions are present when exclusive
access is required for a table.
DbiGetCurrSession
( phSes
phSes
phDBlSes
RETURNS: DBIResult
DbiGetCurrSession retrieves the handle for the current session.
DbiGetCursorForTable
( hDb, szTableName,
szDriverType,
phCursor )
hDb
hDBlDb
szTableName
PChar
szDriverType
PChar
phcursor
phDBlCur
RETURNS : DBIResult
DbiGetCursorForTable retrieves the cursor for the specified table in the
current session.
DbiGetCursorProps
( hCursor, pcurProps )
hCursor
hDBlCur
pcurProps
pCURProps
RETURNS: DBIResult
The function DbiGetCursorProps retrieves the properties of the specified cursor and places them in the CURProps structure.
( szfrlame, pdbDesc )
szName
PChar
pdbDesc
pDBDesc
RETURNS: DBIResult
DbiGetDatabaseDesc retrieves description information about the
base from the configuration file and fills the DBDesc structure.
DbiGetDateFormat
pfmtDate
data-
( pfmtDate )
pFMTDate
RETURNS: DBIResult
DbiGetDateFormat returns the date format for the current session.
DbiGetDirectory ( hDb, bDefau/t,
szDir )
hDb
hDBlDb
bDefauIt
Boolean
This
parameter
determines
whether
the
PChar
directory
information.
RETURNS: DBIResult
The function DbiGetDirectoty
retrieves either the default directory or
the current directory, depending on the setting in the bDefault
parameter.
DbiGetDriverDesc
( szDriverType,
pcfrvType )
szDriverType
PChar
N-vType
PDRWpe
RETURNS: DBIResult
The function DbiGetDriverDesc retrieves information about a database
driver and fills the DRVType structure.
DbiGetErrorContext (econtext,
econtext
INTl6
szcontext )
The
context
type.
PChar
32
RETURNS: DBIResult
DbiGetErrorContext gives the application the ability to get more information regarding a BDE error. The DBIResult is translated to its string
equivalent and passed as a parameter to this function. The function
returns specific extended information with regard to the error string
passed.
DbiGetErrorEntry ( uEntty, pulNativeError,
uEntry
UlNTlb
pulNativeError
pUINT32
szError )
szError
PChar
receive
RETURNS: DBIResult
Boolean
Not
pDBlErrlnfo
currently
used.
RETURNS: DBIResuit
DBlResult
szError
PChar
RETURNS: DBIResult
g$kT
;,v+a..$*..-sF$ i
5:
Part Ill-The
.
Well-Rounded Application
DbiGetExactRecordCount
( hcursor, piRecCount
hCursor
hDBlCur
piRecCount
pUINT32
RETURNS:
DBIResult
pDest, 66lank )
hCut-sor
hDBlCur
iField
UINTl6
pRecBuf
Byte
pDest
Byte
bBlank
pBOOL
RETURNS:
DBIResult
hDBlCur
pfldDesc
pFLDDesc
RETURNS:
DBIResult
( szDriverType,
szDriverType
PChar
szTableType
PChar
szFieldType
PChar
PfldTyw
pFLDType
RETURNS:
DBIResult
323
iFi/terSeqNo,
cursor
pFilterinfo
hCursor
hDBlCur
The
hFilter
hDBlFilter
iFilterld
UINTI6
The
filter
identification
iFilterSeqNo
UINTI6
The
filter
sequence
pFilterinfo
pFlLTERlnfo
handle.
number.
number.
FILTERlnfo.
RETURNS: DBIResult
DbiGetFilterInfo
DbiGetlndexDesc
( Kursor, i/ndexSegNo,
pidxDesc )
hCursor
hDBlCur
The
cursor
handle.
ilndexSeqNo
UINTl6
The
subscript
that
indicates
the
position
pl DXDesc
RETURNS: DBIResult
The function DbiGetIndexDesc
index.
DbiGetlndexDescs
( Kursor, pidxDesc )
hCursor
hDBlCur
The
cursor
handle.
pidxDesc
plDXDesc
RETURNS : DBIResult
The function DbiGetIndexDescs retrieves the properties for all indexes
associated with the cursor. The user must supply a structure sufficiently
large to contain all of the index information.
DbiGetlndexForField
pidxflesc )
hCursor
hDBlCur
The
iFld
UINTl6
bProdTagOnly
Boolean
pidxDesc
plDXDesc
production
handle.
tags
are
searched.
Port /l/--The
Well-Rounded Application
RETURNS: DBIResult
( Kursor,
szlndexkme, szTagName,
hCursor
hDBlCur
szlndexName
PChar
SzTagName
Pchar
(DBASE/FoxPro
only)
ilndexld,
The
index
tag
name.
ilndexld
UINTI 6
pilndexSeqNo
pUINTl6
number.
RETURNS: DBIResult
szDriverType
PChar
szlndexType
PChar
pidxType
pIDXType
RETURNS: DBIResult
szDriver
PChar
pObjName
PChar
pLdName
PChar
RETURNS: DBIResult
( hCursor, pLdObj )
hDBlCur
pVOlD
325
RETURNS: DBIResult
DbiGetLdObj
cursor.
DbiGetLinkStatus
( Kursor,
phCursorMstr,
hCursor
hDBlCur
phCursorMstr
phDBlCur
phCursorDet,
phCursorSib )
phCursorDet
phDBlCur
phCursorSib
phDBlCur
applicable.
RETURNS: DBIResult
The function DbiGetLinkStatus returns the master and detail cursors of
the specified linked cursor.
DbiGetNetUserName ( szNetUserName )
szNetUserName
PChar
RETURNS : DBIResult
DbiGetNerUserName retur-ns the users network login name. User
names are available for all networks supported by Microsoft Windows.
DbiGetNextRecord
( Kursor,
hCursor
hDBlCur
eLock
DBILockType
pRecBuf
Byte
precProps
pRECProps
RETURNS: DBIResult
The function DbiGetNextRecord retrieves the next sequential row from
the specified cursor.
326
_ ._
( pfmtNumber
pfmtNumber
pFMTNumber
RETURNS: DBIResult
( eObjType, szObjName,
phObj )
eObiType
DBIOBJType
szObjName
PChar
phObj
phDBlObj
RETURNS: DBIResult
hObj
hDBlObj
eObjType
DBlOBJType
phObj
phDBlObj
RETURNS: DBIResult
DbiGetObjFromObj
hCursor
hDBlCur
eLock
DBlLockType
pRecBuf
Byte
precProps
pRECProps
RETURNS : DBIResult
hObj
hDBlObj
iProp
UINT32
pPropValue
pVOlD
327
piLen )
property
to
retrieve.
iMaxLen
piLen
UINTl6
pUlNT
I6
buffer.
length.
RETURNS: DBIResult
The function DbiGetProp retrieves the properties of an object.
DbiGetRecord
hCursor
hDBlCur
eLock
DBILockType
pRecBuf
Byte
precProps
pRECProps
data.
Pointer to a structure of type RECProps.
RETURNS: DBIResult
DbiGetRecord retrieves the current record from the specified cursor.
DbiGetRecordCount
( hCursor, piRecCount
hCursor
hDBlCur
piRecCount
pUINT32
RETURNS : DBIResul t
The function DbiGetRecordCount returns the approximate number of
records in the specified cursor. The number r-eturned is affected by the
filters and ranges that are in effect at the time of the function call.
DbiGetRecordForKey
pRec6uf)
( hcursor, bDirectKey,
hCursor
hDBlCur
bDirectKey
Boolean
buffer.
UINTI6
iLen
UINTl6
PKey
Byte
keys.
pRecBuf
Byte
record
is
returned.
RETURNS: DBIResult
DbiGetRecordForKey retrieves a record from the specified cursor that
matches the key value specified.
DbiGetRelativeRecord
( hcursor, iPosOfFet,
hCursor
hDBlCur
iPosOffset
INT32
eLock
DBlLockType
pRecBuf
Byte
precProps
pRECProps
data.
RETURNS: DBIResult
DbiGetRelativeRecord
positions the record pointer to the record relative
to the current position of the cursor.
DbiGetRintDesc
( Kursor, iRintSeqNo,
printDesc )
hCursor
hDBlCur
iRintSeqNo
UINTl6
The
referential
integrity
sequence
number.
printDesc
pRJNTDesc
RETURNS: DBIResult
The function DbiGetRintDesc retrieves the referential integrity
descriptor that matches the referential integrity sequence number and
the cursor.
Appendix A--BIDE
..._._
DbiGetSeqNo
329
( hcursor, piSeqNo )
hCursor
hDBlCur
piSeqNo
pUINT32
record.
RETURNS: DBIResult
The function DbiGetSeqNo retrieves the sequence number of the current record in the table.
DbiGetSesln
fo ( psesln fo )
pSESlnfo
pseslnfo
RETURNS: DBIResult
DbiGetSesInfo
DbiGetSysConfig
( psysconfig )
pSYSConfig
psysconfig
RETURNS: DBIResult
The function DbiGetSysConfig retrieves system configuration information for the BDE.
DbiGetSyslnfo
( psysln fo )
psyslnfo
pSYSlnf0
RETURNS: DBIResult
DbiGetSysInfo
DbiGetSysVersion
psysversion
RETURNS: DBIResult
DbiGetSysVersion returns the system version information, including the
BDE version number, date, and time.
DbiGetTableOpenCount
piOpenCount )
( hDb, szTableName,
SzDriverType,
hDb
hDBlDb
szTableName
PChar
szDriverType
PChar
piOpenCount
pUlNT
I6
RETURNS: DBIResult
The function DbiGetTableOpenCount returns a count of the total number of cursors that have been opened on the specified table.
DbiGetTableTypeDesc
( szDriverType,
szTableType, ptb/Type )
szDriverType
PChar
szTableType
PChar
ptblType
pTBLType
RETURNS: DBIResult
(pfmtTime
pFMTTime
)
Pointer to a structure of type FMTTime.
RETURNS: DBIResult
hDb
hDBlDb
hXact
hDBlXact
The
pxlnfo
pXlnf0
transaction
handle.
RETURNS: DBIResult
hCursor
hDBlCur
iValSeqNo
UINTl6
pVCHKDesc
33 1
RETURNS: DBIResult
DbiGetVchkDesc retrieves the validity check specified by the sequence
number and cursor.
Dbifnit ( pEnv )
pEnv
pDBlEnv
RETURNS: DBIResult
DbiInit
DbilnitRecord
( Kursor, pRec6uf)
hCursor
hDBlCur
pRecBuf
Byte
RETURNS: DBIResult
DbiInitRecord initializes a record buffer, an operation required before a
record can be inserted.
DbifnsertRecord
hCursor
hDBlCur
eLock
DBlLockType
pRecBuf
Byte
RETURNS: DBIResult
The function DbiInsertRecord
the cursor.
DbilsRecordLocked
( Kursor, pbLocked )
hCursor
hDBlCur
pbLocked
pBOOL
is locked.
RETURNS: DBIResult
DbiIsRecordLocked
Well-Rounded Application
DbilsTtbleLocked
hCursor
hDBlCur
epdxLock
DBILockType
piLocks
pUlNTl6
RETURNS: DBIResult
( hcursor, bShared )
hCursor
hDBlCur
bShared
pBoolean
RETURNS: DBIResult
( hMstrCursor,
hDet/Cursor,
iLnkFields, piMstrfields,
hMstrCursor
hDBlCur
hDetlCursor
hDBlCur
iLnkFields
UINTl6
piMstrFields
pUlNTl6
piDetlFields
pUINTl6
RETURNS: DBIResult
DbiLinkDetail sets the link between two cursors. The detail cursor will
only display records that have matching values in the master cursor.
DbiLinkDetailToExp
hCursorMstr
( KursorMstr,
KursorDetl,
iKeyLen, szMstrExp )
hDBlCur
hDBiCur
table.
hCursorDetl
UINTI6
szMstrExp
PChar
333
RETURNS : DBIResult
DbiLinkDetailToExp
expression.
DbiLoadDriver
( szfhiver Type )
szDriverType
PChar
RETURNS: DBIResult
DbiLoadDriver loads the specified driver.
DbiMokePermanent
hCursor
hDBlCur
szName
PChar
bOverWrite
Boolean
RETURNS: DBIResult
DbiMakePermanent
hDBlCur
pRecBuf
Byte
bFreeLock
Boolean
record.
RETURNS: DBIResult
The function DbiModifyRecord updates the current record with the
data contained in the record buffer.
DbiNativeToAnsi
pLdObj
( pldObj,
pAnsiStr
PChar
data.
t$?--~
&
kr,?
us!.
:*::
I ;.
PChar
iLen
UINTl6
pbDataLoss
pBoolean
RETURNS:
DBIResult
hCursor
hDBlCur
pRecBuf
pBYTE
iField
UINTI6
eOpenMode
DBlOpenMode
RETURNS:
DBIResult
( hCfg, eOpenMode,
hDBlCfg
eConfigMode,
szCfgPath,
eOpenMode
DBlOpenMode
eConfigMode
CFGMode
The
configuration
cfgpersistent
szCfgPath
PChar
IS
mode.
Only
supported.
phCur
RETURNS:
phDBlCur
Pointer
to a cursor handle.
DBIResult
PChar
file.
szDbType
PChar
eOpenMode
DBlOpenMode
eShareMode
DBlShareMode
szPassword
Pchar
iOptFlds
UINTI6
The
pOptFldDesc
pFLDDesc
number
of
optional
parameters.
Byte
phDb
phDBlDb
RETURNS : DBIResult
DbiOpenDatabase opens a database in the current session.
DbiOpenDatabaseList
phCur
( phCur )
phDBlCur
RETURNS: DBIResult
The function DbiOpenDatabaseList
returns a cursor on a list of accessible databases found in the configuration file.
DbiOpenDriverList
phCur
( phCur )
phDBlCur
RETURNS: DBIResult
DbiOpenDriverList returns a list of drivers available to the application.
DbiOpenFamilyList
phFm/Cur )
hDb
hDBlDb
szTableName
PChar
szDriverType
PChar
phFmlCur
phDBlCur
RETURNS: DBIResult
36 n
( hD6, szTab/eName,
szDriverType,
bPhyTypes,
hDb
hDBlDb
szTableName
PChar
szDriverType
PChar
bPhyTypes
Boolean
phCur
returned.
phDBlCur
RETURNS: DBIResult
( szDriverType,
szTb/Type, phCur )
szDriverType
PChar
szTblType
PChar
phCur
phDBlCur
RETURNS: DBIResult
DbiOpenFieldTypesList
creates a table containing the legal field types
for a given table or driver type.
DbiOpenFieldXlt ( szSrcDriverType,
szSrcLangDrq pfldSrc,
pszDestTab/eType, SzDstLangDrv, pfldDest, pbDataLoss, phXIt )
PChar
szSrcLangDrv
PChar
pfldSrc
pFLDDesc
pszDestTableType
PChar
The destination
szDstLangDrv
PChar
szSrcDriverType
table type.
destination.
pfldDest
pFLDDesc
pbDataLoss
pBoolean
phDBlXlt
337
RETURNS: DBIResult
The function DbiOpenFieldXlt
builds a field translation object used to
translate a logical or physical field type into any other compatible type.
DbiOpenFileList
hDb
hDBlDb
szWild
PChar
phCur
phDBlCur
RETURNS: DBIResult
DbiOpenFileList creates a table containing a list of files contained
within a database.
DbiOpenFunctionArgList
( hDb, szFuncName,
uoverioad,
phCur )
hDb
hDBlDb
szFuncName
PChar
uOverload
UINT16
The
phCur
phDBlCur
overload
number.
RETURNS: DBIResult
DbiOpenFunctionArgList returns 21 cursor containing the aI-guments to
the specified function.
DbiOpenFunctionList
hDb
hDBlDb
eoptBits
DBlFUNCOpts
(InterBase
functions.
phCur
phDBlCur
RETURNS: DBIResult
DbiOpenFunctionList retrieves a description of a data source function.
DbiOpenlndex
( hCursor, szlndexName,
ilndexld )
hCursor
hDBlCur
szlndexName
PChar
Part
/l/-The
Well-Rounded
ifndex\C
Application
-_
UINTI6
RETURNS : DBIResult
The tinction DbiOpenIndex opens the specified index for the table
associared with the cursor.
DbiOpenlndexList
hDb
hDBlDb
szTableName
PChar
szDriverType
PChar
pt?CL!r
phDBlCur
RETURNS:
DBIResult
(szDriverType, phCur )
szDrwerType
PChar
phCur
phDBlCur
RETURNS:
DBIResult
DbiOpenIndexTypesList
creates a cursor containing all supported index
r)pes for the driver provided.
DbiOpenLockList (hcursor,
bA//Users, bAIILockTypes,
phLocks
hCursor
hDBlCur
bAllUsers
Boolean
bAllLockTypes
Boolean
phLocks
RETURNS:
phDBlCur
DBIResult
( hDb, szTab/eName,
hDBlDb
szDriverType, phChkCur )
The database handle
PChar
szDriverType
PChar
phChkCur
phDBlCur
339
RETURNS: DBIResult
DbiOpenRintList
creates a cursor containing the referential integrity
links and their descriptions for the specified cursor.
DbiOpenSecurityList
( hDb, szTableName,
szDriverType,
phSecCur )
hDb
hDBlDb
szTableName
PChar
szDriverType
PChar
phSecCur
phDBlCur
RETURNS: DBIResult
DbiOpenSecurityList creates a cursor containing the record-level security information about the specified table.
DbiOpenSPList
( hDb, b&ended,
hDb
hDBlDb
bExtended
Boolean
Not
bSystem
Boolean
szQual
PChar
Must be NIL
phCur
phDBlCur
used.
RETURNS : DBIResult
The function DbiOpenSPList creates a cursor containing information
about a stored procedure associated with the specified database.
DbiOpenSPParamList
phCur )
( hDb, szSP#ame,
bPhyTypes, uOverload,
hDb
hDBlDb
szSPName
PChar
The
bPhyTypes
Boolean
procedure
name.
UINTI 6
The
phDBlCur
overload
number.
340
RETURNS: DBIResult
The function DbiOpenSPParamList
creates a cursor that lists the parameters for a specified stored procedure.
DbiOpenTable( hDb, szTab/eName, szDriverType, szlndexhlame,
szlndexTagName,ilndexld, eOpenMode,
eShareMode,exItMode,
bUniDirectional, pOptParams, phcursor ,I
hDb
hDBlDb
szTableName
PChar
szDriverType
PChar
szlndexName
PChar
szlndexTagName
PChar
ilndexld
UINTl6
eOpenMode
DBlOpenMode
eShareMode
DBlShareMode
exItMode
XLTMode
bUniDirectional
Boolean
pOptParams
Byte
Not
phCursor
phDBlCur
currently
used.
RETURNS: DBIResult
DbiOpenTable
that table.
hDBlDb
bExtended
Boolean
bSystem
Boolean
szWild
PChar
phCur
phDBlCur
RETURNS: DBIResult
DbiOpenTableList
creates a cursor containing information about all of
the tables accessible to the application.
( szDriverType,
phCur )
szDriverType
PChar
phCur
phDBlCur
RETURNS:
34 1
DBIResult
( hUsers )
phDBlCur
RETURNS:
DBIResult
hDb
hDBlDb
szTableName
PChar
szDriverType
PChar
phChkCur
phDBlCur
DbiPackTable
szDriverType, bRegen/dxs )
hDb
hDBlDb
hCursor
hDBlCut
szTableName
PChar
szDriverType
PChar
bRegenldxs
BOOL
Determines
whether
or
not
out-of-date
RETURNS : DBIResult
DbiPackTabte performs a pack operation on the specified table to optimize use of space.
@jY;
y;?
_
*- *
I.
9..
g.,
1:
342 n
DbiPutSIob
hCursor
hDBlCur
pRecBuf
Byte
iField
UINTI6
UINT32
iOffSet
iLen
UINT32
Byte
pSrc
RETURNS: DBIResult
DbiPutBlob
DbiPutField
hCursor
hDBlCur
iField
UINT16
pRecBuf
We
pSrc
Byte
RETURNS: DBIResult
DbiPutField writes data to a specified field in the supplied record
buffer.
DbiQAlloc
( hDb, eQryLang,
phStmt )
hDb
hDBlDb
eQ+-ang
DBlQtyLang
phStmt
phDBlStmt
Pointer
RETURNS: DBIResult
DbiQAlloc
hDBlStmt
The
statement
handle.
phCur
phDBlCur
343
RETURNS: DBIResult
The function DbiQExec executes a previously prepared query associated
with the specified statement handle. If a result set is generated, the cursor handle to that result set is returned.
DbiQExecDirect
hDb
hDBlDb
eQ+ng
DBlQtyLang
szQuery
PChar
phCur
phDBlCur
RETURNS: DBIResult
DbiQExecDirect executes a SQL or QBE query and returns a cursor handle for the result set.
DbiQfIxecProcDirect
pRecf3ufi phCur )
hDb
hDBlDb
szProc
PChar
The
uParamDescs
UINTI6
Number
paParamDescs
pSPParamDesc
Array
pRecBuf
Byte
phCur
phDBlCur
stored
of
of
procedure
parameter
parameter
name.
descriptors.
descriptors.
RETURNS: DBIResult
DbiQExecProcDirect executes a stored procedure and returns that cursor handle to the result set.
DbiQFree
( phStmt )
phStmt
phDBlStmt
RETURNS : DBIResult
The function DbiQFree releases the resources associated with a previously prepared query statement.
( hStmt, phCur )
hStmt
hDBlStmt
The
statement
handle.
phCur
phDBlCur
RETURNS: DBIResult
The function DbiQGetBaseDescs returns information regarding the original database, table, and field names of the columns that make up a
result set.
DbiQInstantiateAnswer
( hStmt, hCursor, szAnswerName,
szAnswerType, bOverWrite, phDstCursor )
hDBlStmt
The
hCursor
hDBlCur
szAnswerName
PChar
szAnswerType
PChar
bOverWrite
Boolean
phDstCursor
phDBlCur
hStmt
statement
handle.
RETURNS: DBIResult
DbiQInstantiateAnswer creates a permanent table from the cursor
specified.
DbiQPrepare
( hStmt, szQuery )
hStmt
hDBlStmt
The
statement
handle.
szQuery
PChar
The
correctly
formatted
query.
RETURNS: DBIResult
DbiQPrepare prepares a SQL or QBE query for execution. It returns a
handle to the prepared statement.
DbiQPrepareProc
( hDb, szf rot, uParamDescs,
pRecBuf, phStmt )
paParamDescs,
hDb
hDBlDb
szProc
PChar
The
stored
uParamDescs
UINTI6
The
number
paParamDescs
pSPParamDesc
procedure
of
name.
parameter
descriptors.
pRecBuf
Byte
phDBlStmt
RETURNS:
returned
statement
handle.
DBIResult
DbiQPrepareProc
procedure.
DbiQSetParams
The
345
hStmt
hDBlStmt
The
statement
handle
uFldDescs
UINTI6
paFldDescs
pFLDDesc
pRecBuf
Byte
RETURNS:
DBIResult
DbiQSetParams
pared query.
DbiQSetProcParams
( hStmt, uParamDescs,
lvithin a pre-
paParamDescs,
pRecBuf)
hStmt
hDBlStmt
The
statement
handle.
uParamDescs
UINTl6
The
number
paParamDescs
pSPParamDesc
pRecBuf
Byte
of
parameter
descriptors
descriptors.
RETURNS:
DBLResdt
Pointer
( hcursor,
iRecords, pBuf)
hCursor
hDBlCur
iRecords
pUINT32
pBuf
Byte
IS
returned
through
this
parameter.
Pointer
the records.
RETURNS:
DBIResult
p
1 346 n
a*-._i &I, *,*/_^^iF
,
_
szDriverType,
hDb
hDBlDb
hCursor
hDBlCur
szTableName
PChar
szDriverType
PChar
szlndexName
PChar
szlndexTagName
PChar
ilndexld
UINTl6
The
index
number.
RETURNS: DBIResult
DbiRegenIndex
regenerates the specified index, ensuring that all current records are in the index and in the proper order.
DbiRegenlndexes
( hCursor )
hCursor
hDBlCur
RETURNS: DBIResult
The function DbiRegenIndexes
indexes for the given cursor.
DbiRegisterCallf3ack
( hcursor,
PCbwi pm )
hCursor
hDBlCur
ecbType
CBType
iClientData
UINT32
iCbBufLen
UINTI6
pCbBuf
pVOl D
pfCb
pfDBlCallBack
data is to be returned.
Pointer to the desired callback function.
RETURNS: DBIResult
DbiRegisterCallBack
application.
( hDb, szTableName,
szDriverType
hDb
hDBlDb
szTableName
PChar
szDriverType
PChar
RETURNS:
347
DBIResult
( hcorsor, bAll )
hCursor
hDBlCur
bAll
Boolean
RETURNS : DBIResult
( Kursor, bA/l,
eLockType )
hCursor
hDBlCur
bAll
Boolean
eLockType
RETURNS:
DBILockType
by eLockType
DBIResult
DbiRelTableLock releases either the specified lock type or all locks for
the specified table.
DbiRenameTable
( hDb, szOldName,
szDriverType,
szNewName
hDb
hDBlDb
szOldName
PChar
szDriverType
PChar
szNewName
PChar
RETURNS:
DBIResult
( Kursor )
hDBlCur
RETURNS: DBIResult
( Kursor )
hDBlCur
RETURNS: DBIResult
( hSes )
hDBlSes
RETURNS: DBIResult
The function DbiSetCurrSession sets the current session for the application to the specified session.
DbiSetDateFormat
pfmtDate
( pfmthte )
pFMTDate
RETURNS: DBIResult
( hDb, szDir )
hDb
hDBlDb
szDir
PChar
RETURNS: DBIResult
349
hCur
hDBlCur
iFields
UINTI6
pFldDesc
pFLDDesc
RETURNS: DBIResult
The function DbiSetFieldMap
DbiSetLockRetry
( iWait )
iWait
INTl6
RETURNS: DBIResult
The function DbiSet1.ockRetr-y sets the record and table lock retry for
the current session.
DbiSetNumberFormat
pfmtNumber
( pfmtNum&er
pFMTNumber
RETURNS: DBIResult
DbiSetNumberFormat
DbiSetPrivateDir
szDir
( szDir )
The full path name of the private
PChar
directory.
RETURNS: DBIResult
DbiSetPrivateDir sets the private directory for the current session.
DbiSetProp
hObj
hDBlObl
)
The object handle to a system, client,
session, driver, database. cursor, or
statement
object.
iProp
UINT32
iPropValue
UINT32
RETURNS : DBIResult
Ill-The Well-Rounded
Application
hDBlCur
bKeyltself
Boolean
iFields I
UINTI6
iLen I
UlNTl6
key.
WY 1
Byte
bKey I lncl
Boolean
iFields
UINTI6
composite
iLen
UINTl6
key.
WY2
Byte
bKey2lncl
Boolean
RETURNS: DBIResult
The function DbiSetRange limits the result set to a set bounded by the
beginning and ending key values.
DbiSetTimeFormat ( pfmtTime )
pfmtTime
pFMTTime
RETURNS: DBIResult
hDBlCur
RETURNS: DBIResult
DbiSetToBegin sets the cursor to the crack before the first record.
DbiSetToBookMark
( hCor, pBookMark )
hCur
hDBlCur
pBookMark
Byte
RETURNS: DBIResult
( hDest, hSrc )
hDest
hDBlCur
hSrc
hDBlCur
RETURNS: DBIResult
hDBlCur
RETURNS: DBIResult
DbiSetToEnd positions the cursor at the crack following the last record
in the cursor.
DbiSetToKey ( hcursor, eSearchCond,
bDirectKey,
hCursor
hDBlCur
eSearchCond
DBlSearchCond
bDirectKey
Boolean
iFields
UlNTl6
buffer.
iLen
UINTI6
key.
We
RETURNS: DBIResult
DbiSetToKey
provided.
DbiSetToRecordNo
( Kursor,
iRecNo )
hCursor
hDBlCur
iRecNo
UINT32
RETURNS: DBIResult
DbiSetToRecordNo positions the cursor on the specified record number.
DbiSetToSeqNo
( Kursor,
iSeqN0 )
hCursor
hDBlCur
iSeclNo
UINT32
RETURNS: DBIResult
The function DbiSetToSeqNo positions the cursor on the specified
sequence number. Paradox only.
DbiSortTable ( hDb, szTab/eName, szDriverType, hSrcCur,
szSortedName, phSortedCur, hDstCur, isortfields, piFie/dNum,
pbCase/nsens, pSortOrder,
plRecsSort )
ppfSortFn,
bRemoveDups,
hDuplicatesCur,
hDb
hDBlDb
szTabieName
PChar
szDriverType
Pchar
The driver
hSrcCur
hDBlCur
type.
Pchar
phSortedCur
phDBlCur
hDstCur
hDBlCur
cursor.
to
UINTI6
353
..~. 1
piFieldNum
pUlNTl6
pbcaselnsens
pBOOL
pSortOrder
pSORTOrder
ppfSortFn
pfSORTCompFn
each field.
Pointer to an array of pointers to
client-supplied
bRemoveDups
destination
hDBlCur
p1RecsSor-t
pUINT32
functions.
Boolean
hDuplicatesCur
compare
table.
number
of
records
Output-the
records
actual
to
sort
in the source
number
of
sorted.
RETURNS: DBIResult
The function DbiSortTable sorts an opened or closed table to itself or to
a destination table. Parameters control removal of duplicates, case sensitivity, special sort functions, and the number of records to be sorted.
DbiStartSession
szName
PChar
The session
phSes
phDBISes
name.
pNetDir
PChar
handle.
RETURNS: DBIResult
DbiSrartSession
DbiSwitchTolndex
bCurrRec )
phcursor
szlndexName,
phDBlCur
szTagName,
ilndexfd,
szlndexName
PChar
szTagName
PChar
ilndexld
UINTI 6
bCurrRec
Boolean
i-,, -,.
$2.
$f;
yy
:$;, .I
354
RETURNS: DBIResult
DbiSwitchToIndex
cursor.
DbiTimeDecode
timeT
TIME
The
piHour
pUINTl6
encoded
time.
pUlNT
I6
pUlNT
I6
RETURNS: DBIResult
DbiTimeDecode decodes a TIME object into its component parts.
DbiTimeEncode
ptimeT)
iHour
UINTI6
The
hour
iMin
UINTl6
The
minute
iMilSec
UINTI6
The
millisecond
ptimeT
pTlME
component.
component.
component.
trme.
RETURNS: DBIResult
The function DbiTimeEncode
object of type TIME.
DbiTimeStampDecode
tsTS
TIMESTAMP
The
pdateD
pDBlDATE
ptimeT
pTlME
the
encoded
DATETIME
encoded
encoded
TIME
DBIDATE
timestamp.
component.
component.
RETURNS : DBIResult
The function DbiTimeStampDecode extracts separate
DBIDATE and TIME components from TIMESTAMP
encoded
355
dateD
DBIDATE
The
encoded
date.
timeT
TIME
The
encoded
time.
ptsTS
pTIMESTAMP
TIMESTAMP
RETURNS: DBIResult
The function DbiTimeStampEncode encodes separate DBIDATE and
TIME into a variable of type TIMESTAMI?
DbiTranslateField
hXlt
hDBlXlt
pSrc
Byte
pDest
Byte
field
RETURNS : DBIResult
DbiTranslateField
DbiTranslateRecordStructure
( szSrcDriverType, iF/ds, pfldssrc,
szDstDriverType,
szlanglkiver, pfldskt, Kreatable )
SzSrcDriverType
PChar
iFIds
UINTl6
pfldsSrc
pFLDDesc
SzDstDriverType
PChar
The destlnatlon
driver type.
szLangDriver
PChar
pfldsDst
pFLDDesc
bcreatable
Boolean
type.
fields.
If True. map to treatable
fields only.
RETURNS: DBIResult
DbiTranslateRecordStructure translates the source drivers fields to
equivalent fields of the destination driver.
DbiTruncateBIob
hCursor
hDBlCur
pRecBuf
Byte
356 n
***e*i _ .i
iField
UINTI6
iLen
UINT32
the
record
structure.
RETURNS: DBIResult
The function DbiTruncateBlob
truncates the length of the specified
BLOB field. This can also be used to delete the contents of a BLOB
field.
DbiUndeleteRecord
( Kursor )
hCursor
hDBlCur
cursor handle.
RETURNS: DBIResult
DbiUndeleteRecord
DbiUnlinkDetail
hDetlCursor
( hDetlCursor )
hDBlCur
RETURNS: DBIResult
The function DbiUnlinkDetail
detail cursor set.
DbiValidateProp
hObj
hDBlObj
iProp
UINT32
bsetting
Boolean
When
the
False,
DbiValidateProp
property.
RETURNS: DBIResult
DbiValidateProp validates a property for a specified object handle
DbiVerifiField
hCursor
hDBlCur
iField
UINTl6
pSrc
Byte
IS
35
., i,
:
pbBlank
pBOOL
j,!
;
RETURNS: DBIResult
DbiVerifyField verifies that the data specified in the parameter pSrc is a -;:
valid data type for the field specified by the iField parameter. It also
:Y
ensures that all validity checks specified for the field are satisfied.
DbiWriteBlock
( Kursor, piRecords,
pBuf)
hCursor
hDBlCur
piRecords
pUINT32
Byte
pBuf
to the number of
written.
RETURNS: DBIResult
The function DbiWriteBlock writes a block of records to the table referenced by the cursor.
,, 2
$i.,Z