Welcome To The Home Of The Visual FoxPro Experts  
home. signup. forum. archives. search. google. articles. downloads. faq. members. weblogs. file info. rss.
SQL SERVER >>  TIPS WHEN USING SQL PASS-THROUGH TO TARGET SQL SERVER BACKENDS

  Pete Sass
  Where is Pete Sass?
 Marathon, Ontario
 Canada
 Pete Sass




I was planning on placing this in an article, but alais I am totally swamped with workload "overload", so I am posting these function code examples here in the forum.

Several weeks ago Ken and I had a few threads passing back and forth on a method to use SQLEXE() and selectively update only those fields that the user has changed in a local
VFP cursor... this is not to write and update every field in SQL-Server backend, but only those field values that were in fact changed. The idea of course is to minimize network
traffic and if you are only changing one field value in a 78 field table, you do not want to write back all the fields, you only want to update the one, or two, or three fields
that were modified.

This simple enough idea lead me to the other pain in the butt, that is using the INSERT INTO command in an SQLEXE() call and typing in all those field names in the
field list and all the values in a value list to use in the SQLEXE() call.

Lazyness has been called the mother of invention in some circles, and this is epecially true in my case facing a very large sales system with 100 plus SQL Server tables and
a lot of them holding 50, 60, 70 plus fields in them.

For those of you who have not used SQL Server backends in a large multi-user application and not familiar with the SQLEXE()VFP function call, this function allows you to pass
pure SQL commands directly to the SQL Server backend. This is called SQL pass-through.

Generally here is how a multi-user system like the one described above works. You create a connection to your SQL-Server backend to start with using a connection string. The connection string looks like this:
cConn = SQLStringConnect("Driver=SQL Server;Server=HPHOME\MAXIMOSQL;UID=PULPSALES;PWD=PULPS;Database=Pulp_Sales")
SQLSETPROP(cConn, 'Transactions', 2)


Now you issue a select statement to move the SQL-Server backend table into a VFP local cursor to work with. This normally with large SQL-Server backends uses a WHERE statement so you do not bog things down bring down the entire table.

OK, now you have your familiar local VFP cursor and you go about designing your screens and binding the textboxes, grids, etc. to the local cursor and their respect fields. You form now works and you add in your navigation buttons. Now comes the harder part and that is the method to update the SQL-Server backend table with the any values in a record modified by a user. As well, as mentioned above you need a mechanism to insert a new record into the SQL-Server backend table when you add a new record into the local VFP table, and enter the appropriate data.

If you are using Cursor Adapters much of worrying about this is removed, but it using SQLEXE() the above two tasks can become tedious. So I have the following two functions
that may assist some of you in your Update and Insert efforts. As well, these two functions I will be expanding upon over the life cycle of my project that I currently have underway, so I maybe adding in validation to see of another user has changed the SQL-Server record while I was editing it. There maybe the case where a field type I have not tested for needs an additional CASE statement in the logic in these two function.

Here is the Update function I called fSQLupdate:
* --- fSQLupdate.prg

* --- Programmer	: Pete Sass
* --- Date      	: 2006.09.26
* --- Calling Syntax: lnUpdateTest = fSQLupdate("Orders","CustomerID",1)
* --- Assuming 		: You are updating a SQL-Server table called "Orders" and
* ---                 this table has a unique "CustomerID" field and in this case
* ---                 the unique CustomerID is a numeric field and I am passing
* ---                 record #1, so this is the record that will get updated.

* --- This function is used to find out what fields have in fact
* --- changed in a local cursor and update only the changed fields when 
* --- writing to a SQL-Server backend table.

* --- Notes: In order for this to work you pass the SQL-Server table name the 
* --- unique field name and the unique ID for the record you have changed.  
* --- This assumes you have conformed to standard backend database design and have
* --- a unique ID associated with every record in every table... if you
* --- do not shame on you.
* --- This function also assumes your backend connection handle name is
* --- cConn.  If you have a different connection handle name, change it
* --- in the code below to refect your conection handle name.
LPARAMETERS lSQLTable, lSQLuniqueFLDName, lSQLuniqueID

* --- Store the local cursor alias name.
lcAlias = ALIAS()

* --- Create all local memory variables that will be using within this function.
STORE "" TO lcFldVal
STORE "" TO lcDatStr
STORE "" TO lcDatTyp
STORE  0 TO lnConnTest
STORE "" TO lcUpdateStr
STORE  0 TO lnFldCount
STORE  0 TO lnArrayPos
STORE  0 TO lnFLDwidth
STORE  0 TO lnFLDdecimals
STORE  0 TO lnHoursDef
STORE "" TO lcUniqueTYP
STORE "" TO lcUniqueVal

* --- Use the TYPE() function to find out the data type of the lSQLuniqueID as
* --- we could be looking at either a character, or a numeric datas type.  If
* --- the data type is a character type I need to enclose it with single quotes.
lcUniqueTYP = lSQLuniqueID
lcDatTyp = TYPE('lcUniqueTYP')

IF lcDatTyp = "C"
   * --- First, remove amy leading and trailing blank spaces.
   lSQLuniqueID = ALLTRIM(lSQLuniqueID)
        
   * --- Secondly, enlose the value within single quotes.
   lSQLuniqueID = "'"+ lSQLuniqueID + "'"
   lcUniqueVal = "C"
ELSE
   lcUniqueVal = "N"
ENDIF


* --- You need to set the HOURS to 12 so it conforms to the default SQL-Server
* --- date and time format that is mm/dd/yyyy hr:mi:ss AM, or  mm/dd/yyyy hr:mi:ss PM
* --- Get the current system hour setting and store in lnHourDef memory variable.
lnHourDef = SET("HOURS")

* --- Set the hours to the correct SQL-Server format.
SET HOURS TO 12


* --- Create an array of the currently opened cursor using the AFIELDS() function
* --- call.  This will be used later on in this UDF to build the Update string
* --- command using SQL pass-through commands.
lnFldCount = AFIELDS(UDFTblArray) 


* --- Process all the fields that have in fact been changed using
* --- the GETFLDSTATE() function and adding them to the string.
FOR lnCnt = 1 TO FCOUNT()
  lcField = FIELD( lnCnt )
  nState = GETFLDSTATE(lcField)
  
   
  * --- The field value has changes save this to my local memory variable lcUpdStr.
  * --- At the same time store the new values in a secondary string called lcDataStr.
  IF nState = 2
     lcFldVal = IIF(EMPTY(lcFldVal), "", "") + lcField 
     
     * --- Get the value out of the field that has been changed.
     lcDatStr = &lcFldVal
   
     * --- OK now I got the field that was changed and I have the new value associated
     * --- with this field, but I have to take into consideration what type the
     * --- data really is.  This is needed as when I try to build my Update string
     * --- if the field is a character type I have to ensure the value is enclosed
     * --- in single quotes.
     * --- Use the TYPE() function call to obtain the field type, character,
     * --- numeric, or date.  
     
     * --- TYPE() function legend.  
     * --- "C" is a Character or Varchar field type,
     * --- "N" is a Numeric, Float, Double, or Integer,
     * --- "D" is a Date type,
     * --- "T" is a DateTime type.
     lcDatTyp = TYPE('lcDatStr')
         

     * --- Case statement logic flow below to cover 3 types of data; character, numeric and datetime.
     DO CASE
     * --- If a type return value of "C" then this is a character field value and
     * --- I must enclose the value within single quotes to work correctly in my
     * --- Update string.


        * --- CASE CHARACTER FIELD TYPE.   
        CASE lcDatTyp = "C"
        
        * --- First, remove amy leading and trailing blank spaces.
        lcDatStr = ALLTRIM(lcDatStr)
        
        * --- Secondly, enlose the value within single quotes.
        lcDatStr = "'"+ lcDatStr + "'"
        
        * --- Make the SQL pass-through update string.
        lcUpdateStr = "UPDATE " + lSQLTable + " SET " + lcFldVal + " = " + lcDatStr
     



        * --- CASE NUMERIC FIELD TYPE.
        CASE lcDatTyp = "N"
           * --- Now in this case we could be dealing with an Integer, or a Numeric value
           * --- that could be accurate to a number of decimal points.  We need to take
           * --- this into consideration when building the string value and in order
           * --- to verify the field value and accuracy we need to use the AFIELDS()
           * --- function call to create an array and test for the numeric field length
           * --- and as mentioned the decimal point accruacy.
           * --- The AFIELDS() function was called and created at the beginning of this
           * --- UDF function and is currently available for us to use now.
        
           * --- Lets find the array element of the field currently being processed.
           * --- Upper function added since array has field names in upper.
           lnArrayPos = ASCAN(UDFTblArray, UPPER(lcFldVal))
        
           * --- Now we know that the field length is 2 elements passed the lnArrayPos
           * --- and we know the decimal point accuracy is 3 elements passed the lnArrayPos
           * --- so lets get these values out now.
           lnFLDwidth = UDFTblArray(lnArrayPos + 2)
           lnFLDdecimals = UDFTblArray(lnArrayPos + 3)
        
           * --- Now we have all the information required to correctly convert the
           * --- numeric value to the correct character expression without loosing
           * --- any accuracy.
           lcDatStr = STR(lcDatStr, lnFLDwidth, lnFLDdecimals)
       
           * --- Now remove any leading and trailing blank spaces.
           lcDatStr = ALLTRIM(lcDatStr)
       
           * --- Make the SQL pass-through update string.
           * --- However, in creating the SQL Update string we need to convert the
           * --- numeric value back again to a numeric, so we enbedd the VAL function
           * --- within the Update string to covert back to a numeric value.
           lcUpdateStr = "UPDATE " + lSQLTable + " SET " + lcFldVal + " = " + lcDatStr
     



        * --- CASE DATE AND TIME FIELD TYPE.
        * --- Note, since this is trageting a SQL-Server backend no date type
        * --- exists and ony the DateTime field type is being processed here.
        CASE lcDatTyp = "T"   
           * --- First, convert the datetime data to a string so it can be
           * --- added into the SQL Update string.
           lcDatStr = TTOC(lcDatStr)
            * --- Secondly, enlose the value within single quotes.
           lcDatStr = "'"+ lcDatStr + "'"
           
           * --- Make the SQL pass-through update string.   
           lcUpdateStr = "UPDATE " + lSQLTable + " SET " + lcFldVal + " = " + lcDatStr

     
     

        * --- CASE LOGIC FIELD TYPE.
        * --- You must pass a "T", or "F" value from Visual FoxPro to update into the
        * --- SQL-Server "bit" type.
        CASE lcDatTyp = "L"   
           
           * --- If the VFP data is .T. change it to a 1 so you can write back into the SQL-Server
           * --- bit field type.
           IF lcDatStr = .T.
              lcDatStr = '1'
           ELSE
              lcDatStr = '0'
           ENDIF
           
           * --- Make the SQL pass-through update string.   
           lcUpdateStr = "UPDATE " + lSQLTable + " SET " + lcFldVal + " = " + lcDatStr
     
     


        * --- CASE MEMO FIELD TYPE.
        * --- This can be treated just the same as a character, or varchar field type.
        CASE lcDatTyp = "M"   
        * --- First, remove amy leading and trailing blank spaces.
        lcDatStr = ALLTRIM(lcDatStr)
        
        * --- Secondly, enlose the value within single quotes.
        lcDatStr = "'"+ lcDatStr + "'"
        
        * --- Make the SQL pass-through update string.
        lcUpdateStr = "UPDATE " + lSQLTable + " SET " + lcFldVal + " = " + lcDatStr


     
     
     OTHERWISE
     * --- Note.  I am not testing for date types since I am assuming this function is only to
     * ---        interface to a SQL-Server backend that does not support a date type field and
     * ---        only uses a datetime field in an "American" standard default format.
     * ---        I am not checking for "O - Object", "Q - Varbinary", or "S - Screen" field types
     * ---        in this function, but an additional needed type could be added with one additional
     * ---        CASE statement with the required conversion applied.  


          =MESSAGEBOX("The system has encountered an unexpected data type, please contact your ITS Department!",0+16,"Data Error")
          * --- Return zero so we know we experienced a problem.

          * --- Reselect the local cursor.
          SELECT (lcAlias)

          RETURN 0
          
     ENDCASE
   

    * --- Finally add the WHERE clause onto the end of the Update string so the
    * --- correct records is in fact updated using SQLEXE() pass-through.   
    
    * --- Assumes the unique ID key passed is a character value.
    IF lcUniqueVal = "C"
       * --- Create the final Update string with the correct WHERE portion and key to update added.
       lcUpdateStr = lcUpdateStr + " WHERE " + lSQLuniqueFLDName + " = " + lSQLuniqueID
    ELSE
    
       * --- Assumes the unique ID key passed is a numeric value so create taking into account the
       * --- numeric value that one must change to add into this string using the STR() function.   
       * --- The STR(lSQLuniqueID,8,0) assumes 99,999,999, or 99 million unique key values in the
       * --- table.
       lcUpdateStr = lcUpdateStr + " WHERE " + lSQLuniqueFLDName + " = " + STR(lSQLuniqueID,8,0)
       
       * --- Messagebox below is used for debugging and checking the final SQL Update string.
       * ---MESSAGEBOX(lcUpdateStr)
       * --- RETURN
       
    ENDIF
     
    * --- Finally, we can use the SQLEXEC() function calls to preform our backend update using
    * --- SQL pass-through.
     
    lnConnTest = SQLEXEC(cConn, lcUpdateStr)
    * --- I am performing a validation here to ensure I do not get an error with my SQLEXEC()
    * --- update command.
    IF lnConntest < 1
       
       AERROR(lcErr)
       MESSAGEBOX( lcErr[3], 16, "Pulp Sales Connection Problem encountered, contact your ITS department!" )
       
       * --- If the commit failed perform a rollback.
       = SQLEXEC(cConn,"ROLLBACK")              

       * --- Reselect the local cursor.
       SELECT (lcAlias)

       
       * --- Return zero so we know we encountered a problem.
       RETURN 0
    ENDIF
    
    * --- Issue the commit to the SQL-Server backend/
    lnConnTest = SQLEXEC(cConn,"COMMIT")
    * --- I am performing a validation here to ensure I do not get an error with my SQLEXEC()
    * --- commit statement.
    IF lnConntest < 1
       
       AERROR( lcErr )
       MESSAGEBOX( lcErr[3], 16, "Pulp Sales Connection Problem encountered, contact your ITS department!" )
       
       * --- If the commit failed perform a rollback.
       = SQLEXEC(cConn,"ROLLBACK")       

       * --- Reselect the local cursor.
       SELECT (lcAlias)

       * --- Return zero so we know we encountered a problem.
       RETURN 0
    ENDIF  


The above function only created the SQL pass-through update commands for those fields
that were changed in the local cursor.


Now for the Insert function:
* ---fSQLinsert.prg

* --- Programmer	: Pete Sass
* --- Date      	: 2006.09.26 
* --- Calling Syntax: lnInsert = fSQLinsert("Orders")
* --- Assuming 		: You are Inserting into a SQL-Server table called "Orders".


* --- Notes:  The basic method of operation in this is to create dynamically
* ---         a field list and a value list to use with the SQL Insert 
* ---         command using SQLEXE() pass-through to a SQL-Server backend table.
LPARAMETERS lSQLTable


* --- Local memory variables used in this UDF.
STORE ""  TO lcLocTBLfyl
STORE  0 TO lnHourDef
STORE "" TO lcFLDstring
STORE "" TO lcVALstring
STORE "" TO lcFLDname
STORE "" TO lnFLDNamePos 
STORE "" TO lcAlias
STORE  0 TO lnLength
STORE "" TO lcInsertString

* --- Get the current cursor alias.
lcAlias = ALIAS()

* --- Fabricate a local cusor name to use in this function.
lcLocTBLfyl = "Loc" + lSQLTable

* --- You need to set the HOURS to 12 so it conforms to the default SQL-Server
* --- date and time format that is mm/dd/yyyy hr:mi:ss AM, or  mm/dd/yyyy hr:mi:ss PM
* --- Get the current system hour setting and store in lnHourDef memory variable.
lnHourDef = SET("HOURS")


* --- Set the hours to the correct SQL-Server format.
SET HOURS TO 12

* --- First thing.
* --- Create an array of the local cursor so we can extract the actual
* --- field values.
AFIELDS(UDFTblValues) 


* --- Second thing.
* --- We need is an outline of the SQL-Server backend table structure.  This
* --- is accomplished using the function call below.
= SQLCOLUMNS(cConn, lSQLTable, 'NATIVE', lcLocTBLfyl)


* --- Move record pointer to the top of main cursor table.
SELECT (lcLocTBLfyl)


GOTO TOP
DO WHILE .NOT. EOF()
   
   * --- Do not process SQL-Server auto incrementing keys.
   IF RTRIM(Type_name) = "int identity"
      
      * --- Move to next record.
      SKIP
   
   ELSE   
   
      * --- Create the field listing string.
      lcFLDname = UPPER(ALLTRIM(Column_name))
      * --- Make the SQL pass-through update string.
      *lcVALstring = lcVALstring + lcFLDval + ","
      lcFLDstring = lcFLDstring + ALLTRIM(Column_name) + ","

     
      * --- Select the current Pulp Sales alias to get current data out
      * --- associated with field being processed.
      SELECT (lcAlias)
     
      * --- Now get the field value.
      lcFLDval = &lcFLDname
      
   
      * --- Now based upon the field name ASCAN into the array
      * --- and find the field value and the field type.
      lnFLDNamePos = ASCAN(UDFTblValues, lcFLDname)
      
         
      * --- Now get the field width.
      lcFLDwidth = UDFTblValues(lnFLDNamePos + 2)
   
   
      * --- Now get the decimal point accurancy.
      lcFLDdecimals = UDFTblValues(lnFLDNamePos + 3)

   

      * /// CORE MECHANICS OF THIS UPDATE VALUE LIST STRING.
      * --- Now we are about to build the value string list.
      * --- I will be using the TYPE() function to verify the coversions required.
      lcFLDtype = TYPE('lcFLDval')
      
      
      * --- Start CASE logic structure.
      DO CASE
      
         * --- The field value is a character type.
         CASE lcFLDtype = "C"
        
            * --- First, remove amy leading and trailing blank spaces.
            lcFLDval = ALLTRIM(lcFLDval)
        
            * --- Secondly, enlose the value within single quotes.
            lcFLDval = "'" + lcFLDval + "'"
        
            * --- Make the SQL pass-through update string.
            lcVALstring = lcVALstring + lcFLDval + ","
            
         


         * --- The field value is a numeric type.   
         CASE lcFLDtype = "N"   
              
              * --- First convert the numeric to a string so it can be added into
              * --- the value string being made.  However, we must take into account
              * --- the length and decimal point accurance when using the STR()
              * --- function call so we do not loose demcimal point accuracy.
              * --- I am using the lcFLDwidth and the lcFLDdecimals memory variables
              * --- that were extracted previously at the beginning of this loop to
              * --- ensure I get the STR() call accurate in my conversion.
             
              
              lcFLDval = STR(lcFLDval, lcFLDwidth, lcFLDdecimals)
                
              * --- Make the SQL pass-through update string.
              lcVALstring = lcVALstring + lcFLDval + ","
            



         * --- The field value is a date and time type.   
         CASE lcFLDtype = "T"   
            
            * --- First, convert the datetime data to a string so it can be
            * --- added into the SQL Update string.
            lcFLDval = TTOC(lcFLDval)
            
            * --- Secondly, enlose the value within single quotes.
            lcFLDval = "'"+ lcFLDval + "'"        

            * --- Make the SQL pass-through update string.
            lcVALstring = lcVALstring + lcFLDval + ","      
      
      
      

           * --- The field value is a logical type.
           * --- You must pass a "T", or "F" value from Visual FoxPro to update into the
           * --- SQL-Server "bit" type.
           CASE lcFLDtype = "L"   
           
              * --- If the VFP data is .T. change it to a 1 so you can write back into the SQL-Server
              * --- bit field type.
              IF lcFLDval = .T.
                 lcFLDval = '1'
              ELSE
                 lcFLDval = '0'
              ENDIF      
      
            * --- Make the SQL pass-through update string.
            lcVALstring = lcVALstring + lcFLDval + ","
            
            
                     
      
           * --- The field type is a memo type.
           * --- This can be treated just the same as a character, or varchar field type.
           CASE lcDatTyp = "M"
           
           * --- First, remove amy leading and trailing blank spaces.
           lcFLDval = ALLTRIM(lcFLDval)
          
           * --- Secondly, enlose the value within single quotes.
           lcFLDval = "'"+ lcFLDval + "'"
              
           * --- Make the SQL pass-through update string.
           lcVALstring = lcVALstring + lcFLDval + ","
 
 
      OTHERWISE
      * --- Note.  I am not testing for date types since I am assuming this function is only to
      * ---        interface to a SQL-Server backend that does not support a date type field and
      * ---        only uses a datetime field in an "American" standard default format.
      * ---        I am not checking for "O - Object", "Q - Varbinary", or "S - Screen" field types
      * ---        in this function, but an additional needed type could be added with one additional
      * ---        CASE statement with the required conversion applied.  

      =MESSAGEBOX("The system has encountered an unexpected data type, please contact your ITS Department!",0+16,"Data Error")
      * --- Return zero so we know we experienced a problem.

      * --- Reselect the local cursor.
      SELECT (lcAlias)

      RETURN 0
 
      
      ENDCASE
      
      
     * --- Move to next record.
     SELECT (lcLocTBLfyl)
     SKIP
      
   
   * --- End of main If condition, IF RTRIM(Type_name) = "int identity"
   ENDIF

  

* --- End of main program loop that is an EOF() loop.
ENDDO



* --- Currently we now have all the fields in the table stored in a string called "lcFLDstring"
* --- and we have all the values stored in a string called "lcVALstring".


* --- The last effort in preparing the field string list and the value string list is to
* --- remove the final trailer comma off of each string and enclosing each string with
* --- double quotes.
* --- First the ending comma to be removed.
lcFLDstring = ALLTRIM(lcFLDstring)
lnLength = LEN(lcFLDstring)
lcFLDstring = SUBSTR(lcFLDstring, 1, lnLength - 1)

lcVALstring = ALLTRIM(lcVALstring)
lnLength = LEN(lcVALstring)
lcVALstring = SUBSTR(lcVALstring, 1, lnLength - 1)


* --- OK, now we got both the field list and the field values strings all prepared in
* --- the correct format lets go for the final INSERT INTO command and write the
* --- new record into the SQL-Server backend table.
* --- Prepare the final INSERT into string to use in SQL pass-through.
lcInsertString = "INSERT INTO " + lSQLTable + " (" + lcFLDstring + ") VALUES(" + lcVALstring +")"

* --- The below messagebox is for viewing and debugging the final INSERT string.
* --- MESSAGEBOX(lcInsertString)


* --- The final Insert into command.
lnConnTest = SQLEXEC(cConn, lcInsertString)
* --- I am performing a validation here to ensure I do not get an error with my SQLEXEC()
* --- Insert command.
IF lnConntest < 1
       
   AERROR(lcErr)
   MESSAGEBOX(lcErr[3], 16, "Pulp Sales Connection Problem encountered, contact your ITS department!")
       
   * --- If the INSERT failed perform a rollback.
   = SQLEXEC(cConn,"ROLLBACK")              

   * --- Reselect the local cursor.
   SELECT (lcAlias)
       
   * --- Return zero so we know we encountered a problem.
   RETURN 0
ENDIF


* --- Now to commit the Insert to the SQL-Server backend table.
lnConnTest = SQLEXEC(cConn,"COMMIT")
* --- I am performing a validation here to ensure I do not get an error with my SQLEXEC()
* --- commit statement.
IF lnConntest < 1
       
   AERROR(lcErr)
   MESSAGEBOX(lcErr[3], 16, "Pulp Sales Connection Problem encountered, contact your ITS department!")
       
   * --- If the commit failed perform a rollback.
   = SQLEXEC(cConn,"ROLLBACK")       

   * --- Reselect the local cursor.
   SELECT (lcAlias)
   * --- Return zero so we know we encountered a problem.
   RETURN 0
ENDIF  


* --- Set the system hours back to it's original setting.
SET HOURS TO lnHourDef

* --- Reselect the local cursor.
SELECT (lcAlias)

RETURN 1

ENDFUNC


Note: If you look at the beginning of each function you will see how to call each function and use it. This week I will be adding a fSQLdelete function that works basically the same way and builds the DELETE string based on passing a unique key. The one thing I need to mention is for the Update and a future Delete pass-through function to work, you need a unique key value that you should by design have in each and every SQL-Server table backend as just common good backend design. Actually, in looking at doing this several weeks ago I thought it would be fair more difficult that it really turned out to be. For those of you interested, you can give these a try. I am sure there are a dozen other ways to achieve what I am doing, and probably another dozen ways to optimize the function above. You comments for improvement, would be welcome. Feel free to make changes as required and if you get a brain-storm please share your improvements.

FEEDBACK

Hai From Viet nam @ 5/29/2008 2:51:16 AM
Dear Sir !
I'm From Viet nam and working with VFP but sort time. My english very
bad (Sorry) I have a question to ask you, thank you for anwser .
I have a SQL command : " Select Cus_ID from DB1!Table1 where Cus_ID
in (Select Cus_ID from DB2!Table2 where REGION = 'VIET_NAM') ".
I want to get result by SQLEXE() Funtion.
Please Help me and I'm Waiting for your Letter
Mr Hai From Ha noi Viet nam!
my Email : xuanhai68@yahoo.com

Glenn Villar @ 2/26/2009 7:48:54 AM
Thank you for providing this article. I have started to move from VFP native tables to SQL Servers, Mysql and Access Database. Your articles are a great help.

Robert Griffith @ 4/1/2009 5:22:45 PM
Pete;
Great article; just what I was looking for. How did you get around the character limit for a command? I'm testing this on a table of 20+ fields and am getting hit with the "string too long' error. Any clues or am I overlooking something obvious?

Thanks again.

Robert Griffith
mc1871@yahoo.com



Your Name: 
Your Feedback: 

Spam Protection:
Enter the code shown: