明輝手游網中心:是一個免費提供流行視頻軟件教程、在線學習分享的學習平臺!

悠哉悠哉,一個復合查詢方法

[摘要]幾乎每個完整的應用程序都會需要一個復合查詢。建立一個功能強大的復合查詢首先必須要能夠動態(tài)生成查詢條件,其次應該能夠對查詢到的數(shù)據進行修改,最后這個復合查詢最好能夠對一對多的兩個表建立條件進行查詢。在VFP里建立查詢的方法主要有這么幾種:一是使用VFP中自帶的SearchClass類;二是建立一個查...
幾乎每個完整的應用程序都會需要一個復合查詢。建立一個功能強大的復合查詢首先必須要能夠動態(tài)生成查詢條件,其次應該能夠對查詢到的數(shù)據進行修改,最后這個復合查詢最好能夠對一對多的兩個表建立條件進行查詢。

在VFP里建立查詢的方法主要有這么幾種:一是使用VFP中自帶的SearchClass類;二是建立一個查詢;三是建立一個視圖,其中包括參數(shù)化視圖、宏替換Sql語句視圖;四是建立一個Grid,將其數(shù)據源設置為SQL語句或臨時表。

不管哪一種方法,其實質都是使用SQL語句。

這幾種方法各有各的優(yōu)點,也都有缺點。

建立查詢的方法最死板,只能建立固定條件的查詢,并且不能更新數(shù)據,最不能滿足要求。

SearchClass類功能強大,但是它只能對一個表建立條件進行查詢,并且它的源代碼太復雜了,幾乎難以進行修改定制;(初學者想必都有過用表單向導建立表單后試圖修改txtbtn類、SearchClass類的經歷吧!看到源代碼后有幾個沒昏倒?)用將Grid的數(shù)據源設置為SQL語句或臨時表的方法無法修改/更新數(shù)據,刷新數(shù)據也比較困難。(這方面的問題在網易虛擬社區(qū)VFP版上有過許多討論,大家可以去看看。)建立視圖的方法中,參數(shù)化視圖也太簡單。不管是用表單控件的值作參數(shù)還是用給參數(shù)兩端加上引號的方法都只能對固定的字段進行查詢。如果是復合查詢,難道要先建立幾十個視圖嗎?最有前途的辦法還是用宏替換SQL語句建立視圖的辦法。視圖有著能夠對數(shù)據進行修改/更新的優(yōu)點,如果能夠動態(tài)生成查詢條件,那么就是最完美的查詢了。建立宏替換sql語句視圖的具體辦法是先動態(tài)生成一個Sql語句sqlstatement,然后用宏替換的方法使用Create Sql view viewname as &sqlstatement來動態(tài)建立視圖,最后將數(shù)據動態(tài)顯示在一個Grid控件中?吹竭@里,VFP大蝦們怕會大喊:Stop!你當我是菜鳥。∧愕霓k法從理論上雖然行的通,但實際做起來就會碰到查詢結果在表格上數(shù)據無法刷新的難題。俺早就試過不行了!你想騙稿費。『俸,這個難題偏偏給我解決了!這就是我洋洋得意的寫這篇文章的原因!問題的解決

幾個月前(好可憐^0^.....),我參照VFP的Sample中的Solution里的Interactively Bulid a sql

statement示例建立了一個復合查詢,想將它集成到我自己的程序中。由于該示例是用browse窗口來顯示查詢結果的,而我自己的應用程序使用了頂層表單,結果編譯后運行才發(fā)現(xiàn)Browse窗口在頂層表單中無法顯示。于是我建立了一個有兩個表單的表單集,用一個表單makesql動態(tài)生成sql語句,在另一個表單form1上用grid來顯示查詢結果的數(shù)據,在給Grid設置數(shù)據源的時候,問題來了。首先使用臨時表來作為表格的數(shù)據源,結果第一次查詢正確,更改條件進行第二次查詢時碰到了眾所周知的"不能更新臨時表"的錯誤;使用sql語句倒是可以,可是在表格顯示數(shù)據前會莫名其妙的先出現(xiàn)一個Browse窗口,必須關閉它后才會顯示表格,由于browse窗口在使用頂層表單的程序中無法顯示,結果導致程序無法繼續(xù)執(zhí)行,這個辦法也不行。

最后只能使用Create sql view viewname as &sqlstatement的辦法了,先隨便建立一個視圖tempview,把表格的Recordsourcetype屬性設置為1-別名,Recordsource屬性設置為視圖別名tempview,

在表單makesql上建立sql語句后的代碼中使用create sql view temp view as &sqlstatement建立視圖Tempview.

執(zhí)行后發(fā)現(xiàn),第一次查詢正確,更改查詢條件后再次查詢,出現(xiàn)"視圖已存在,要改寫嗎?"的情況,按下"確定"后,出現(xiàn)的表格中沒有數(shù)據。

避免出現(xiàn)對話框的問題好解決,在建立視圖前先用rename view tempview to oldview,然后用Delete view oldview將舊的視圖刪除就可以了。代碼如下:



set database to databasename &&databasename是你的數(shù)據庫名稱

&&注意:即使你打開了數(shù)據庫也必須寫這個語句!否則會出現(xiàn)"找不到數(shù)據庫"的錯誤。

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

*****************************************************************************

但是這樣做了以后,表格上沒有數(shù)據的問題仍然存在。查找資料后發(fā)現(xiàn),用Create sql view語句編

程建立視圖的方法,建立視圖后要先保存視圖定義,再打開視圖后視圖中才有數(shù)據。因此,必須將Creat

sql view語句部分代碼修改如下:

************************************************

Create sql view tempview as &sqlstatement

use

use tempview

************************************************

滿以為這下問題解決了,結果更慘。出現(xiàn)的表格上不但沒有數(shù)據,連表頭、網格都不見了!這個問題

百思不得其解,查找資料也沒有結果,最后不了了之,一直困擾了我?guī)讉月。

就在昨晚,我上床的時候突然靈光一現(xiàn):既然表格無法動態(tài)加載數(shù)據源視圖,那么干脆連包含表格的

表單也動態(tài)生成!只要表單動態(tài)生成,那么表單上的表格對象的數(shù)據源不就完全重新加載了嗎?也用不著刷新什么的了!而且,用這個方法也用不著先建立一個tempview視圖,完全在程序中動態(tài)生成就可以了主意一定,馬上下床動手。將原來包含表格的表單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

*********************************************

這下總可以了吧?運行程序,結果出現(xiàn)對話框"在事件或方法中不能嵌套類定義!"。我@#$%&*....什么嘛!教科書、幫助文件中的示例prg都是這么寫的啊!
不過好在還有辦法,我手工建立一個類總行了吧!在類庫mybut中新建一個按鈕類cmdReturn,設置它的cation屬性為"返回",click事件代碼為
thisform.release。在上面的代碼前插入set Classlib to mybut additive(注意:如果不加additive參數(shù),將關閉所有之前打開的類庫。,然后將最后的類定義語句Define...EndDefine全部刪除。運行,新表單出現(xiàn)!且慢,這個表單上怎么什么東西都沒有?:-((打開調試器,在"局部"窗口中察看,發(fā)現(xiàn)明明有cmdReturn1、Grid1對象!怎么回事?仔細察看他們的每個屬性,發(fā)現(xiàn)原來它們的visible屬性都為false!原來,我們平?吹降膸椭械氖纠际莗rg文件,在這些文件中用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!終于實現(xiàn)了動態(tài)生成查詢條件,動態(tài)顯示結果。只要再用dbsetprop()語句設置視圖為可更新,就能對查詢結果進行修改/更新了!這是我見到過的最完美的復合查詢了!就這么簡單?沒錯,就這么簡單!一個困擾數(shù)月的問題,研究的時候峰回路轉,最終結果卻是如此輕松!這就是編程的藝術吧!
這個問題的解決,雖然走了許多彎路,但是也讓我們了解了許多VFP的原理,難道不是很值得嗎?!光憑書本的知識,你永遠也無法了解這些東西的。有人說:讀三年的書,還不如寫一個月的程序,不是嗎?!其他:

我在本文中省略了開頭的動態(tài)生成sql語句的部分,具體的做法大家可以研究一下VFP自帶的示例應用程序solution中databases目錄下的view/queries目錄中的Interactively Bulid a sql statement示例,你甚至可以稍作修改就在你自己的程序中使用該表單。

我曾經就其原理寫了一篇文章《交互式建立sql復合查詢-vfp示例應用程序詳解》貼在網易虛擬社區(qū)VFP版,有興趣的朋友可以在精華區(qū)查到,讀完該篇文章,你應該可以自行修改該程序使之能夠對一對多數(shù)據庫進行查詢了。因為該文篇幅很長,又比較枯燥,就不在這兒解說了。

要注意的是:原示例程序用于生成sql語句查詢。要改為用于建立sql視圖,必須作一些修改:1、在sql查詢中不必限定表別名和數(shù)據庫名,而建立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ù)據庫的名字。以上修改的實質是,給要查詢的字段名限定其所在的表別名,給

select form的表別名限定所屬的數(shù)據庫。

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)在所有的任務都完成了。做完所有的這一切,花不了十分鐘,你就建立了一個強大的復合查詢!