Embedded SQL SQL is a very powerful non-procedural language in which you can communicate tasks to the computer using simple commands. A procedural language is one in which you must give the computer step by step instructions for accomplishing a task. To accomplish this might require many lines of codes. Examples of procedural languages are C/C++, Basic, and Cobol. Chapter 8 in your book uses Cobol to demonstrate Embedded SQL. This handout however will use C language to demonstrate Embedded SQL. The reason for this is that I know C much better than Cobol and between the handout and the chapter you will have a good example of how to embed SQL in two popular languages. As powerful as SQL is, it is often necessary to complete task’s that are beyond the capabilities of SQL. In such cases, you need to use a procedural language to do so. Oracle provides pre-compilers to accomplish the embedding process. The pre-processor is similar to the C pre-processor. It compiles the code before the actual compilers get’s it’s hands on it and converts the SQL code to the native languages code, in the case of C by making function calls. Oracle calls it’s line SQL pre-compilers Pro*language-name, i.e. Pro*C/C++, Pro*Cobol, Pro*Fortran, etc. In addition to these compilers there are Oracle interfaces for Perl (DBI), Java (SQLJ and JDBC), Visual Basic (ODBC), and others. The Oracle Pro*C compiler provides C with additional include files and data-types. Most SQL commands in Pro*C begin with EXEC SQL. Example: EXEC SQL INCLUDE SQLCA; This tells the Pro*C pre-compiler to include the SQLCA (SQL Communications Area). SQLCA is a C structure that contains a member called SQLCODE, which is used to determine the result of the last SQL execution. If the execution is normal, SQLCODE is zero. If the execution is not normal, the value in SQLCODE indicates the problem occurred (for example, not finding any rows that satisfy the condition in the WHERE clause is SQLCODE #1403). Programs should contain statements that check the value of the SQLCODE after each SQL statement is executed. 1 of 24 Example: EXEC SQL INCLUDE SQLCA; VARCHAR strLast; /* Pro*C data-type */ EXEC SQL SELECT last INTO :strLast FROM customer WHERE customer_number = :intCustomer_nr; if (sqlca.sqlcode == 1403) printf (“Customer %d is not found\n”, intCustomer_nr); else printf (“Customer %d is %s\n”, intCustomer_nr, strLast.arr); /* VARCHAR is actually an Oracle Pro*C data-type that is represented by a C structure. The structure is made up of two members: 1) arr is actually member that stores the data. 2) len is a member that stores the strings length. */ -- Notice that in Embedded SQL the variables must be proceeded by a ‘:’ Updating, Inserting, and Deleting in Embedded SQL: The standard SQL Update, Insert, and Delete commands work by simply adding the EXEC SQL and preceding the variables with a ‘:’ Insert VARCHAR strLast; VARCHAR strFirst; printf (“Enter Customer Last-Name:”); scanf (“%s”, strLast.arr); printf (“Enter Customer First-Name:”); scanf (“%s”, strFirst.arr); EXEC SQL INSERT INTO customer (customer_number, last, first) VALUES (customer_seq.NextVal, :strLast.arr, :strFirst.arr); 2 of 24 Delete EXEC SQL BEGIN DECLARE SECTION; int intCustomer_nr; EXEC SQL END DECLARE SECTION; /* All Host variables (C in this instance) must be identified to Oracle by placing them between the Above two DECLARE SECTION statements. */ printf (“Enter Customer # to delete:”); scanf (“%d”, intCustomer_nr); EXEC SQL DELETE FROM customer WHERE customer_number = :intCustomer_nr; Update printf (“Enter Customer # to Update:”); scanf (“%d”, intCustomer_nr); printf (“Enter New Last-Name:”); scanf (“%s”, strLast.arr); printf (“Enter New First-Name:”); scanf (“%s”, strFirst.arr); EXEC SQL UPDATE customer SET last = :strLast.arr, First = :strFrist.arr WHERE customer_number = :intCustomer_nr; The previous examples all illustrated single row queries. However what if you need to process multiple rows. In order to do this we must use SQL cursors. A cursor is a pointer to a row in the collection of rows retrieved by a SQL statement. The cursor advances one row at a time to provide sequential, record at a time access to the retrived rows so the rows can be processed by C. By using a cursor, C can process the set of retrieved rows as though they are records in a sequential file. The first step in using a cursor is to declare the cursor and describe the associated query. 3 of 24 Example EXEC SQL DECLARE custgroup CURSOR FOR SELECT customer_number, last, first FROM customer WHERE slsrep_number = :intSlsrep_nr; The above command does not cause the query to be executed at this time, it only declares a cursor named custgroup and associates the cursor with the indicated query. Using a query in C involves three commands: OPEN, FETCH, and CLOSE. Opening the cursor causes the query to be executed and makes the results available to the program. Executing a FETCH advances the cursor to the next row in the set of rows retrieved by the query and places the contents of the row in the indicated host variables. Finally, closing the cursor deactivates it. Data retrieved by the execution of the query is no longer available. The cursor could be opened again later and processing could begin again. Example: EXEC SQL OPEN custgroup; EXEC SQL FETCH custgroup INTO :intCustomer_nr, :strLast, :strFirst; EXEC SQL CLOSE custgroup; UPDATING CURSORS: You can update the rows encountered in processing cursors. In order to indicated that an update is required, you include an addition clause (FOR UPDATE OF) in the cursor definition. Example: DECLARE credgroup CURSOR FOR SELECT customer_number, last, first, balance, credit_limit FROM customer WHERE slsrep_number = :intSalesRep_nr FOR UPDATE OF credit_limit; 4 of 24 /* To update the credit limits, you must include the FOR UPDATE OF credit_limit clause in the cursor declaration. The OPEN and CLOSE statements do not change. */ EXEC SQL OPEN credgroup; EXEC SQL FETCH credgroup INTO :intCustomer_nr, :strLast.arr, :strFirst.arr, :fltBalance, :fltCredit_limit; while (sqlca.sqlcode == 0) { if (fltCredit_limit > fltBalance) printf (“%s %s\n”, strFirst.arr, strLast.arr); else if (fltCredit_limit > 500) EXEC SQL UPDATE customer SET credit_limit = credit_limit + 200 WHERE CURRENT OF credgroup; else EXEC SQL UPDATE customer SET credit_limit = credit_limit + 100 WHERE CURRENT OF credgroup; EXEC SQL FETCH credgroup INTO :intCustomer_nr, :strLast.arr, :strFirst.arr, :fltBalance, :fltCredit_limit; } EXEC SQL CLOSE credgroup; ERROR HANDLING: Programs must be able to handle exceptional conditions that can arise when the database is accessed. Because problems are communicated through a value in SQLCODE, one legitimate way to handle problems is to check the value in SQLCODE after each executable SQL statement and then take appropriate action based on the indicated problem. However with all the potential conditions, this method becomes very 5 of 24 cumbersome. Fortunately, as you will see, there is another way to handle such conditions using the WHENEVER statement. There are two types of error conditions that need to be addressed. The first type consist of unusual but normal conditions, such as not retrieving any data to match a given condition, attempting to store a row that violates a duplicate key constraint, etc. The value in SQLCODE for such conditions is a positive number. The appropriate action is to print an error message and continue processing. The appropriate action for the END OF DATA (SQLCODE 100) message probably is termination of some loop and continuation with the rest of the program, as with the last example, No error message is required. The other type of condition is far more serious, because it consist of the abnormal and unexpected conditions or, in a very real sense, the fatal ones. Examples of this include Out of Disk Space, Damaged Database, etc. The SQLCODE for such conditions is a negative number. The appropriate action usually is to print a final message and terminate the program. The WHENEVER statement allows you to handle these errors in a global manner. Example: EXEC SQL WHENEVER SQLERROR DO error_routine(); EXEC SQL WHENEVER SQLWARNING CONTINUE; EXEC SQL WHENEVER NOT FOUND CONTINUE; In the WHENEVER statement, SQLERROR represents abnormal or fatal conditions (SQLCODE < 0), SQLWARNING represents the unusual but normal conditions (SQLCODE > 0), and NOT FOUND represents the special warning END OF DATA (SQLCODE 100). The WHENEVER statement ends either with DO followed by a function name or with the word CONTINUE. The WHENEVER statement indicates how each of these conditions should be handled if and when they occur. A Complete Pro*C Example: #include <string.h> #include <stdlib.h> #include <stdio.h> 6 of 24 EXEC SQL INCLUDE SQLCA; int main () { VARCHAR username[15]; VARCHAR password[15]; VARCHAR strLast[21]; VARCHAR strFirst[16]; VARCHAR strStreet[21]; VARCHAR strCity[16]; VARCHAR strZip_code[11]; EXEC SQL BEGIN DECLARE SECTION; int intCustomer_nr; int intSlsrep_nr; float fltBalance; float fltCredit_limit; EXEC SQL END DECLARE SECTION; printf ("\nEnter User Name:"); scanf ("%s", username.arr); printf ("\nEnter Password:"); scanf ("%s", username.arr); strcpy (username.arr, "dds"); strcpy (password.arr, "dds"); username.len = strlen(username.arr); password.len = strlen(password.arr); EXEC SQL CONNECT :username IDENTIFIED BY :password; if (sqlca.sqlcode < 0) { printf ("\nError Connecting to Oracle\n%s", sqlca.sqlerrm.sqlerrmc); EXEC SQL ROLLBACK WORK RELEASE; exit (1); } 7 of 24 else printf ("\nConnected to Oracle..."); printf ("\nEnter Sales Rep #"); scanf ("%d", &intSlsrep_nr); EXEC SQL DECLARE sales_cust CURSOR FOR SELECT customer_number, last, first, balance, credit_limit FROM customer WHERE slsrep_number = :intSlsrep_nr; if (sqlca.sqlcode != 0) { printf ("\nError Declaring Cursor: %s", sqlca.sqlerrm.sqlerrmc); EXEC SQL ROLLBACK WORK RELEASE; exit (1); } EXEC SQL OPEN sales_cust; if (sqlca.sqlcode != 0) { printf ("\nError Declaring Cursor: %s", sqlca.sqlerrm.sqlerrmc); EXEC SQL ROLLBACK WORK RELEASE; exit (1); } EXEC SQL FETCH sales_cust INTO :intCustomer_nr, :strLast, :strFirst, fltBalance, fltCredit_limit; if (sqlca.sqlcode == 0) { printf ("Customers for Sales-Rep # %d\n", intSlsrep_nr); printf ("Customer # Last Name First Name Balance Credit Limit\n"); printf ("---------- ------------- ----------- --------- ------------\n"); } else { printf ("\nError Finding Sales Rep \n%s", sqlca.sqlerrm.sqlerrmc); EXEC SQL ROLLBACK WORK RELEASE; 8 of 24 exit (1); } while (sqlca.sqlcode == 0) { printf ("%10d %15s %15s %9.2f %9.2f\n", intCustomer_nr, strLast.arr, strFirst.arr, ftlBalance, fltCredit_limit); EXEC SQL FETCH sales_cust INTO :intCustomer_nr, :strLast, :strFirst, :fltBalance, :fltCredit_limit; } EXEC SQL CLOSE sales_cust; EXEC SQL COMMIT WORK RELEASE; /* Commit all Oracle Work. */ return 0; } Dynamic SQL: Dynamic SQL is a complex programming technique that allows the host program to accept or build SQL statements at runtime and take explicit control over data-type conversion. Because of the possible complexities in programming dynamic SQL, it has been known to scare away the best of developers. There are several methods of doing dynamic SQL, we will look at some of them today. Dynamic SQL – Method 1: Method 1 allows the developer to build a dynamic SQL statement and immediately execute it using the EXECUTE IMMEDIATE command. This method does not use host variables within the SQL statement and has two result conditions of success or failure. Statements in method one are parsed every time they are executed and do not allow SELECT statements. When you use dynamic SQL, this is the most straight foward method of all the methods allowed. Example: #include <string.h> 9 of 24 #include <stdlib.h> EXEC SQL BEGIN DECLARE SECTION; #include <stdio.h> EXEC SQL END DECLARE SECTION; EXEC SQL INCLUDE SQLCA; int main () { VARCHAR oid[2]; VARCHAR sqlStatement; int intOldSalesRep_nr, intNewSalesRep_nr; strcpy (oid.arr, “/”); /* This tells Oracle to use the Unix Username and Password. */ oid.len = strlen(oid.arr); EXEC SQL CONNECT :oid; if (sqlca.sqlcode < 0) { printf (“\n%s”, sqlca.sqlerrm.sqlerrmc); EXEC SQL ROLLBACK WORD RELEASE; exit (1); } else { printf (“\nConnected to Oracle…”); EXEC SQL COMMIT WORK RELEASE; } printf (“\nEnter the Old Sales-Rep #”); scanf (“%d”, &intOldSalesRep_nr); printf (“\nEnter the New Sales-Rep #”); scanf (“%d”, &intNewSalesRep_nr); sprintf (sqlStatement.arr, “UPDATE customer “ “SET slsrep_number = %d “ 10 of 24 “WHERE slsrep_number = %d “, intNewSalesRep_nr, intOldSalesRep_nr); EXEC SQL EXECUTE IMMEDIATE :sqlStatement; EXEC SQL COMMIT WORK RELEASE; exit (0); } Dynamic SQL – Method 2: Method 2 of dynamic SQL also is fairly straight foward and almost identical to Method 1, except that host variables are allowed. These variables need to be known at pre-compile time. With Method 2, the SQL is parsed just once but can be executed many times. This method enables the user to use the USING clause. Every placeholder in the prepared dynamic SQL statement must match a corresponding host variable in the USING clause. Example: #include <string.h> #include <stdlib.h> EXEC SQL BEGIN DECLARE SECTION; #include <stdio.h> EXEC SQL END DECLARE SECTION; EXEC SQL INCLUDE SQLCA; int main () { VARCHAR oid[2]; VARCHAR sqlStatement; NUMBER intOldSalesRep_nr; /* NUMBER is an Oracle data-type */ NUMBER intNewSalesRep_nr; strcpy (oid.arr, “/”); /* This tells Oracle to use the Unix Username and Password. */ oid.len = strlen(oid.arr); 11 of 24 EXEC SQL CONNECT :oid; if (sqlca.sqlcode < 0) { printf (“\n%s”, sqlca.sqlerrm.sqlerrmc); EXEC SQL ROLLBACK WORD RELEASE; exit (1); } else { printf (“\nConnected to Oracle…”); EXEC SQL COMMIT WORK RELEASE; } sprintf (sqlStatement.arr, “UPDATE customer “ “SET slsrep_number = :c “ “WHERE slsrep_number = :d “); /* :c & :d are place holders. */ printf (“\nEnter the Old Sales-Rep #”); scanf (“%d”, &intOldSalesRep_nr); printf (“\nEnter the New Sales-Rep #”); scanf (“%d”, &intNewSalesRep_nr); while (intOldSalesRep_nr > 0 && intNewSalesRep_nr > 0) { EXEC SQL C1 FROM :sqlStatement; EXEC SQL EXECUTE C1 USING :intNewSalesRep_nr, :intOldSalesRep_nr; printf (“\nEnter the Old Sales-Rep #”); scanf (“%d”, &intOldSalesRep_nr); printf (“\nEnter the New Sales-Rep #”); scanf (“%d”, &intNewSalesRep_nr); } EXEC SQL COMMIT WORK RELEASE; 12 of 24 exit (0); } Dynamic SQL – Method 3: Method 3 of dynamic SQL allows your program to accept or build a dynamic query and then process it using a PREPARE statement with the DECLARE, OPEN, FETCH, and CLOSE cursor commands. Method 3 is particular useful for CURSORS and can be used with SELECT statements. Example: #include <string.h> #include <stdlib.h> EXEC SQL BEGIN DECLARE SECTION; #include <stdio.h> int intCustomer_nr; int intSlsrep_nr; float fltBalance; float fltCredit_limit; EXEC SQL END DECLARE SECTION; EXEC SQL INCLUDE SQLCA; int main () { VARCHAR username[15]; VARCHAR password[15]; VARCHAR sqlStatement[255]; VARCHAR oid[15]; VARCHAR strLast[21]; VARCHAR strFirst[16]; VARCHAR strStreet[21]; VARCHAR strCity[16]; VARCHAR strZip_code[11]; strcpy (username.arr, "dds"); strcpy (password.arr, "dds"); username.len = strlen(username.arr); 13 of 24 password.len = strlen(password.arr); EXEC SQL CONNECT :username IDENTIFIED BY :password; if (sqlca.sqlcode < 0) { printf ("\nError Connecting to Oracle\n%s", sqlca.sqlerrm.sqlerrmc); EXEC SQL ROLLBACK WORK RELEASE; exit (1); } else printf ("\nConnected to Oracle..."); printf ("\nEnter Sales Rep #"); scanf ("%d", &intSlsrep_nr); sprintf (sqlStatement.arr, "SELECT customer_number, last, first, balance, credit_limit " "FROM customer WHERE slsrep_number = :v1"); sqlStatement.len = strlen(sqlStatement.arr); EXEC SQL PREPARE S1 FROM :sqlStatement; EXEC SQL DECLARE C1 CURSOR FOR S1; if (sqlca.sqlcode != 0) { printf ("\nError Declaring Cursor: %s", sqlca.sqlerrm.sqlerrmc); EXEC SQL ROLLBACK WORK RELEASE; exit (1); } while (intSlsrep_nr > 0) { EXEC SQL OPEN C1 USING :intSlsrep_nr; EXEC SQL FETCH C1 INTO :intCustomer_nr, :strLast, :strFirst, fltBalance, fltCredit_limit; if (sqlca.sqlcode == 0) 14 of 24 { printf ("Customers for Sales-Rep # %d\n", intSlsrep_nr); printf ("Customer # Last Name First Name Balance Credit Limit\n"); printf ("---------- ------------- ----------- --------- ------------\n"); } else { printf ("\nError Finding Sales Rep \n%s", sqlca.sqlerrm.sqlerrmc); EXEC SQL ROLLBACK WORK RELEASE; exit (1); } while (sqlca.sqlcode == 0) { printf ("%10d %15s %15s %9.2f %9.2f\n", intCustomer_nr, strLast, strFirst, fltBalance, fltCredit_limit); EXEC SQL FETCH C1 INTO :intCustomer_nr, :strLast, :strFirst, :fltBalance, :fltCredit_limit; } EXEC SQL CLOSE C1; printf ("\nEnter Sales Rep #"); scanf ("%d", &intSlsrep_nr); } EXEC SQL COMMIT WORK RELEASE; /* Commit all Oracle Work. */ return 0; } 15 of 24 Java/JDBC Example: import java.sql.*; import java.util.Hashtable; public class ListStudents { public static void main(String[] args) { String url = "jdbc:oracle:thin:@localhost:1521:ggu8i"; String driver="oracle.jdbc.driver.OracleDriver"; String host = "localhost"; String dbName = ""; String username = "rtimlin"; String password = "oracle"; listStudents (driver, url, username, password); } public static void listStudents (String driver, String url, String username, String password) { try { Class.forName(driver).newInstance(); Connection conn = DriverManager.getConnection( url, username, password); System.out.println ("Students Table\n"); Statement statement = conn.createStatement(); String query = "SELECT * FROM student"; ResultSet rst = statement.executeQuery(query); ResultSetMetaData rstMetaData = rst.getMetaData(); int columnCount = rstMetaData.getColumnCount(); for (int i = 1; i < columnCount+1; i++) { System.out.print(rstMetaData.getColumnName(i) + " "); } 16 of 24 System.out.println(); while(rst.next()) { System.out.print(rst.getInt(1) + " "); System.out.print(rst.getString(2) + " "); System.out.print(rst.getString(3) + " "); System.out.print(rst.getString(4) + " "); System.out.print(rst.getString(5) + " "); System.out.println(rst.getString(6) + " "); } } catch (ClassNotFoundException cnfe) { System.out.println("Error Loading Driver: " + cnfe); } catch (SQLException sqle) { System.out.println("Error Connecting: " + sqle); } catch (Exception e) { System.out.println("Error Connecting: " + e); } } } Java Server Pages Example: <!-- FindDemo.jsp Author: Robert Timlin Created: March 29th, 2001 Purpose: CIS 325 Class Executed by Tomcat, part of the apache project. To download the Tomcat server goto: www.apache.org --> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"> <!-Taken from Core Servlets and JavaServer Pages from Prentice Hall and Sun Microsystems Press, http://www.coreservlets.com/. &copy; 2000 Marty Hall; may be freely used or adapted. --> <%@ page import="java.lang.*" %> <%@ page import="java.util.*" %> 17 of 24 <%@ page import="java.sql.*" %> <HTML> <HEAD> <TITLE>Phone/Address Lookup by name</TITLE> </HEAD> <BODY BGCOLOR="#FDF5E6"> <H1 ALIGN="CENTER">Welcome to Find My Friends</H1> <H2>Information on Your Friends:</H2> <FORM ACTION="<%= response.encodeURL(“FindDemo.jsp") %>"> <BIG><CENTER> <INPUT TYPE="TEXTBOX" NAME="FindName"> <INPUT TYPE="SUBMIT" VALUE="Submit"> <% String findName = request.getParameter("FindName"); // Use setAttribute instead of putValue in version 2.2. if (findName != null) { String driver = "sun.jdbc.odbc.JdbcOdbcDriver"; String host = ""; String dbName = ""; String url = "jdbc:odbc:generic"; String username = ""; String password = ""; try { Class.forName(driver); Connection conn = DriverManager.getConnection( url, username, password); Statement statement = conn.createStatement(); String query = "SELECT lname, fname, phone, street, city, state, zip " + " FROM demoinfo " + " WHERE ALLTRIM(UPPER(lname)) LIKE '" + findName.toUpperCase() + "%'" + " ORDER BY lname, fname"; ResultSet rst = statement.executeQuery(query); 18 of 24 out.println("<TABLE BORDER=1 ALIGN=\"CENTER\">\n" + "<TR BGCOLOR=\"#FFAD00\">\n" + " <TH>Name<TH>Phone#<TH>Street<TH>City, ST ZIP\n"); while(rst.next()) { out.print("<TR>\n<TD>" + rst.getString(1) + ", " + rst.getString(2)); out.print("\n<TD>" + rst.getString(3)); out.print("\n<TD>" + rst.getString(4)); out.println("\n<TD>" + rst.getString(5) + ", " + rst.getString(6) + " " + rst.getString(7)); } out.println("</TABLE>\n</BODY></HTML>"); } catch (ClassNotFoundException cnfe) { out.println("Error Loading Driver: " + cnfe); } catch (SQLException sqle) { out.println("Error Connecting: " + sqle); } catch (Exception e) { out.println("Error: " + e); } } %> </CENTER></BIG></FORM> </BODY> </HTML> 19 of 24 Visual Basic Example: Private Sub cmdSearch_Click() Dim SQLString As String, rst As Recordset If txtLname.Text <> "" And txtFname.Text <> "" Then SQLString = " SELECT lname, fname, medrecno, birthdate " & _ " FROM demoinfo " & _ " WHERE lname LIKE '" & txtLname.Text & "%'" & _ " AND fname LIKE '" & txtFname.Text & "%'" ElseIf txtFname.Text <> "" Then SQLString = " SELECT lname, fname, medrecno, birthdate " & _ " FROM demoinfo " & _ " WHERE fname LIKE '" & txtFname.Text & "%'" ElseIf txtLname.Text <> "" Then SQLString = " SELECT lname, fname, medrecno, birthdate " & _ " FROM demoinfo " & _ " WHERE lname LIKE '" & txtLname.Text & "%'" Else MsgBox "Enter Something" txtLname.SetFocus Exit Sub End If Set rst = deGeneric.cnGeneric.Execute(SQLString) If rst.RecordCount <= 0 Then MsgBox "Nothing Found" txtLname.SetFocus Exit Sub End If Do While Not rst.EOF MSFlexGrid1.AddItem rst.Fields(0) & vbTab & _ rst.Fields(1) & vbTab & rst.Fields(2) & _ vbTab & rst.Fields(3) rst.MoveNext Loop 20 of 24 End Sub Private Sub cmdExcel_Click() Dim oExcel As Object, iRow As Integer deSales.rscdCustomer.Open Set oExcel = CreateObject("excel.application") With oExcel .Workbooks.Add .Columns("A").ColumnWidth = 9 .Columns("B").ColumnWidth = 20 .Columns("C").ColumnWidth = 20 .Columns("D").ColumnWidth = 12 .Columns("E").ColumnWidth = 5 .Columns("F").ColumnWidth = 15 .Columns("G").ColumnWidth = 30 .Rows("1:7").Font.Size = 12 .Rows("1:5").Font.Bold = True .Rows("6").Font.Bold = True .Rows("6").Font.Underline = True .Columns("A").NumberFormat = "####0" .Cells(1, 11).Value = "SALES - Customer Table" ' .cells(2, 11).Value = " " & Now + " - " & Now + 30 .Cells(4, 2).Value = "**************** Table Data ***************" .Cells(6, 1).Value = "Customer #" .Cells(6, 2).Value = "Customer Name" .Cells(6, 3).Value = "Address" .Cells(6, 4).Value = "City " .Cells(6, 5).Value = "State" .Cells(6, 6).Value = "Zip/Postal" .Cells(6, 7).Value = "Contact" iRow = 7 deSales.rscdCustomer.MoveFirst Do While Not deSales.rscdCustomer.EOF .Cells(iRow, 1).Value = deSales.rscdCustomer.Fields(0) .Cells(iRow, 2).Value = deSales.rscdCustomer.Fields(1) 21 of 24 .Cells(iRow, 3).Value = deSales.rscdCustomer.Fields(2) .Cells(iRow, 4).Value = deSales.rscdCustomer.Fields(3) .Cells(iRow, 5).Value = deSales.rscdCustomer.Fields(4) .Cells(iRow, 6).Value = deSales.rscdCustomer.Fields(5) .Cells(iRow, 7).Value = RTrim(deSales.rscdCustomer.Fields(6)) & " " & deSales.rscdCustomer.Fields(7) iRow = iRow + 1 deSales.rscdCustomer.MoveNext Loop .Visible = True MsgBox "Excel is Running", vbInformation, "Excel" .ActiveWorkbook.SaveAs "c:\temp\customer.xls", 1 .ActiveWorkbook.Close .Quit End With End Sub Private Sub Command1_Click() Dim msWord As Object Dim SQLString As String, mFile As String Dim rst As Recordset SQLString = " select lname, fname, street, city, state, zip, formname " & _ " from demoinfo Where alltrim(medrecno) = '" & txtMedRecoNo.Text & "'" ' mUserName = UserName(cur_user()) mFile = "c:\temp\junk.doc" mlname = "John" mfname = "Doe" msWord = CreateObject("Word.Basic") msWord.FileNewDefault msWord.Insert (Chr(13) + Chr(13) + Chr(13) + Chr(13)) msWord.Insert (mdy(Date) + Chr(13) + Chr(13) + Chr(13)) msWord.Insert (Chr(13)) msWord.Bold 22 of 24 msWord.Insert RTrim(rst.Fields(1).Value) + " " & RTrim(rst.Fields(2).Value) msWord.Insert (RTrim(rst.Fields(3).Value) + Chr(13)) msWord.Insert (RTrim(rst.Fields(3).Value)) + ", " + RTrim(rst.Fields(4).Value) + " " + RTrim(rst.Fields(5).Value) + Chr(13) msWord.Insert (Chr(13) + "RE: " + pDescrip + Chr(13)) msWord.Insert Chr(13) + "Dear " + RTrim(rst.Fields(7).Value) + " " + RTrim(RTrim(rst.Fields(2).Value)) + Chr(13) msWord.Insert (Chr(13) + Chr(13) + "Sincerely, " + Chr(13)) msWord.Insert Chr(13) + Chr(13) + mfname + " " + mlname + Chr(13) msWord.Bold msWord.FileSaveAs mFile, msWord.ConverterLookup("Word 6.0/95") msWord.AppMaximize MsgBox "Word is Running" msWord.FileClose Set msWord = Nothing End Sub Private Sub Command2_Click() DataReport1.Show End Sub Private Sub Form_Load() deSales.cnSales.Open MSFlexGrid1.ColWidth(0) = 2000 MSFlexGrid1.ColWidth(1) = 1800 MSFlexGrid1.ColWidth(2) = 1800 MSFlexGrid1.ColWidth(3) = 1800 MSFlexGrid1.Row = 0 MSFlexGrid1.Text = "Company Name" MSFlexGrid1.Col = 1 MSFlexGrid1.Text = "Address" MSFlexGrid1.Col = 2 MSFlexGrid1.Text = "Last Name" MSFlexGrid1.Col = 3 MSFlexGrid1.Text = "First Name" End Sub 23 of 24 Private Sub MSFlexGrid1_DblClick() MsgBox MSFlexGrid1.Text End Sub 24 of 24