The following script and report show how to do something which cannot be done by a single SELECT statement using a join. Consider the following: two tables have an attribute in common upon which they may be joined. The first table contains a list of all values for this attribute, that is, it contains a list of group members. The second table contains zero, one, or several records for each value of the attribute in the first table; that is, it may or may not contain several records for each group member. Suppose it is necessary to produce a report with an entry for every member of the group. Each entry should contain some data from the first table and any data for that group member from the second table.
This cannot be done using a simple join. If the two tables are joined, the resulting set of records will include only those items where the attribute value occurs in both tables; it will not include data from the first table for those members of the group which have no entries in the second table.
An example of this is a weekly report which lists all employees and shows the jobs they performed on each day of the week. Every employee should be listed on the report, and for each employee at least one entry is printed for each day of the week, even if that employee did nothing on that day.
This can be done with two SELECT statements. The first retrieves all the records from the personnel table, sorting them by name, and the second retrieves all the records from the hours table, sorting by date. Every name retrieved from the personnel table is printed; these records are from the first SELECT statement. Next, all the records from the hours table (the records from the second SELECT statement) are gone through. For each date the date is printed, and every personnel number checked against the current name in the personnel table. If the number is the same, the job description and hours are printed. A flag is set to avoid having an extra new line printed after dates on which the mechanics worked. The whole process of checking all the records from the second SELECT is then gone through for the next name from the first SELECT, and so on.
The report script and the report we want are shown below:
/* Breakdown of jobs per employee per day */ LET PAGEWIDTH = 80; LET DATABASE = "repairs"; SELECT name, number FROM personnel SORT BY name; BECOMES CONTEXT one; SELECT FROM "hours" SORT BY date; BECOMES CONTEXT two; HEADER NEWLINE 2; PRINT "SUMMARY OF JOBS/EMPLOYEE FOR EACH DAY" WIDTH pagewidth CENTER, NEWLINE 3; PRINT "Employee", col 15, "Day", COL 30, "Job no.",COL 40, "Hours", COL 50, "Job Description", NEWLINE; NEWLINE; END; LET lineflag = 0; FOR EACH GROUP OF name IN CONTEXT one PRINT NAME, ":"; LET empno = number; FOR EACH GROUP OF DATE IN CONTEXT two PRINT COL 15, DATE PIC "www aaa dd"; FOR EACH RECORD IF (empno = number) PRINT COL 30, ATTR 'job no.', COL 40, ATTR "hours" WIDTH 5 CENTER, COL 50, description WIDTH 30, NEWLINE; LET lineflag = 1; END; END; IF (lineflag = 1) LET lineflag = 0; ELSE NEWLINE; END; END; NEWLINE; END;
SUMMARY OF JOBS/EMPLOYEE FOR EACH DAY
| Employee | Day | Job no. | Hours | Job Description |
| Jones |
Mon Feb 28 Tue Mar 1 Wed Mar 2 Thu Mar 3 |
|||
| Jordan |
Mon Feb 28 Tue Mar 1 Wed Mar 2 Thu Mar 3 |
|||
| Kilroy |
Mon Feb 28 Tue Mar 1 Wed Mar 2 Thu Mar 3 |
352 352 352 352 352 |
2 2 6 8 2 |
Replace rear wheel Replace headlights Bent left door Major body work Replace windshield |
| Mosca |
Mon Feb 28 Tue Mar 1 Wed Mar 2 Thu Mar 3 |
350 352 352 410 410 |
2 2 3 3 2 |
Oil change Adjust carburetor Wheel alignment Tune up Synch pistons |
| Peterson |
Mon Feb 28 Tue Mar 1 Wed Mar 2 Thu Mar 3 |
|||
| Scarlatti |
Mon Feb 28 Tue Mar 1 Wed Mar 2 Thu Mar 3 |
|||
| Wladislaw |
Mon Feb 28 Tue Mar 1 Wed Mar 2 Thu Mar 3 |
396 401 401 401 401 |
1 2 2 1 3 |
Lube & grease Tire repair Change tires Sparks & tune Wheel alignment |