8.4.3 創建記錄集
創建一個記錄集十分容易,通過調用Recordset對象的Open方法來實現:
Recordset.Open [Source], [ActiveConnection], [CursorType], [LockType], [Options]
其參數及說明如表8-3所示:
表8-3 Open方法的參數及說明
參 數
說 明
Source
數據源。可以是數據庫中的表名、存儲的查詢或過程、SQL字符串、Command對象或適用於提供者的其他命令對象
ActiveConnection
記錄集使用的連接。可以是一個連接字符串或者一個打開的Connection對象
CursorType
使用的光標類型。必須是定義的光標類型中的一種,缺省值為adForwardOnly
LockType
使用的鎖定類型。必須是定義的鎖定類型中的一種,缺省值為adLockReadOnly
Options
告訴提供者Source參數的內容是什麼,如表、文本字符串等等
例如,要打開數據庫pubs中authors表上的記錄集:
Dim rsAuthors
Set rsAuthors = Server.CreateObject("ADODB.Recordset")
rsAuthors.Open "authors", strConn
' Do something here
rsAuthors.Close
Set rsAuthors = Nothing
注意,有幾個參數沒有指定。實際上,所有的參數都是可選的,可以在打開記錄集之前為它們設置相應的屬性值:
Dim rsAuthors
Set rsAuthors = Server.CreateObject("ADODB.Recordset")
With rsAuthors
.Source = "authors"
.ActiveConnection = strConn
.CursorType = adOpenForwardOnly
.LockType = adLockReadOnly
.Open
End With
' Do something here
rsAuthors.Close
Set rsAuthors = Nothing
一旦打開記錄集,當前指針自動地位於第一條記錄上。如果在記錄集中沒有記錄,那麼EOF和BOF屬性都是True:
rsAuthors.Open "authors", strConn
If rsAuthors.BOF and rsAuthors.EOF Then
' Recordset is empty
End If
1. Options參數
Open方法的Options參數允許指定命令文本內容。它可以是以下CommandTypeEnum常數之一:
· adCmdText:文本命令,比如SQL字符串。
· adCmdTable:表名。
· adCmdStoredPRoc:存儲過程名。
· adCmdFile:保存的記錄集的文件名。
· adCmdTableDirect:表名。
· adCmdURLBind:URL地址。
adCmdTable與adCmdTableDirect的區別很小,如果想使用表中的全部列,使用adCmdTableDirect將由於ADO執行了某些內部優化而使運行速度變得稍快一些。
如果沒有指定命令的類型,ADO必須推算出執行的命令的類型,這將導致額外的開銷。
這裡還有兩個選項:adCmdUnspecifIEd表示沒有指定類型;adCmdUnknow表示命令的類型未知。一般地可能不會使用它們。
額外的選項
Options參數可以是以上常數中的任一個,但也可以加入下列ExecuteOptionEnum常數:
· adAsyncExcute:異步地執行命令。
· adAsyncFetch:取得初始的行集後,異步地獲取剩下的行。
· adAsyncFetchBlocking:除了獲取記錄不阻止命令運行以外,其他與adAsyncFetch相似。
· adExechteNoRecords:命令不返回任何記錄。
異步處理意味著在後台執行操作,可以運行命令,然後繼續其他工作,而不需要等待其執行完畢(同步操作)。當創建用戶界面時,這顯得特別方便,因為可以從命令執行中返回,向用戶顯示一些內容,而同時數據的獲取仍然在後台進行。當返回記錄集時,這對ASP程序員來說不是很有用,因為腳本語言不支持ADO事件,所以記錄集何時已完成填充移無法得知。當處理更新、刪除或插入數據命令以及不返回記錄集的時候,可以使用異步操作,即僅在不關心結果的情況下才能使用。
在另一方面,adExecuteNoRecords選項十分有用。它告訴ADO執行的命令不返回任何數據。所以,就沒有必要創建記錄集(總之,可能為空)。這會加速正在運行的更新或添加數據的查詢操作。
為了加入這些選項之一,可以使用Or符號(等同於加號“+”)
adCmdStoredProc Or adExecuteNoRecords
adCmdStoreProc + adExecuteNoRecords
在下一章,將看到對相關內容更詳細的介紹,因為這在處理命令(而不是記錄集)時會更有用。
2. 在記錄集中移動
一旦打開一個記錄集,經常需要遍歷每一條記錄。這需要使用EOF屬性。當到達記錄集的末尾時,EOF就變為True,因為可以這樣創建一個循環:
rsAuthors.Open "authors", strConn
While Not rsAuthors.EOF
Response.Write rsAuthors("au_lname:) & ", " & _
rsAuthors("au_fname") & "<BR>"
rsAuthors.MoveNext
Wend
上面的例子一直循環到EOF屬性為True時才退出。MoveNext方法用於移到下一條記錄。
如果記錄集允許向後移動,則可以使用MovePrevious方法。在這種情況下,循環中需要檢測BOF屬性值。另外分別還有移動到第一條和最後一條記錄的MoveFirst和MoveLast方法:
rsAuthors.Open "authors", strConn, adOpenDynamic
' Now on first record
rsAuthors.MoveLast
' Now on last record
rsAuthors.MovePrevious
rsAuthors.MovePrevious
' Now three rows from the end of the recordset
rsAuthors.MoveFirst
' Back at the beginning again
3. 使用FIElds集合
Fields集合包含記錄集中每一字段(列)的Fields對象。FIElds集合是記錄集的缺省集合,因此在訪問字段時可以省略,就如同上面的While...Wend例子中的情況。因此,有多種訪問字段的方法:
rsAuthors.FIElds("au_lname").Value
rsAuthors("au_lname).Value
rsAuthors(1).Value
rsAuthors.FIElds(1).Value
可以使用字段名,或使用它在集合中的位置。使用名字是最好的,因為這樣將使代碼更易於維護。
Value屬性是字段的缺省屬性,因此也可以省略,比如:
rsAuthors("au_lname")
如果想遍歷所有字段,可以使用For Each結構:
For Each fldAuthor In rsAuthors.FIElds
Response.Write fldAuthor.Name & " : " & _
fldAuthor.Value & "<BR>"
Next
這個例子將打印每一個字段的名字和值。
4. 書簽
當在記錄集中移動時,可能需要保留記錄的位置,以後再移回來。同真實的書簽相似,一個記錄集書簽是一個指向單個記錄的唯一的指針。
為了使用書簽,只需將Bookmark屬性值賦予一個變量:
varBkmk = rsAuthors.Bookmark
然後,可以在記錄集中移動,以後可以通過相反的命令將記錄移到做過書簽標記的相應記錄上:
rsAuthors.Bookmark = varBkmk
在記錄集中查找記錄時,書簽是非常有用的。在本章稍後的8.4.5節中有一個相關的例子。
注意,並非所有記錄集都支持書簽,Supports方法(在下面討論)將能識別其是否支持書簽。
值得注意的重要一點是,不能跨越不同的記錄集使用書簽,即使這些記錄集是相同的命令創建的。考慮一下以下代碼:
rsAuthors.Open "authors", strConn
rsAuthorsOther.Opne "authors", strConn
varBkmk = rsAuthors.Bookmark
varBkmkOther = rsAuthorsOther.Bookmark
盡管兩個記錄集是用相同的命令創建的,但記錄集的書簽是不一樣的。
可以使用Clone方法獲得可交換的書簽,但在這裡我們不討論它。
5. 支持的功能
如上所述,並非所有的記錄集都支持書簽。還有許多其他的記錄集選項也不是被所有的提供者或記錄集類型支持的,因此可以用Supports方法驗證一下。
Supports方法使用一個或多個CursorOptionEnum值作為參數,返回True或False表明是否支持該選項。這些值的列表相當龐大,所以將其列於附錄F中。
例如:
If rsAuthors.Supports(adBookmark) Then
' The recordset supports bookmarks
varBkMk = rsAuthors.Bookmark
End If
可以使用Or或加號“+”組合多個常數:
If rsAuthors.Supports(adBookmark Or adFind) Then
' The recordset supports bookmarks and use of Find
varBkMk = rsAuthors.Bookmark
End If
8.4.4 過濾記錄集
過濾是一種暫時地限定記錄集中可見記錄的一種方法。如果僅顯示記錄集中的某些記錄,但又不需要每次都重新查詢數據庫,這種方法非常有用。
1. 使用條件過濾
Filter屬性擁有多個參數,其中一個就是條件表達式,它非常像SQL中Where子句:
rsAuthors.Filter = "state = 'ca'"
這個語句限定記錄集只顯示州名為ca的記錄。使用這個過濾條件將使當前指針回到第一條匹配記錄上。可以遍歷記錄集中的全部記錄,並且只有匹配條件的記錄才可見。
不僅僅限於單一條件,還可以使用And或Or把多個條件連接在一起:
rsAuthors.Filter = "au_lname = 'Homer' Or au_lname = 'Francis'"
這將過濾出姓為Francis或Homer的記錄。
上面的例子顯示了一個列匹配一個值的過濾方法,也可以使用下面操作符中的任何一種:
<:小於。
>:大於。
<=:小於等於。
>=:大於等於。
<>:不等於。
LIKE:通配符。
當使用通過配符操作時,可以使用“*”或“%”符號。例如:
rsAuthors.Filter = "au_lname LIKE 'Ho%'"
“*”或“%”作為一個通配符,匹配任何字符,因此上面的例子會匹配au_lname字段中以“Ho”字符開始的所有記錄。
可以使用空字符串清空過濾條件,這樣將顯示全部記錄:
rsAuthors.Filter = ""
2. 使用常數過濾
Filter屬性也能用FilterGroupEnum常數作為其參數:
· adFilterNone:清空當前過濾條件,與使用一個空字符串的效果相同。
· adeFilterPendingRecords:只顯示那些已改變的,但還沒送到服務器的記錄,只在成批更新模式下可用。
· adFilterAffectedRecords:只顯示那些受上一次調用Delete、Resync、UpdateBatch和CancelBatch方法影響的記錄。
· adFilterFetchedRecords:顯示高速緩存中的記錄,即上一次調用讀取記錄的命令時的結果。
· adFilterConflictingRecords:顯示在上一次成批更新中更新失敗的記錄。
稍後會看到關於成批更新的介紹。
3. 使用書簽過濾
最後一種過濾記錄集的方法是使用一個書簽數組。可以使用這個技術創建一個記錄列表,然後再應用一個過濾條件對其過濾。例如:
rsAuthors.Open "authors", strConn, adOpenKeyset, _
adLockReadOnly, adCmdTableDirect
' Save bookmark for the first record
avarBkmk(0) = rsAuthors.Bookmark
' Move forward two records
rsAuthors.MoveNext
rsAuthors.MoveNext
' Save bookmark for the third record
avarBkmk(1) = rsAuthors.Bookmark
' Move to the end and save the bookmark
rsAuthors.MoveLast
avarBkmk(2) = rsAuthors.Bookmark
' Now apply the filter
rsAuthors.Filter = Array(avarBkmk(0), avarBkmk(1), avarBkmk(2))
' Now loop through the recordset
While Not rsAuthors.EOF
Response.Write rsAuthors("au_lname") & "<BR>"
rsAuthors.MoveNext
Wend
當循環至記錄集末尾位置時,會發現只有三條記錄,因為只有三個書簽應用於過濾條件。
注意,不能直接使用數組avarBkmk,必須使用Array函數將各個書簽轉換成不同的數組。
8.4.5 查找記錄
查找單個的記錄由Find方法來完成。它類似於使用條件的過濾方法:
rsAuthors.Find "au_lname = 'Lloyd'"
它們之間最主要的區別在於這種方法只能有一個條件,不允許使用And或Or。
可以使用可選的參數指定一些額外的選項,其完整的語法如下:
Recordset.Find Criteria, [SkipRows], [SearchDirection], [Strat]
SkipRows是一個數字,表示在開始查找記錄前跳過的行數。缺省為0,查詢從當前行開始。
SearchDirection可以是adSearchForward,表示向前搜索記錄;或者adSearchBackward,表示向後搜索記錄。
Start是一個書簽,指出開始查找記錄的位置。
如果打開相應的記錄,當前指針將位於匹配的記錄上,如果沒有找到記錄,那麼將位於下面兩個位置中的一個:
· 如果是向前搜索,則位於記錄集末尾位置的後面,EOF被設置為True。
· 如果是向後搜索,則位於記錄集開始位置的前面,BOF被設置為True。
使用書簽保存位置
如果沒有找到相應的記錄,記錄的重新定位可以由書簽輕松解決,因為可以為當前位置制作書簽,如果在查找記錄過程中沒有找到所需的記錄,那麼再移回到上次保存的位置。
例如:
' Save the current position
varBkmk = rsAuthors.Bookmark
' Find the record
rsAuthors.Find "au_lname = 'Sussman'"
' Was it found
If Not rsAuthors.EOF Then
Response.Write "Found: " & rsAuthors ("au_lname") & ", " & _
rsAuthors("au_fname") & "<BR>"
Else
Response.Write "Not found. Moving <BR>"
rsAuthors.Bookmark = varBkmk
End If
使用Filter屬性強於Find方法的一個原因是Find語句只能一個查詢條件,而Filter屬性允許指定多個條件。也就是說,當想要查找的字段條件不止一個時,不能使用Find方法。然而,可以先過濾記錄,如果找到記錄可以再刪除過濾條件。
8.4.6 修改記錄
大部分的Web只用來顯示信息,而Web應用程序正變得越來越普遍。在這種情形下,如果只擁有只讀數據確實沒有什麼用處。創建一個應用程序,幾乎總是需要修改現存的數據或是添加新的數據,其方法有許多。在本節,將學習如何使用Recordset對象的方法來更改數據。在下一章,將會看到如何使用查詢完成相同的任務。
可以設置除了adLockReadOnly之處的鎖定類型配合使用Recordset對象的方法去修改數據(假定有相應的權限)。記住,缺省的鎖定類型是只讀的。
1. 添加記錄
要在記錄集中添加記錄,使用AddNew方法。有兩種使用AddNew的方法。第一種沒有任何參數,僅僅調用AddNew,在記錄集的最後添加一個空記錄。在調用Update方法保存所做的更改之前,可以隨意地修改字段中的數據:
With rsAuthors
.Open "authors", strConn, adOpenDynamic, _
adLockOptimistic, adCmdTableDirect
.AddNew
.FIElds("au_id") = "123-12-1234"
.FIElds("au_lname") = "Lloyd"
.FIElds("au_fname") = "Janine"
.FIElds("contract") = 0
.Update
End With
這只是添加了一條新記錄,設置四個強制型的字段值。
另一種方法是使用AddNew方法的可選參數,這是兩個數組,一個是字段名,另一個是字段的值。
With rsAuthors
.Open "authors", strConn, adOpenDynamic, _
adLockOptimistic, adCmdTableDirect
.AddNew Array("au_id", "au_lname", "au_fname", "contract"), _
Array("123-12-1234", "Lloyd", "Janine", 0)
End With
這個方法不需要調用Update方法。
2. 編輯記錄
編輯記錄與添加記錄的方法相似,不同之處在於不需要調用AddNew方法:
strSQL = "SELECT * FROM authors" & _
"WHERE au_lname='Lloyd'"
With rsAuthors
.Open strSQL, strConn, adOpenDynamic, _
adLockOptimistic, adCmdText
.FIElds("contract") =1
.Update
End With
這僅僅是將當前記錄(在這種情況下是第一條記錄,因為剛剛打開記錄集)的contract字段的值賦為1。
3. 刪除記錄
刪除記錄需調用Delete方法。刪除哪一條記錄取決於可選的參數,可以是下面AffectEnum常數中的一個:
· adAffectCurrent:刪除當前記錄,缺省操作。
· adAffectGroup:刪除匹配當前過濾條件的所有記錄。
· adAffectAll:刪除記錄集中的全部記錄。
· adAffectAllChapters:刪除所有段(chapter)中的記錄。
最簡單的調用形式是:
rsAuthors.Delete
這將刪除當前記錄。如果有一個過濾條件,並想刪除所有匹配該條件的記錄,那麼僅需加上適當的常數:
rsAuthors.Delete adAffectGroup
4. 自動遞增的字段
當添加一條新記錄時,一般會碰到這樣一個問題:如何處理那些自動遞增的或標識字段(Identity Filed)。這些字段是由服務器自動更新的數字字段,一般用於為每一行提供一個唯一的字段值。當數據庫含有多個表時,那麼這個唯一的字段經常被當作關聯表的外鍵。所以,添加一條新記錄時,經常需要找出它們的值。
例如,考慮一下有兩個字段的表,一個自動遞增的ID字段(SQL Server中的IDENTITY字段或Access中的AutoNumber字段),一個字段名為Name的文本字段。現在考慮一下下面的向表中添加記錄的代碼:
With rsData
.Open "tblTest", adOpenDynamic, adLockOptimistic, adCmdTableDirect
.AddNew
.FIElds("Name") = "Janine"
.Update
intID = .FIElds("ID")
End With
看上去很平常,但添加記錄後是否能夠取到這個值依賴於光標的類型、鎖定的類型以及ID字段是否被索引。表8-4列出了哪些組合允許獲取新插入的ID字段的值。其他沒有列在表中的組合不能返回ID字段的值。
表8-4 獲取ID字段的值與光標、鎖定的類型及ID字段是否被索引的關系
提供者
對象
索引
光標位置
光標類型
鎖定類型
ODBC
Access97
是
服務器
鍵集
悲觀型
樂觀型
Access2000
是
服務器
鍵集
悲觀型
樂觀型
否
客戶
所有
悲觀型
樂觀型
SQL Server 6.5
是
服務器
鍵集
悲觀型
樂觀型
SQL Server 7.0
是
服務器
鍵集
悲觀型
樂觀型
客戶
所有
悲觀型
樂觀型
否
客戶
所有
悲觀型
樂觀型
Jet 4.0
Access97
是
服務器
所有
所有
Access 2000
是
服務器
所有
所有
否
客戶
所有
悲觀型
樂觀型
SQL OLE DB
SQL Server 6.5
SQL Server 7.0
是
服務器
鍵集
悲觀型
樂觀型
客戶
所有
悲觀型
樂觀型
否
客戶
所有
悲觀型
樂觀型
這清楚地說明必須使用正確的組合,才能保證能取得ID字段的正確值。否則,可能會得到0、空值或NULL,這取決於組合的方式。
在下一章中處理存儲過程時,將見到另一種從SQL Server獲取IDENTITY字段值的方法。
8.5 管理錯誤
處理數據存儲時,發生錯誤的可能性總是存在的:安全性問題,試圖更新已被其他用戶刪除的記錄,諸如此類的問題很多。不能保證一切都運行良好,因此必須構建某種形式的錯誤控制。
在第7章中,研究了ASP頁面中一般的錯誤處理,但現在涉及的是數據存儲,所以必須考慮使用額外的代碼進行錯誤處理。先看一下Errors集合,再討論其如何滿足ASP 3.0的錯誤處理機制。