Listing 5-1: The SQL that produced the sample relational report shown in Figure 5-3 SELECTCompany.CompanyID,Company.CompanyName,Employee.Lastname,Employee.Firstname,Employee.SalaryFROM C
Trang 1A relational database consists of one or more tables containing data, where the data in one table may relate to data in one or more of the other tables Tables are related by sharing key values between their rows, and these related tables are joined together in SELECT statements
to produce relational result sets
A database containing two tables, Company and Employee, for example, may look somethinglike what is shown in Figure 5-1
Figure 5-1: An example of two related tables.
For now, don’t worry about the notation being used — that is all explained in Chapter 8 — just
look at how the data itself is related
The data contained in these two tables may look something like what is shown in Figure 5-2
As you can see from the matching values of CompanyID between the two tables, someemployees are related to one company, and some to another Visualizing these rows asslightly separated into their own related groups, as shown in Figure 5-3, sometimes helps
Trang 2Figure 5-2: Sample data contained in the two related tables.
Figure 5-3: Visualizing related data separated into groups.
Trang 3Okay, now to take a look at how this visualization works in the real world Most likely, you’veseen reports that look something like Figure 5-4:
Figure 5-4: A sample report of related data.
Notice how the report in Figure 5-4 repeats the company data for each employee? We askedthe database server to produce this report with the simple piece of SQL code in Listing 5-1
Listing 5-1: The SQL that produced the sample relational report shown
in Figure 5-3
SELECTCompany.CompanyID,Company.CompanyName,Employee.Lastname,Employee.Firstname,Employee.SalaryFROM
Company,EmployeeWHERE
Company.CompanyID = Employee.CompanyIDORDER BY
Company.CompanyName,Employee.Lastname,Employee.Firstname
In plain English, this SQL code is asking the database server to do the following:
“Look in both the Company and Employee tables, and for every row that matches by the value
in each table’s CompanyID column, return the values stored in the Company table’s CompanyID
Trang 4and CompanyName columns and the values stored in the Employee table’s Lastname,Firstname, and Salary columns, sorted by CompanyName, Lastname, and Firstname.”
We cover the different types of relational joins and the syntax used to create them in Chapter 9,but for now, you probably get the basic idea that we “joined” these two tables together byusing the following clause:
WHERE Company.CompanyID = Employee.CompanyIDThis was the condition used to give us only those rows from both tables where the CompanyIDvalues in the Company table equaled the CompanyID values in the Employee table This syntax
is simple to understand, and for this reason, most people learn SQL by writing their joins inthe WHERE clauses, but you can (and should) use a more modern and flexible syntax, known asSQL-92, that to produce relational queries Listing 5-2 shows how our earlier SQL statementlooks expressed in SQL-92 syntax
Listing 5-2: Listing 5-1 expressed in SQL-92 syntax
SELECTCompany.CompanyID,Company.CompanyName,Employee.Lastname,Employee.Firstname,Employee.Salary
FROM Company INNER JOIN Employee
ON Company.CompanyID = Employee.CompanyID
ORDER BYCompany.CompanyName,Employee.Lastname,Employee.Firstname
The code changes very little and is still understandable — especially after you learn the
differ-ences between the different types of joins (inner joins, left outer joins, and so on) But you gain
quite a lot by using SQL-92 syntax, such as the capability to easily change join types and thecapability to easily and accurately describe even the most complicated multitable result sets
You should get in the habit of writing your statements that contain relational joins by usingSQL-92 syntax if your database supports it
Now that you have a basic overview of what the “relational” part of a relational database isand the basics of how to perform a query against it, you can move on to an overview of theSQL language itself
An Overview of Structured Query Language (SQL)
SQL, or Structured Query Language, is a common language for querying and manipulating
data As have all standards, SQL has gone through a number of revisions to take advantage ofnew functionality and to incorporate better methods Some database server products sup-port the very latest standards, but most don’t The standard that we explain here is the SQL-
92 Standard, which is currently in use — at least to some extent — by the majority of databaseproducts on the market as of this writing
Trang 5If your database product doesn’t support the SQL-92 join syntax (most notably, Oracle 8idoesn’t), it instead supports some manner of encoding special character sequences into theWHEREclause to create different types of joins If so, substitute the appropriate WHERE clausefor the type of join that you want to perform.
SELECT Statements
You use SELECT statements to query the database and return a set of results Listing 5-3, forexample, returns the CompanyID, CompanyName, Address, City, State, and ZipCode columns
of all rows in the Company table:
Listing 5-3: A simple SELECT statement
SELECTCompanyID,CompanyName,Address,City,State,ZipCodeFROMCompany
Figure 5-5 shows what that result set from this listing looks like
Figure 5-5: The result set from Listing 5-3.
The most important step toward writing perfect SELECT statements is visualizing the true
nature of the result set that you want returned to you We cover visualization in more depth
in Chapter 9, but for now, you should start with our first and most basic premise, as follows:
All SQL query results are in the form of a single table of rows and columns.
This statement holds true for every query regardless of whether the query concerns a singletable or multiple joined tables, whether or not grouping or aggregate functions are used, oranything else Even if the result set consists of a single value, it is still a table with a single col-umn containing one row of data
Trang 6This reality is in sharp contrast to the way that many people visualize a relational result set.
Many envision their result set structured like the output from a report writer such as CrystalReports, with headings, subtotals, grand totals, and so on It is this incorrect visualizationthat often leads developers down the wrong road toward incorrectly written SQL statementsthat attempt to group and aggregate values in ways that approximate the printed report butthat the database cannot process Perhaps you’ve at some point received an error back fromyour database similar to the following:
Column ‘Company.CompanyID’ is invalid in the select list because it isnot contained in either an aggregate function or the GROUP BY clause
If so, it is because you wrote an SQL statement that attempted to produce an impossibleresult set We discuss what is and isn’t possible with SELECT statements that use GROUP BY,HAVING, DISTINCT, and aggregate functions later on, in Chapter 9, but for now, we concen-trate on writing a few more basic SELECT statements
Listing 5-3 seems simple enough, but what if you want only those columns for the one pany with a CompanyID of 10? For that, you need to add a WHERE clause to the SQL state-ment, as shown in Listing 5-4
com-Listing 5-4: Adding a WHERE clause to the SELECT statement returns
only those rows that satisfy its criterion
SELECTCompanyID,CompanyName,Address,City,State,ZipCodeFROMCompany
WHERE CompanyID = 10
This WHERE clause filters out all rows in the Company table except the one with a CompanyIDthat equals 10 Figure 5-6 shows the result
Figure 5-6: The result set from Listing 5-4.
If you use a string value as a criterion for a WHERE clause, you must surround the string withsingle quotes, as shown in Listing 5-5
Trang 7Listing 5-5: Using a string as a criterion in the WHERE clause
SELECTCompanyID,CompanyName,Address,City,State,ZipCodeFROMCompany
WHERE State = ‘GA’
Listing 5-5 returns a result set containing all rows of the Company table with a State value of
‘GA’, as shown in Figure 5-7
Figure 5-7: The result set from Listing 5-5.
Those single quotes around the State value aren’t just for show; if we didn’t use them inListing 5-5, the database server would throw an error
If you want to use more than one criterion in your WHERE clause, you simply separate eachcriterion with AND, as shown in Listing 5-6
Listing 5-6: Using multiple criteria in the WHERE clause
SELECTCompanyID,CompanyName,Address,City,State,ZipCodeFROMCompany
WHERE State = ‘GA’ AND CompanyName LIKE ‘A%’
Trang 8Listing 5-6 returns only those rows in the Company table with a State value of Georgia (‘GA’)and a company name that begins with A, as shown in Figure 5-8.
Figure 5-8: The result set from Listing 5-6.
As you can probably imagine, if you want to return rows where the State is ‘GA’ or the
CompanyName began with A, you would replace the AND in the WHERE clause with an OR
Now that you’re getting the hang of it, try sorting the result set from this statement in zip-code order You can do so by adding an ORDER BY clause, as shown in Listing 5-7
Listing 5-7: Adding an ORDER BY clause to sort the result set
SELECTCompanyID,CompanyName,Address,City,State,ZipCodeFROMCompanyWHEREState = ‘GA’ ANDCompanyName LIKE ‘A%’
ORDER BY ZipCode
Sorting is simply a matter of adding an ORDER BY clause and specifying the column on which
to perform the sort The results are as shown in Figure 5-9
Figure 5-9: The result set from Listing 5-7.
If you want to sort in descending zip-code order, you simply add the DESC qualifier, as shown
in Listing 5-8
Trang 9Listing 5-8: Specifying a sort in descending order
SELECTCompanyID,CompanyName,Address,City,State,ZipCodeFROMCompanyWHEREState = ‘GA’ ANDCompanyName LIKE ‘A%’
ORDER BYZipCode DESC
The result of executing Listing 5-8 is as shown in Figure 5-10
Figure 5-10: The result set from Listing 5-8.
If the DESC qualifier isn’t specified, your database server assumes that you want to sort inascending order The qualifier for sorting in ascending order is ASC, so add it to your statement ifyou would be more comfortable explicitly specifying it Personally, we explicitly specify all ourqualifiers because doing so gets us in the good habit of thinking more precisely about our code
If you want to sort by more than one column, just add the additional sorting columns rated by commas, as shown in Listing 5-9
sepa-Listing 5-9: Specifying multiple sort orders
SELECTCompanyID,CompanyName,Address,City,State,ZipCodeFROMCompanyWHEREState = ‘GA’ AND
Trang 10CompanyName LIKE ‘A%’
ORDER BY City DESC, ZipCode DESC
Listing 5-9 first sorts the retrieved rows in descending city order and then within each city indescending zip-code order, as shown in Figure 5-11
Figure 5-11: The result set from Listing 5-9.
Now you get into a very useful technique known as aliasing, which enables you to assign a pseudonym (literally, a “false name”) to table and column names Sometimes, aliasing is a sim-
ple convenience for making cryptic column names more readable in the client application(that is, ColdFusion), but at other times, aliasing becomes absolutely necessary for using aquery’s result set in the client application, as you see in Listings 5-10 and 5-11, and inChapters 9 and 11
Start with Listing 5-10, which assigns pseudonyms to the CompanyID, CompanyName, andZipCodecolumns
Listing 5-10: Using pseudonyms to provide aliases for columns
SELECTCompanyID AS CompNum,
CompanyName AS Name,
Address,City,State,ZipCode AS Zip
FROMCompanyWHEREState = ‘GA’ ANDCompanyName LIKE ‘A%’
ORDER BYZipCode DESC
After the result set from Listing 5-10 is returned to ColdFusion, the column names are nolonger referred to by their original column names but rather by the pseudonyms assigned tothem, as shown in Figure 5-12
Trang 11Figure 5-12: The result set from Listing 5-10.
In other words, if you execute Listing 5-10 via a CFQUERY call that names the resultOutputListing510, the CFOUTPUT that you use to display the result set looks something likethat in Listing 5-11 Revisit Chapter 2 if you need a refresher on using CFQUERY
Listing 5-11: Referring to aliased columns in a CFOUTPUT call
Listing 5-12: Abbreviating Listing 5-2 by assigning pseudonyms
SELECT
c.CompanyID, c.CompanyName, e.Lastname, e.Firstname, e.Salary
FROMCompany AS c INNER JOIN Employee AS e
ON c.CompanyID = e.CompanyIDORDER BY
c.CompanyName, e.Lastname, e.Firstname
By the way, the use of AS is not absolutely necessary, and in fact, you most often do not see
ASused in aliasing table names There is no special reason for this convention; this seems to
Trang 12be the most popular convention Listing 5-13 shows the more common method for assigningaliases to table names.
Listing 5-13: Short-form aliasing
SELECTc.CompanyID,c.CompanyName,e.Lastname,e.Firstname,e.SalaryFROM
Company c INNER JOIN Employee e
ON c.CompanyID = e.CompanyIDORDER BY
c.CompanyName,e.Lastname,e.Firstname
Listing 5-13 shows the way that you typically see table names aliased As a side note, youdon’t need to use AS in aliasing column names either; eliminating them entirely is perfectlyacceptable The common practice, however, is to use AS in aliasing column names but not use
ASin aliasing tables names
Go ahead and practice a good number of different SELECT statements on your own databases
to become comfortable with them and make sure that you practice at least a few relationalSELECTstatements by using SQL-92 syntax and pseudonyms so that you are prepared for themore complicated SELECT statements that you encounter later on in your development career
After you’re finished experimenting, and after you’ve finished working through chapters 6through 8, make sure that you thoroughly read Chapter 9 so that you can gain a full under-standing of the intricacies of complicated SELECT statements that contain GROUP BY, HAVING,and DISTINCT clauses, aggregate functions, and relational joins
The following sections show you how to manipulate the data in your database by usingINSERT, UPDATE, and DELETE statements
INSERT Statements
Data is inserted into tables by using INSERT statements Listing 5-14 shows a typical INSERTstatement
Listing 5-14: A simple INSERT statement
INSERT INTO Company (CompanyName,Address,City,
Continued
Trang 13Listing 5-14 (continued)
State,ZipCode)
Listing 5-14 inserts a single row into the Company table with the values shown in the listing
As you can see, the values must be specified in the same order that the columns are listed;this is the only way that your database knows which values go into which columns
The word INTO is optional and has no effect on the execution of your code It simply makesyour code more naturally English-like and, therefore, easier to read INSERT Company is iden-tical to INSERT INTO Company as far as SQL is concerned
Now insert another row — this time into the Employee table — by using the code in Listing 5-15
Listing 5-15: Inserting a row into the Employee table
INSERT INTO Employee (CompanyID,
SSN,Firstname,Lastname,Salary,DateOfBirth)
VALUES (10,
‘123-45-6789’,
‘Stan’,
‘Cox’,11325.00,
‘04/01/1967’
)
The INSERT statement in Listing 5-15 is very similar to that in Listing 5-14, except that thistime, two of the values that you are passing are numeric, and as you can see, they are not sur-rounded by single quotes If you surround a numeric value with single quotes, the databasethrows an error because the data types between the value and the column designed to con-tain it do not match
You may also notice that one of the values is a date Dates are often difficult and error-prone
to manipulate in a database, mainly because every database treats date processing differently
Trang 14In these listings, we are assuming that you are using Microsoft SQL Server 2000, which takes
a string value in the form of a date and automatically converts or “casts” the string value into
an actual date value Oracle does the same thing, but only if Oracle’s default date formatmatches the format of the value Microsoft Access requires you to either delimit such a stringvalue by using pound symbols (separate from those used by ColdFusion) or to supply a trueODBC date value by using ColdFusion’s CreateODBCDateTime() function
To execute this INSERT from a ColdFusion CFQUERY call, you simply wrap the CFQUERY callaround the INSERT statement, as shown in Listing 5-16
Listing 5-16: Executing an INSERT statement from a CFQUERY call
<cfquery name=”InsertEmployee” datasource=”Chapter5”>
INSERT INTO Employee (CompanyID,
SSN,Firstname,Lastname,Salary,DateOfBirth)
VALUES (10,
‘123-45-6789’,
‘Stan’,
‘Cox’,11325.00,
Listing 5-17: Executing an INSERT statement from a CFQUERY call by
using ColdFusion variables
<cfquery name=”InsertEmployee” datasource=”Chapter5”>
INSERT INTO Employee (CompanyID,
SSN,Firstname,Lastname,Salary,DateOfBirth
Continued
Trang 15Listing 5-17 (continued)
)VALUES (
Always use Trim() on string values to remove trailing spaces and Val() on numeric values
to ensure that data of compatible types are received by the database You also want to setthe MAXLENGTH attributes of string form fields to the lengths of the table columns for whichthey are destined These techniques go a long way toward preventing the majority ofdatabase errors, so use them everywhere that your Web application touches your database
So far in these listings, you’ve seen how to insert individual rows into tables by using valuesacquired outside the database — such as form variables entered by the user and client vari-ables managed by ColdFusion Although this use of the INSERT statement is by far the mostcommon, you can also insert data into a table by using data from one or more other tables
In Listing 5-18, you insert some of the columns from some of the rows in the Employee tableinto a table named Temp Figure 5-13 shows the structures of the three tables
The INSERT statement in Listing 5-18 inserts into the Temp table all the employees with lastnames beginning with M or other letters later in the alphabet
Listing 5-18: Using INSERT to populate one table
from the data in another
INSERT INTO Temp (SSN,
Firstname,Lastname)
SELECTSSN,Firstname,LastnameFROM
EmployeeWHERE
Lastname >= ‘M’
If you execute Listing 5-18 against an empty Temp table, Figure 5-14 shows what that Temptable contains as a result
Tip
Trang 16Figure 5-13: The Company, Employee, and Temp tables.
Figure 5-14: Result of executing Listing 5-18 on an empty Temp table.
Think of the SELECT clause of Listing 5-18 as if it were a direct replacement for the VALUESportion of a typical INSERT statement Instead of the single row of values specified by theVALUESclause, the SELECT clause specifies an entire set of rows, resulting in that set of rows
being inserted into the destination table
Remember what we said about all SELECT statements resulting in a single table — even tional result sets? You can use this to your advantage in populating a table, as we do in thepreceding example To insert all employees of companies in Georgia into the Temp table, forexample, you would execute Listing 5-19
Trang 17rela-Listing 5-19: Populating a table from a relational query
INSERT INTO Temp (SSN,
Firstname,Lastname,CompanyName)
SELECTe.SSN,e.Firstname,e.Lastname,c.CompanyNameFROM
Company c INNER JOIN Employee e
ON c.CompanyID = e.CompanyIDWHERE
c.State = ‘GA’
If you execute this listing against an empty Temp table, Figure 5-15 shows what the Temptable contains as a result
Figure 5-15: Result of executing Listing 5-19 on an empty Temp table.
After you understand how to insert data into tables by using SQL, you can move on to ing data that is already in the database, as the following section describes
updat-UPDATE Statements
Data already present in tables is modified by using UPDATE statements The basic form of anUPDATEstatement sets one or more columns of a set of rows in a single table to specified val-ues The set of rows is defined by the criteria specified in the UPDATE statement’s WHEREclause You must be very careful to specify exactly which row or rows you want to update, oryou can permanently modify the wrong data If you forget to include the WHERE clause, youupdate all the rows in the table to the new values, so be careful
Trang 18Now try executing a simple UPDATE statement The Stan Cox Society has a CompanyID (itsunique key) value of 10 If the Stan Cox Society moves from its current address to its newaddress in Chicago, you update its row in the Company table by using the UPDATE statement
in Listing 5-20
Listing 5-20: A simple UPDATE statement
UPDATECompanySET
Address = ‘One WebGenius Drive’,City = ‘Chicago’,
State = ‘IL’,ZipCode = ‘60035’
WHERECompanyID = 10
The UPDATE clause specifies the one table being updated; the SET clause pairs specified ues with the columns for which they are destined; and the WHERE clause specifies the set ofrows to be updated Remember that even a single row in a result set is indeed an entire table
val-of rows and columns; that particular table just so happens to contain only one row
After you embed Listing 5-20 in a CFQUERY call, you need to “scrub” the values supplied to thetable columns to prevent throwing database errors, as shown in Listing 5-21
Listing 5-21: Embedding an UPDATE statement in a CFQUERY call
<cfquery name=”UpdateCompany” datasource=”Chapter5”>
UPDATECompanySET
Address = ‘#Trim(Form.Address)#’,City = ‘#Trim(Form.City)#’,State = ‘#Trim(Form.State)#’,ZipCode = ‘#Trim(Form.ZipCode)#’
WHERECompanyID = #Val(Form.CompanyID)#
Trang 19the criteria specified in the DELETE statement’s WHERE clause As in using the UPDATE ment, you must be very careful to specify exactly which row or rows that you want to delete,
state-or you can permanently delete the wrong data If you fstate-orget to include the WHERE clause, youdelete all the rows in the table, so be careful
Use Listing 5-22 to delete an employee who’s been playing Quake during business hours
Listing 5-22: A basic DELETE statement
DELETE FROMEmployeeWHERE
SSN = ‘123-45-6789’
As you can see, basic DELETEs are the simplest SQL statements, because you specify only thetable and the criteria that determine the set of data to be deleted
Notice how we said “the set of data to be deleted.” Visualizing the DELETE statement
operat-ing over a predefined set of data, rather than as some algorithm movoperat-ing through a table,deleting individual rows that match the criteria of a WHERE clause or a relational join, can helpyou understand just how DELETE works
In Listing 5-22, the WHERE clause is what predefines the set of data to be deleted In effect, theWHEREclause produces its own “intermediate” result set, which is a single table of rows andcolumns, as are all result sets (even if that table contains only a single row) This intermedi-ate result set is that on which the DELETE statement operates, resulting in that set of data get-ting deleted from the Employee table
This example may seem a roundabout way of explaining the DELETE statement, but as yousee in the following example, such visualization becomes absolutely necessary if your DELETEstatement becomes more complex
So now is a good time to state the following as our second premise:
Each step in processing an SQL statement results in a set of data — either an intermediate result set or the final result set — that is in the form of a single table containing rows and columns.
Now you get to see how such visualizing helps you handle more complex delete statements.Similar to the INSERT statement’s INTO, the FROM that lies between DELETE and the table name
is superfluous; it is there only to make the DELETE statement more naturally English-like.DELETEstatements become more complicated if the set to be deleted is determined by a rela-tional result set (Although this feature is currently supported only by SQL Server and SybaseAdaptive Server, it serves as a good teaching example for understanding how SQL works withdata in sets rather than in individual rows.) If you want to delete the employees of all companies
in Florida, for example, you can replace the WHERE clause with a relational join, as in Listing 5-23
Trang 20Listing 5-23: Deleting from a table based on a relational result set
DELETE FROMEmployeeFROM Employee e INNER JOIN Company c
ON e.CompanyID = c.CompanyIDWHERE
c.State = ‘FL’
This example is a little confusing at first, until you break it down and look at each part separately
The first thing that you do is remove the superfluous FROM word You have two FROM clauses
in this relational DELETE statement, so you keep only the one that is absolutely needed inListing 5-24
Listing 5-24: Understanding a relational DELETE
DELETEEmployee
FROM Employee e INNER JOIN Company c
ON e.CompanyID = c.CompanyIDWHERE
In a basic DELETE statement, you delete directly from the table that stores the data; a simpleWHEREclause is all that you need to specify the result set to be deleted, and, as in all resultsets, the WHERE clause defines a single table of rows
But if you’re dealing with a relational delete, the result set to be deleted spans more than theone table that stores the data to be deleted Fear not, however, because a simple JOINbetween the tables again reduces the FROM clause’s result set back down to a single table: therelational result set shown in Figure 5-16
The result set in Figure 5-16 is then filtered by the WHERE clause to let through only thoserows where the State is Florida The final result of the FROM clause — the result set that isused to determine which rows in the Employee table are actually deleted — is then fed to theDELETEclause as the set of rows to be deleted, thereby resulting in the result set shown inFigure 5-17
Trang 21Figure 5-16: Visualizing the relational result set.
Figure 5-17: Visualizing the relational result set after the WHERE clause has been applied.
But wait a minute! You’re not deleting from this relational result set; you’re deleting from theEmployee table itself How does this relational result set tell the DELETE clause which rows todelete in the Employee table?
SQL looks at the constituent parts that make up that relational result set, and it sees thatthose rows came from both the Employee and Company tables It then finds the actual rowsfrom the Employee table that made up the relational result set and uses those as the set ofrows to be deleted from the Employee table Figure 5-18 should help you conceptualize thisprocess
Pretty cool, huh? This same technique can be used (in databases that support this nique) to specify multiple rows to be modified by using an UPDATE statement
tech-If your database server doesn’t support updating and deleting by using references to
rela-tional result sets, you can use the subquery technique shown in Listing 5-25 instead A
sub-query is a sub-query within another sub-query
Trang 22Figure 5-18: Visualizing how SQL finds the rows to be deleted
from the Employee table
Listing 5-25: Deleting by using a subquery
DELETE FROMEmployeeWHERE CompanyID IN (SELECT
CompanyIDFROM
CompanyWHEREState = ‘FL’
)
As in the days of Algebra I in junior high school, you can resolve Listing 5-25 from the inside
out Look at the SELECT statement inside the parentheses: This statement is your subquery,
so named because it is a query “beneath” the main query statement The intermediate resultset from this subquery looks as shown in Figure 5-19
Trang 23Figure 5-19: The intermediate result set of the subquery in Listing 5-25.
This is the set or “list” of CompanyIDs that is used as the argument of the DELETE clause
“Delete Employee rows with CompanyID values that appear in this list” is basically whatListing 5-25 is telling your database server to do Figure 5-20 may help you conceptualize thisprocess
Figure 5-20: The concept of deleting via a subquery.
The subquery technique in Listing 5-25 may seem simpler code than its equivalent relationaljoin, but it is much slower on large data sets So if your database supports deleting by usingreferences to relational result sets, use the relational result set technique instead
Trang 24under-Remember that not what you know isn’t what’s important but how you visualize To “know”
is to memorize how to do a specific thing, but if you visualize, you gain an understanding ofbehavior patterns, and from these patterns, you can not only interpolate and extrapolate atremendous amount of practical knowledge, but you can also learn specific techniques faster
as well
Trang 26Using ColdFusion Forms
ColdFusion forms extend normal HTML forms by adding a side validation framework that is easy to implement No fasterway is available to develop intelligent forms for your ColdFusionapplication
client-Implementing Client-Side Validation
One of the biggest oversights that ColdFusion developers make is not
to validate data before it leaves the form If you are expecting theuser to enter a date, make sure that he enters a date If a value can’t
be left blank, make sure that the user enters a value before ting the form Thankfully, ColdFusion’s built-in client-side validation iseasy to implement
submit-This section is an introduction to using client-side validation withCFFORM For more in-depth coverage, see Chapter 20 of this book,which also covers other forms of data validation with ColdFusion
By simply using CFFORM in place of the normal HTML FORM tag, youtell ColdFusion to create the entire validation framework for you
Then, by using ColdFusion form control tags in place of normal HTMLform control tags, you tell ColdFusion to use that framework Take,for example, the following code:
<cfform action=”dummy.cfm” method=”POST”
Using graphical userinterface controls
Trang 27<! if (!_CF_hasValue(_CF_this.Nickname, “TEXT” )) {
if (!_CF_onError(_CF_this, _CF_this.Nickname,_CF_this.Nickname.value, “You must enter your Extreme Snowboardingnickname.”)) {
return false;
}}
if (!_CF_hasValue(_CF_this.Age, “TEXT” )) {
if (!_CF_onError(_CF_this, _CF_this.Age, _CF_this.Age.value,
“Bummer, dude! Your age makes you ineligible for ExtremeSnowboarding!”)) {
return false;
}}
if ((!_CF_checkrange(_CF_this.Age.value, 7, 70))) {
if (!_CF_onError(_CF_this, _CF_this.Age, _CF_this.Age.value,
“Bummer, dude! Your age makes you ineligible for ExtremeSnowboarding!”)) {
return false;
}}
if (!_CF_checkinteger(_CF_this.Age.value)) {
if (!_CF_onError(_CF_this, _CF_this.Age, _CF_this.Age.value,
“Bummer, dude! Your age makes you ineligible for ExtremeSnowboarding!”)) {
return false;
}}return true;
}// >
Trang 28<input type=”submit”>
</FORM>
After this form is submitted, the JavaScript function _CF_checktestForm() is called beforeanything else can happen Each ColdFusion form control is tested according to its validationattributes The Age field, for example, is tested to determine whether it contains a value,whether that value is an integer, and whether that integer is between 7 and 70 If any of thesetests fail, the validation message Bummer, dude! Your age makes you ineligible forExtreme Snowboarding!appears in a JavaScript alert box via the _CF_onError() functioninside the external JavaScript file cfform.js, and the form is not submitted If all form datapasses the validation tests, the form is submitted to its action template
Client-side validation like this will not work if the user turns off JavaScript in his browser
Using CFINPUT
The example in the preceding section uses CFINPUT tags, so you already know how to usethis tag Following are the data formats that CFINPUT can validate:
✦ date: A date in the standard U.S format mm/dd/yyyy.
✦ eurodate: A date in the standard European format dd/mm/yyyy.
✦ time: A valid time in the format hh:mm:ss.
✦ float: A floating-point number.
✦ integer: A whole number.
✦ telephone: A valid U.S telephone number in the format ###-###-####, where the
hyphens can be replaced with spaces, and the first digits of the first and second groupcannot be zero
✦ zipcode: A valid U.S zip code, either five or nine digits in the format #####-####,
where the hyphen can be replaced with a space
✦ creditcard: Strips all blanks and dashes from the card number and uses the standard
mod 10 algorithm to validate the card number
✦ social_security_number: A valid social security number in the format ###-##-####,
where the hyphens can be replaced by spaces
✦ regular_expression: If the other possible values don’t fit your need, you can give
your own validation pattern in the form of a JavaScript regular expression
Where you cannot use CFINPUT
Just as CFINPUT does, the INPUT tag has a Type attribute that tells which type of control theINPUTtag represents The types of INPUT controls described in the following sections cannot
be duplicated by CFINPUT:
Hidden form fields
You see in Chapter 2 that you can use hidden form fields to pass data to the server withoutthe user needing to enter or even see the data A hidden form field looks as follows:
<cfoutput>
Caution
Trang 29<input type=”hidden” name=”CompanyID” value=”#URL.CompanyID#”>
</cfoutput>
This control passes the URL.CompanyID to the action page without the user’s intervention
Submit buttons
TheSubmit button is a simple version of the INPUT tag because it doesn’t need a Name
attribute It looks as follows:
<input type=”submit” value=”Update Database”>
No form variable is created for this Submit button, because we don’t give the INPUT tag aname The Value attribute contains the text that appears on the button
Reset buttons
The Reset button is another type of INPUT tag Its code looks much like that of the Submit
but-ton, as follows:
<input type=”reset” value=”Reset Form”>
After the user clicks a Reset button, all the controls on the form are reset to the state theywere in at the time that the user first came to the form
Action buttons
Submit and Reset buttons aren’t the only kinds of button that you can have on your form Amore generalized type of button that can call a JavaScript function is the action button, and its
code looks as follows:
<input type=”button” value=”Call a Function” onclick=”doSomething();”>The Type is now button, but the value serves the same purpose that it does for the Submitand Reset buttons The onClick attribute tells the browser which JavaScript function to callafter the user clicks the button
File browsers
Some forms can upload a file to the server This is where the file-browser control comes in.
The following code creates such a control:
<input type=”file” name=”FileToUpload”>
The file INPUT type creates a file-browser control similar to the one shown in Figure 6-1.After the user clicks the Browse button, he gets the Choose File dialog that he can use toselect any file on his machine He chooses a file, and after the form is submitted, the file issent to the server for processing
A snafu occurs if you use the file INPUT type — one that catches even experiencedColdFusion developers off guard Whenever you’re using the file INPUT type, always add the
following attribute and value to your FORM or CFFORM tag:
<cfform enctype=”multipart/form-data”>
If you forget this attribute, the upload does not work.
To process the submitted file on the server, use the CFFILE Upload action For information
on CFFILE, see Chapter 60
Trang 30Figure 6-1: A file-browser control created by using INPUT TYPE=”File”.
Text areas
A text area is a large, multiline input field in which the user can enter several lines of text To
put a text area on a form, use the TEXTAREA tag as follows:
<textarea name=”Comments” rows=”5” cols=”40”></textarea>
As do the INPUT and CFINPUT tags, TEXTAREA has a Name attribute, which becomes the name
of the form variable on the action page TEXTAREA, however, does not have a Size orMaxlengthattribute — instead, Rows and Cols attributes specify the size of the text area
TEXTAREAhas no Value attribute Everything between the <textarea> and </textarea>
tags is the initial value of the text area as the form first appears, and the content of the textarea is its value after the form is submitted
Using CFSELECT
CFINPUTis useful for information such as a person’s name or age, where the user can enterfree-form data, but if the user has only a few choices, such as which company he works for or inwhich state he lives, giving the user a list of options, from which he can choose only one, makesmore sense You use CFSELECT to give the user a select menu containing a list of options
Explicitly specifying options
The following code produces a select menu with three options:
<cfselect name=”Rating” size=”1” required=”yes”>
<option value=”3” selected>Good</option>
is added to that option Form.Rating contains 3, 2, or 1, corresponding to the option thatthe user chooses, and this is the value that is contained in Form.Rating after it is received
by the action template
Trang 31Populating options from a CFQUERY call
Say that you want a list from which a user may choose the company for which an employeeworks You could manually code all the option tags, one for each company, but that’s ahassle — and what happens if you add or delete a company? A better option is to populate theCFSELECT’s options from a table of companies Listing 6-1 shows what you do in Chapter 2
Listing 6-1: Populating a select menu’s options from a CFQUERY
<cfquery name=”GetCompanies”
datasource=”#Request.MainDSN#”>
SELECT
CompanyID, CompanyName
FROMCompanyORDER BYCompanyName
message=”Please select a Company.”></cfselect>
The CFQUERY call returns the companies from the database as a result set namedGetCompanies, and CFSELECT uses the GetCompanies result set to generate one OPTION tagfor every record in the result set, using the columns specified in the Value and Displayattributes to populate the value and display text of each OPTION tag
Using check boxes and radio buttons
Check boxes are simple on/off toggle controls that can exist either on their own or in a group
of multiple check boxes, any number of which can be selected at once Radio buttons always
exist in groups of two or more, and only one button in a group can be selected at any giventime
Creating single check boxes
The following CFINPUT tag creates a check-box control in the user’s browser:
<cfinput type=”Checkbox” name=”IsVolunteer” value=”1”>
The CFINPUT creates a check box named IsVolunteer The value of the check box is 1, so ifthe box is selected as the user submits the form, the value of Form.IsVolunteer is 1 If thecheck box isn’t selected as the user submits the form, no form variable corresponding to thecheckbox is created If the action template expects this variable to exist, but it doesn’t, itthrows an error
Trang 32To prevent this problem, add a CFPARAM tag to the action template, as follows:
<cfparam name=”Form.IsVolunteer” default=”0”>
Now, as the form is submitted, Form.IsVolunteer contains 1 if the IsVolunteer check box
is selected and zero if the check box isn’t selected
Creating groups of check boxes
Check boxes work well by themselves for simple on/off values, where each check box sents a single attribute of some thing Suppose, however, that you have a group of relatedcheck boxes, as shown in Figure 6-2
repre-Figure 6-2: A group of related check boxes.
In this case, you can’t use a single check box by itself to represent the multiple choices made
by the user Each choice is an option that can co-exist with the other options, so this group ofcheck boxes really represents a single multivalued attribute of some thing rather than a col-lection of individual attributes
Check boxes belonging to the same group are implemented by assigning them all the samenameattribute, as follows:
<cfinput type=”checkbox” name=”IsMemberOf” value=”UnitedWay”> UnitedWay<br>
<cfinput type=”checkbox” name=”IsMemberOf” value=”BaseballTeam”>
<cfparam name=”Form.IsMemberOf” default=””>
But what does Form.IsMemberOf contain? With only a single check box, Form.IsVolunteercontains 1 if the check box is selected, but now that you have multiple check boxes, all of thevalues of the selected boxes are put together, with commas in between each value In otherwords, if you select United Way and Employee Wellness Committee, Form.IsMemberOf con-tains UnitedWay,Wellness
Using radio buttons
Radio buttons are a cross between a single check box and a group of check boxes On the onehand, radio buttons always occur in groups of two or more buttons with the same name, asdoes a group of multiple check boxes On the other hand, because only one button in a group
of radio buttons can be selected at any given time, a single value is submitted to the server
To make the situation even stranger, if the user chooses none of the options in a radio buttongroup, the form variable doesn’t exist at all on the action page — the same thing that happenswith check boxes
Trang 33Say that company policy changes, and now an employee may be a member of only one pany club at a time You would change the group of check boxes into a group of radio buttons,
The result of this code would look like what is shown in Figure 6-3
Figure 6-3: A group of radio buttons.
Notice that Type=”checkbox” is now Type=”radio” In addition, the first radio button is tially selected (Note that if you hadn’t made this first option initially selected, you would runthe risk of passing nothing to the action page as mentioned earlier.) Always select the firstradio button in a group of radio buttons so that the corresponding form variable alwaysexists, even if the user doesn’t make a selection
ini-Implementing Server-Side Validation
Client-side validation does not work if the user turns off JavaScript in his browser You mustaccount for this possibility by putting even more data validation in your ColdFusion code.Say, for example, that you had a field in your application where the user could enter his age,
as follows:
<cfinputtype=”text”
<cfif NOT IsNumeric(Form.Age)>
<! - CFLOCATION back to form page or display message here ->
</cfif>
IsDate()and IsNumericDate() are also useful functions for this type of validation.Other forms of server-side validation include testing for specific ranges of values, specificenumerated values, and whether a value was submitted (as opposed to an empty field or afield containing only spaces) All this testing may seem a lot of extra work, but if you don’timplement server-side validation to supplement the client-side validation, you run the realrisk of exposing your code and database to bad data, and the consequences can be extreme
Trang 34Graphical User Interface Widgets
CFINPUTand INPUT are great for basic form controls, but at times you need a special controlthat is not available as part of a standard HTML form Fortunately, ColdFusion provides youwith a few advanced graphical user-interface controls, or widgets, that extend the functional-
ity of standard forms
Figure 6-4 shows what the slider control looks like
Figure 6-4: A slider control produced
As do the other form-INPUT tags, CFSLIDER has a Name attribute that becomes the name ofthe form variable on the action page The Label appears in the slider control area; the
%value%marker shows where in the label the current value appears Range takes two bers separated by commas that represent the minimum and maximum values of the slidercontrol Scale is the interval at which the slider button stops — in this case, all the salariesmust be in multiples of 1,000
num-The tag also includes a Height and Width attribute to specify the size of the slider controland a LookAndFeel attribute, which tells the Java applet what graphical treatment to use indrawing the slider control We use the METAL look and feel here because we happen to likehow it looks; WINDOWS and MOTIF are the other two available values for LookAndFeel
The CFSLIDER tag has additional attributes See Chapter 51 for a listing of CFSLIDER’sattributes
With all the advantages posed by the CFSLIDER tag, you may be surprised to find out thatCFSLIDERisn’t very useful for creating Web applications, as the following list explains:
Trang 35✦ Typically, a slider control accompanies some immediate change in the user’s ment; for example, changing a volume slider sets the computer’s speaker volume to anew level In a Web application, however, the user must submit the form containing theslider control to the server, which then takes the slider control’s position and doessomething with it.
environ-✦ CFSLIDER is slow to load, so the user must wait quite a while for a control that wouldprobably be better served by a simple CFINPUT with range validation
✦ As do all graphical user-interface widgets, CFSLIDER requires Java on the client computer
✦ Graphical user-interface widgets work fairly well in Internet Explorer for Windows butoften create problems for Netscape and Macintosh users
CFTEXTINPUT
CFTEXTINPUTimplements a text-input field (as does CFINPUT) by using a Java applet.You have no reason whatsoever to use CFTEXTINPUT It performs the same purpose as
<cfinput type=”text”>but with an unacceptable increase in overhead The only attributes
of CFTEXTINPUT that aren’t part of CFINPUT involve font and background styling and colors,all of which can be accomplished by using style sheets If you must use CFTEXTINPUT, refer toChapter 51
CFTREE
CFTREEcreates a tree control similar to the one shown in Figure 6-5
Figure 6-5: A tree control created by
using CFTREE and CFTREEITEM
CFTREEis slightly more complicated than CFSLIDER because, in addition to configuring thetree control by using CFTREE, you must also specify the tree items by using CFTREEITEM, asshown in Listing 6-2
Trang 36Listing 6-2: The code that produced the tree control shown
<cftreeitem value=”1” display=”North America”>
<cftreeitem value=”2” display=”United States” parent=”1”>
<cftreeitem value=”3” display=”Canada” parent=”1”>
<cftreeitem value=”4” display=”Mexico” parent=”1”>
<cftreeitem value=”5” display=”Europe”>
<cftreeitem value=”6” display=”Great Britain” parent=”5”>
<cftreeitem value=”7” display=”France” parent=”5”>
<cftreeitem value=”8” display=”Germany” parent=”5”>
<cftreeitem value=”9” display=”Spain” parent=”5”>
<cftreeitem value=”10” display=”Italy” parent=”5”>
<cftreeitem value=”11” display=”Austria” parent=”5”>
</cftree>
As do all other form controls, CFTREE has a Name that becomes the name of the form variable
on the action template
Inside CFTREE are the CFTREEITEM tags that specify the items in the tree Each CFTREEITEMtag has a Value and a Display, which act just as their equivalents in the CFSELECT tag do: Ifthe user chooses North America in the tree control, as shown in Figure 6-5,
Form.EmployeeLocationcontains 1
The second CFTREEITEM tag in Listing 6-2 also has a Parent attribute Parent tells theCFTREEITEMto relate itself to the CFTREEITEM with a Value that matches Parent — in otherwords, the tree item for North America
CFTREE also has a Height, Width, and LookAndFeel as does CFSLIDER CFTREE has a ratherlarge number of attributes See Chapter 51 for complete details
Populating a CFTREE control from a query
Suppose that you add to the Employee table a ManagerID column that holds the EmployeeID
of an Employee’s manager You can SELECT the data from the Employee table in a CFQUERY,loopover the query with CFLOOP, and put out CFTREEITEM tags, but doing so is somewhattedious to program Wouldn’t just pointing CFTREE to a query result set and having CFTREE
do all the rest be better?
First, get all the employees from the database by using the following code:
<cfquery name=”GetEmployees”
datasource=”TreeTest”>
SELECT
EmployeeID AS ItemID, ManagerID AS ParentItemID, FirstName + ‘ ‘ + LastName AS Description
Trang 37FROMEmployeeORDER BYFirstName + ‘ ‘ + LastName
</cfquery>
This query has some new syntax First, notice that, instead of just selecting EmployeeID andManagerID, the query selects EmployeeID AS ItemID and ManagerID AS ParentItemID.The query aliases these columns so that, although the database sees them as EmployeeIDand ManagerID, ColdFusion sees them as ItemID and ParentItemID And instead of select-ing FirstName or LastName individually, the query selects the concatenation of FirstName+ ‘ ‘ + LastName The expression is aliased so that the employee name is available toColdFusion as the Description column of the GetEmployees result set
ColdFusion must make the data presentable to the CFTREE or it does not work correctly.CFTREErequires that data used to populate the tree control be in the same order that youused them in manually specifying each CFTREEITEM tag in the section “CFTREE,” earlier inthis chapter, and for this purpose, you use a third-party custom tag named CFX_Make_Tree,
CFGRIDcreates a grid of data, much as a spreadsheet does CFGRID can be used in a number
of different ways The simplest is where the user can do nothing other than view the data inthe grid You can also enable the user to select a cell, column, or row and pass the selectedvalues to the action page CFGRID can also enable the user to edit values in the grid controland submit the modified data to the action page
In this section, we are going to cover only data-driven calls to CFGRID, because that is by farthe most common way that CFGRID is used For information on manually defining rows and
Trang 38columns in a CFGRID control, see the discussions of CFGRIDCOLUMN and CFGRIDROW inChapter 51.
Using CFGRID to browse data
The simplest form of CFGRID is one in which the user simply browses data, as follows:
<cfquery name=”GetEmployees”
datasource=”CFMXBible”>
SELECTSSN,FirstName,LastName,SalaryFROMEmployeeORDER BY SSN
Enabling the user to select a single cell in a CFGRID control
Suppose that you change the SelectMode attribute as follows:
You rarely have any reason to use the SINGLE SelectMode
Enabling the user to select a column in a CFGRID control
Change the SelectMode again, as follows:
<cfgrid name=”EmployeeData”
height=”200”
width=”400”
Trang 39col-Form.EmployeeData.<selectedcolumnname>, with the name of the selected column ing <selectedcolumnname> The cell values are strung together in row order into a comma-delimited list.
replac-Enabling the user to select a row in a CFGRID control
Again, change the SelectMode, this time as follows:
sub-Enabling the user to edit content in a CFGRID control
Change the SelectMode one last time, as follows:
Trang 40The Caveats of Using CFFORM
With all the benefits of CFFORM that you see in this chapter, you may be surprised to knowthat you shouldn’t use CFFORM unless you really need to, for the following reasons:
✦ CFINPUT validation requires some overhead to generate the validation framework
✦ The graphical user interface controls — CFSLIDER, CFTEXTINPUT, CFTREE, andCFGRID— all require the user to have Java installed on his machine, which alsorequires overhead
✦ Sometimes you experience problems making the graphical user-interface controls work
in an environment other than Internet Explorer for Windows
Carefully consider your user base before forcing CFFORM controls on them Although thesecontrols may be fine in the controlled environment of an intranet, the general public is oftenquick to abandon you if problems rear their ugly heads
Summary
In this chapter, you learn how to use CFFORM and its many form controls to enable the user toenter data You learn how to use CFINPUT to perform client-side validation and avoid overbur-dening your Web server, and you also learn to implement server-side validation as well Youlearn how to use CFSELECT drop-down lists to restrict a user’s choices and that you must revert
to using INPUT instead of CFINPUT to create certain controls Finally, you learn how to ment the graphical user-interface widgets that ColdFusion provides to make your life easier