Hacker’s Guide to Visual FoxPro
An irreverent look at how Visual FoxPro really works. Tells you the inside scoop on every command, function, property, event and method of Visual FoxPro.
You taught me language; and my profit on ‘t
Is, I know how to curse: the red plague rid you,
For learning me your language!
—William Shakespeare, The Tempest, 1611–1612
SQL, which stands for Structured Query Language, is a
set-based language. Unlike Xbase, which cares about individual records, SQL is
interested mostly in groups of records. (The Xbase concept most similar to
SQL’s point of view is the FOR
clause.) The name “SQL” is read by
some folks as a series of letters (“ess queue ell”) and by others as
“sequel”—we use either one, depending on the phase of the moon,
though we lean more toward the latter these days.
The biggest difference between SQL and Xbase is that Xbase is procedural while SQL is not. In Xbase, you have to tell the computer exactly what to do to get from point A to point B—without step-by-step instructions, Xbase has no clue. SQL, on the other hand, lets you tell it what you want and it figures out how to give it to you. This is most apparent with SELECT-SQL, which lets you indicate what records should be placed in a result set without having to navigate the tables containing them.
In database theory, languages for manipulating databases are divided into two sets: the Data Definition Language (DDL) and the Data Manipulation Language (DML). Despite its name, SQL (like Xbase) incorporates both. The entire SQL language (defined by an ANSI standard) is quite large; FoxPro has supported a subset for many years now, with that subset growing in almost every version. (Supporting only a subset is not a unique FoxPro weakness. In fact, few vendors support the entire standard at any level, and most have proprietary, incompatible extensions as well.)
SQL first poked its way into FoxPro 2.0 with two DDL
commands (CREATE TABLE
and CREATE CURSOR
) and two DML commands (SELECT
and
INSERT
). Naturally, this wasn’t enough for FoxPro users and they immediately
started clamoring for more. Visual FoxPro finally added to the set, with the
addition of both DDL and DML commands.
The most recently added data-definition command (new in VFP
3) is ALTER TABLE
, an extremely powerful command that lets you add and remove
fields and indexes, set various properties of fields, and create persistent
relations among tables. All this in one command—no wonder its syntax diagram
occupies nearly a full page.
CREATE TABLE
has been significantly enhanced in Visual
FoxPro, too. Its syntax diagram is also pretty hefty.
VFP 3.0 also introduced two new SQL data manipulation
commands: DELETE
and UPDATE
. DELETE
is fairly similar to the Xbase version of
DELETE
, though it has a few wrinkles of its own. (See its entry in the
“Visual FoxPro Reference” for the nasty details.) UPDATE
is a lot
like Xbase’s REPLACE
, though again there are some differences, primarily in syntax.
What makes these two commands such welcome additions is that you can use
exactly the same commands on FoxPro data and on data originating from other
sources. Besides, all those folks who’ve been writing SQL code in other
languages really want to do it their way.
VFP 5 didn’t add any new SQL commands to the language, but
it seriously enhanced our old favorite, SELECT
. Queries offer the chance to lay
out joins explicitly and to use the various flavors of outer joins. VFP 5 also
gave us the ability to limit SELECT
’s result set by number or percent of
matching records, though this isn’t quite as powerful as it sounds. VFP 7 added
to SELECT
again, finally giving us the ability to make the cursors it creates
read-write (see below for an explanation of “cursors”).
In theory, you could do all your data definition and manipulation using only FoxPro’s SQL commands. Or you could ignore the SQL commands and use only the traditional Xbase commands. We know people who prefer either approach, including some who’ll zealously campaign for their chosen method.
We don’t recommend either, though, for two reasons. Some things are easier to do in Xbase while others are easier in SQL. (And some work best with a marriage of the two.) If you’re using one approach and your code starts to seem convoluted, step back and see if you can use the other. For example, if a process takes five successive complex queries, stop and think about whether you can do it by setting relations instead.
The second reason we can’t arbitrarily recommend one approach over the other is speed: Some things are faster in Xbase; others are faster in SQL. There’s really no way to tell which approach is faster without testing. Any time you can think of both procedural and SQL solutions, you should probably test both solutions to see which runs faster. Sometimes, the results will surprise you.
In addition to tables, SQL works with data in cursors. The term “cursor” is shorthand for “CURrent Set Of Records.”
In FoxPro, a cursor is similar to a table, though it has some differences. The most important difference is that cursors are temporary. When you close them, they disappear into thin air, never to be seen again—at least not until you create them again from scratch.
There are three ways to actively create cursors in Visual
FoxPro. As you’d expect, the CREATE CURSOR
command creates cursors. You list
the fields you want and their characteristics, and FoxPro does the rest,
thoughtfully leaving the new cursor open in the current work area.
SELECT
-SQL also can create cursors to hold query results.
Cursors created this way are read-only, unless you explicitly make them
read-write. (The simple approach to making cursors read-write was added in VFP
7; in older versions, there is a trick for this. USE
the cursor AGAIN
in
another work area.)
The VFP-only method for creating cursors is to open a view.
By definition, all SQL views use cursors. When you issue USE <viewname>
,
the underlying query is executed and the results put in a cursor. Views may be
updateable, depending on how they’re defined.
In addition to these three types of cursors, there’s also a
tendency in VFP to refer to any open table, cursor, or view as a cursor—hence,
functions like CURSORSETPROP()
.
Oh, why don’t you work
Like other men do?
—Anonymous, Hallelujah, I’m a Bum, c. 1907
Unlike Xbase, SQL has no concept of a current work area. Each SQL command, instead, indicates what table it’s working on. Tables are opened automatically if they’re not already open and, when appropriate, they’re secretly reopened.
Even though they don’t officially recognize work areas,
several of the SQL commands do change the current work area. Both CREATE
commands (CREATE CURSOR
and CREATE TABLE
) leave you in the previously empty
work area containing the newly created object. If you send query results to a
cursor or table, SELECT
makes the work area containing the results current.
INSERT INTO
, DELETE
and UPDATE
don’t change work areas, but
they do open the table if it’s not already open.
Consistency is contrary to nature, contrary to life.
The only completely consistent people are the dead.
—Aldous Huxley
The seven SQL commands available in Visual FoxPro have no fewer than three different ways of referring to the table (or tables) they operate on.
SELECT
and DELETE
use a FROM
clause to list tables. INSERT
uses INTO
. The others don’t have a special keyword—the table name simply
follows the command itself (as in CREATE TABLE Inconsistent
…). We can’t
blame Microsoft (or even Fox) for this one, though, because the commands do
conform in this respect to the ANSI standard for SQL. This is a good place to
point out that, like most other SQL database systems, while FoxPro’s SQL commands
are ANSI-compliant in some respects, they deviate in others. The bottom line
is, if you’re familiar with ANSI SQL, you’ll want to check the FoxPro manuals
or Help before you start coding.
Three of the four DML commands in FoxPro’s version of SQL
use a WHERE
clause to determine which records are affected. WHERE
is
essentially identical to the Xbase FOR
clause, with minor variations in string
comparisons (see SET ANSI
and SET EXACT
) and operator syntax. In DELETE
and UPDATE
,
WHERE
filters the table. In SELECT
, WHERE
both filters the data and can specify
join conditions used to combine records from multiple tables (but see
“Won’t You JOIN me?” below).
The WHERE
clause can contain any valid logical condition. To
optimize these commands, make sure tags exist for each expression involving a
field that appears in the WHERE
clause.
Unlike VFP’s other SQL commands, SELECT
can involve multiple
tables directly. (Other commands may include multiple tables through
sub-queries, but not in the listing of tables.) When there is more than one
table, you need to indicate how to match up records from the different tables.
In VFP 3 and earlier versions of FoxPro, you did this in the WHERE
clause along
with your filter conditions. Beginning in VFP 5, you can also use the ANSI JOIN
syntax to specify how to combine tables.
The advantage of the newer syntax is that it lets you
specify outer joins (where unmatched records in one or more tables are included
in the result) as well as the inner joins that WHERE
lets you include. For
details on the JOIN
syntax, see the entry for SELECT-SQL in the “Visual
FoxPro Reference.”
Set thine house in order.
—The Second Book of Kings, 20:1
Because SQL is set-based, certain concepts fundamental to
Xbase don’t have exact analogues in SQL. SQL usually doesn’t care where a
record is in the table; there’s no such thing as a SQL record number. When you
manipulate records with SQL commands, you mostly do it based on the records’ contents,
not their positions. SQL has no NEXT
or REST
clauses.
Similarly, FoxPro’s version of SQL doesn’t know from first
and last (or, as we call them in Xbase, TOP
and BOTTOM
). Before VFP 5, there
was no way to say “the first 10” or “the most recent 25.”
VFP 5 added TOP n
and TOP n PERCENT
syntax to SELECT
, but its utility is quite
limited—the clause applies to the overall query results, not to each group
within the result. When you need “the first 10” or whatever in each
group of records, you have to use a hybrid approach that combines SQL with
Xbase’s record number to produce the desired results. It requires a multi-step
process and you still need enough space to select all the matching records
first. (Tamar has published the solution to this one elsewhere, most recently
in the November ‘99 issue of FoxPro Advisor.)
Debugging SQL commands tends to be a little trickier than debugging Xbase or OOP language. That’s because a single SQL statement may be a whole page of code. Finding a syntax error in a 30-line query is like the proverbial search for a needle in a haystack.
So what do you do? The first step is to check for the obvious, especially if you’ve just been editing. Make sure every line but the last ends with a semicolon, make sure there are commas between all the fields in the field list (and not after the last field), check for typos, and so forth. If all that fails to turn up the problem, start taking the command apart: Remove and comment out (you’ll have to use some cut-and-paste here since VFP can’t handle commented lines in the middle of a command) whole clauses or parts of clauses until you figure out what the offender is. Another good approach is to avoid the problem in the first place by building up your SQL statements, especially queries, a little at a time. Then, when an error occurs, you’ll know it’s related to the last thing you added.
The other issue you can run into is performance—commands, especially queries, that don’t run fast enough. Check out “Faster than a Speeding Bullet” in “Franz and Other Lists” for overall performance tips, and look at SYS(3054)
in the Reference section for a way to find out exactly what’s bogging your query down.