Visual Basic 的數(shù)據(jù)庫編程
作者:孫志剛
Visual Basic作為應(yīng)用程序的開發(fā)“利器”也表現(xiàn)在數(shù)據(jù)庫應(yīng)用程序的開發(fā)上,它良好的界面和強(qiáng)大的控件功能使數(shù)據(jù)庫編程變得簡單多了。但即便如此,數(shù)據(jù)庫應(yīng)用程序的開發(fā)仍然算得上是VB編程中的難點,這是因為你不僅要熟悉VB中關(guān)于數(shù)據(jù)庫編程方面的知識(當(dāng)然這是十分簡單的)還要了解數(shù)據(jù)庫的知識。所以我們先介紹一下數(shù)據(jù)庫的基本知識,算是學(xué)習(xí)數(shù)據(jù)庫編程前的熱身運(yùn)動吧!

一、熱身運(yùn)動
  首先需要聲明是,我們這里介紹的數(shù)據(jù)庫知識都是指的關(guān)系數(shù)據(jù)庫。所謂關(guān)系數(shù)據(jù)庫就是將數(shù)據(jù)表示為表的集合,通過建立簡單表之間的關(guān)系來定義結(jié)構(gòu)的一種數(shù)據(jù)庫。
  不管表在數(shù)據(jù)庫文件中的物理存儲方式如何,它都可以看作一組行和列,與電子表格的行和列類似。在關(guān)系數(shù)據(jù)庫中,行被稱為記錄,而列則被稱為字段。下面是一個客戶表的例子。
  表1 客戶表
客戶號 姓名 地址 城市 街道 郵編
1723 Doe John 1234 Ffth Avenue New York NY 1004
3391 Smith Mary 9876 Myrtle Lavee Bosten MA 6078
3765 Blasel Mortimer 2296j River Road peoria IL 7011

  此表中每一行是一個記錄,它包含了特定客戶的所有信息,而每個記錄則包含了相同類型和數(shù)量的字段:客戶號、姓名等等。
  表 是一種按行與列排列的相關(guān)信息的邏輯組,類似于工作單表。
  字段 數(shù)據(jù)庫表中的每一列稱作一個字段。表是由其包含的各種字段定義的,每個字段描述了它所含有的數(shù)據(jù)。創(chuàng)建一個數(shù)據(jù)庫時,須為每個字段分配一個數(shù)據(jù)類型、最大長度和其它屬性。字段可包含各種字符、數(shù)字甚至圖形。
  記錄 各個客戶有關(guān)的信息存放在表的行,被稱為記錄。一般來說,數(shù)據(jù)庫表創(chuàng)建時任意兩個記錄都不能相同。
  鍵 鍵就是表中的某個字段(或多個字段),它(們)為快速檢索而被索引。鍵可以是唯一的,也可以是非唯一的,取決于它(們)是否允許重復(fù)。唯一鍵可以指定為主鍵,用來唯一標(biāo)識表的每行。例如,在前面的例子中,客戶標(biāo)識號 (客戶號) 是表的主鍵,因為客戶號唯一地標(biāo)識了一個客戶。
  關(guān)系 數(shù)據(jù)庫可以由多個表組成,表與表之間可以以不同的方式相互關(guān)聯(lián)。例如,客戶數(shù)據(jù)庫還可以有一個包含某個客戶的所有定單的表。它只用“客戶號”字段來引用該定單的客戶,而不在定單表中的每項重復(fù)所有客戶信息,如下表所示:
  表2 定貨表

定貨 客戶號 日期 內(nèi)容 數(shù)量
14764 3391 2/23/94 27 $22.95
14932 3391 3/17/94 46 $9.57
15108 8765 2/15/96 27 $22.95

  在這個表中,客戶號字段引用了客戶表中的 客戶號字段,從而把定單和客戶聯(lián)系起來了。可以看到,客戶 3391 (Mary Smith) 在 94 年 2 月 23 日訂購了 27 項,在 94 年 3 月 17 日訂購了 46 項。用來建立關(guān)系的鍵叫做外部鍵,因為它與“外部”表(客戶表)的主鍵關(guān)聯(lián)。
  一對多和多對多關(guān)系 上表中的關(guān)系類型叫做一對多關(guān)系,因為一個客戶可以發(fā)出多個定單,而某個特定的定單只能是一個客戶所發(fā)。也可以建立多對多的關(guān)系。例如,列出所有可以銷售的項(存貨)的盤存表:
表3 盤存表

內(nèi)容 描述 供應(yīng)商 費(fèi)用 盤存
27 Straw Hat Garden Supply Co. $14.00 50
46 Garden gloves Garden Supply Co. $4.50 75
102 hanging floral industries $6.00 137

  從盤存表中,可以看到在客戶和存貨項之間存在多對多的關(guān)系。也就是說,一個客戶可以訂購多個存貨項,而一個存貨項又能夠被多個客戶訂購。多對多關(guān)系是通過兩個獨立的一對多關(guān)系來定義的,公共的“多”表包含了兩個其它表的外部鍵。在該例中,定貨s 表與 盤存 表(通過 “內(nèi)容”)與 Customer 表(通過 客戶號)都相關(guān)聯(lián)。通過這三個表,我們可以看到,Mary Smith (客戶號 3391) 訂購了 Straw Hat (“內(nèi)容” 27) 和Garden Gloves (“內(nèi)容” 46),而 Mary Smith (客戶號 3391) 和 Mortimer Blaselflatz (客戶號 8765) 都訂購了Straw Hat (“內(nèi)容” 27)。如果把客戶表和盤存表的相關(guān)字段與 定貨表的“定貨”字段聯(lián)結(jié)起來,建立一個“關(guān)聯(lián)”表,那么這個關(guān)系就更清楚了。
表4 關(guān)聯(lián)表:按客戶號和內(nèi)容排序

定貨號 客戶號 姓名 內(nèi)容 描述
14764 33391 Smith Mary 27 Straw Hat
14932 33391 Smith Mary 46 Garden Gloves
15168 8765 Blaselfatz Mortimer 27 Straw Hat

  規(guī)范化 數(shù)據(jù)庫設(shè)計者的任務(wù)就是組織數(shù)據(jù),而組織數(shù)據(jù)的方法,應(yīng)能消除不必要的重復(fù),并為所有必要信息提供快速查找路徑。為了達(dá)到這種目標(biāo)而把信息分離到各種獨立的表中去的過程,叫作規(guī)范化。
  規(guī)范化是用許多指定的規(guī)則和不同級別的范式來進(jìn)行規(guī)范的復(fù)雜過程。該過程的研討已超出了本文的范圍。但是,大多數(shù)簡單數(shù)據(jù)庫的規(guī)范化可以用下面簡單的經(jīng)驗規(guī)則來完成:包含重復(fù)信息的表必須分成獨立的幾個表來消除重復(fù)。
  例如,使學(xué)生和課程對應(yīng)的學(xué)生數(shù)據(jù)庫,包含了下表所示的信息。
表5

學(xué)生 課程 描述 教授
1 4 Introduction to Physiology Dawson

2 3 Applied Basketweaing Carruth
3 1 Physics for Short-定貨 cooks Adms
4 2 Introduction to Physiology Dawsons

  如果有選學(xué)了十二門課程的 1000 個學(xué)生,每門課程的說明和教師將顯示100多次─ 對選了那門課程的每個學(xué)生都要重復(fù)一次。要避免這種低效率,應(yīng)當(dāng)把表分成兩個獨立的表來規(guī)范化,一個用來表示學(xué)生,另一個用來表示課程,如表6,表7所示。


學(xué)生 課程
1 4
2 3
3 1
4 4
  課程 描述 教授
1 Physics for Short-定貨 cooks Adms
2 Counterculture Sociology Beckely
3 Applied Basketweaing Carruth
4 Introduction to Physiology Dawsons

表6
  表7


  現(xiàn)在表被規(guī)范化了,所以,要改變特定課程的課程描述或“數(shù)據(jù)”,只要改變一個記錄就可以了。
以上是關(guān)于數(shù)據(jù)庫的基本知識,這是學(xué)習(xí)數(shù)據(jù)庫編程所必須的。雖然數(shù)據(jù)庫技術(shù)作為一門學(xué)科,其深度和廣度不是這點篇幅能描述的,但作為入門和簡單數(shù)據(jù)庫編程應(yīng)該是足夠了。
  好了,下面我們就可以開始練練了。我們經(jīng)常遇到數(shù)據(jù)庫系統(tǒng)是登記系統(tǒng),不管你是在單位,或是參加什么組織,登記是免不了的,而且它的結(jié)構(gòu)比較簡單,我們就以一個登記系統(tǒng)為例吧。分析一下該系統(tǒng)所涉及到的數(shù)據(jù)。

二、磨刀不誤砍柴功

  對于登記,要跟蹤的信息包括:
●    姓名   ●    性別
●     籍貫   ●    年齡
●    出生年月 ●    單位
●    地址   ●    郵政編碼
●    電話   ●    傳真
  當(dāng)然,可以簡單地創(chuàng)建一個表,使得上述的每個數(shù)據(jù)項對應(yīng)一個字段。
  現(xiàn)在需要給表指派主鍵,用以唯一標(biāo)識每一條記錄,在登記表中分別添加登記號作為唯一鍵,這樣就保證數(shù)據(jù)庫中的任兩條記錄都不同了。
  對數(shù)據(jù)庫作出以上分析后,我們就可以開始建立數(shù)據(jù)庫了。

三、建營扎寨
  在這里我們學(xué)習(xí)怎樣建立數(shù)據(jù)庫,首先需要確定要建立數(shù)據(jù)庫的類型。在Visual Basic中通過數(shù)據(jù)訪問控件或數(shù)據(jù)訪問對象(DAO)可以訪問下列數(shù)據(jù)庫:
  1. JET數(shù)據(jù)庫,即Microsoft Access
  2. ISAM數(shù)據(jù)庫,如:dBase,F(xiàn)oxPro等
  3. ODBC數(shù)據(jù)庫,凡是遵循ODBC標(biāo)準(zhǔn)的客戶/服務(wù)器數(shù)據(jù)庫。如:Microsoft SQL Server、Oracle
  一般來說,如果要開發(fā)個人的小型數(shù)據(jù)庫系統(tǒng),用Access數(shù)據(jù)庫比較合適,要開發(fā)大、中型的數(shù)據(jù)庫系統(tǒng)用ODBC數(shù)據(jù)庫更為適宜。而dBase和FoxPro數(shù)據(jù)庫由于已經(jīng)過時,除非特別的情況,否則不要使用。在我們的例子中,當(dāng)然選用Access數(shù)據(jù)庫了。建立Access數(shù)據(jù)庫有兩種方法:一是在Microsoft Access中建立數(shù)據(jù)庫。點擊“新建”按鈕就可以建立新的表了(如圖1)。這里我們主要介紹第二種方法:使用可視化數(shù)據(jù)管理器,不需要編程就可創(chuàng)建數(shù)據(jù)庫?梢暬瘮(shù)據(jù)管理器是一個非常有用的應(yīng)用程序,它是VB企業(yè)版和專業(yè)版附帶的,在目錄..DevStudiovbsamplesVisdata下,其界面如下圖。
  點擊菜單“文件”項下“新建”子項“Microsoft ACCESS”子項的“版本7.0 MDB”項。在彈出窗口中輸入新建數(shù)據(jù)庫的名稱“登記”,出現(xiàn)下面圖3所示窗口:
  要生成新的表,右鍵單擊數(shù)據(jù)庫窗口彈出菜單,然后選擇“新表”命令,在隨后出現(xiàn)的“表結(jié)構(gòu)”對話框中建立所要的字段。每次向表中加入新的字段,單擊“增加字段”按鈕,會出現(xiàn)圖4 的“增加字段”對話框。
  “增加字段”對話框中的選項如表10所示,根據(jù)字段的類型,有些選項是無效的,無法讀取。
在我們建立的登記數(shù)據(jù)庫中,各個字段的類型如表11。

  要注意的是,由于字段登記號用來唯一標(biāo)志記錄的,因此,它不能由用戶輸入。所以在定義該字段時需要定義為Long數(shù)據(jù)類型,“自動生成字段”項有效,并選中這一項。這樣當(dāng)用戶每輸入一條新記錄時,系統(tǒng)就會在該字段上自動輸入一個與其它記錄不同的值。
在ACCESS數(shù)據(jù)庫中,關(guān)鍵字是用索引實現(xiàn)的,作為編程人員在對表類型的記錄集編程時,只需調(diào)用索引名。在查詢時,Rushmore技術(shù)自動用索引信息優(yōu)化查詢。完成表定義后,點擊“增加索引”按鈕,彈出如圖5所示窗口。
  在窗口中右邊有三個選項,其意義如表 12。
  添加索引對話框選項
  完成之后如圖6。
  當(dāng)然,學(xué)會數(shù)據(jù)庫的建立也并非一朝一夕的事,讀者不妨多練習(xí)一下。下面你就可以運(yùn)行VB開始我們的編程了。

四、千里相會
  Visual Basic 數(shù)據(jù)庫應(yīng)用程序有三個部分,如圖7所示。
  用戶程序是程序員開發(fā)的,也是我們即將用VB來編寫的部分。數(shù)據(jù)庫引擎是數(shù)據(jù)庫驅(qū)動程序,使用它程序員可以用統(tǒng)一的格式訪問各種數(shù)據(jù)庫,不管這個數(shù)據(jù)庫是本地的 Visual Basic 數(shù)據(jù)庫,還是所支持的其它任何格式的數(shù)據(jù)庫格式,所使用的數(shù)據(jù)訪問對象和編程技術(shù)都是相同的。數(shù)據(jù)庫則是我們上面完成的部分。從這個結(jié)構(gòu)可以看出用戶與正在訪問的特定數(shù)據(jù)庫無關(guān)。那我們在用VB編寫數(shù)據(jù)庫程序時,就需要使程序能夠訪問指定的數(shù)據(jù)庫。
  如果是簡單的數(shù)據(jù)庫應(yīng)用,可以使用 Data 控件來執(zhí)行大部分?jǐn)?shù)據(jù)訪問操作,而根本不用編寫代碼。與 Data 控件相捆綁的控件自動顯示來自當(dāng)前記錄的一個或多個字段的數(shù)據(jù)。

DATA數(shù)據(jù)控件
屬性
CONNECT屬性 指定打開的數(shù)據(jù)庫類型,并且包括參數(shù),如用戶和口令等。
例如:
打開Access數(shù)據(jù)庫(缺。
CONNECT=“ACCESS”
打開ODBC數(shù)據(jù)庫
CONNECT=“ODBC;DATABASE=??;UID=??;PWD=??;DSN=??”
DATABASENAME屬性 確定數(shù)據(jù)控件訪問哪一個數(shù)據(jù)庫。
對于多表數(shù)據(jù)庫它為具體的數(shù)據(jù)庫文件名,例如:ACCESS數(shù)據(jù)庫
DATABASENAME=“D:...DEMO.MDB"
  對于單表數(shù)據(jù)庫它為具體的數(shù)據(jù)庫文件所在的目錄,而具體文件名放在RECORDSOURCE屬性中,例如:訪問FOXPRO數(shù)據(jù)庫文件D:FOXDEMO.DBF
DATABASENAME=“D:FOX”
RECORDSOURCE=“DEMO”不帶文件擴(kuò)展名
RECORDSOURCE屬性
  確定數(shù)據(jù)控件的記錄集,即:所要訪問的數(shù)據(jù)內(nèi)容。它可以是一個表名、存儲查詢名或SQL語句。例如:訪問Register表所有數(shù)據(jù) :
RECORDSOURCE=“Register”訪問RC表中1973年以前出生的數(shù)據(jù):
RECORDSOURCE=“SELECT *FROM Register WHERE [BIRTHDAY]<#1/1/1973#"
注意:當(dāng)我們在運(yùn)行時修改了該屬性后,需要調(diào)用REFRESH方法刷新記錄集。

方法
REFRESH方法 當(dāng)我們在運(yùn)行時修改了Record-
Source屬性后,需要調(diào)用該方法刷新記錄集。
UPDATERECORD方法 將綁定在數(shù)據(jù)控件上的控件的數(shù)據(jù)寫入數(shù)據(jù)庫中。即:當(dāng)我們修改了數(shù)據(jù)后調(diào)用該方法確定修改。
CANCELUPDATE方法 將數(shù)據(jù)庫中的數(shù)據(jù)重新讀到綁定在數(shù)據(jù)控件上的控件中。即:當(dāng)我們修改了數(shù)據(jù)后調(diào)用該方法放棄修改。

事件
  VALIDATE事件 當(dāng)我們移動記錄集記錄指針時發(fā)生。例如:我們將記錄集記錄指針從A移動到記錄B時當(dāng)產(chǎn)生VALIDATE事件時,記錄指針仍在記錄A上。
Sub XXXX_Validate(Action As integer,Save As integer)
其中:
Action 指出如何產(chǎn)生了該事件,如:移動,增加,查詢等。
Save 表示是否保存已修改的數(shù)據(jù)。當(dāng)我們修改了綁定在數(shù)據(jù)控件的數(shù)據(jù),又沒有UPDATERECORD,則移動指針時,Save=True。如果在事件中令Save=False,則放棄修改。
例如:

Sub XXXX_Validate(Action As integer,Save As integer)
If Save then
I= MsgBox("Dada changed,Save?",vbYesNo)
If I = vbNo then
Save = False
End if
End if
End Sub

Reposition事件 當(dāng)我們移動記錄集指針時發(fā)生。例如:我們將記錄集記錄指針從A移動到記錄B 時,當(dāng)產(chǎn)生Reposition事件時,記錄指針已移動到B上。
通常我們在該事件中顯示當(dāng)前的指針位置。例如:
Sub XXXX_Reposition()
XXXX.Caption=??
XXXX.RecordSet.AbsolutePosition + 1
End Sub

  了解了DATA控件之后我們就可以連接數(shù)據(jù)庫了,F(xiàn)在我們可以編寫一個應(yīng)用程序。因為雖然我們建立了Register數(shù)據(jù)庫,但是數(shù)據(jù)庫中卻沒有數(shù)據(jù),我們程序的目的就是向數(shù)據(jù)庫中輸入數(shù)據(jù)。它的運(yùn)行情況如圖8。
  各個文本框正好對應(yīng)著表Register的各個字段,在文本框中輸入數(shù)據(jù),點擊“增加”按鈕,就完成了一條記錄的輸入。我們看一下,DATA控件是怎樣和數(shù)據(jù)庫連接起來的,各個文本框又是怎樣和DATA控件捆綁起來的。
  在DATA控件的CONNECT屬性中,選中“ACCESS”項,在DatabaseName屬性中,輸入“C:TEMP登記.mdb”,在RecordSource屬性中,選中“Register”,這樣就完成了數(shù)據(jù)庫與DATA控件的連接,也就是完成了與應(yīng)用程序的連接。

  數(shù)據(jù)庫中各個字段又是怎樣和文本框連接起來的呢?在VB中,我們可以將普通控件綁定在數(shù)據(jù)控件上,來完成自動地顯示、更新記錄集的數(shù)據(jù)。常用的可綁定的控件有:Label,Text,checkBox,Image等。通過設(shè)置這些控件的DataSource和DataField屬性來完成綁定。

DataSource 屬性 表示綁定到哪一個數(shù)據(jù)控件上,程序中我們可能使用多個數(shù)據(jù)控件。
DataField 屬性 表示綁定到記錄集的哪一個記錄上。
  現(xiàn)在我們需要把Text1與表“登記”中的姓名字段連接起來。完成DATA控件的連接之后,在Text1控件的DataSource屬性中,選中“Data1”,在DataField屬性中,選中“姓名”值。用同樣的方法,將各個文本框分別綁定到對應(yīng)的字段上,就完成了文本框的捆綁。
  下面我們編寫兩個按鈕命令,完成其相應(yīng)的操作了。喂!別著急,還有一個重要的對象沒講呢!
  當(dāng)應(yīng)用程序啟動時,Data 控件被自動地初始化。如果 Connect、DatabaseName、Options、RecordSource、Exclusive、ReadOnly 和 RecordsetType 屬性是合法的, Microsoft Jet 數(shù)據(jù)庫引擎就會試圖創(chuàng)建一個新的基于這些屬性的 Recordset 記錄集對象。Recordset 對象可以表示表中的記錄或者作為查詢結(jié)果的記錄,使用 Recordset 對象可以在記錄一級上對數(shù)據(jù)庫中的數(shù)據(jù)進(jìn)行處理。這在數(shù)據(jù)庫編程中是一個十分重要的,也是比較復(fù)雜的對象。   
  Recordset 對象有三種類型:表、動態(tài)集、快照,它們之間存在明顯的區(qū)別。
  表類型的 Recordset 對象是指當(dāng)前數(shù)據(jù)庫中的表在創(chuàng)建表類型的記錄集時,數(shù)據(jù)庫引擎打開的表。后續(xù)的數(shù)據(jù)操作都是直接對表進(jìn)行的。只能對單個的表打開表類型的記錄集,而不能對聯(lián)接或者聯(lián)合查詢打開表類型的記錄集。與其它類型的 Recordset 對象相比,表類型的搜索與排序速度最快。
  動態(tài)集類型的 Recordset 對象可以是本地的表,也可以是返回的行查詢結(jié)果。它實際上是對一個或者幾個表中的記錄的一系列引用?捎脛討B(tài)集從多個表中提取和更新數(shù)據(jù),其中包括鏈接的其它數(shù)據(jù)庫中的表。動態(tài)集類型具有一種與眾不同的特點:不同數(shù)據(jù)庫的可更新聯(lián)接。利用這種特性,可以對不同類型的數(shù)據(jù)庫中的表進(jìn)行可更新的聯(lián)接查詢。動態(tài)集和它的基本表可以互相更新。如果動態(tài)集中的記錄發(fā)生改變,同樣的變化也將在基本表中反映出來。在打開動態(tài)集的時候,如果其他的用戶修改了基本表,那么動態(tài)集中也將反映出被修改過的記錄。動態(tài)集類型是最靈活的Recordset 類型,也是功能最強(qiáng)的。不過,它的搜索速度與其它操作的速度不及表類型的 Recordset。
  快照類型的 Recordset 對象包含的數(shù)據(jù)是固定的,它反映了在產(chǎn)生快照的一瞬間數(shù)據(jù)庫的狀態(tài)。從 Microsoft Jet 數(shù)據(jù)源得到的快照是不可更新的,從開放數(shù)據(jù)庫互連 (ODBC) 數(shù)據(jù)源得到的某些快照是可以更新的,這取決于數(shù)據(jù)庫系統(tǒng)本身的能力。與動態(tài)集類型和表類型的 Recordset 對象相比,快照的處理開銷較少。因此,它執(zhí)行查詢和返回數(shù)據(jù)的速度更快,特別是在使用 ODBC 數(shù)據(jù)源時?煺疹愋捅4媪吮碇兴杏涗浀耐暾麖(fù)本,因此,如 果記錄的個數(shù)很多,快照的性能將比動態(tài)集慢得多。為了確定快照與動態(tài)集哪一個更快,可以先以動態(tài)集方式打開記錄集,然后再以快照方式打開它。
  具體使用什么記錄集,取決于需要完成的任務(wù):是要更改數(shù)據(jù)呢,還是簡單地查看數(shù)據(jù)。例如,如果必須對數(shù)據(jù)進(jìn)行排序或者使用索引,可以使用表。因為表類型的 Recordset 對象是做了索引的,它定位數(shù)據(jù)的速度是最快的。如果希望能夠?qū)Σ樵冞x定的一系列記錄進(jìn)行更新,可以使用動態(tài)集。如果在特殊的情況下不能使用表類型的記錄集,或者只須對記錄進(jìn)行掃描,那么使用快照類型可能會快一些。
  一般來說,盡可能地使用表類型的 Recordset 對象,它的性能通?偸亲詈玫。
  為選擇特定的 Recordset 類型,把 Data 控件的RecordsetType屬性設(shè)成:

RecordSet記錄集屬性
BOF屬性 當(dāng)記錄集記錄指針指向第一條記錄時返回True
EOF屬性 當(dāng)記錄集記錄指針指向最后一條記錄時返回True
AbsloutePosition屬性 返回當(dāng)前記錄集記錄指針,第一條記錄為0,是只讀屬性
Bookmark屬性 String類型,返回或設(shè)置當(dāng)前記錄集記錄指針的書簽,是可讀寫屬性。每一條記錄都有自己唯一的書簽,它與記錄在記錄集中的順序無關(guān)。將Bookmark屬性存放到變量中,后面可以通過將該變量賦值給Bookmark屬性,并返回到這個記錄。
注意:程序中使用BookMark屬性重定位記錄指針,而不能使用Abslouteposition
NoMatch屬性 當(dāng)我們使用Find方法查詢時如果未找到則返回True。常與BookMark屬性同時使用。
例如:查找[NAME]字段中第一個姓李的人

Dim S As String
With XXXX.RecordSet
S = .BookMark
.FindFirst "[NAME] Like '李*'"
if .NoMatch then
MsgBox "數(shù)據(jù)未找到“
.BookMark = S
End if
End With

記錄集方法
AddNew方法 向記錄集增加一條新記錄
Delete方法 從記錄集中將當(dāng)前記錄刪除。在刪除后常使用MoveNext方法移動指針。
例如:

With XXXX.RecordSet
.Delete
.MoveNext
if .EOF then .MoveLast
End With

MoveXXXX方法
MoveFirst 將記錄集指針移動到第一條記錄上
MoveLast 將記錄集指針移動到最后一條記錄上
MovePrevious 將記錄集指針移動到前一條記錄上
MoveNext 將記錄集指針移動到下一條記錄上
FindXXXX方法
FindFirst在記錄集中查詢符合條件的第一條記錄
FindLast 在記錄集中查詢符合條件的最后一條記錄
FindPrevious 在記錄集中查詢符合條件的前一條記錄
FindNext 在記錄集中查詢符合條件的下一條記錄
  好了,有了這么充分的知識了,編寫兩個按鈕命令簡直是小菜一碟,先來試一下,添一個“增加”命令按鈕吧。

Private Sub Command1_Click()
Data1.Recordset.AddNew
End Sub

  哇!怎么這么簡單,再看一下“刪除”命令按鈕

Private Sub Command2_Click()
Data1.Recordset.Delete
Data1.Recordset.AddNew
End Sub

  就這樣行了嗎?運(yùn)行程序吧,OK!一切正常,迫不及待地輸入一條記錄,點擊“增加”按鈕,怎么?出問題了!因為你只有在進(jìn)行了AddNew方法后才可以輸入數(shù)據(jù),好吧,在窗口的初始化時就增加一條新記錄吧。

Private Sub Form_Initialize()
Data1.Recordset.AddNew
End Sub
  輸入完了數(shù)據(jù),我們打算退出程序,很自然的我們執(zhí)行關(guān)閉窗口操作,就順利地結(jié)束了輸入工作。真的很順利嗎?打開數(shù)據(jù)庫,看看數(shù)據(jù)庫中的數(shù)據(jù),我們發(fā)現(xiàn)剛才輸入的最后一條記錄沒有存入數(shù)據(jù)庫中。這個很好解釋,每當(dāng)我們調(diào)用AddNew方法時,它就將輸入的記錄存入數(shù)據(jù)庫中,而當(dāng)我們關(guān)閉窗口時,剛輸入的記錄并沒有保存到數(shù)據(jù)庫中,那么在關(guān)閉窗口之前對DATA控件進(jìn)行一次刷新就可以將數(shù)據(jù)存入數(shù)據(jù)庫中了。

Private Sub Form_QueryUnload(Cancel As Inte              ger, UnloadMode As Integer)
Data1.Refresh
End Sub

  到了這里,我們似乎可以稍稍輕松了一點,這個窗口的功能差不多完成了。但是我不得不給你提出一個忠告:在數(shù)據(jù)庫系統(tǒng)中,應(yīng)盡量將錯誤在應(yīng)用級上處理。這句話看起來似乎有點抽象,實際上用在這個程序中就簡單多了。在表Register中,我們將出生日期定義為Date/Time類型,如果在程序運(yùn)行時,在該字段對應(yīng)的文本框中輸入的不是Date/Time格式,在向數(shù)據(jù)庫提交數(shù)據(jù)時會出現(xiàn)什么情況呢?數(shù)據(jù)庫會向用戶報告錯誤信息。然而這樣對應(yīng)用程序并不好,這樣的錯誤應(yīng)該由用戶程序處理,而不是交給數(shù)據(jù)庫去處理,所以在數(shù)據(jù)提交之前就應(yīng)該檢查該字段的輸入是否合法。

Private Sub Text3_LostFocus()
If IsDate(Text3.Text) Or Text3.Text = "" Then          '檢查是否輸入合法數(shù)據(jù)
Exit Sub
End If
MsgBox ("輸入錯誤,請輸入你出生的年月日!")
,將選取不合法的數(shù)據(jù),以便重新輸入,并使控制焦點不動
Text3.SetFocus
Text3.SelStart = 0
Text3.SelLength = Len(Text3.Text)
End Sub

  上面雖是應(yīng)用程序處理錯誤的一個小例子,可是這種在應(yīng)用級處理錯誤的思想是十分重要的。

五、尋尋覓覓
  在數(shù)據(jù)庫管理系統(tǒng)中,輸入和查詢就象兩個孿生姐妹不可或缺,下面將介紹如何創(chuàng)建查詢窗口。查詢窗口的設(shè)計分為兩部分:查詢結(jié)果和查詢條件。查詢結(jié)果是指用戶所需要的數(shù)據(jù),它包括根據(jù)查詢條件查詢出來的記錄,但并非表中每個字段里的數(shù)據(jù)都需要提供給用戶。比如在我們這個例子中,登記表中的登記號的值是用戶不感興趣的,所以在查詢結(jié)果中,我們不希望顯示RegID字段的值。查詢條件是用戶提出的查詢要求。比如在我們這個系統(tǒng)中,可以有姓名條件,當(dāng)用戶想知道某個人的具體情況,他可以輸入此人的姓名,就查詢出此人各方面的情況;也可以有年齡條件,當(dāng)用戶輸入某個年齡段,就會查詢出處于這個年齡段的所有人的信息。到底采用哪些查詢條件,這需要開發(fā)者根據(jù)用戶和系統(tǒng)的要求進(jìn)行設(shè)計,其具體實現(xiàn)過程大都大同小異。為了節(jié)省篇幅,我們就僅以年齡為條件進(jìn)行查詢。查詢窗口運(yùn)行情況如圖9。
在這個程序中我們使用了一個控件DBGrid,這個控件用來顯示查詢結(jié)果,選中VB的“工程”菜單下的“部件……”項,在控件標(biāo)簽中,選中“Microsoft Data Bound Grid Control 5.0”即可,在工具箱中就會出現(xiàn)DBGrid控件的小圖標(biāo)。其使用和其它控件一樣。
  為了和數(shù)據(jù)庫連接,DATA控件是不可少的,回憶一下,該怎樣設(shè)置它的屬性,OK!同輸入窗口一樣,在CONNECT屬性中,選中“ACCESS”項。在DatabaseName屬性中,輸入“C:TEMP登記.mdb”。在RecordSource屬性中,選中......嘿,嘿,這里稍微有點不同,如果按輸入窗口那樣的設(shè)置,查詢結(jié)果中就會包含登記號字段了。在此屬性中我們應(yīng)該輸入SQL語句:
  select name as 姓名,sex as 性別,hometown as 籍貫,age as 年齡,birthday as 生日,company as 單位,address as 地址,zip as 郵編,telephone as 電話,fax as 傳真 from Register。別著急,盡管這條語句有點長,實際上卻比較簡單。這條語句的語法是:
  SELECT 字段名,字段名,……FROM 表名 WHERE 條件;
  對照語法,我們可以看出輸入的SQL語句的含義:從表Register中查詢姓名,性別,籍費(fèi),……字段的值。只要在字段列表中不選中登記號字段,在查詢結(jié)果中,就不會顯示登記號的值了。如果你夠細(xì)心的話就會注意到我們所寫的SQL語句中在字段列表中并不僅僅輸入字段名,在其后面還增加了as……項,如“name as 姓名”,這是為name字段取一個別名“姓名”,以便在DBGrid控件中顯示字段名時,就會顯示“姓名”而不是“name”。
  完成了DATA控件屬性的設(shè)置就可以將DBGrid控件捆綁到DATA控件上,其方法同輸入窗口。對了,將“DataSource”屬性設(shè)置為“DATA1”即可,F(xiàn)在不妨運(yùn)行一下程序,真令人興奮,DBGrid顯示出表中所有的信息?墒窃鯓语@示符合條件的數(shù)據(jù)呢?再看一看上面的SQL語法,WHERE段后可以輸入查詢條件,比如:需要年齡在20到30歲之間的人員信息,其語句為:Select name,... From register where age>20 and age<30;
  我們只要根據(jù)用戶輸入的條件構(gòu)成新的SQL語句,并利用DATA控件的Refrensh方法刷新數(shù)據(jù)庫,就可以完成條件查詢了。
  整個程序十分簡單,當(dāng)用戶在文本框中輸入年齡段后,點按“查詢”命令,就會顯示符合條件的查詢結(jié)果。程序代碼如下:

Option Explicit
Const allinfo = "select name as 姓名,sex as 性別,hometown as 籍貫,age as 年齡,birthday as 生日,company as 單位,address as 地址,zip as 郵編,telephone as 電話,fax as 傳真 from 登記" '定義不帶條件的SQL查詢語句

Private Sub Command1_Click()
Dim t1, m, n As String
'構(gòu)造SQL語句的WHERE條件段
If IsNumeric(Text1.Text) Then
m = " age >" + Text1.Text
End If
If IsNumeric(Text2.Text) Then
n = " age < " + Text2.Text
End If
If m = "" And n = "" Then
t1 = ""
Else
If m = "" Then
t1 = n
Else
If n = "" Then
t1 = m
Else
t1 = m + " and " + n
End If
End If
End If
If t1 <> "" Then
t1 = " where " + t1
End If
Data1.RecordSource = allinfo + t1
'將RecordSource屬性的值設(shè)置為新的SQL語句
Data1.Refresh
‘刷新數(shù)據(jù)庫,獲得符合當(dāng)前條件的查詢結(jié)果
End Sub

  在這個程序中,你還可以加上姓名查詢、籍貫查詢等,其設(shè)計過程都是通過構(gòu)造SQL語句來實現(xiàn)的。另外,還需要注意的一點是既然是查詢窗口,當(dāng)然允許修改,將DBGrid控件的AllowUpdate屬性值設(shè)為False。OK!運(yùn)行程序吧,!萬事大吉。看一看應(yīng)用程序,再看一看你編寫的代碼,不可思議,這么短的代碼居然完成了這么強(qiáng)大的功能。事實就是這樣,數(shù)據(jù)庫編程中最重要的是對概念的清楚,與之相比,技巧好象擺在了次要位置。讓我們閉上眼睛,想一想我們需要了解那些東西吧:DATA控件,RECORDSET對象、控件的捆綁、簡單的SQL查詢語句。熟悉了這些東西,你就可以進(jìn)行數(shù)據(jù)庫編程了,如果你對開發(fā)數(shù)據(jù)庫系統(tǒng)比較感興趣,可以再加強(qiáng)一下在數(shù)據(jù)庫設(shè)計和SQL語句方面的知識。但數(shù)據(jù)庫編程真的總是這樣簡單嗎?如果已經(jīng)熟悉了上面的內(nèi)容,建議你看一下下面這一節(jié)。

六、撥云見霧
  還記得我們在前面說過Visual Basic 提供了兩種與 Jet 數(shù)據(jù)庫引擎接口的方法嗎?Data 控件和數(shù)據(jù)訪問對象。我們已經(jīng)見識了DATA控件,確實給人意想不到的方便,但Data 控件只給出有限的訪問現(xiàn)存數(shù)據(jù)庫的功能。而 DAO 模型則可以全面控制數(shù)據(jù)庫的完整編程接口。這兩種方法事實上并不是互斥的,實際上,它們常同時使用。
  DAO 模型是設(shè)計關(guān)系數(shù)據(jù)庫系統(tǒng)結(jié)構(gòu)的對象類集合。它們提供了完成管理這樣一個系統(tǒng)所需的全部操作屬性和方法,包括創(chuàng)建數(shù)據(jù)庫,定義表、字段和索引,建立表間的關(guān)系,定位和查詢數(shù)據(jù)庫等工具。DAO結(jié)構(gòu)的主要對象如圖10所示。
  Visual Basic 中的數(shù)據(jù)庫編程就是創(chuàng)建DAO(數(shù)據(jù)訪問對象)。這些對象對應(yīng)于被訪問物理數(shù)據(jù)庫的不同部分,如 Database、TableDef、Field 和 Index 對象,用這些對象的屬性和方法來實現(xiàn)對數(shù)據(jù)庫的操作,能夠在 Visual Basic 窗體中使用綁定和非綁定控件來顯示操作結(jié)果并接收用戶輸入。這樣就簡化了代碼,它賦予程序員很大的靈活性。因為可以使用同樣的對象、屬性和方法來處理各種不同的數(shù)據(jù)庫格式。同時,如果從一種數(shù)據(jù)庫格式變到另一種格式(例如,將本地的 Microsoft Access 數(shù)據(jù)庫轉(zhuǎn)換為網(wǎng)絡(luò)上的 SQL Server 數(shù)據(jù)庫),只需變動少量的代碼就可以適應(yīng)這種改變。甚至可以創(chuàng)建這樣的應(yīng)用程序,在單一的查詢或報表中連接來自多個不同數(shù)據(jù)庫的表。
  我們先介紹DAO對象,從上圖中我們可以看到,DAO對象中最關(guān)鍵的是DBEngine對象,所有的數(shù)據(jù)庫操作都要通過它來完成。

DBEngine對象
DBEngine對象相當(dāng)于jet數(shù)據(jù)庫引擎,不需要創(chuàng)建該對象。
CreateWorkspace方法:創(chuàng)建一個工作區(qū)對象
例如 :
Dim ws As Workspace
Set ws = DbEngine.CreateWorkspace                 (SpName,UserID,password",SpType)
其中
SpName 工作區(qū)名稱String
UserID 用戶名 String
Password 口令String
SpType 工作區(qū)類型 = dbUsejet jet工作區(qū)
= dbUseODBCODBC工作區(qū)

Workspace對象
  Workspace對象為用戶定義一個會話,通過與之關(guān)聯(lián)的用戶名和口令建立一個安全級別。當(dāng)不需要安全級別時可使用缺省的工作區(qū)DBEngine.Workspace。
方法:
OpenDatebase    打開一個數(shù)據(jù)庫
Close    關(guān)閉當(dāng)前工作區(qū)
BeginTrans    啟動一個事務(wù)
CommitTrans    提交當(dāng)前事務(wù)
RollBack    回滾當(dāng)前事務(wù)
例如我們需要打開一個數(shù)據(jù)庫。
我們調(diào)用Workspace的OpenDatabase方法打開一個數(shù)據(jù)庫。
例如:
Dim db As DataBase
Set db=OpenDatabase(dbname,exc,readonly,source)
其中:
dbname    數(shù)據(jù)庫文件名(對于單表數(shù)據(jù)庫為路徑)
exc = TRUE 表示打開數(shù)據(jù)庫后,該數(shù)據(jù)庫不能被其它應(yīng)用程序訪問。
    FALSE 表示共享地打開數(shù)據(jù)庫
Readonly=TRUE 表示打開數(shù)據(jù)庫后,只能對數(shù)據(jù)庫進(jìn)行讀操作
=FALSE 表示打開數(shù)據(jù)庫后,可對數(shù)據(jù)庫進(jìn)行讀寫操作
source數(shù)據(jù)源名,用于指定打開地數(shù)據(jù)庫類型。

創(chuàng)建記錄集
  我們使用DataBase對象的OpenRecordSet方法打開一個記錄集。
例如:
Dim rs As RecordSet
Set rs=db.OpenRecordSet(source,type)
其中:
source 指定記錄集的內(nèi)容,它可以是一個表名,查詢名或SQL語句
type 創(chuàng)建記錄集的類型
=dbOpenTable表型記錄集
=dbOpenDynaset動態(tài)型記錄集
=dbOpenSnapshot快照型記錄集

訪問ODBC數(shù)據(jù)庫
  Visual Basic 通過 DAO 和 Jet 引擎可以識別三類數(shù)據(jù)庫:Visual Basic 數(shù)據(jù)庫,外部數(shù)據(jù)庫,ODBC 數(shù)據(jù)庫。在開發(fā)大中型數(shù)據(jù)庫系統(tǒng)中一般都采用ODBC 數(shù)據(jù)庫,如 Microsoft SQL Server等。DAO(數(shù)據(jù)訪問對象)的 ODBCDirect 模式允許直接訪問 ODBC 數(shù)據(jù)。另外,遠(yuǎn)程數(shù)據(jù)對象 (RDO) 庫和遠(yuǎn)程數(shù)據(jù)控件 (RDC)也 允許直接訪問 ODBC 數(shù)據(jù)。
要訪問ODBC數(shù)據(jù)庫需要兩步,首先需要在WINDOWS 95 或WINDOWS NT的控制面板中設(shè)置ODBC數(shù)據(jù)源名(DSN)。然后在OpenDatabase方法的Source參數(shù)中使用該數(shù)據(jù)源名。
例如:訪問Microsoft SQL Server
Source="ODBC;DSN=MyDSN;UID=sa;PWD=xx;DataBase=pubs"
在程序中使用DAO
  DAO的強(qiáng)大功能確實讓人興奮,只是較之DATA控件要復(fù)雜得多。在復(fù)雜的數(shù)據(jù)庫處理中,這兩種方法常常同時使用。要使用DAO,必須選中VB的“工程”菜單下的“部件……”項,在控件標(biāo)簽中選中"Microsoft DAO 3.5 Object Library"之后就可以在程序中定義DAO變量了。在這篇文章中我們所舉的數(shù)據(jù)庫例子比較簡單,有興趣的朋友不妨采用數(shù)據(jù)訪問對象(DAO)來實現(xiàn)這個程序。

七、高屋建瓴
  到了這里,似乎VB數(shù)據(jù)庫編程的內(nèi)容也差不多完整了,但是你完全可以再往前走一步。在目前的數(shù)據(jù)庫開發(fā)過程中,客戶/服務(wù)器體系結(jié)構(gòu)占這個領(lǐng)域的主體地位,利用VB進(jìn)行客戶/服務(wù)器應(yīng)用程序的開發(fā)是完全可行的。同時,在程序開發(fā)過程中,還可以利用面向?qū)ο蠹夹g(shù),提高你的程序的可重用性和可維護(hù)性?傊,數(shù)據(jù)庫應(yīng)用程序的開發(fā)始終是技術(shù)上和理論上的熱點。使用最高級的方法,最先進(jìn)的技術(shù),你會變得越來越COOL!