Creating a 2-Table Outer Join Report with a Union: This method solves the problem by breaking it down into two parts: 1. Create one report to list customers with invoices 2. Create a second report to list customers with no invoices and combine it with the first report. First, let’s create the report that list customers with invoices: 1. Using the AR Invoices data set, add a new detail report, giving it the name CUST_A, and description “Customers with Invoices.” 2. Select the following data items: ARCUST Company Number ARCUST Company Name ARMAST Invoice Number ARMAST Total with Tax 3. Choose to sort and group by ARCUST Company Number. CUST_A is a straightforward report which gets its information from ARCUST and ARMAST. However, as explained above, customers with no invoices can't be included this way. So you need to create a second report (which we will name CUST_B) which will list customers with no invoices. Then the two reports will be combined (union). To create the second report that lists customers with no invoices: 1. While in CUST_A, select Save As, and save the report as CUST_B, with a description of “Customers with No Invoices.” 2. Return to the Report Manager and click on the Dictionary icon at the top of the screen. In the Data Item Editor you need to create the following calculated data items:
A Filter Only data item. The Filter Only data item is a calculated data item that, when added to the secondary report, limits retrieval of parent records to those with no child records (in this case, customers with no invoices). The Filter Only data item consists of a filter that specifies a nested SQL SELECT statement (called a subquery). Whenever you include this special data item in a report, Foxfire!/ Pro Series adds this clause to the resulting SQL SELECT statement, limiting retrieval of customers to those with no invoices.
Placeholder data items. The placeholders are data items you must create with expressions that return blank values of exactly the same data type and result width as they would have had if they were populated with data. 1. To create the Filter Only data item:
In the Data Item Editor, select Add, and complete the fields as follows: Data Item: Customers with No Invoices Expression: (subquery) Data Type: Logical Primary Source File: (ARCUST) Primary Source Alias: ARCUST 2. Select Filter, and complete the following fields: Allow Filter: Only Filter (Where) Expression: ARCUST.CUSTNO NOT IN (SELECT ARMAST.CUSTNO AS CUSTNOR8 FROM (ARMAST)ARMAST) 3. To create a placeholder data item for a character-type data item:
In the Data Item Editor, select Add, and complete the fields as follows: Data Item: BLANK Invoice Number Expression: SPACE(10) Data Type: Character Result Field Name: XINVNOR8 Primary Source File: (ARCUST) Primary Source Alias: ARCUST Column Header: Invoice Number Output Picture: XXXXXXXXXX Output Width: 10 4. To create a placeholder data item for a numeric-type data item:
In the Data Item Editor, select Add, and complete the fields as follows: Data Item: BLANK Total with Tax Expression: 000000000000.00 Data Type: Numeric Result Field Name: XINVAMT Primary Source File: (ARCUST) Primary Source Alias: ARCUST Column Header: Invoice Total Output Picture: 999999999999.99 Output Width: 15, 2 Return to the Report Manager, and select CUST_B. 1. Select Data Items, and remove from the Selected list the data items ARMAST Invoice Number ARMAST Total with Tax. From the Available list, select BLANK Invoice Number and BLANK Total with Tax. Save.
2. Select Filter. Select Add, and select the data item Customers with No Invoices. Save.
3. So the report CUST_B is created with new data items, the filter-only data item, “Customers with No Invoices” and two “placeholder” data items, BLANK Invoice Number and BLANK Total with Tax. The next step is to join the two reports.
Before you join, or merge, the results of the two reports, run each report separately to see that it produces the desired results. Run CUST_A and confirm that it lists customers with invoices. Then run CUST_B and confirm that it lists customers with no invoices.
4. To merge the results of the two reports:
Select the report CUST_A.
Select Filter. In the section Merge with Other Query Results (Union), select Run Report, and enter CUST_B as the name of the report to merge with. While this solution requires many steps, the advantage is that it produces results with relatively fast performance. The downside is that you must assure that any filter conditions (outside of the filter-only data item) are applied to BOTH reports. Changes to the filter in one of the reports are not automatically reflected in the other. Note that the Sort and Group settings in the parent report control the results. The Sort and Group settings in the child report are ignored. Creating a 3-Table Outer Join Report by Editing the SQL: What if your report consists of three (or more) tables instead of two? This increases the complexity of the outer join and is best approached by editing and saving the SQL. First you must examine the logic of your report. What unmatched records are to be included? For our example, we are going to create a inventory report using the tables ICITEM, ICSUPL, and ICIQTY. The parent table is ICITEM (Inventory Master file), and we want to list all parent records (all items) in ICITEM, whether or not they have child records in ICSUPL (Inventory Supplier file) or ICIQTY (Item Qty at Location file). What are all the possible combinations of matching and not matching among the three tables? For every item in ICITEM, there may be none, one or many matching records in ICSUPL. For every item in ICITEM, there may be none, one or many matching records in ICIQTY. You want the report to include an item from the parent table (ICITEM) when there is a matching record in both of the child tables, in either of the child tables, or in neither of the child tables. In the report’s SQL statement, you need to create a SELECT statement for each possible case: Item is in: ICITEM ICIQTY ICSUPL Case 1: records match in x x x Case 2: records match in x x Case 3: records match in x x Case 4: records match in x In Case 1, which is really the inner join, the report includes an item when the item is in all three tables. In Case 2, the report includes an item if it is only in ICITEM and ICIQTY (and not in ICSUPL). In Case 3, the report includes an item if it is only in ICITEM and ICSUPL (and not in ICIQTY). In Case 4, the report includes an item if it is found in ICITEM only. After you have identified all the cases for which SELECT statements need to be created, you are ready to begin creating the report. F Be sure you have a report data set that contains the three tables you need for your report. If one does not exist, you can edit an existing one to add the necessary table(s), or you can use the Setup Wizard to create one. Refer to the manual for instructions. Case 1: Since the first case is for items that are in all three tables, which Foxfire!/ Pro Series does automatically when you create a report, we will begin by creating a normal, detail report: 1. In the Report Manager, select New. 2. Select Detail as the report type. 3. In the Report Editor, Complete the report name and description: Name: IC3X Description: Items with Supplier and Serial/Store/Bin Information 4. In the Data Item Selector, choose the following items: ICITEM Item Number ICITEM Standard Cost ICSUPL Vendor Part Number ICSUPL Vendor Number ICIQTY OH Qty 5. Move to the Output Selector, and select Special Processing Options. Under Query Generation Options, select Stored SQL, and have Foxfire!/ Pro Series generate the SQL. It should look like the following statement without the line numbers:
line no. SQL SELECT statement 1 SELECT ALL; 2 ICITEM.ITEM AS ITEMI7,; 3 ICITEM.STDCOST,; 4 ICSUPL.VPARTNO,; 5 ICSUPL.VENDNO,; 6 ICIQTY.QONHAND; 7 FROM (ICITEM) ICITEM,; 8 (ICSUPL) ICSUPL,; 9 (ICIQTY) ICIQTY; 10 INTO CURSOR IC3X; 11 WHERE ICITEM.ITEM = ICIQTY.ITEM; 12 AND ICITEM.ITEM = ICSUPL.ITEM Case 2: Now you need to edit the SQL to add a SELECT statement for case 2, where the ICITEM item has a matching record in ICIQTY but not in ICSUPL. Because the second SELECT statement will be selecting records from ICITEM and ICIQTY only, you must edit the statement to remove selection of ICSUPL items and replace them with blank placeholders (refer to the blank data items created in the example for the 2-table outer join report, above): 1. Edit line 1 (first line of the first SELECT statement) to change it from SELECT ALL to SELECT DISTINCT. We will be combining the results of several SELECT statements,which would cause duplication of some records. By selecting DISTINCT, FoxPro will remove duplicates from the result. 2. On line 12, add a semi-colon (;) after the last word. 3. On line 13, after the first SELECT statement, type: UNION ALL; 4. On line 14, type: SELECT then copy lines 2 through 12 and paste them immediately after SELECT. 5. Edit the list of data items in the second SELECT statement to replace ICSUPL.VPARTNO with SPACE(15) and replace ICSUPL.VENDNO with SPACE(6). 6. In the FROM section of the SELECT… FROM statement, remove the line: (ICSUPL) ICSUPL 7. Replace the concluding WHERE statement with a WHERE clause to include ICITEM items that are not in ICSUPL: WHERE .T.; AND ICITEM.ITEM = ICIQTY.ITEM; AND ((Icitem.Item); NOT IN (SELECT DISTINCT; Icsupl.Item; FROM (ICSUPL) ICSUPL))
8. At this point, your SQL statement should look like this: SELECT DISTINCT; ICITEM.ITEM AS ITEMI7,; ICITEM.STDCOST,; ICSUPL.VPARTNO,; ICSUPL.VENDNO,; ICIQTY.QONHAND; FROM (ICITEM) ICITEM,; (ICSUPL) ICSUPL,; (ICIQTY) ICIQTY; INTO CURSOR IC3X; WHERE ICITEM.ITEM = ICSUPL.ITEM; AND ICITEM.ITEM = ICIQTY.ITEM; UNION ALL; SELECT ICITEM.ITEM AS ITEMI7,; ICITEM.STDCOST,; SPACE(15),; SPACE(6),; ICIQTY.QONHAND; FROM (ICITEM) ICITEM,; (ICIQTY) ICIQTY; WHERE .T.; AND ICITEM.ITEM = ICIQTY.ITEM; AND ((Icitem.Item); NOT IN (SELECT DISTINCT; Icsupl.Item; FROM (ICSUPL) ICSUPL))
Case 3: For case 3, the report includes the item if it is only in ICITEM and ICSUPL. To add a SELECT statement for case 3, repeat the process described for case 2, including: · Remove selection of items from ICIQTY, replacing ICIQTY data items with blank placeholders. · Add a WHERE clause for items in ICITEM that are not in ICIQTY When complete, you should have added a the SELECT statement for case 3 that should look like the following: UNION ALL; SELECT ICITEM.ITEM,; ICITEM.STDCOST,; ICSUPL.VPARTNO,; ICSUPL.VENDNO,; 000000000.00; FROM (ICITEM) ICITEM,; (ICSUPL) ICSUPL; WHERE .T.; AND ICITEM.ITEM = ICSUPL.ITEM; AND ((Icitem.Item); NOT IN (SELECT DISTINCT; ICIQTY.ITEM; FROM (ICIQTY) ICIQTY)) Case 4: For case 4, the report includes an item if it is only in ICITEM. To add a SELECT statement for case 4, repeat the process described for case 2, including: · Remove selection of data items from ICIQTY and ICSUPL, including replacing ICIQTY and ICSUPL data items with blank placeholders · Add a WHERE clause for items in ICITEM that are not in ICIQTY and ICSUPL. When complete, the entire SQL should look like the following: SELECT DISTINCT; ICITEM.ITEM AS ITEMI7,; ICITEM.STDCOST,; ICSUPL.VPARTNO,; ICSUPL.VENDNO,; ICIQTY.QONHAND; FROM (ICITEM) ICITEM,; (ICSUPL) ICSUPL,; (ICIQTY) ICIQTY; INTO CURSOR IC3X; WHERE ICITEM.ITEM = ICSUPL.ITEM; AND ICITEM.ITEM = ICIQTY.ITEM; UNION |