Local SQL

The Borland Database Engine (BDE) enables limited access to database tables through local SQL (Structure Query Language).  Local SQL (sometimes called "client-based SQL") is a subset of ANSI-standard SQL enhanced to support Paradox and dBASE naming conventions for tables and fields (called "columns" in SQL).

Local SQL enables SQL to be used on "local" tables.  The term "local" in this context refers to any database tables that do not reside on a database server, specifically Paradox or dBASE tables.  The SQL statements are broken down into two different categories DDL (Data Definition Language) and DML (Data Manipulation Language).

This section describes naming conventions, syntax enhancements, and syntax limitations for local SQL. Also provided are syntax and usage examples of two supported catgories of SQL statements: Data Manipulation Language (DML) and Data Definition Language (DDL).

Naming Conventions
Naming conventions for tables and columns.

Data Manipulation
SQL statements in Data Manipulation Language (DML) used for selecting, inserting, updating, and deleting table data. DML statements are transformed into BDE QBE (Query By Example) syntax and then processed by the BDE query engine.

Data Definition
SQL statements in Data Definition Language (DDL) used for creating, altering, and dropping tables, and for creating and dropping indexes. The DDL transforms directly into BDESDK function calls.

For more basic examples and naming conventions, see the Borland Database Engine Online Reference.  For a complete introduction to ANSI-standard SQL, see one of the many third-party books available at your local computer book store.


Naming Conventions

ANSI-standard SQL confines each table or column name to a single word comprised of alphanumeric characters and the underscore symbol (_). Local SQL, however, is enhanced to support more comprehensive names.

Tables

Local SQL supports full file and path specifications for table names. Table names with path or file-name extensions must be enclosed in single or double quotation marks. For example,

SELECT * FROM 'PARTS.DBF'

SELECT * FROM "C:\SAMPLE\PARTS.DBF"

Local SQL also supports BDE aliases for table names. For example,

SELECT * FROM ":PDOX:TABLE1"

If you omit the file extension for a local table name, the table is assumed to be the table type specified in the Default Driver setting in the System page of the BDE Configuration Utility.

Finally, local SQL permits table names to duplicate SQL keywords as long as those table names are enclosed in single or double quotation marks. For example,

SELECT PASSID FROM "PASSWORD"

Columns

Local SQL supports Paradox and dBASE multi-word column names and column names that duplicate SQL keywords as long as those column names are

Enclosed in single or double quotation marks

Prefaced with an SQL table name or table correlation name

For example, the following column name is two words:

SELECT E."Emp Id" FROM EMPLOYEE E

In the next example, the column name duplicates the SQL DATE keyword:

SELECT DATELOG."DATE" FROM DATELOG


Data Manipulation

With some restrictions, local SQL supports the following statements for data manipulation:

SELECT, for retrieving existing data

INSERT, for adding new data to a table

UPDATE, for modifying existing data

DELETE, for removing existing data from a table

The following sections describe parameter substitution, aggregate, string, and date functions, and operators available to DML statements in local SQL.

Parameter Substitutions in DML Statements

Set (Aggregate) Functions

String Functions

Date Function

Operators

For additional illustrative examples, see:

DML Examples


SELECT

The SELECT statement is used to retrieve data from one or more tables. A SELECT that retrieves data from multiple tables is called a "join."  Local SQL supports the following form of the SELECT statement:

SELECT [DISTINCT] column_list

FROM table_reference

[WHERE search_condition]

[ORDER BY order_list]

[GROUP BY group_list]

[HAVING having_condition]

Except as noted below, all clauses are handled as in ANSI-standard SQL. Clauses in square brackets are optional.

The column_list indicates the columns from which to retrieve data. For example, the following statement retrieves data from two columns:

SELECT PART_NO, PART_NAME

FROM PARTS

Choose one of the following topics for more information on using SELECT:

FROM Clause

WHERE Clause

ORDER BY Clause

GROUP BY Clause

HAVING Clause

Heterogeneous Joins


FROM Clause

The FROM clause specifies the table or tables from which to retrieve data. table_reference can be a single table, a comma-delimited list of tables, or can be an inner or outer join as specified in the SQL-92 standard. For example, the following statement specifies a single table:

SELECT PART_NO

FROM "PARTS.DBF"

The next statement specifies a left outer join for table_reference:

SELECT * FROM PARTS LEFT OUTER JOIN INVENTORY

ON PARTS.PART_NO = INVNTORY.PART_NO


WHERE Clause

The optional WHERE clause reduces the number of rows returned by a query to those that match the criteria specified in search_condition. For example, the following statement retrieves only those rows with PART_NO greater than 543:

SELECT * FROM PARTS

WHERE PART_NO > 543

The WHERE clause can now include the IN predicate, followed by a parenthesized list of values. For example, the next statement retrieves only those rows where a part number matches an item in the IN predicate list:

SELECT * FROM PARTS

WHERE PART_NO IN (543, 544, 546, 547)

Important:	A search_condition cannot include subqueries.


ORDER BY Clause

The ORDER BY clause specifies the order of retrieved rows. For example, the following query retrieves a list of all parts listed in alphabetical order by part name:

SELECT * FROM PARTS

ORDER BY PART_NAME ASC

The next query retrieves all part information ordered in descending numeric order by part number:

SELECT * FROM PARTS

ORDER BY PART_NO DESC

Calculated fields can be ordered by correlation name or ordinal position. For example, the following query orders rows by FULL_NAME, a calculated field:

SELECT LAST_NAME || ', ' || FIRST_NAME AS FULL_NAME, PHONE,

FROM CUSTOMER

ORDER BY FULL_NAME


GROUP BY Clause

The GROUP BY clause specifies how retrieved rows are grouped for aggregate functions. In local SQL, any column names that appear in the GROUP BY clause must also appear in the SELECT clause.


HAVING Clause

The HAVING BY clause specifies conditions records must meet to be included in the return from a query. It is a conditional expression used in conjunction with the GROUP BY clause.  Groups that do not meet the expression in the HAVING clause are ommitted from the result set.


Heterogeneous Joins

Local SQL supports joins of tables in different database formats; such a join is called a "heterogeneous join."

When you perform a heterogeneous join, you may select a local alias. To select an alias, choose SQL|Select Alias. If you have not selected an alias, Local SQL will attempt to find the table in the current directory of the database which is being used. For example, the alias :WORK: might be the database handle passed into the function.

When you specify a table name after selecting a local alias:

For local tables, specify either the alias or the path.

For remote tables, specify the alias.

The following statement retrieves data from a Paradox table and a dBASE table:

SELECT DISTINCT C.CUST_NO, C.STATE, O.ORDER_NO

FROM "CUSTOMER.DB" C, "ORDER.DBF" O

WHERE C.CUST_NO = O.CUST_NO

You can also use BDE aliases in conjunction with table names.


INSERT

In local SQL, INSERT is restricted to a list of values:

INSERT INTO CUSTOMER (FIRST_NAME, LAST_NAME, PHONE)

VALUES(:fname, :lname, :phone_no)

Insertion from one table to another through a subquery is not allowed.


UPDATE

There are no restrictions on or extensions to the ANSI-standard UPDATE statement.


DELETE

There are no restrictions on or extensions to the ANSI-standard DELETE statement.


Parameter Substitutions in DML Statements

Variables or parameter markers (?) can be used in DML statements in place of values. Variables must always be preceded by a colon (:). For example:

SELECT LAST_NAME, FIRST_NAME

FROM "CUSTOMER.DB"

WHERE LAST_NAME > :var1 AND FIRST_NAME < :var2


Set (Aggregate) Functions

The following ANSI-standard SQL set (or "aggregate") functions are available to local SQL for use with data retrieval:

SUM(), for totaling all numeric values in a column

AVG(), for averaging all non-NULL numeric values in a column

MIN(), for determining the minimum value in a column

MAX(), for determining the maximum value in a column

COUNT(), for counting the number of values in a column that match specified criteria

Note:	Expressions are not allowed in set functions.


String Functions

Local SQL supports the following ANSI-standard SQL string manipulation functions for retrieval, insertion, and updating:

UPPER(), to force a string to uppercase

LOWER(), to force a string to lowercase

TRIM(), to remove repetitions of a specified character from the left, right, or both sides of a string

SUBSTRING() to create a substring from a string


Substring

SUBSTRING() takes a string and creates a substring of that string.

   SELECT SUBSTRING( CUSTNAME FROM 1 FOR 10 )  FROM CUSTOMER

This query return the first 10 characters of the CUSTNAME column.


Date Function

Local SQL supports the EXTRACT() function for isolating a single numeric field from a date/time field on retrieval using the following syntax:

EXTRACT (extract_field FROM field_name)

For example, the following statement extracts the year value from a DATE field:

SELECT EXTRACT(YEAR FROM HIRE_DATE)

FROM EMPLOYEE

You can also extract MONTH, DAY, HOUR, MINUTE, and SECOND using this function.

Note:	EXTRACT does not support the TIMEZONE_HOUR or TIMEZONE_MINUTE clauses.


Operators

Local SQL supports the following operators:

Type	Operator

Arithmetic	, , *, /

Comparison	<, >, =, <>, IS NULL

Logical	AND, OR, NOT

String concatenation	||


DML Examples

The DML syntax is limited to what QBE can execute. The following clauses are supported:

  SELECT FROM, WHERE, ORDER BY, GROUP BY, and HAVING

The following aggregates are supported:

  SUM, AVG, MIN, MAX, COUNT

The following operators are supported:

  , -, *, /, =, < >, IS NULL

UPDATE, INSERT, DELETE operations are allowed.

The following examples show DML statements used with standard databases:

Example 1: Requesting a cursor to an answer table

This function returns a cursor to an answer table generated by a SQL statement:

DBIResult rslt;
hDBICur   hCur;
{
pBYTE sqlText = "SELECT City, Street "
"FROM  'C:\\EMPL.DB' "
"WHERE Age>65"
rslt = DbiQExecDirect (hDb, langSQL, sqlText, &hCur);

}

Example 2: Renaming an answer table

The following example demonstrates how a client may rename the answer table generated by an SQL query:

hDBIStmt   hStmt;     // Statement handle
DBIResult  rslt;      // Return code.
CHAR       szNewAnsName[20] = "result.dbf";
pBYTE      sqlText = "SELECT City, Street "
"FROM  'C:\\EMPL.DB' "
"WHERE Age>65"

if( !rslt = DbiQPrepare(hDb, langSQL, sqlText, &hStmt) )
{

if( !rslt = DbiSetProp(hStmt, stmtANSNAME, (UINT32)szNewAnsName) );
rslt = DbiQExec(hStmt, &hCur);
rslt = DbiQFree (&hStmt);
}
// There is now an answer table named
// result.dbf in the BDE private directory

Example 3: UPDATE

update goods
set city = 'Santa Cruz'
where goods.city = 'Scotts Valley'

Example 4: INSERT

insert
into goods ( part_no, city )
values ( 'aa0094', 'San Jose' )

Example 5: DELETE

delete
from goods
where part_no = 'aa0093'

Example 6: SELECT used to join

The following example illustrates how the SELECT statement is supported as an equivalent to a JOIN:

select distinct p.part_no, p.quantity, g.city
from parts p, goods g
where p.part_no = g.part_no
and p.quantity > 20
order by p.quantity, g.city, p.part_no

A SELECT statement that contains a join must have a WHERE clause in which at least one field from each table is involved in an equality check.

Example 7: Sub-selects

Sub-select queries are not supported. The following example illustrates this unsupported syntax:

select p.part_no
from parts p
where p.quantity in
(select i.quantity
from inventory i
where  i.part_no = 'aa9393')

Example 8: GROUP BY

The following examples illustrate the GROUP BY clause:

select part_no, sum(quantity) as PQTY
from parts
group by part_no

select country
from customer
group by country
having sum(cust_no) >= 3

Note:	Aggregates in the SELECT clause must have GROUP BY clause if a projected field is used, as shown in the first example above.

Example 9: ORDER BY

The following example illustrates the ORDER BY with a DESCENDING clause:

select distinct customer_no
from c:\data\customer
order by customer_no descending


Data Definition

Local SQL supports data definition language (DDL) for creating, altering, and dropping tables, and for creating and dropping indexes. All other ANSI-standard SQL DDL statements are not supported. In particular, views are not supported.

Local SQL does not permit the substitution of variables for values in DDL statements.

The following DDL statements are supported:

CREATE TABLE

ALTER TABLE

DROP TABLE

CREATE INDEX

DROP INDEX

For additional illustrative examples see:

DDL Examples


CREATE TABLE

CREATE TABLE is supported with the following limitations:

Column definitions based on domains are not supported.

Constraints are limited to PRIMARY KEY for Paradox. Constraints are unsupported in dBASE.

For example, the following statement creates a Paradox table with a PRIMARY KEY constraint on the LAST_NAME and FIRST_NAME columns:

CREATE TABLE "employee.db"

(

LAST_NAME CHAR(20),

FIRST_NAME CHAR(15),

SALARY NUMERIC(10,2),

DEPT_NO SMALLINT,

PRIMARY KEY(LAST_NAME, FIRST_NAME)

)

The same statement for a dBASE table should omit the PRIMARY KEY definition:

CREATE TABLE "employee.dbf"

(

LAST_NAME CHAR(20),

FIRST_NAME CHAR(15),

SALARY NUMERIC(10,2),

DEPT_NO SMALLINT

)

Creating Paradox and dBASE Tables

You create a Paradox or dBASE table using local SQL by specifying the file extension when naming the table:

".DB" for Paradox tables

".DBF" for dBASE tables

If you omit the file extension for a local table name, the table created is the table type specified in the Default Driver setting in the System page of the BDE Configuration Utility.

Data Type Mappings for CREATE TABLE

The following table lists SQL syntax for data types used with CREATE TABLE, and describes how those types are mapped to Paradox and dBASE types by the BDE:

SQL Syntax	BDE Logical	Paradox	dBASE
SMALLINT	fldINT16	Short	Number (6,10)
INTEGER	fldINT32	Long Integer	Number (20,4)
DECIMAL(x,y)	fldBCD	BCD	N/A
NUMERIC(x,y)	fldFLOAT	Number	Number (x,y)
FLOAT(x,y)	fldFLOAT	Number	Float (x,y)
CHARACTER(n)	fldZSTRING	Alpha	Character
VARCHAR(n)	fldZSTRING	Alpha	Character
DATE	fldDATE	Date	Date
BOOLEAN	fldBOOL	Logical	Logical
BLOB(n,1)	fldstMEMO	Memo	Memo
BLOB(n,2)	fldstBINARY	Binary	Binary
BLOB(n,3)	fldstFMTMEMO	Formatted memo	N/A
BLOB(n,4)	fldstOLEOBJ	OLE	OLE
BLOB(n,5)	fldstGRAPHIC	Graphic	N/A
TIME	fldTIME	Time	N/A
TIMESTAMP	fldTIMESTAMP	Timestamp	N/A
MONEY	fldFLOAT, fldstMONEY	Money	Number (20,4)
AUTOINC	fldINT32, fldstAUTOINC	Autoincrement	N/A
BYTES(n)	fldBYTES(n)	Bytes	N/A
x = precision (default: specific to driver)

y = scale (default: 0)

n = length in bytes (default: 0)

1-5 = BLOB subtype (default: 1)


ALTER TABLE

Local SQL supports the following subset of the ANSI-standard ALTER TABLE statement. You can add new columns to an existing table using this ALTER TABLE syntax:

ALTER TABLE table ADD column_name data_type [, ADD column_name data_type ...]

For example, the following statement adds a column to a dBASE table:

ALTER TABLE "employee.dbf" ADD BUILDING_NO SMALLINT

You can delete existing columns from a table using the following ALTER TABLE syntax:

ALTER TABLE table DROP column_name [, DROP column_name ...]

For example, the next statement drops two columns from a Paradox table:

ALTER TABLE "employee.db" DROP LAST_NAME, DROP FIRST_NAME

ADD and DROP operations can be combined in a single statement. For example, the following statement drops two columns and adds one:

ALTER TABLE "employee.dbf" DROP LAST_NAME, DROP FIRST_NAME, ADD FULL_NAME CHAR[30]


DROP TABLE

DROP TABLE deletes a Paradox or dBASE table. For example, the following statement drops a Paradox table:

DROP TABLE "employee.db"


CREATE INDEX

CREATE INDEX enables users to create indexes on tables using the following syntax:

CREATE INDEX index_name ON table_name (column [, column ...])

Using CREATE INDEX is the only way to create indexes for dBASE tables. For example, the following statement creates an index on a dBASE table:

CREATE INDEX NAMEX ON "employee.dbf" (LAST_NAME)

Paradox users can create only secondary indexes with CREATE INDEX. Primary Paradox indexes can be created only by specifying a PRIMARY KEY constraint when creating a new table with CREATE TABLE.


DROP INDEX

Local SQL provides the following variation of the ANSI-standard DROP INDEX statement for deleting an index. It is modified to support dBASE and Paradox file names.

DROP INDEX table_name.index_name | PRIMARY

The PRIMARY keyword is used to delete a primary Paradox index. For example, the following statement drops the primary index on EMPLOYEE.DB:

DROP INDEX "employee.db".PRIMARY

To drop any dBASE index, or to drop secondary Paradox indexes, provide the index name. For example, the next statement drops a secondary index on a Paradox table:

DROP INDEX "employee.db".NAMEX


DDL Examples

The following examples show the use of DDL statements with standard databases.

Example 1a: DDL (DROP TABLE)

When the table name contains a "." character, enclose the name in quotation marks:

drop table "c:\data\customer.db"

Example 1b: DDL (DROP TABLE)

No quotation marks are used if the table name does not contain the "." character:

drop table clients

Example 2: DDL (CREATE INDEX)

create index part on parts (part_no)

Paradox: Paradox primary indexes can be created only when creating the table. Secondary indexes are created as case insensitive and maintained, when possible.

dBASE: dBASE indexes are created as maintained. The Index name specified is the tag name.

For more information about different types of indexes, see DbiAddIndex in the Borland Database Engine Online Reference.

Example 3: DDL (DROP INDEX)

The syntax for drop index is tablename.indexname:

drop index parts.part_no

Paradox: For Paradox only, the syntax tablename.primary indicates the primary index:

drop index parts.primary