悠哉悠哉,一個(gè)復(fù)合查詢方法
發(fā)表時(shí)間:2024-02-16 來(lái)源:明輝站整理相關(guān)軟件相關(guān)文章人氣:
[摘要]幾乎每個(gè)完整的應(yīng)用程序都會(huì)需要一個(gè)復(fù)合查詢。建立一個(gè)功能強(qiáng)大的復(fù)合查詢首先必須要能夠動(dòng)態(tài)生成查詢條件,其次應(yīng)該能夠?qū)Σ樵兊降臄?shù)據(jù)進(jìn)行修改,最后這個(gè)復(fù)合查詢最好能夠?qū)σ粚?duì)多的兩個(gè)表建立條件進(jìn)行查詢。在VFP里建立查詢的方法主要有這么幾種:一是使用VFP中自帶的SearchClass類;二是建立一個(gè)查...
幾乎每個(gè)完整的應(yīng)用程序都會(huì)需要一個(gè)復(fù)合查詢。建立一個(gè)功能強(qiáng)大的復(fù)合查詢首先必須要能夠動(dòng)態(tài)生成查詢條件,其次應(yīng)該能夠?qū)Σ樵兊降臄?shù)據(jù)進(jìn)行修改,最后這個(gè)復(fù)合查詢最好能夠?qū)σ粚?duì)多的兩個(gè)表建立條件進(jìn)行查詢。
在VFP里建立查詢的方法主要有這么幾種:一是使用VFP中自帶的SearchClass類;二是建立一個(gè)查詢;三是建立一個(gè)視圖,其中包括參數(shù)化視圖、宏替換Sql語(yǔ)句視圖;四是建立一個(gè)Grid,將其數(shù)據(jù)源設(shè)置為SQL語(yǔ)句或臨時(shí)表。
不管哪一種方法,其實(shí)質(zhì)都是使用SQL語(yǔ)句。
這幾種方法各有各的優(yōu)點(diǎn),也都有缺點(diǎn)。
建立查詢的方法最死板,只能建立固定條件的查詢,并且不能更新數(shù)據(jù),最不能滿足要求。
SearchClass類功能強(qiáng)大,但是它只能對(duì)一個(gè)表建立條件進(jìn)行查詢,并且它的源代碼太復(fù)雜了,幾乎難以進(jìn)行修改定制;(初學(xué)者想必都有過(guò)用表單向?qū)Ы⒈韱魏笤噲D修改txtbtn類、SearchClass類的經(jīng)歷吧!看到源代碼后有幾個(gè)沒(méi)昏倒?)用將Grid的數(shù)據(jù)源設(shè)置為SQL語(yǔ)句或臨時(shí)表的方法無(wú)法修改/更新數(shù)據(jù),刷新數(shù)據(jù)也比較困難。(這方面的問(wèn)題在網(wǎng)易虛擬社區(qū)VFP版上有過(guò)許多討論,大家可以去看看。)建立視圖的方法中,參數(shù)化視圖也太簡(jiǎn)單。不管是用表單控件的值作參數(shù)還是用給參數(shù)兩端加上引號(hào)的方法都只能對(duì)固定的字段進(jìn)行查詢。如果是復(fù)合查詢,難道要先建立幾十個(gè)視圖嗎?最有前途的辦法還是用宏替換SQL語(yǔ)句建立視圖的辦法。視圖有著能夠?qū)?shù)據(jù)進(jìn)行修改/更新的優(yōu)點(diǎn),如果能夠動(dòng)態(tài)生成查詢條件,那么就是最完美的查詢了。建立宏替換sql語(yǔ)句視圖的具體辦法是先動(dòng)態(tài)生成一個(gè)Sql語(yǔ)句sqlstatement,然后用宏替換的方法使用Create Sql view viewname as &sqlstatement來(lái)動(dòng)態(tài)建立視圖,最后將數(shù)據(jù)動(dòng)態(tài)顯示在一個(gè)Grid控件中?吹竭@里,VFP大蝦們怕會(huì)大喊:Stop!你當(dāng)我是菜鳥!你的辦法從理論上雖然行的通,但實(shí)際做起來(lái)就會(huì)碰到查詢結(jié)果在表格上數(shù)據(jù)無(wú)法刷新的難題。俺早就試過(guò)不行了!你想騙稿費(fèi)!嘿嘿,這個(gè)難題偏偏給我解決了!這就是我洋洋得意的寫這篇文章的原因!問(wèn)題的解決
幾個(gè)月前(好可憐^0^.....),我參照VFP的Sample中的Solution里的Interactively Bulid a sql
statement示例建立了一個(gè)復(fù)合查詢,想將它集成到我自己的程序中。由于該示例是用browse窗口來(lái)顯示查詢結(jié)果的,而我自己的應(yīng)用程序使用了頂層表單,結(jié)果編譯后運(yùn)行才發(fā)現(xiàn)Browse窗口在頂層表單中無(wú)法顯示。于是我建立了一個(gè)有兩個(gè)表單的表單集,用一個(gè)表單makesql動(dòng)態(tài)生成sql語(yǔ)句,在另一個(gè)表單form1上用grid來(lái)顯示查詢結(jié)果的數(shù)據(jù),在給Grid設(shè)置數(shù)據(jù)源的時(shí)候,問(wèn)題來(lái)了。首先使用臨時(shí)表來(lái)作為表格的數(shù)據(jù)源,結(jié)果第一次查詢正確,更改條件進(jìn)行第二次查詢時(shí)碰到了眾所周知的"不能更新臨時(shí)表"的錯(cuò)誤;使用sql語(yǔ)句倒是可以,可是在表格顯示數(shù)據(jù)前會(huì)莫名其妙的先出現(xiàn)一個(gè)Browse窗口,必須關(guān)閉它后才會(huì)顯示表格,由于browse窗口在使用頂層表單的程序中無(wú)法顯示,結(jié)果導(dǎo)致程序無(wú)法繼續(xù)執(zhí)行,這個(gè)辦法也不行。
最后只能使用Create sql view viewname as &sqlstatement的辦法了,先隨便建立一個(gè)視圖tempview,把表格的Recordsourcetype屬性設(shè)置為1-別名,Recordsource屬性設(shè)置為視圖別名tempview,
在表單makesql上建立sql語(yǔ)句后的代碼中使用create sql view temp view as &sqlstatement建立視圖Tempview.
執(zhí)行后發(fā)現(xiàn),第一次查詢正確,更改查詢條件后再次查詢,出現(xiàn)"視圖已存在,要改寫嗎?"的情況,按下"確定"后,出現(xiàn)的表格中沒(méi)有數(shù)據(jù)。
避免出現(xiàn)對(duì)話框的問(wèn)題好解決,在建立視圖前先用rename view tempview to oldview,然后用Delete view oldview將舊的視圖刪除就可以了。代碼如下:
set database to databasename &&databasename是你的數(shù)據(jù)庫(kù)名稱
&&注意:即使你打開(kāi)了數(shù)據(jù)庫(kù)也必須寫這個(gè)語(yǔ)句!否則會(huì)出現(xiàn)"找不到數(shù)據(jù)庫(kù)"的錯(cuò)誤。
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 "沒(méi)有找到符合條件的紀(jì)錄!"
#DEFINE TITLE_LOC "沒(méi)有找到紀(jì)錄"
=MESSAGEBOX(MSG_LOC,64+0+0,TITLE_LOC)
ELSE
thisform.hide
thisformset.form1.show
Endif
*****************************************************************************
但是這樣做了以后,表格上沒(méi)有數(shù)據(jù)的問(wèn)題仍然存在。查找資料后發(fā)現(xiàn),用Create sql view語(yǔ)句編
程建立視圖的方法,建立視圖后要先保存視圖定義,再打開(kāi)視圖后視圖中才有數(shù)據(jù)。因此,必須將Creat
sql view語(yǔ)句部分代碼修改如下:
************************************************
Create sql view tempview as &sqlstatement
use
use tempview
************************************************
滿以為這下問(wèn)題解決了,結(jié)果更慘。出現(xiàn)的表格上不但沒(méi)有數(shù)據(jù),連表頭、網(wǎng)格都不見(jiàn)了!這個(gè)問(wèn)題
百思不得其解,查找資料也沒(méi)有結(jié)果,最后不了了之,一直困擾了我?guī)讉(gè)月。
就在昨晚,我上床的時(shí)候突然靈光一現(xiàn):既然表格無(wú)法動(dòng)態(tài)加載數(shù)據(jù)源視圖,那么干脆連包含表格的
表單也動(dòng)態(tài)生成!只要表單動(dòng)態(tài)生成,那么表單上的表格對(duì)象的數(shù)據(jù)源不就完全重新加載了嗎?也用不著刷新什么的了!而且,用這個(gè)方法也用不著先建立一個(gè)tempview視圖,完全在程序中動(dòng)態(tài)生成就可以了主意一定,馬上下床動(dòng)手。將原來(lái)包含表格的表單form1刪除,在上述的代碼中最后一句
thisformset.form1.show前插入以下代碼:
*************************************************************
thisformset.addobject("form1","form")
with thisformset.form1
.caption="查詢結(jié)果"
.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
*********************************************
這下總可以了吧?運(yùn)行程序,結(jié)果出現(xiàn)對(duì)話框"在事件或方法中不能嵌套類定義!"。我@#$%&*....什么嘛!教科書、幫助文件中的示例prg都是這么寫的!
不過(guò)好在還有辦法,我手工建立一個(gè)類總行了吧!在類庫(kù)mybut中新建一個(gè)按鈕類cmdReturn,設(shè)置它的cation屬性為"返回",click事件代碼為
thisform.release。在上面的代碼前插入set Classlib to mybut additive(注意:如果不加additive參數(shù),將關(guān)閉所有之前打開(kāi)的類庫(kù)。缓髮⒆詈蟮念惗x語(yǔ)句Define...EndDefine全部刪除。運(yùn)行,新表單出現(xiàn)!且慢,這個(gè)表單上怎么什么東西都沒(méi)有?:-((打開(kāi)調(diào)試器,在"局部"窗口中察看,發(fā)現(xiàn)明明有cmdReturn1、Grid1對(duì)象啊!怎么回事?仔細(xì)察看他們的每個(gè)屬性,發(fā)現(xiàn)原來(lái)它們的visible屬性都為false!原來(lái),我們平常看到的幫助中的示例都是prg文件,在這些文件中用addobject()方法向表單添加的對(duì)象在顯示表單后都是可見(jiàn)的。而在表單的scx文件中使用addobject()方法建立的任何東西,其visible屬性都為false!本質(zhì)上,用createobject()、addobject()方法建立的對(duì)象,其實(shí)只是在內(nèi)存中建立了一個(gè)對(duì)象變量,必須再用語(yǔ)句使它們實(shí)例化。比如,我們常用的mainform.show語(yǔ)句就是如此,沒(méi)有使用show方法,mainform就只是內(nèi)存中的一個(gè)變量,而不是一個(gè)表單不可見(jiàn)的實(shí)例!反之,thisform.release語(yǔ)句則從根本
上釋放了表單上所有的對(duì)象變量,從而在內(nèi)存中完全清除了表單.而在prg文件中與scx文件中用addobject()方法建立表單和控件的順序是不一樣的,在prg文件中是先向表單添加控件再顯示表單,表單上的控件繼承了表單的visible屬性,當(dāng)表單實(shí)例化時(shí)控件也實(shí)例化;而在scx文件使用addobject()方法是先顯示表單,然后再向表單添加控件的,因此必須手工設(shè)置控件的visible屬性為Ture。
當(dāng)然這樣比較麻煩,干脆在mybut類庫(kù)中手工建立一個(gè)Resultform表單類,在該表單類上添加一個(gè)命令按鈕cmdReturn和一個(gè)Grid1,設(shè)置命令按鈕cmdReturn的caption屬性為"返回",Click事件代碼為thisform.release,設(shè)置Grid1的RecordSourceType屬性為1-別名,RecordSource屬性為Tempview,這樣就
不用在代碼中手工輸入thisformset.form1.cmdReturn1.visible=.t.語(yǔ)句,省事多了。
最終的程序代碼如下:
*******************************************************
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 "沒(méi)有找到符合條件的紀(jì)錄!"
#DEFINE TITLE_LOC "沒(méi)有找到紀(jì)錄"
=MESSAGEBOX(MSG_LOC,64+0+0,TITLE_LOC)
ELSE
set Classlib to mybut additive
thisformset.addobject("form1","Resultform")
thisform.hide
thisformset.form1.show
Endif
********************************************************
運(yùn)行程序,一切ok!終于實(shí)現(xiàn)了動(dòng)態(tài)生成查詢條件,動(dòng)態(tài)顯示結(jié)果。只要再用dbsetprop()語(yǔ)句設(shè)置視圖為可更新,就能對(duì)查詢結(jié)果進(jìn)行修改/更新了!這是我見(jiàn)到過(guò)的最完美的復(fù)合查詢了!就這么簡(jiǎn)單?沒(méi)錯(cuò),就這么簡(jiǎn)單!一個(gè)困擾數(shù)月的問(wèn)題,研究的時(shí)候峰回路轉(zhuǎn),最終結(jié)果卻是如此輕松!這就是編程的藝術(shù)吧!
這個(gè)問(wèn)題的解決,雖然走了許多彎路,但是也讓我們了解了許多VFP的原理,難道不是很值得嗎?!光憑書本的知識(shí),你永遠(yuǎn)也無(wú)法了解這些東西的。有人說(shuō):讀三年的書,還不如寫一個(gè)月的程序,不是嗎?!其他:
我在本文中省略了開(kāi)頭的動(dòng)態(tài)生成sql語(yǔ)句的部分,具體的做法大家可以研究一下VFP自帶的示例應(yīng)用程序solution中databases目錄下的view/queries目錄中的Interactively Bulid a sql statement示例,你甚至可以稍作修改就在你自己的程序中使用該表單。
我曾經(jīng)就其原理寫了一篇文章《交互式建立sql復(fù)合查詢-vfp示例應(yīng)用程序詳解》貼在網(wǎng)易虛擬社區(qū)VFP版,有興趣的朋友可以在精華區(qū)查到,讀完該篇文章,你應(yīng)該可以自行修改該程序使之能夠?qū)σ粚?duì)多數(shù)據(jù)庫(kù)進(jìn)行查詢了。因?yàn)樵撐钠荛L(zhǎng),又比較枯燥,就不在這兒解說(shuō)了。
要注意的是:原示例程序用于生成sql語(yǔ)句查詢。要改為用于建立sql視圖,必須作一些修改:1、在sql查詢中不必限定表別名和數(shù)據(jù)庫(kù)名,而建立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是你的數(shù)據(jù)庫(kù)的名字。以上修改的實(shí)質(zhì)是,給要查詢的字段名限定其所在的表別名,給
select form的表別名限定所屬的數(shù)據(jù)庫(kù)。
2、修改RunSql命令按鈕的Click事件代碼,將原代碼:
*************************************************************************
cMacro = ALLTRIM(THISFORM.edtSQL.Value) + "INTO CURSOR TEMPQUERY"
*************************************************************************
中的 (+ "INTO CURSOR TEMPQUERY")部分刪除,將cMacro改為sqlstatement.并將除了下面部分外的全
部代碼刪除:
*************************
IF USED(lcOldAlias)
SELECT (lcOldAlias)
ENDIF
*************************
,插入上面的最終代碼就可以了。
ok!現(xiàn)在所有的任務(wù)都完成了。做完所有的這一切,花不了十分鐘,你就建立了一個(gè)強(qiáng)大的復(fù)合查詢!