用vfp与sql server构建Client/Server应用程序(SPT)(1)2007-05-09一些题外话 最近有一种好的现象——越来越多的Visual FoxPro用户开始注意到Client/Server应用程序的重要性,他们开始安装SQL Server了。作为开发人员,我们没有必要深入认识SQL Server的种种(我个人以为那是数据库管理员-DBA的事情),我们要解决的是怎样使Visual FoxPro与SQL Server在一起顺利的运行,怎样发挥两者的优越特性。“远程视图”一章已经涉及到了许多深层次的问题,但遗憾的是(可能是文章太过枯燥)很少有人关注它!笔者到现在还是认为,“远程视图”一文绝对是目前网络上能得到的最好的简体中文学习资料,没有哪篇文章会为你深入介绍“更新冲突发生的原理”,没有哪篇文章会为你讲解“Visual FoxPro发送更新到SQL Server 的原理”,没有哪篇文章会为你讲解“各种连接属性的意义”……在国内众多Visual FoxPro图书中,我最喜欢的作者是台湾的章立民先生。我个人以为Visual FoxPro的章立民、Delphi的李维、Visual Basic的王国荣是那种多数读者看到是他们写的作品就应该买的那类作者。三位大家的书我都有拜读,窃以为:文采最棒的是章立民先生,名气最小的也是章立民先生(也许是因为Visual FoxPro的关系)。中国铁道出版社为国内的Visual FoxPro事业做了一件好事——在99年前后出版了章立民先生的四册图书,分别是:《Visual FoxPro 6.X 中文版程序设计--教学指南》、《Visual FoxPro 6.X 中文版程序设计--基础加强》、《Visual FoxPro 6.X 中文版程序设计--问题集》、《Visual Foxpro 6.X 中文版程序设计--应用实务》。除了教学指南外,其余三本都是进阶应用的好书,但很奇怪的是他们在市场上销售的并不理想,直到今年上半年在上海书市中还能觅到,相反的一些粗制滥造的东西却是卖得火爆,真是咄咄怪事!我不认识章立民先生,与中国铁道出版社更无关系,但我愿意为他们做广告,如果章立民能为Visual FoxPro 7写出更好的东西,我们将感激不尽,也希望国内出版社积极参与引进!本文在写作上参考了《Visual Foxpro 6.X 中文版程序设计--应用实务》的内容,但绝对不是抄袭,大家有兴趣可以买一本读上一读。为什么要引入SPT的概念我们已经讲解了“远程视图”,您也许已经发现了远程视图的不足,是的远程视图的缺陷正是SPT的优势所在:
- 执行除Select以外的其他的SQL语句,如Insert 及Update等
- 执行后端数据库的存储过程
- 执行后端数据库的特殊的函数、命令
- 一次得到多个数据集合
这些都是我们在系统开发时要遇到的实现问题,Visual FoxPro 小组注意到了,于是提出了SPT的概念。SPT是SQL Pass Through的简写,它与远程视图在一起共同组成Visual FoxPro远程数据访问体系,利用它们你就可以开发完整的ClientServer构架的系统了!与“远程视图”相比,SPT技术也有以下缺陷:
- 没有图形用户界面
- 必须人工维护连接
- 数据集合是“可读写光标”,要使它成为“可更新光标”必须进行设定
从我的经验来看待“远程视图”与SPT,我以为两者是Visual FoxPro远程数据访问体系的双臂,相辅相成。不用SPT,系统就会变得弱智、愚笨;不用远程视图,系统开发会变得缓慢、繁琐。我以为,“远程视图”是组件化的SPT,是微软公司对SPT部分功能的图像化封装,所以它显得更有生产效率,同时由于系统作得多了,功能也就弱了,“远程视图”能做得就是那些事情——从数据源读取一个结果集,在客户端对它维护,并自动将变动结果更新到数据源。然而作为系统开发,我们会在实践中遇到林林总总、奇奇怪怪的需求,只有“远程视图”独立担当显然是不行的,所以SPT的出现有它的必然性。在其他的远程数据处理组件中也有类似的概念,例如ADO中的RecordSet相当于Visual FoxPro的远程视图,Command相当于Visual FoxPro的SPT。13个SPT函数Visual FoxPro 中内置了13个以SQL开头的函数,我们把它们称为SPT函数。就是这13个函数完成了Visual FoxPro的所有的SQL Pass Though功能。从功能上看,我们可以把它们分成五个部分:连接函数连接建立函数:SqlConnect(),SqlStringConnect()连接的断开函数:SqlDisconnect()核心的执行函数SQL语句传输及执行函数:SqlExec(),SqlPrapare()批次模式下更多数据集的取得函数:SqlMoreResults()异步模式下撤销正在执行的SPT的函数:SqlCancel()事务处理函数SqlCommit(),SqlRollBack()连接通道属性函数SqlGetProp(),SqlSetProp()数据源的信息截取函数SqlTables(),SqlColumns()连接到SQL Server连接SqlConnect()的两种用法一般来说SqlConnect有两种用法:直接调用操作系统里的用户型或系统型DNS;使用当前数据库DBC的连接对象。如果已经在操作系统中制作了一个名为LocalServer的系统型DNS,在命令窗口中我们就可以直接键入:SQLCONNECT("localserver","sa","")如果当前数据库中存在连接对象,我们就可以这样调用它:SQLCONNECT("NorthWind")SqlStringConnect()函数的用法在用了SqlConnect以后,大家也许会发现不太自由,能不能及时动态建立连接呢?可以的,用SqlStringConnect()就可以了:SQLSTRINGCONNECT("driver=SQL Server;Server=See-you;Uid=sa;pwd=;database=northwind")我们解释一下参数字符串的意义,driver指明了使用哪一个ODBC驱动程序,这里是SQL Server;Server是指SQL Server的服务器名称,我的服务器叫:See-You;UID是在SQL Server的用户名称,这里使用SA;PWD是用户口令,这里为空值;最后我们指定了要连接的数据库的名字,这次我们仍然使用NorthWind数据库。所有这些条件组成了一个字符串,各条件用分号分割,我的实验表明对这大小写的要求不高,当然不包括用户口令和用户名。关于连接的两大问题连接建立起来很简单,但尚有问题要解决:怎么判断连接是否成功?怎么屏蔽SQL Server的登陆对话框?第一个问题很好解决,只要SqlConnect()或是SqlStringConnect()返回正整数(>0的数)就表示连接成功,得到的正整数很重要——就是连接句柄!STORE SQLCONNECT("LocalServer", "sa","错误密码") TO gnConnHandle
IF gnConnHandle <= 0
= MESSAGEBOX("连接错误", 16, "连接到SQL Server")
ELSE
= MESSAGEBOX("连接成功", 48, "连接到SQL Server")
ENDIF在有些时候,连接发生错误会弹出以下两个提示框(有时出现前一个、有时出现后一个,有时连续出现):

连接错误提示

SQL Server的登录提示这是一个让人迷惑的的问题,我以为:第一个“连接错误提示”是限于Visual FoxPro或ODBC的层次,也就是说与SQL Server无关,这种错误的出现往往是找不到SQL Server服务器之类的问题,如以下的语句:SQLSTRINGCONNECT("driver=SQL Server;Server=See-y1ou;Uid=sa;pwd=;database=northwind")这是为了让用户更正连接信息的,但是在实际开发中没有哪个程序员希望此对话框暴露在用户面前。如果我们使用的连接是DBC的连接对象,问题就比较好解决,因为在建立连接时我们可以设定连接对话框的出现规律:

同样的我们可以通过命令来控制“显示 ODBC 登录提示”:DBSETPROP("connect1","CONNECTION" ,"DISPLOGIN" ,1) &&发生连接错误时弹出
DBSETPROP("connect1","CONNECTION" ,"DISPLOGIN" ,2) &&任何时候总是误时弹出
DBSETPROP("connect1","CONNECTION" ,"DISPLOGIN" ,3) &&发任何时候都不弹出但是当我们使用连接字符串、直接使用操作系统的DNS时,它们都不是Visual FoxPro的DBC中的连接对象,就不能用以上的方法控制,我们可以设定Visual FoxPro的环境:SQLSETPROP(0,"DispLogin" ,3)注意我对第一个参数赋值为0,表明设定的是对Visual FoxPro的系统环境进行设定。值得注意的是,系统环境设定只是与这种即时产生连接有效,对存放在DBC中的连接对象并不起作用,根据连接对象建立的连接的有关属性还是要通过DBSETPROP()来设定。断开连接建立的连接就应该再不用时及时断开,断开连接请使用SqlDisConnect()函数。Local hConn
hconn=SQLCONNECT("NorthWind")
?SQLDISCONNECT(hconn)
//返回1表示连接断开成功这里有三个问题提请大家注意。第一,如果hconn是一个有效的连接句柄,对它执行SqlDisConn()函数会有这样的结果,成功返回1,其他表示断开操作失败;第二:如果hconn不是一个有效的连接句柄,Visual FoxPro将触发一个错误,错误代号为1466,怎样判断某个连接句柄是不是有效的方法,我们在后文介绍;第三,如果想一下子断开所有的连接,您可以使用这样的方法:SQLDISCONNECT(0)从后端得到数据集得到一个数据集SQLEXEC(nConnectionHandle, [cSQLCommand, [cCursorName]])先来解释一下SQLEXEC()函数:参数nConnectionHandle表示连接句柄;参数cSQLCommand表示要传送的语句,注意此语句一定是数据源认得的语句,而不是Visual FoxPro的语句。这一点从SQL Pass Though这个名字上就能看得出了,顾名思义:Visual FoxPro只不过是是将别人的语言传送给别人,这一点在SQLEXEC()函数体现的特别突出;参数cCursorName表示得到的结果集的名字,如果省略,返回的结果集将以Sqlresult命名。如果返回1表示,执行成功;如果返回0表示正在执行;如果返回负数表示执行失败。如果我们希望得到NorthWind数据库中的Employees表的数据,就可以执行以下命令:SQLEXEC(hconn,"SELECT * FROM EMPLOYEES","MyCursor")
Browse判断连接有效性这是一个非常实际的问题,我们建立了一条连接以后,怎样知道Visual FoxPro与SQL Server的通信是正常的,机连接是确实有效的,只有一种方法——应用这条连接,可以这样:?SQLEXEC(hconn,"")
&&一个空的命令发送,返回1的话证明连接可用!大型语句的传递技巧如果想得到一个更为复杂的结果集合,我们可以传送依据大型的SQL-Select描述。这里有一些经验送给大家:
- 如果传送的语句太大(大约是超过256个字符,其实没有必要去计数只要你觉得语句很大时就应该考虑我的建议,不然您就没有办法传送大型的语句),您直接把要传送的语句放入SQLEXEC()中,Visual FoxPro就会报错,说无法认得此语句,于是大家就又多了诽谤Visual FoxPro的一条把柄!(根据我的研究,在Delphi中也有此问题)解决之道是:把长长的语句先放在一个变量中,再将此变量作为参数赋给SQLEXEC()函数。
- 关于分隔符的使用技巧,Visual FoxPro中分隔字符串使用三种符号:双引号、单引号、方括号。在配置要传送的语句时一定要用双引号作为字符串的分隔符,其他的两个符号应另作他用。
以上的建议绝对经典,以下我们就用实例来说明:在SQL Server中要得到详细销售情况的SQL-Select语句如下(详细的知识见本人撰写的《SELECT-SQL 周周谈》一文):SELECT ORDERS.SHIPNAME,ORDERS.SHIPADDRESS,ORDERS.SHIPCITY,ORDERS.SHIPREGION,ORDERS.SHIPPOSTALCODE,
ORDERS.SHIPCOUNTRY,ORDERS.CUSTOMERID,CUSTOMERS.COMPANYNAME AS CUSTOMERNAM,
EMPLOYEES.FIRSTNAME+" "+EMPLOYEES.LASTNAME AS SALESPERSON,
ORDERS.ORDERID,ORDERS.ORDERDATE,ORDERS.REQUIREDDATE,
ORDERS.SHIPPEDDATE,SHIPPERS.COMPANYNAME AS SHIPPERNAME,
[ORDER DETAILS].PRODUCTID,PRODUCTS.PRODUCTNAME,[ORDER DETAILS].UNITPRICE,[ORDER DETAILS].QUANTITY,[ORDER DETAILS].DISCOUNT,
[ORDER DETAILS].UNITPRICE*[ORDER DETAILS].QUANTITY*(1-[ORDER DETAILS].DISCOUNT) AS EXTENDEDPRICE, ORDERS.FREIGHT
FROM ((((EMPLOYEES INNER JOIN ORDERS ON EMPLOYEES.EMPLOYEEID=ORDERS.EMPLOYEEID)
INNER JOIN CUSTOMERS ON ORDERS.CUSTOMERID=CUSTOMERS.CUSTOMERID)
INNER JOIN SHIPPERS ON ORDERS.SHIPVIA=SHIPPERS.SHIPPERID)
INNER JOIN [ORDER DETAILS] ON ORDERS.ORDERID=[ORDER DETAILS].ORDERID)
INNER JOIN PRODUCTS ON [ORDER DETAILS].PRODUCTID=PRODUCTS.PRODUCTID这是一句12行,不计空格864个字符的大型语句,现在要通过Visual FoxPro的SQLEXEC()函数传送:local csq
&&配置待传送的语句
csql=csql+"SELECT ORDERS.SHIPNAME,ORDERS.SHIPADDRESS,ORDERS.SHIPCITY,ORDERS.SHIPREGION,"
csql=csql+"ORDERS.SHIPPOSTALCODE,ORDERS.SHIPCOUNTRY,ORDERS.CUSTOMERID,"
csql=csql+"CUSTOMERS.COMPANYNAME AS CUSTOMERNAM,"
csql=csql+"EMPLOYEES.FIRSTNAME+" "+EMPLOYEES.LASTNAME AS SALESPERSON,ORDERS.ORDERID,ORDERS.ORDERDATE,"
csql=csql+"ORDERS.REQUIREDDATE,ORDERS.SHIPPEDDATE,SHIPPERS.COMPANYNAME AS SHIPPERNAME,"
csql=csql+"[ORDER DETAILS].PRODUCTID,PRODUCTS.PRODUCTNAME,[ORDER DETAILS].UNITPRICE,"
csql=csql+"[ORDER DETAILS].QUANTITY,[ORDER DETAILS].DISCOUNT,"
csql=csql+"[ORDER DETAILS].UNITPRICE*[ORDER DETAILS].QUANTITY*(1-[ORDER DETAILS].DISCOUNT) AS EXTENDEDPRICE,"
csql=csql+"ORDERS.FREIGHT"
csql=csql+" FROM ((((EMPLOYEES INNER JOIN ORDERS ON EMPLOYEES.EMPLOYEEID=ORDERS.EMPLOYEEID)"
csql=csql+" INNER JOIN CUSTOMERS ON ORDERS.CUSTOMERID=CUSTOMERS.CUSTOMERID)"
csql=csql+" INNER JOIN SHIPPERS ON ORDERS.SHIPVIA=SHIPPERS.SHIPPERID)"
csql=csql+" INNER JOIN [ORDER DETAILS] ON ORDERS.ORDERID=[ORDER DETAILS].ORDERID)"
csql=csql+" INNER JOIN PRODUCTS ON [ORDER DETAILS].PRODUCTID=PRODUCTS.PRODUCTID"
&&传送,得到的光标名称为默认的SqlResult
SQLEXEC(hconn,csql)解释:由于语句很长,我花费了多条赋值语句才完成配置这个字符串。大家注意到了我选用双引号作为字符串分隔符,这里面是有大学问的。在SQL Server中有待空格的表名如:Order Details,当它用于SQL语句时,就必须要用方括号标示,所以如果在Visual FoxPro中如果用方括号分割字符串的话,就会有冲突!;单引号是SQL Server中分隔字符串、日期值的符号,在Visual FoxPro中如果使用单引号分隔字符串的话,也会造成冲突,当然在以上语句中我们没有看到单引号的身影,不过我们做一个变动,大家就会感到问题的存在了!local csq
&&配置待传送的语句
csql=csql+"SELECT ORDERS.SHIPNAME,ORDERS.SHIPADDRESS,ORDERS.SHIPCITY,ORDERS.SHIPREGION,"
csql=csql+"ORDERS.SHIPPOSTALCODE,ORDERS.SHIPCOUNTRY,ORDERS.CUSTOMERID,"
csql=csql+"CUSTOMERS.COMPANYNAME AS CUSTOMERNAM,"
csql=csql+"EMPLOYEES.FIRSTNAME+" "+EMPLOYEES.LASTNAME AS SALESPERSON,ORDERS.ORDERID,ORDERS.ORDERDATE,"
csql=csql+"ORDERS.REQUIREDDATE,ORDERS.SHIPPEDDATE,SHIPPERS.COMPANYNAME AS SHIPPERNAME,"
csql=csql+"[ORDER DETAILS].PRODUCTID,PRODUCTS.PRODUCTNAME,[ORDER DETAILS].UNITPRICE,"
csql=csql+"[ORDER DETAILS].QUANTITY,[ORDER DETAILS].DISCOUNT,"
csql=csql+"[ORDER DETAILS].UNITPRICE*[ORDER DETAILS].QUANTITY*(1-[ORDER DETAILS].DISCOUNT) AS EXTENDEDPRICE,"
csql=csql+" ORDERS.FREIGHT"
csql=csql+" FROM ((((EMPLOYEES INNER JOIN ORDERS ON EMPLOYEES.EMPLOYEEID=ORDERS.EMPLOYEEID)"
csql=csql+" INNER JOIN CUSTOMERS ON ORDERS.CUSTOMERID=CUSTOMERS.CUSTOMERID)"
csql=csql+" INNER JOIN SHIPPERS ON ORDERS.SHIPVIA=SHIPPERS.SHIPPERID)"
csql=csql+" INNER JOIN [ORDER DETAILS] ON ORDERS.ORDERID=[ORDER DETAILS].ORDERID)"
csql=csql+" INNER JOIN PRODUCTS ON [ORDER DETAILS].PRODUCTID=PRODUCTS.PRODUCTID"
csql=csql+" WHERE ORDERS.ORDERDATE BETWEEN "1996-10-01" AND "1997-09-30""
csql=csql+" AND ORDERS.CUSTOMERID LIKE "%C%""
csql=csql+" AND [ORDER DETAILS].QUANTITY>50"
&&传送,得到的光标名称为默认的SqlResult
SQLEXEC(hconn,csql)传递变量上面一个例子充分证明了我关于字符串分隔符的经验,接着我又为大家提出了一个新课题:怎样传递变量到SQL Server中。上例中,我们为语句加入了Where字句,下面我们把其中的条件值变为变量,这样这句SQL传递才变得真正的有意义!我以为关于变量的传递有两种方法,下面逐一介绍。第一种是利用:问号+变量来传递的,这是一种常用的方法,用起来也很简单,不用担心变量的类型,ODBC会自动的转化。下面的例子中就涉及到了日期型、数值型、字符型变量,我们根本不用担心变量类型的转化,只要在前面加上问号填入语句中就行了:local csq
&&要传送的变量
dDate1={^1996-10-01}
dDate2={^1997-09-30}
cCustomerID="%C%"
nQty=50
&&配置待传送的语句
csql=csql+"SELECT ORDERS.SHIPNAME,ORDERS.SHIPADDRESS,ORDERS.SHIPCITY,ORDERS.SHIPREGION,"
csql=csql+"ORDERS.SHIPPOSTALCODE,ORDERS.SHIPCOUNTRY,ORDERS.CUSTOMERID,"
csql=csql+"CUSTOMERS.COMPANYNAME AS CUSTOMERNAM,"
csql=csql+"EMPLOYEES.FIRSTNAME+" "+EMPLOYEES.LASTNAME AS SALESPERSON,ORDERS.ORDERID,ORDERS.ORDERDATE,"
csql=csql+"ORDERS.REQUIREDDATE,ORDERS.SHIPPEDDATE,SHIPPERS.COMPANYNAME AS SHIPPERNAME,"
csql=csql+"[ORDER DETAILS].PRODUCTID,PRODUCTS.PRODUCTNAME,[ORDER DETAILS].UNITPRICE,"
csql=csql+"[ORDER DETAILS].QUANTITY,[ORDER DETAILS].DISCOUNT,"
csql=csql+"[ORDER DETAILS].UNITPRICE*[ORDER DETAILS].QUANTITY*(1-[ORDER DETAILS].DISCOUNT) AS EXTENDEDPRICE,"
csql=csql+" ORDERS.FREIGHT"
csql=csql+" FROM ((((EMPLOYEES INNER JOIN ORDERS ON EMPLOYEES.EMPLOYEEID=ORDERS.EMPLOYEEID)"
csql=csql+" INNER JOIN CUSTOMERS ON ORDERS.CUSTOMERID=CUSTOMERS.CUSTOMERID)"
csql=csql+" INNER JOIN SHIPPERS ON ORDERS.SHIPVIA=SHIPPERS.SHIPPERID)"
csql=csql+" INNER JOIN [ORDER DETAILS] ON ORDERS.ORDERID=[ORDER DETAILS].ORDERID)"
csql=csql+" INNER JOIN PRODUCTS ON [ORDER DETAILS].PRODUCTID=PRODUCTS.PRODUCTID"
csql=csql+" WHERE ORDERS.ORDERDATE BETWEEN ?dDate1 AND ?dDate2"
csql=csql+" AND ORDERS.CUSTOMERID LIKE ?cCustomerID"
csql=csql+" AND [ORDER DETAILS].QUANTITY>?nQty"
&&传送,得到的光标名称为默认的SqlResult
SQLEXEC(hconn,csql)分析:我们先来看一看书写的规范,请看:csql=csql+" WHERE ORDERS.ORDERDATE BETWEEN ?dDate1 AND ?dDate2"
csql=csql+" AND ORDERS.CUSTOMERID LIKE ?cCustomerID"
csql=csql+" AND [ORDER DETAILS].QUANTITY>?nQty"问号+变量作为参数放置在SQL语句中,你也许会奇怪:问号+变量怎么还要放在Visual FoxPro的双引号里面,这不是作为一个字符串了吗?我们首先要清楚Visual FoxPro的SQLEXEC()传递的只能是一个字符串,所以问号+变量应当作为字符串的一部分!SQLEXEC()会将这个字符串给ODBC,ODBC分析一下这个字符串,他会发现问号+变量的特殊写法,于是它传递给SQL Server的语句是(整句太长,我就选择最后一段):WHERE ORDERS.ORDERDATE BETWEEN @P1 AND @P2 AND ORDERS.CUSTOMERID LIKE @P3 AND [ORDER DETAILS].QUANTITY>@P4 ", N"@P1 datetime,@P2 datetime,@P3 varchar(3),@P4 float", "Oct 1 1996 12:00AM", "Sep 30 1997 12:00AM", "%C%", 5.000000000000000e+001看到没有,所有变量都被重新命名,并用SQL Server的写法规范!第二种方法是利用Visual FoxPro把变量合并到语句中去,产生一个字符串,这样的话传递给SQL Server的语句就是一个完整的字符串,其中不带变量,这样做的好处是更有效率,因为ODBC不用处理变量的传递,SQL Server也不用处理变量信息了。local csq
&&要传送的变量
dDate1={^1996-10-01}
dDate2={^1997-09-30}
cCustomerID="%C%"
nQty=50
&&配置待传送的语句
csql=csql+"SELECT ORDERS.SHIPNAME,ORDERS.SHIPADDRESS,ORDERS.SHIPCITY,ORDERS.SHIPREGION,"
csql=csql+"ORDERS.SHIPPOSTALCODE,ORDERS.SHIPCOUNTRY,ORDERS.CUSTOMERID,"
csql=csql+"CUSTOMERS.COMPANYNAME AS CUSTOMERNAM,"
csql=csql+"EMPLOYEES.FIRSTNAME+" "+EMPLOYEES.LASTNAME AS SALESPERSON,ORDERS.ORDERID,ORDERS.ORDERDATE,"
csql=csql+"ORDERS.REQUIREDDATE,ORDERS.SHIPPEDDATE,SHIPPERS.COMPANYNAME AS SHIPPERNAME,"
csql=csql+"[ORDER DETAILS].PRODUCTID,PRODUCTS.PRODUCTNAME,[ORDER DETAILS].UNITPRICE,"
csql=csql+"[ORDER DETAILS].QUANTITY,[ORDER DETAILS].DISCOUNT,"
csql=csql+"[ORDER DETAILS].UNITPRICE*[ORDER DETAILS].QUANTITY*(1-[ORDER DETAILS].DISCOUNT) AS EXTENDEDPRICE,"
csql=csql+" ORDERS.FREIGHT"
csql=csql+" FROM ((((EMPLOYEES INNER JOIN ORDERS ON EMPLOYEES.EMPLOYEEID=ORDERS.EMPLOYEEID)"
csql=csql+" INNER JOIN CUSTOMERS ON ORDERS.CUSTOMERID=CUSTOMERS.CUSTOMERID)"
csql=csql+" INNER JOIN SHIPPERS ON ORDERS.SHIPVIA=SHIPPERS.SHIPPERID)"
csql=csql+" INNER JOIN [ORDER DETAILS] ON ORDERS.ORDERID=[ORDER DETAILS].ORDERID)"
csql=csql+" INNER JOIN PRODUCTS ON [ORDER DETAILS].PRODUCTID=PRODUCTS.PRODUCTID"
ccsql=csql+" WHERE ORDERS.ORDERDATE BETWEEN ""+TRANSFORM(dDate1)+"" AND ""+TRANSFORM(dDate2)+"""
csql=csql+" AND ORDERS.CUSTOMERID LIKE "%"+cCustomerID+"%""
csql=csql+" AND [ORDER DETAILS].QUANTITY>"+TRANSFORM(nQty)
&&传送,得到的光标名称为默认的SqlResult
SQLEXEC(hconn,csql)这里的写法是由一些技巧的,就是努力做到模仿SQL Server的规范塑造字符串:ccsql=csql+" WHERE ORDERS.ORDERDATE BETWEEN ""+TRANSFORM(dDate1)+"" AND ""+TRANSFORM(dDate2)+"""
csql=csql+" AND ORDERS.CUSTOMERID LIKE "%"+cCustomerID+"%""
csql=csql+" AND [ORDER DETAILS].QUANTITY>"+TRANSFORM(nQty)如果将变量计算后代入,大家会发现如下的字符串:"WHERE ORDERS.ORDERDATE BETWEEN "1996-10-01" AND "1997-09-30" AND ORDERS.CUSTOMERID LIKE "%C%" AND [ORDER DETAILS].QUANTITY>50"看这就是前文我们没有涉及到变量传递的那句SQL语句,很简单吧——合并变量到SQL语句中,塑造纯正的SQL 语句!完全是Visual FoxPro在字符串计算方面的技巧,这里值得一提的是TransForm()函数,这是个很有用的函数,它能够将各种类型的值改造成为标准的字符串,特别像日期型这样做在多种写法的数据,他也能轻易规范!