一个复合查询方法
2007-11-11 05:33:22 来源:WEB开发网 闂傚倸鍊搁崐鎼佸磹閹间礁纾归柟闂寸绾惧綊鏌熼梻瀵割槮缁炬儳缍婇弻鐔兼⒒鐎靛壊妲紒鐐劤缂嶅﹪寮婚悢鍏尖拻閻庨潧澹婂Σ顔剧磼閻愵剙鍔ょ紓宥咃躬瀵鎮㈤崗灏栨嫽闁诲酣娼ф竟濠偽i鍓х<闁绘劦鍓欓崝銈囩磽瀹ュ拑韬€殿喖顭烽幃銏ゅ礂鐏忔牗瀚介梺璇查叄濞佳勭珶婵犲伣锝夘敊閸撗咃紲闂佺粯鍔﹂崜娆撳礉閵堝洨纾界€广儱鎷戦煬顒傗偓娈垮枛椤兘骞冮姀銈呯閻忓繑鐗楃€氫粙姊虹拠鏌ュ弰婵炰匠鍕彾濠电姴浼i敐澶樻晩闁告挆鍜冪床闂備浇顕栭崹搴ㄥ礃閿濆棗鐦遍梻鍌欒兌椤㈠﹤鈻嶉弴銏犵闁搞儺鍓欓悘鎶芥煛閸愩劎澧曠紒鈧崘鈹夸簻闊洤娴烽ˇ锕€霉濠婂牏鐣洪柡灞诲妼閳规垿宕卞▎蹇撴瘓缂傚倷闄嶉崝搴e垝椤栫偛桅闁告洦鍨扮粻鎶芥倵閿濆簼绨藉ù鐘荤畺濮婃椽妫冨☉娆愭倷闁诲孩鐭崡鎶芥偘椤曗偓瀹曞爼顢楁径瀣珫婵犳鍣徊鍓р偓绗涘洤绠查柛銉墮閽冪喖鏌i弬鎸庢喐闁荤喎缍婇弻娑⑩€﹂幋婵囩亪濡炪値鍓欓悧鍡涒€旈崘顔嘉ч幖绮光偓鑼嚬缂傚倷绶¢崰妤呭箰閹间焦鍋╅柣鎴f绾偓闂佺粯鍔曠粔闈浳涢崘顔兼槬闁逞屽墯閵囧嫰骞掗幋婵愪紑閻庤鎸风粈渚€鍩為幋锔藉亹闁圭粯甯╂导鈧紓浣瑰劤瑜扮偟鍒掑▎鎾宠摕婵炴垶鐭▽顏堟煙鐟欏嫬濮囨い銉︾箞濮婃椽鏌呴悙鑼跺濠⒀傚嵆閺岀喖鎼归锝呯3闂佹寧绻勯崑娑㈠煘閹寸姭鍋撻敐搴樺亾椤撴稒娅婇柡灞界У濞碱亪骞忕仦钘夊腐闂備焦鐪归崐鏇㈠箠閹邦喗顫曢柟鎯х摠婵挳鏌涢幘鏉戠祷闁告挸宕—鍐Χ閸℃浠搁梺鑽ゅ暱閺呮盯鎮鹃悜钘壩ㄧ憸澶愬磻閹剧粯鏅查幖绮瑰墲閻忓秹姊虹紒妯诲鞍婵炲弶锕㈡俊鐢稿礋椤栨氨鐤€闂傚倸鐗婄粙鎰姳閼测晝纾藉ù锝堟閻撴劖鎱ㄥΟ绋垮婵″弶鍔欓獮妯兼嫚閼碱剦妲伴梻浣稿暱閹碱偊宕愭繝姣稿洭寮舵惔鎾存杸濡炪倖姊婚妴瀣啅閵夛负浜滄い鎾跺仜濡插鏌i敐鍥у幋妤犵偞甯¢獮瀣籍閳ь剟鎮楁繝姘拺閻熸瑥瀚崕妤呮煕濡 鍋撻悢鎻掑緧婵犵數濮烽弫鍛婃叏閻戣棄鏋侀柛娑橈攻閸欏繑銇勯幘鍗炵仼缁炬儳顭烽弻鐔煎礈瑜忕敮娑㈡煃闁垮鐏﹂柕鍥у楠炴帡宕卞鎯ь棜缂傚倸鍊风粈渚€藝闁秴鏋佸┑鐘虫皑瀹撲線鏌涢埄鍐姇闁稿﹦鍏橀弻娑樷攽閸℃浼€濡炪倖姊归崝鏇㈠煘閹达附鍊婚柛銉㈡櫇鏍¢梻浣告啞閹稿鎮烽敂鐣屸攳濠电姴娲﹂崵鍐煃閸濆嫬鏆熼柨娑欑矒濮婇缚銇愰幒鎴滃枈闂佸憡鐟ユ鎼佸煝閹炬枼鍫柛顐ゅ枔閸樻悂鏌h箛鏇炰户缁绢厼鐖煎畷鎴﹀箻鐠囪尙鐤€婵炶揪绲介幉锟犲磹椤栫偞鈷戠痪顓炴噹娴滃綊鎮跺☉鏍у姦闁糕斁鍋撳銈嗗笒閸燁偊鎯冨ú顏呯厸濞达絽婀辨晶顏堟煃鐟欏嫬鐏撮柟顔界懇瀵爼骞嬮悩杈敇闂傚倷绀佸﹢杈ㄧ仚闂佺濮ょ划搴ㄥ礆閹烘绫嶉柛顐ゅ枎娴犺櫣绱撴担鍓插創妞ゆ洘濞婇弫鍐磼濞戞艾骞堥梻浣告惈濞层垽宕濆畝鍕€堕柣妯肩帛閻撴洟鏌熼懜顒€濡煎ù婊勫劤閳规垿鏁嶉崟顐℃澀闂佺ǹ锕ラ悧鐘茬暦濠靛鏅濋柍褜鍓熼垾锕傚锤濡も偓閻掑灚銇勯幒宥堝厡缂佺姴澧介埀顒€鍘滈崑鎾斥攽閻樿京绐旈柛瀣殔閳规垿顢欑涵鐑界反濠电偛鎷戠徊鍨i幇鏉跨闁瑰啿纾崰鎾诲箯閻樼粯鍤戦柤绋跨仛濮f劙姊婚崒姘偓鐑芥嚄閼哥數浠氭繝鐢靛仜椤曨參宕楀Ο渚殨妞ゆ劑鍊栫€氭氨鈧懓澹婇崰鏍р枔閵婏妇绡€闁汇垽娼ф牎缂佺偓婢樼粔鐟邦嚕閺屻儱绠甸柟鐑樼箘閸炵敻鏌i悩鐑橆仩閻忓繈鍔岄蹇涘Ψ瑜夐崑鎾舵喆閸曨剙纰嶅┑鈽嗗亝缁诲倿锝炶箛娑欐優闁革富鍘鹃敍婊冣攽閳藉棗鐏犻柟纰卞亰閿濈偛顓奸崶鈺冿紳婵炶揪缍侀ˉ鎾诲礉瀹ュ鐓欑紒瀣仢閺嗛亶鏌i敐鍥у幋妤犵偛顑夐弫鍐焵椤掑倻涓嶅┑鐘崇閸嬶綁鏌涢妷鎴濆暟妤犲洭鎮楃憴鍕碍缂佸鎸抽垾鏃堝礃椤斿槈褔鏌涢埄鍏狀亪妫勫鍥╃=濞达絽澹婇崕鎰版煕閵娿儱顣崇紒顔碱儏椤撳吋寰勭€n亖鍋撻柨瀣ㄤ簻闁瑰搫绉堕ˇ锔锯偓娈垮枛閻忔繈鍩為幋锕€鐓¢柛鈩冾殘娴狀垶姊洪崨濠庣劶闁告洦鍙庡ú鍛婁繆閵堝繒鍒伴柛鐕佸灦瀹曟劙宕归锝呭伎濠碘槅鍨抽崢褎绂嶆ィ鍐╁€垫慨妯煎亾鐎氾拷

核心提示:几乎每个完整的应用程序都会需要一个复合查询,建立一个功能强大的复合查询首先必须要能够动态生成查询条件,一个复合查询方法,其次应该能够对查询到的数据进行修改,最后这个复合查询最好能够对一对多的两个表建立条件进行查询,ok!现在所有的任务都完成了,做完所有的这一切,在VFP里建立查询的方法主要有这么几种:一是使用VFP中自
几乎每个完整的应用程序都会需要一个复合查询。建立一个功能强大的复合查询首先必须要能够动态生成查询条件,其次应该能够对查询到的数据进行修改,最后这个复合查询最好能够对一对多的两个表建立条件进行查询。
在VFP里建立查询的方法主要有这么几种:一是使用VFP中自带的SearchClass类;二是建立一个查询;三是建立一个视图,其中包括参数化视图、宏替换Sql语句视图;四是建立一个Grid,将其数据源设置为SQL语句或临时表。
不管哪一种方法,其实质都是使用SQL语句。
这几种方法各有各的优点,也都有缺点。
建立查询的方法最死板,只能建立固定条件的查询,并且不能更新数据,最不能满足要求。
SearchClass类功能强大,但是它只能对一个表建立条件进行查询,并且它的源代码太复杂了,几乎难以进行修改定制;(初学者想必都有过用表单向导建立表单后试图修改txtbtn类、SearchClass类的经历吧!看到源代码后有几个没昏倒?)用将Grid的数据源设置为SQL语句或临时表的方法无法修改/更新数据,刷新数据也比较困难。(这方面的问题在网易虚拟社区VFP版上有过许多讨论,大家可以去看看。)建立视图的方法中,参数化视图也太简单。不管是用表单控件的值作参数还是用给参数两端加上引号的方法都只能对固定的字段进行查询。如果是复合查询,难道要先建立几十个视图吗?最有前途的办法还是用宏替换SQL语句建立视图的办法。视图有着能够对数据进行修改/更新的优点,如果能够动态生成查询条件,那么就是最完美的查询了。
建立宏替换sql语句视图的具体办法是先动态生成一个Sql语句sqlstatement,然后用宏替换的方法使用Create Sql view viewname as &sqlstatement来动态建立视图,最后将数据动态显示在一个Grid控件中。
看到这里,VFP大虾们怕会大喊:Stop!你当我是菜鸟啊!你的办法从理论上虽然行的通,但实际做起来就会碰到查询结果在表格上数据无法刷新的难题。俺早就试过不行了!你想骗稿费啊!
嘿嘿,这个难题偏偏给我解决了!这就是我洋洋得意的写这篇文章的原因!
问题的解决
==========
几个月前(好可怜^0^.....),我参照VFP的Sample中的Solution里的Interactively Bulid a sqlstatement示例建立了一个复合查询,想将它集成到我自己的程序中。由于该示例是用browse窗口来显示查询结果的,而我自己的应用程序使用了顶层表单,结果编译后运行才发现Browse窗口在顶层表单中无法显示。于是我建立了一个有两个表单的表单集,用一个表单makesql动态生成sql语句,在另一个表单form1上用grid来显示查询结果的数据,在给Grid设置数据源的时候,问题来了。首先使用临时表来作为表格的数据源,结果第一次查询正确,更改条件进行第二次查询时碰到了众所周知的"不能更新临时表"的错误;使用sql语句倒是可以,可是在表格显示数据前会莫名其妙的先出现一个Browse窗口,必须关闭它后才会显示表格,由于browse窗口在使用顶层表单的程序中无法显示,结果导致程序无法继续执行,这个办法也不行。
最后只能使用Create sql view viewname as &sqlstatement的办法了,先随便建立一个视图tempview,把表格的Recordsourcetype属性设置为1-别名,Recordsource属性设置为视图别名tempview,在表单makesql上建立sql语句后的代码中使用create sql view temp view as &sqlstatement建立视图Tempview.
执行后发现,第一次查询正确,更改查询条件后再次查询,出现"视图已存在,要改写吗?"的情况,按下"确定"后,出现的表格中没有数据。
避免出现对话框的问题好解决,在建立视图前先用rename view tempview to oldview,然后用Delete view oldview将旧的视图删除就可以了。代码如下:
****************************************************************************
set database to databasename &&databasename是你的数据库名称
&&注意:即使你打开了数据库也必须写这个语句!否则会出现"找不到数据库"的错误。
if used("tempview")
rename view tempview to oldview
delete view oldview
endif
Create sql view tempview as &sqlstatement
=requery()
IF _TALLY = 0
#DEFINE MSG_LOC "没有找到符合条件的纪录!"
#DEFINE TITLE_LOC "没有找到纪录"
=MESSAGEBOX(MSG_LOC,64+0+0,TITLE_LOC)
ELSE
thisform.hide
thisformset.form1.show
Endif
*****************************************************************************
但是这样做了以后,表格上没有数据的问题仍然存在。查找资料后发现,用Create sql view语句编程建立视图的方法,建立视图后要先保存视图定义,再打开视图后视图中才有数据。因此,必须将Creatsql view语句部分代码修改如下:
************************************************
Create sql view tempview as &sqlstatement
use
use tempview
************************************************
满以为这下问题解决了,结果更惨。出现的表格上不但没有数据,连表头、网格都不见了!这个问题
百思不得其解,查找资料也没有结果,最后不了了之,一直困扰了我几个月。
就在昨晚,我上床的时候突然灵光一现:既然表格无法动态加载数据源视图,那么干脆连包含表格的表单也动态生成!只要表单动态生成,那么表单上的表格对象的数据源不就完全重新加载了吗?也用不着刷新什么的了!而且,用这个方法也用不着先建立一个tempview视图,完全在程序中动态生成就可以了。主意一定,马上下床动手。将原来包含表格的表单form1删除,在上述的代码中最后一句
thisformset.form1.show前插入以下代码:
*************************************************************
thisformset.addobject("form1","form")
with thisformset.form1
.caption="查询结果"
.width=600
.height=400
.Autocenter=.t.
.controlbox=.f.
endwith
thisformset.form1.addobject("cmdReturn1","cmdReturn")
with thisformset.form1.cmdReturn1
.top=360
.left=270
endwith
thisformset.form1.addobject("grid1","gird")
with thisformset.form1.grid1
.Recordsourcetype=1
.Recordsource="tempview"
.top=10
.left=20
.height=300
.width=560
endwith
**************************************************************
在程序的最后加入:
*********************************************
Define class cmdReturn as commandbutton
caption="返回"
procdure click
thisform.release
endproc
enddefine
*********************************************
这下总可以了吧?运行程序,结果出现对话框"在事件或方法中不能嵌套类定义!"。我@#$%&*....什
么嘛!教科书、帮助文件中的示例prg都是这么写的啊!
不过好在还有办法,我手工建立一个类总行了吧!
在类库mybut中新建一个按钮类cmdReturn,设置它的cation属性为"返回",click事件代码为thisform.release。在上面的代码前插入set Classlib to mybut additive(注意:如果不加additive参数,将关闭所有之前打开的类库!),然后将最后的类定义语句Define...EndDefine全部删除。
运行,新表单出现!且慢,这个表单上怎么什么东西都没有?:-((打开调试器,在"局部"窗口中察看,发现明明有cmdReturn1、Grid1对象啊!怎么回事?仔细察看他们的每个属性,发现原来它们的visible属性都为false!原来,我们平常看到的帮助中的示例都是prg文件,在这些文件中用addobject()方法向表单添加的对象在显示表单后都是可见的。而在表单的scx文件中使用addobject()方法建立的任何东西,其visible属性都为false!
本质上,用createobject()、addobject()方法建立的对象,其实只是在内存中建立了一个对象变量,必须再用语句使它们实例化。比如,我们常用的mainform.show语句就是如此,没有使用show方法,mainform就只是内存中的一个变量,而不是一个表单不可见的实例!反之,thisform.release语句则从根本
上释放了表单上所有的对象变量,从而在内存中完全清除了表单.
而在prg文件中与scx文件中用addobject()方法建立表单和控件的顺序是不一样的,在prg文件中是先向表单添加控件再显示表单,表单上的控件继承了表单的visible属性,当表单实例化时控件也实例化;而在scx文件使用addobject()方法是先显示表单,然后再向表单添加控件的,因此必须手工设置控件的visible属性为Ture。
当然这样比较麻烦,干脆在mybut类库中手工建立一个Resultform表单类,在该表单类上添加一个命令按钮cmdReturn和一个Grid1,设置命令按钮cmdReturn的caption属性为"返回",Click事件代码为thisform.release,设置Grid1的RecordSourceType属性为1-别名,RecordSource属性为Tempview,这样就
不用在代码中手工输入thisformset.form1.cmdReturn1.visible=.t.语句,省事多了。
最终的程序代码如下:
*******************************************************
set database to databasename
if used("tempview")
rename view tempview to oldview
delete view oldview
endif
Create sql view tempview as &sqlstatement
=requery()
IF _TALLY = 0
#DEFINE MSG_LOC "没有找到符合条件的纪录!"
#DEFINE TITLE_LOC "没有找到纪录"
=MESSAGEBOX(MSG_LOC,64+0+0,TITLE_LOC)
ELSE
set Classlib to mybut additive
thisformset.addobject("form1","Resultform")
thisform.hide
thisformset.form1.show
Endif
********************************************************
运行程序,一切ok!终于实现了动态生成查询条件,动态显示结果。只要再用dbsetprop()语句设置视图为可更新,就能对查询结果进行修改/更新了!这是我见到过的最完美的复合查询了!就这么简单?没错,就这么简单!一个困扰数月的问题,研究的时候峰回路转,最终结果却是如此轻松!这就是编程的艺术吧!
这个问题的解决,虽然走了许多弯路,但是也让我们了解了许多VFP的原理,难道不是很值得吗?!光凭书本的知识,你永远也无法了解这些东西的。有人说:读三年的书,还不如写一个月的程序,不是吗?!其他:
我在本文中省略了开头的动态生成sql语句的部分,具体的做法大家可以研究一下VFP自带的示例应用程序solution中databases目录下的view/queries目录中的Interactively Bulid a sql statement示例,你甚至可以稍作修改就在你自己的程序中使用该表单。
我曾经就其原理写了一篇文章《交互式建立sql复合查询-vfp示例应用程序详解》贴在网易虚拟社区VFP版,有兴趣的朋友可以在精华区查到,读完该篇文章,你应该可以自行修改该程序使之能够对一对多数据库进行查询了。因为该文篇幅很长,又比较枯燥,就不在这儿解说了。
要注意的是:原示例程序用于生成sql语句查询。要改为用于建立sql视图,必须作一些修改:
1、在sql查询中不必限定表别名和数据库名,而建立sql视图却必须这样做。因此需要修改makesql表单的
自定义方法bldsql的代码,将源代码下面的部分:
**************************************************************************
IF !EMPTY(lcOperand)
lcValue2 = THISFORM.ValidateType(THIS.cboField2.Value,lcValue2)
lcWHERE = lcOperand + " " + lcField2 + " " + ;
lcRelation2 + " " + lcValue2
ENDIF
** Create the first part of the WHERE condition
lcWHERE = "WHERE " + lcField1 + " " + lcRelation1 + " " + lcValue1 + " " + lcWHERE
** Create the full SQL command using the base table for the form
lcSQL = "SELECT * FROM " + lcAlias + " " + lcWHERE
****************************************************************************
修改为:
****************************************************************************
If !empty(lcOperand)
lcValue2 = thisform.ValidateType(this.cboField2.value,lcValue2)
lcWhere = lcOperand + " " + lcAlias + "." + lcField2 + " " +;
lcRelation2 + " " +lcValue2
Endif
lcWhere = "Where "+ lcAlias + "." + lcField1 + lcRelation1 + " ";
+ lcValue1 + " " + lcWhere
lcSql = "Select * From " + "DatabaseName!" + lcAlias + " " + lcWhere
****************************************************************************
DatabaseName是你的数据库的名字。以上修改的实质是,给要查询的字段名限定其所在的表别名,给
select form的表别名限定所属的数据库。
2、修改RunSql命令按钮的Click事件代码,将原代码:
*************************************************************************
cMacro = ALLTRIM(THISFORM.edtSQL.Value) + "INTO CURSOR TEMPQUERY"
*************************************************************************
中的 (+ "INTO CURSOR TEMPQUERY")部分删除,将cMacro改为sqlstatement.并将除了下面部分外的全
部代码删除:
*************************
IF USED(lcOldAlias)
SELECT (lcOldAlias)
ENDIF
*************************
,插入上面的最终代码就可以了。
ok!现在所有的任务都完成了。做完所有的这一切,花不了十分钟,你就建立了一个强大的复合查询!
更多精彩
赞助商链接