If you include two tables in a query without specifying a join condition, every record in the first table will be joined with every record in the second table, with huge results as a consequence.
SELECT order_id, employee.emp_id FROM orders, employee ? _tally && 16170 SELECT order_id, employee.emp_id FROM orders, employee ; WHERE orders.emp_id=employee.emp_id ? _tally && 1078
SELECT order_id, employee.emp_id FROM orders, employee, customer ; WHERE orders.emp_id=employee.emp_id ? _tally && 98098
SELECT test1.f1 FROM test1,test2 WHERE test1.f1=test2.f1 ? _tally && 4 (A, B, C, D)
? _tally && 14 (A, B, C, D and 10 blank records )
SELECT test1.f1 FROM test1,test2 ; WHERE test1.f1=test2.f1 AND !EMPTY(test1.f1) AND !EMPTY(test2.f1) ? _tally && 4
We have an application designed to manage sales in a large company where access rights to the application have to be strictly defined. Each employee belongs to a group, and each group has access to a number of functions in the application. The customer wants to have a report listing, for each group, all the employees that belong to the group and all the functions to which the group has access. This document will be used to collect authorizations from department heads to the access rights.
We will use a simplified database made of 4 files : Employee, Group, Function and GrpFunc.
The report the customer wants looks like this, and was produced before in Word :
<B>Group : Sales</B> <B>Functions :</B> Customers Orders <B>Members :</B> Dupont, Sales Manager Durant, Sales Coordinator Demeyer, Salesman <B>Authorizations :</B>
SELECT ; grpfunc.grp_code, ; function.func_code, ; function.func_desc, ; groups.grp_desc, ; groups.grp_desc + function.func_desc AS order1 ; FROM function, grpfunc, groups ; WHERE function.func_code = grpfunc.func_code AND ; grpfunc.grp_code=groups.grp_code AND ; grpfunc.grp_code IN (SELECT grp_code FROM group) ; ORDER BY grp_desc, func_desc
SELECT ; groups.grp_code, ; employee.empl_name, ; employee.empl_func ; FROM employee, groups ; WHERE groups.grp_code = employee.grp_code ; ORDER BY grp_desc, employee.empl_name
SELECT ; ‘A’ AS SubQuery, ; grpfunc.grp_code, ; function.func_code, ; function.func_desc, ; groups.grp_desc, ; SPACE(20) AS empl_name, ; SPACE(20) AS empl_func ; FROM function, grpfunc, groups ; WHERE function.func_code = grpfunc.func_code AND ; grpfunc.grp_code=groups.grp_code AND ; grpfunc.grp_code IN (SELECT grp_code FROM groups) ; ORDER BY grp_desc, func_desc SELECT ; ‘B’ AS SubQuery, ; groups.grp_code, ; SPACE(10) AS func_code, ; SPACE(20) AS func_desc, ; SPACE(20) AS grp_desc, ; SPACE(40) AS order1, ; employee.empl_name, ; employee.empl_func ; FROM employee, groups ; WHERE groups.grp_code = employee.grp_code ; ORDER BY grp_desc, empl_name
SELECT ; ‘A’ AS SubQuery, ; grpfunc.grp_code, ; function.func_code, ; function.func_desc, ; groups.grp_desc, ; SPACE(20) AS empl_name, ; SPACE(20) AS empl_func ; FROM function, grpfunc, groups ; WHERE function.func_code = grpfunc.func_code AND ; grpfunc.grp_code=groups.grp_code AND ; grpfunc.grp_code IN (SELECT grp_code FROM groups) ; UNION ; SELECT ; ‘A’ AS SubQuery, ; groups.grp_code, ; SPACE(10) AS func_code, ; SPACE(20) AS func_desc, ; groups.grp_desc, ; employee.empl_name, ; employee.empl_func ; FROM employee, groups ; WHERE groups.grp_code = employee.grp_code ; ORDER BY grp_desc, SubQuery, func_desc, empl_name
The self join
Using traditional xBase techniques, you could open a table in two diffrent work areas and establish a relation between the two, for instance to detect missing numbers in a sequence. As Rushmore is turned off when you link tables using SET RELATION, we will try a similar technique using SELECT SQL, called the self join. Let’s create a simple table, Table1 with one numeric field (Nr). The table contains a broken sequence of numbers: To prepare the self join, we will first open the table in two work areas, using two different alias :
USE table1 IN 0 ALIAS t1 USE table1 IN 0 AGAIN ALIAS t2
SELECT t1.nr, t2.nr FROM t1,t2 ; WHERE t2.nr=t1.nr+1
Aggregate functions in WHERE, GROUP BY and HAVING clauses
Aggregate functions like SUM(), COUNT(), MAX(), cannot be used in WHERE and ORDER BY clauses, because they don’t apply to a single record. A workaround is using an alias name for the column names, or refer to the column by its number as in the following SELECT statement :
SELECT SUM(order_amt), to_country FROM orders GROUP BY to_country ORDER BY 1
SELECT SUM(order_amt), to_country FROM orders ; GROUP BY to_country ; ORDER BY 1 ; HAVING SUM(order_amt) > 100000
SELECT order_date, COUNT(*) FROM orders GROUP BY 1 HAVING COUNT(*)>1
Modify table structure with SQL SELECT
Table structures can be modified easily within an application using the SELECT SQL command : Let’s use our table1 table again, modifying the numeric field nr into a caracter field, and adding a name field of 20 caracters :
SELECT STR(nr,5,0), SPACE(20) AS Name FROM table1 INTO TABLE table2
RENAME Table2.dbf TO Table1.dbf
On the contrary to what happens in traditional xBase, SET EXACT ON/OFF has no effect on SQL SELECT. The equivalent here is obtained using SET ANSI ON/OFF. There are some differences though as we will see in the following example :
SET ANSI OFF SELECT cust_id FROM orders WHERE cust_id = ‘A’
SET ANSI ON SELECT cust_id FROM orders WHERE cust_id = ‘A’
SELECT cust_id FROM orders WHERE cust_id = ‘A’
SELECT cust_id FROM orders WHERE ‘A’ = cust_id
? "ANTON" = "A" && .T. ? "A" = "ANTON" && .F.