適用于:
Microsoft® SQL™ Server 2000
摘要:學(xué)習如何充分利用 SQL Server 2000 的全文搜索功能。本文包含有關(guān)實(shí)現最大吞吐率和最佳性能的幾點(diǎn)提示和技巧。
使用 Microsoft® SQL™ Server 2000 的全文搜索功能,可以對在非結構化文本數據上生成的索引執行快速、靈活的查詢(xún)。常用的全文搜索工具是網(wǎng)站的搜索引擎。為了幫助讀者理解全文搜索功能的最佳使用方法,本文介紹了大量抽象概念;并對優(yōu)化全文索引和查詢(xún)以實(shí)現最大吞吐率和最佳性能,提供了幾點(diǎn)提示和技巧。
全文搜索功能在 SQL Server 7.0 中引入。全文搜索的核心引擎建立在 Microsoft Search (MSSearch) 技術(shù)上,Microsoft Exchange 和 Microsoft SharePoint™ Portal Server 等產(chǎn)品中也采用了此項技術(shù)。
SQL Server 7.0 全文搜索中公開(kāi)的功能可提供基本的文本搜索功能,并使用早期版本的 MSSearch;而 SQL Server 2000 的全文搜索實(shí)現則包含一組可靠的索引和查詢(xún)功能,并在 SQL Server 7.0 的基礎之上添加了幾項增強功能。這些增強功能包括:通過(guò) Microsoft 群集服務(wù)完全支持群集操作,能夠過(guò)濾和索引 IMAGE 列中存儲的文檔,提供改進(jìn)的語(yǔ)言支持,以及在性能、可縮放性和可靠性方面進(jìn)行了改進(jìn)。
MSSearch 生成、維護和查詢(xún)文件系統中(而不是 SQL Server 中)存儲的全文索引。MSSearch 進(jìn)行全文索引時(shí)使用的邏輯和物理存儲單元是目錄。全文目錄在每個(gè)數據庫中包含一個(gè)或多個(gè)全文索引 - 可以為 SQL Server 中的每個(gè)表創(chuàng )建一個(gè)全文索引,且索引中可以包含該表中的一列或多列。每個(gè)表只能屬于一個(gè)目錄,且每個(gè)表只能創(chuàng )建一個(gè)索引。我們將簡(jiǎn)單介紹有關(guān)組織全文目錄和索引的最佳方案 - 但首先,讓我們來(lái)簡(jiǎn)單了解一下全文搜索的工作原理。
要為 SQL Server 中存儲的文本數據創(chuàng )建全文索引,應該先完成以下幾步準備工作。第一步是以全文方式啟用包含要生成索引的文本數據的數據庫(如果您尚未執行此操作)。
注意:執行以下語(yǔ)句將丟棄并重新創(chuàng )建屬于要啟用全文搜索的數據庫的所有全文目錄。除非要重新創(chuàng )建全文目錄,否則請確保在要啟用的特定數據庫中未創(chuàng )建任何全文目錄。如果您是 sysadmin 角色的成員或此數據庫的 db_owner,可以繼續進(jìn)行并發(fā)出以下語(yǔ)句:
use Northwindexec sp_fulltext_database ‘enable‘
接下來(lái),您需要創(chuàng )建全文目錄,以存儲全文索引。正如前面所提到的,此目錄中的數據存儲在文件系統中(而不是 SQL Server 中),因此,在考慮全文目錄的存儲位置時(shí)應該仔細選擇。除非指定其他位置,否則全文目錄將存儲在 FTDATA 目錄(位于 Microsoft SQL Server\MSSQL 存儲位置中)的子目錄中。以下是在非默認位置創(chuàng )建全文目錄的方法:
exec sp_fulltext_catalog ‘Cat_Desc‘, ‘create‘, ‘f:\ft‘
在本例中,全文目錄將創(chuàng )建為“f:\ft”的子目錄,如果您查看文件系統的該部分,將看到它有了自己的目錄。MSSearch 使用的全文目錄的命名規則是:
SQL+dbid+catalogID
目錄 ID 從 00005 開(kāi)始,并且每新建一個(gè)目錄就遞增 1。
如果可能的話(huà),最好在其所在的物理驅動(dòng)器上創(chuàng )建全文目錄。如果生成全文索引的進(jìn)程需要進(jìn)行大量的 I/O 操作(具體而言,就是從 SQL Server 中讀取數據,然后向文件系統寫(xiě)入索引),則應避免使 I/O 子系統成為瓶頸。
那么,全文目錄有多大呢?通常情況下,全文目錄的系統開(kāi)銷(xiāo)比 SQL Server 中存儲的數據(對其進(jìn)行全文索引)量高出大約 30%;但是,此規則取決于數據中唯一單詞(或主鍵)的分布,以及被您視為是干擾詞的單詞的分布。干擾詞(或終止詞)是指要排除在全文索引和查詢(xún)以外的詞語(yǔ)(因為它們不是您感興趣的搜索詞,而且出現頻率很高,所以只會(huì )使索引變得很大,而不會(huì )有實(shí)際效果)。稍后,我們將介紹有關(guān)干擾詞選擇方面的注意事項,以及如何優(yōu)化干擾詞以改善查詢(xún)性能。
如果您尚未執行此操作,請在每個(gè)要生成全文索引的表上創(chuàng )建一個(gè)唯一的單列非空索引。這個(gè)唯一索引用于將表中的每一行映射到 MSSearch 內部使用的一個(gè)唯一可壓縮主鍵。接下來(lái),您需要讓 MSSearch 知道您要為表創(chuàng )建全文索引。對表發(fā)出以下語(yǔ)句可將該表添加到所選的全文目錄中(在本例中,它是我們在前面創(chuàng )建的“Cat_Desc”):
exec sp_fulltext_table ‘Categories‘, ‘create‘, ‘Cat_Desc‘,‘PK_Categories‘
下一步是向此全文索引添加列。您可以為每一列選擇一種語(yǔ)言,如果該列的類(lèi)型為 IMAGE,則必須再指定一列,以指示 IMAGE 列的每一行中存儲的文檔類(lèi)型。
在列語(yǔ)言選擇方面,有一些重要但尚未成文的注意事項。這些注意事項與文本的標記方式以及 MSSearch 對文本的索引方式有關(guān)。被索引的文本是通過(guò)一個(gè)稱(chēng)作單詞分隔符(用作單詞邊界標記)的組件提供的。在英文中,單詞分隔符通常是空格或某種形式的標點(diǎn)符號;而在其他語(yǔ)言中(例如德語(yǔ)),單詞或字符可以組合在一起;因此,所選的列語(yǔ)言應表示要存儲在該列的行中的語(yǔ)言。如果不確定,最好的方法通常是使用中性單詞分隔符(只使用空格和標點(diǎn)符號執行標記功能)。選擇列語(yǔ)言的另一個(gè)好處是“尋根溯源”。全文查詢(xún)中的尋根溯源是指在特定語(yǔ)言中搜索某一單詞的所有變化形式的過(guò)程。
選擇語(yǔ)言的另一個(gè)考慮因素與數據的表示方法有關(guān)。對于非 IMAGE 列數據來(lái)說(shuō),不需要執行特殊的過(guò)濾操作;而文本通常需要將單詞分隔組件按原樣傳遞。單詞分隔符主要用于處理書(shū)面文本。因此,如果文本中有任何類(lèi)型的標記(例如 HTML),則在索引和搜索過(guò)程中,語(yǔ)言精確性將不會(huì )很高。這種情況下,您有兩種選擇 - 首選方法是只將文本數據存儲在 IMAGE 列中,并指明其文檔類(lèi)型,以便對其進(jìn)行過(guò)濾。如果不選擇此方法,則可以考慮使用中性單詞分隔符,并且可能的話(huà),在干擾詞列表中添加標記數據(例如 HTML 中的“br”)。在指定了中性語(yǔ)言的列中不能進(jìn)行任何基于語(yǔ)言的尋根溯源,但有些環(huán)境可能會(huì )要求您選擇此方法。
在知道列選項后,通過(guò)發(fā)出以下語(yǔ)句在全文索引中添加一列或兩列:
exec sp_fulltext_column ‘Categories‘, ‘Description‘, ‘a(chǎn)dd‘
您可能注意到,此處未指定任何語(yǔ)言 - 這種情況下,將使用默認的全文語(yǔ)言??梢酝ㄟ^(guò)系統存儲過(guò)程“sp_configure”為服務(wù)器設置默認全文語(yǔ)言。
將所有列添加到全文索引后,即可執行填充操作。填充方法之多實(shí)在是不勝枚舉,此處不作詳細介紹。在本例中,只需對表啟動(dòng)完全填充,并等待它執行完畢:
exec sp_fulltext_table ‘Categories‘, ‘start_full‘
您可能希望使用 FULLTEXTCATALOGPROPERTY 或 OBJECTPROPERTY 函數來(lái)監視填充狀態(tài)。要獲取目錄填充狀態(tài),可以執行:
select FULLTEXTCATALOGPROPERTY(‘Cat_Desc‘, ‘Populatestatus‘)
通常情況下,如果完全填充正在進(jìn)行,則返回的結果是“1”。有關(guān)如何使用 FULLTEXTCATALOGPROPERTY 和 OBJECTPROPERTY 的詳細信息,請參閱 SQL Server Books Online。
查詢(xún)全文索引與執行 SQL Server 中的標準關(guān)系型查詢(xún)略有不同。由于索引是在 SQL Server 外部進(jìn)行存儲和管理的,因此全文查詢(xún)處理大部分由 MSSearch 完成(因此,那些一部分是關(guān)系型、一部分基于全文的查詢(xún)將被單獨處理),這樣做有時(shí)會(huì )損害性能。
從本質(zhì)上說(shuō),執行全文查詢(xún)時(shí),查詢(xún)詞傳遞給 MSSearch,后者遍歷其內部數據結構(索引),并向 SQL Server 返回主鍵和排位值。如果執行 CONTAINS 或 FREETEXT 查詢(xún),則通??床坏街麈I或排位值,但如果執行 CONTAINSTABLE 或 FREETEXTTABLE 查詢(xún),則將獲得這些值,然后這些值通常會(huì )與基表合并在一起。與基表合并主鍵的進(jìn)程需要很高的系統開(kāi)銷(xiāo) - 稍后,我們將向您介紹一些巧妙的方法以盡量減少或完全避免這種合并。
如果您通過(guò)不斷思考,對全文查詢(xún)如何返回數據有了一個(gè)初步了解,就可以推測出 CONTAINS/FREETEXT 查詢(xún)僅執行 CONTAINSTABLE/FREETEXTTABLE 查詢(xún)并與基表進(jìn)行合并。有了這樣的了解,您應該避免使用這些類(lèi)型的查詢(xún),除非不這樣做的開(kāi)銷(xiāo)更高。在 Web 搜索應用程序中,使用 CONTAINSTABLE 與 FREETEXTTABLE 比使用不帶 TABLE 的同類(lèi)函數好得多。
到現在為止,您已經(jīng)知道全文查詢(xún)是用來(lái)從 SQL Server 之外存儲的 MSSearch 索引中訪(fǎng)問(wèn)數據的特殊方法,還知道如果盲目地與基表進(jìn)行合并,就會(huì )遇到麻煩。應該了解的另外一個(gè)重要內容是 CONTAINS 樣式查詢(xún)與 FREETEXT 樣式查詢(xún)之間的本質(zhì)差別。
CONTAINS 查詢(xún)用于對所查詢(xún)的所有詞語(yǔ)執行完全匹配查詢(xún)。無(wú)論您只查找單個(gè)單詞,還是查找以“orange”開(kāi)頭的所有單詞,系統只返回包含所有搜索詞的結果。因此,CONTAINS 查詢(xún)速度很快,因為它們通常返回很少的結果,并且不需要執行過(guò)多的附加處理。CONTAINS 查詢(xún)的缺點(diǎn)包括令人生厭的干擾詞過(guò)濾問(wèn)題。經(jīng)驗豐富的開(kāi)發(fā)人員以及過(guò)去使用過(guò)全文搜索的數據庫管理員,在試圖匹配只包含單個(gè)干擾詞的單詞或詞組時(shí),曾遇到過(guò)“您的查詢(xún)只包含干擾詞”這樣令人吃驚的錯誤。要避免收到此錯誤,方法之一是在執行全文查詢(xún)之前過(guò)濾出干擾詞。向包含干擾詞的 CONTAINS 查詢(xún)返回結果是不可能的,因為此類(lèi)查詢(xún)只返回與整個(gè)查詢(xún)字符串完全匹配的結果。由于干擾詞不是全文索引項,因此包含干擾詞的 CONTAINS 查詢(xún)不會(huì )返回任何行。
FREETEXT 查詢(xún)消除了 CONTAINS 查詢(xún)中偶爾出現的所有警告說(shuō)明。當發(fā)出 FREETEXT 查詢(xún)時(shí),實(shí)際上發(fā)出的是詞根查詢(xún)。因此,當您搜索“root beer”時(shí),“root”和“beer”包含其所有形式(尋根溯源與語(yǔ)言相關(guān);所用的語(yǔ)言由生成索引時(shí)指定的全文列語(yǔ)言確定,并且在所有查詢(xún)的列中必須相同),并且系統將返回至少與這些詞語(yǔ)之一匹配的所有行。
FREETEXT 查詢(xún)的負面影響是它們通常比 CONTAINS 查詢(xún)耗用更多的 CPU - 因為要尋根溯源以及返回更多的結果,就需要包含更復雜的排位計算。不過(guò),基于 FREETEXT 的查詢(xún)非常靈活,而且速度非???,是基于 Web 的搜索應用程序中通常使用的最佳選擇。
我經(jīng)常遇到使用全文搜索的用戶(hù),他們問(wèn)我排位編號是什么意思,以及如何將排位編號轉換成某種用戶(hù)可以理解的值。對這個(gè)問(wèn)題,回答可長(cháng)可短,在這里我將進(jìn)行簡(jiǎn)要回答。簡(jiǎn)單而言,這些排位編號不如結果返回的順序那樣重要。也就是說(shuō),當您按照排位對結果進(jìn)行排序時(shí),總是首先返回關(guān)聯(lián)程度最高的結果。排位值本身常常變化 - 全文搜索使用概率排位算法,即返回的每個(gè)文檔的關(guān)聯(lián)性受全文索引中的任何或所有其他文檔的直接影響。
有些人認為,一種有助于增加某些行排位的技巧是在這些行的全文索引列中重復常用的搜索關(guān)鍵字。盡管在某種程度上,這種方法可能會(huì )提高這些行因某些關(guān)鍵字而首先返回的幾率,但在其他情況下,可能會(huì )適得其反 - 而且還存在使詞語(yǔ)查詢(xún)性能降低的風(fēng)險。較好的解決方案是為搜索應用程序實(shí)現“最佳選擇”系統(請參閱以下示例),這樣就可以確保首先返回某些文檔。多次重復使用關(guān)鍵字會(huì )使這些特定關(guān)鍵字的全文索引擴大,并使得 MSSearch 在查找正確行和計算排位時(shí)浪費時(shí)間。如果全文索引數據量很大,并嘗試使用了此方法,您可能會(huì )發(fā)現某些全文查詢(xún)很耗時(shí)。如果能夠實(shí)現更細致(也可能更精確)的“最佳選擇”系統,您會(huì )發(fā)現它明顯改善了查詢(xún)性能。
多次重復數據的另一個(gè)問(wèn)題與用于組合關(guān)系型查詢(xún)和全文查詢(xún)的常用技巧有關(guān)。許多使用全文搜索的用戶(hù)都深受此問(wèn)題的困擾,每當他們試圖將某種過(guò)濾器應用于全文查詢(xún)返回的結果時(shí),便會(huì )遇到這樣的問(wèn)題。正如前面所說(shuō)的,全文查詢(xún)?yōu)槊總€(gè)匹配行返回一個(gè)主鍵和一個(gè)排位 - 要收集有關(guān)這些行的任何詳細信息,必須與它的基表進(jìn)行合并。由于從無(wú)限制的全文查詢(xún)中可能會(huì )返回任意數量的結果,因此合并可能需要大量系統開(kāi)銷(xiāo)。人們發(fā)現避免合并的一個(gè)有效方法是只在全文索引中添加要過(guò)濾的數據(如果可能)。換句話(huà)說(shuō),如果用戶(hù)要從報紙上所有文章的正文中搜索關(guān)鍵字“Ichiro”,并且只希望返回該報上體育專(zhuān)欄中的文章,則查詢(xún)語(yǔ)句通常如下所示:
-- [方法 1:]-- 開(kāi)銷(xiāo)最高:先全部選擇,然后再合并和過(guò)濾SELECT ARTICLES_TBL.Author, ARTICLES_TBL.Body, ARTICLES_TBL.Dateline,FT_TBL.[rank]FROM FREETEXTTABLE(Articles, Body, ‘Ichiro‘) AS FT_TBLINNER JOIN Articles AS ARTICLES_TBLON FT_TBL.[key] = ARTICLES_TBL.ArticleIDWHERE ARTICLES_TBL.Category = ‘Sports‘-- [方法 2:]-- 可以使用,但會(huì )導致意外結果并變慢,或者會(huì )返回不準確的結果:-- 執行全文過(guò)濾,并且只提取主鍵和排位-- (處理在 Web 服務(wù)器上完成)SELECT [key], [rank]FROM CONTAINSTABLE(Articles, *, ‘FORMSOF(INFLECTIONAL(‘Ichiro‘)AND "sports"‘)
這兩個(gè)查詢(xún)要么不必要地占用大量系統開(kāi)銷(xiāo),要么存在返回錯誤結果的可能性(在第二個(gè)查詢(xún)中,“sports”很可能出現在所有類(lèi)型的文章中)。這兩項技術(shù)還存在其他變體,但這是兩種非常簡(jiǎn)單的模型。如果可行,我通常建議您對數據進(jìn)行水平劃分。即,“類(lèi)別”列的每個(gè)可能值都自成一列(或表),并且與該文章相關(guān)的可搜索關(guān)鍵字僅存儲在此列中。采用此方法,而不是使用一個(gè)“正文”列和一個(gè)“類(lèi)別”列,可以去掉“類(lèi)別”列,而使用存儲可搜索關(guān)鍵字的“Body_<category>”列。如以下示例所示:
-- 如果您可以調整架構,這非常有效 – 每個(gè)類(lèi)別-- 都成為自己的列(或表格),并且需要命中的-- 全文索引也較少。這明顯需要作一些解釋……SELECT [key], [rank]FROM FREETEXTTABLE(Articles, Body_Sports, ‘Ichiro‘)
對于包含大量數據,且這些數據可適應此架構(或許是主架構)更改的系統,其性能會(huì )得到顯著(zhù)的提高。但在何時(shí)應用多個(gè)過(guò)濾器或不應用過(guò)濾器方面卻有著(zhù)明顯的限制。當然,還有其他的方法可以解決這些問(wèn)題。通過(guò)以上示例,您會(huì )了解一種將某些搜索條件抽象到架構的方法 - 實(shí)際上是“欺騙”優(yōu)化程序(更確切的說(shuō)是“成為”優(yōu)化程序),因為在 SQL Server 本身的全文查詢(xún)中當前不存在本地優(yōu)化。
人們在聊天時(shí)常常問(wèn)我的另一個(gè)問(wèn)題是如何才能分頁(yè)顯示全文查詢(xún)結果。換句話(huà)說(shuō),如果我要發(fā)出“root beer”查詢(xún),一次在某一 Web 頁(yè)上顯示 40 個(gè)結果,并且只希望返回該頁(yè)面上的 40 個(gè)結果(例如,如果我在第三頁(yè),我希望僅返回第 81 至第 120 條結果)。
對于分頁(yè)顯示結果,我曾見(jiàn)過(guò)多種方法,但沒(méi)有一種方法能夠做到百分之百有效。我所推薦的方法可以最大程度地減少全文查詢(xún)執行的次數(實(shí)際上,對于要分頁(yè)顯示的每個(gè)結果集只需執行一次),并將 Web 服務(wù)器用作一個(gè)簡(jiǎn)單的緩存。從更高的層面來(lái)講,您只需在全文查詢(xún)中檢索一個(gè)完整的主鍵和排位值行集合(如果需要,可以在架構中使用最佳選擇并提取常用過(guò)濾器),并將其存儲在 Web 服務(wù)器的內存中(這取決于您的應用程序和負載,想象將 <32 字節的典型主鍵大小與 <4 字節的排位大小相加 [等于 <36 字節],然后乘以通常返回的結果集 <1000 行,最后等于 <35K。假定一個(gè)在任何給定時(shí)間返回 <1000 個(gè)活動(dòng)查詢(xún)結果集中的一個(gè)活動(dòng)緩存集,您將發(fā)現此活動(dòng)緩存集在 Web 服務(wù)器上占用的內存少于 35MB - 這還可以接受)。
為了分頁(yè)顯示結果,該進(jìn)程只遍歷 Web 服務(wù)器的內存中存儲的數組,并對 SQL Server 發(fā)出 SELECT 以便只顯示需要顯示的行和列。這又回到了全文查詢(xún)僅返回主鍵和排位的概念中 - SELECT(甚至許多這樣的查詢(xún)語(yǔ)句)比全文查詢(xún)的速度快許多倍。使用 SELECT 而不是與基表合并多個(gè)行,并結合多個(gè)其他策略,您可以保留 SQL Server 計算機上更多的 CPU 周期,并且更有效、更劃算地利用 Web 領(lǐng)域。
另一種可以替代 Web 服務(wù)器端緩存的方法是在 SQL Server 自身中緩存結果集,并定義多種用于瀏覽這些結果的方法。雖然本文著(zhù)重說(shuō)明 Web 服務(wù)器 (ASP) 級別的應用程序設計,但 SQL Server 的可編程功能還為生成高性能的 Web 搜索應用程序提供了強大的框架。
Microsoft SQL Server 2000 的全文搜索功能為索引和查詢(xún)數據庫中存儲的非結構化文本數據提供了可靠、快速而靈活的方法。如果要廣泛地將這種快速、準確的搜索功能應用于各種應用程序,那么很有必要充分利用其速度和精確性,來(lái)實(shí)現全文搜索解決方案。通過(guò)分布計算負載并通過(guò)某些巧妙的方式對數據進(jìn)行組織,可以省下錢(qián)來(lái)購買(mǎi)其他硬件和軟件,以擺脫因不必要的緩慢查詢(xún)帶來(lái)的困擾。在開(kāi)發(fā)優(yōu)秀的搜索應用程序時(shí),通常要考慮到許多因素和注意事項,希望本文提供的信息和示例對您學(xué)習使用 SQL Server 2000 生成出色的 Web 搜索應用程序會(huì )有所幫助。
改進(jìn)全文查詢(xún)性能和有效性的一種可行方法是實(shí)現“最佳選擇”系統。此系統是一種很簡(jiǎn)單的方法,可確保某些與特定查詢(xún)表達式匹配的行先于其他行返回。最佳選擇沒(méi)有復雜的預編程邏輯(例如,SharePoint Portal Server 就包含這樣的邏輯),因此,通常是首選辦法。
在本示例中挑選出最佳選擇,并將唯一的主鍵和一些關(guān)鍵字存儲在單獨的表中。FREETEXTTABLE 查詢(xún)對(非常小的)最佳選擇表執行,并且從該查詢(xún)中返回的任何結果都與對基表的 FREETEXTTABLE 查詢(xún)結果一同返回。在給定這些搜索條件下,最先返回的將是所有“最佳選擇”行,隨后是被 MSSearch 視為關(guān)聯(lián)程度最高的行(以遞減順序返回)。
下面是一個(gè)非常簡(jiǎn)單的用于創(chuàng )建最佳選擇系統的示例腳本。
use myDbcreate table documentTable(ftkey int not null, document ntext)create unique index DTftkey_idx on documentTable(ftKey)/*在此插入文檔(要生成全文索引的所有文檔)*/-- 為所有文檔表創(chuàng )建全文目錄和索引exec sp_fulltext_catalog ‘documents_cat‘, ‘create‘, ‘f:\ftCats‘exec sp_fulltext_table ‘documentTable‘, ‘create‘, ‘documents_cat‘,‘DTftkey_idx‘exec sp_fulltext_column ‘documentTable‘, ‘document‘, ‘a(chǎn)dd‘exec sp_fulltext_table ‘documentTable‘, ‘start_change_tracking‘exec sp_fulltext_table ‘documentTable‘, ‘start_background_updateindex‘/*現在創(chuàng )建最佳選擇表和索引(添加應該始終最先返回的文檔)*/create table bestBets(ftKey int not null, keywords ntext)create unique index BBftkey_idx on bestBets(ftKey)/*在此插入最佳選擇*/-- 為最佳選擇表創(chuàng )建全文目錄和索引exec sp_fulltext_catalog ‘bestBets_cat‘, ‘create‘, ‘f:\ftCats‘exec sp_fulltext_table ‘bestBets‘, ‘create‘, ‘bestBets_cat‘, ‘BBftkey_idx‘exec sp_fulltext_column ‘bestBets‘, ‘keywords‘, ‘a(chǎn)dd‘exec sp_fulltext_table ‘bestBets‘, ‘start_change_tracking‘exec sp_fulltext_table ‘bestBets‘, ‘start_background_updateindex‘
首先創(chuàng )建了一個(gè)通用的“所有文檔”表,用于存儲所有要全文索引的文檔。通常情況下,文檔表中包含其他列,但在本文中,只包含兩列 - 主鍵索引和文檔本身。全文目錄和索引是為文檔表而創(chuàng )建的。
接著(zhù)創(chuàng )建了“最佳選擇”表,用于存儲所有全文查詢(xún)中首先返回的特殊文檔。此表只需具有全文主鍵列和文檔本身(對將某些文檔作為查詢(xún)目標的策略進(jìn)行優(yōu)化,包括在該文檔本身不包含的文檔中添加其他關(guān)鍵字)。全文目錄和索引是為最佳選擇表而創(chuàng )建的。
最佳選擇表和文檔表可以共享文檔(最佳選擇文檔還存儲在常規文檔表中,它們共享同一個(gè)主鍵值),也可以相互排斥(最佳選擇文檔只存儲在最佳選擇表中)。為便于檢索,使最佳選擇表與文檔表互斥更為容易 - 這樣做就無(wú)需從最佳選擇和返回的普通搜索結果行集合中刪除共享操作。另一方面,使用此方法維護文檔可能很難實(shí)現,因為在此方法中,要在查詢(xún)中添加邏輯來(lái)刪除返回的行集合之間的共享文檔。
如果給定上面的表,則可以創(chuàng )建兩個(gè)存儲過(guò)程,以便對最佳選擇表和文檔表進(jìn)行搜索??墒褂?Web 服務(wù)器級別的邏輯或其他存儲過(guò)程來(lái)緩存和顯示所需結果(與最佳選擇一起使用時(shí),請參閱下面有關(guān)緩存、顯示和分頁(yè)的一個(gè)完整、有效的示例)。
首先,創(chuàng )建一個(gè)用于檢索最佳選擇行(如果有)的存儲過(guò)程:
create procedure BBSearch @searchTerm varchar(1024) asselect [key], [rank] from freetexttable(bestBets, keywords, @searchTerm) order by [rank] desc
確保已對傳入搜索字符串進(jìn)行清理,以避免在服務(wù)器上隨意執行 T-SQL,并確保用單引號將該字符串括起。這種情況下,使用 FREETEXTTABLE 比使用 CONTAINSTABLE 要好,因為 FREETEXTTABLE 將采用尋根溯源功能,并找到與任何搜索詞相匹配的最佳選擇。
接下來(lái),第二個(gè)存儲過(guò)程檢索與常規搜索標準匹配的文檔(如果有):
create procedure FTSearch @searchTerm varchar(1024) asselect [key], [rank] from freetexttable(documentTable, keywords, @searchTerm) order by [rank] desc
此外,請確保已清理傳入搜索字符串,并用單引號將該字符串括起。
執行這些存儲過(guò)程時(shí),應該在兩個(gè)存儲過(guò)程中傳入相同的搜索詞,首先執行最佳選擇搜索,然后執行普通全文搜索。下一節更全面地介紹了在構建 Web 搜索應用程序時(shí),如何與其他全文搜索技術(shù)一起使用最佳選擇。
在本例中,我們實(shí)現了一個(gè)幾乎利用了本文介紹的所有優(yōu)化方案的 Web 搜索應用程序。我們對聯(lián)機零售商目錄使用簡(jiǎn)單的搜索引擎方案,并假定在通信量很高的情況下,所有用戶(hù)都期待在很短的響應時(shí)間內獲得結果。本示例使用了前一節中的最佳選擇表和存儲過(guò)程。
此應用程序只是一些可用于實(shí)現最佳全文搜索性能的高級策略的簡(jiǎn)單示例。本示例使用了 ASP,也可使用 ISAPI、ASP.NET 或其他平臺來(lái)實(shí)現具有各自?xún)?yōu)缺點(diǎn)的類(lèi)似解決方案。會(huì )話(huà)對象并不一定對所有應用程序都適用,如果使用不當,可能帶來(lái)一定程度的危險。在本例中,我們使用會(huì )話(huà)對象來(lái)實(shí)現快速有效的緩存機制 - 當然還有許多其他方法可以在不同程度上實(shí)現該功能。
下面是 ASP 頁(yè)的通用代碼:
<% @Language = "VBScript" %><% Response.buffer = true %><html><head><title>FT 測試</title></head><body><pre>----------------- 開(kāi)始測試 ------------------<%Dim firstRow ‘ 分頁(yè)顯示行時(shí)的第一行Dim lastRow ‘ 分頁(yè)顯示行時(shí)的最后一行Dim pageSize ‘ 頁(yè)面大?。看蔚男袛担〥im cn ‘ 連接對象Dim rs ‘ FT 主鍵/排位返回的結果集(重復使用)Dim useCache ‘ 使用緩存或命中 FT(0:不使用;1:使用)Dim alldata ‘ 要緩存的結果行集合Dim bbdata ‘ 要緩存的最佳選擇行集合Dim connectionString ‘ SQL 連接字符串‘ 確定是否要從緩存獲取數據‘ 默認為否,否則接受傳入的數據if (request.Form("useCache") <> "") thenuseCache = request.Form("useCache")elseif (request.QueryString("useCache") <> "") thenuseCache = request.QueryString("useCache")elseuseCache = 0end if‘ 設置常量pageSize = 24firstRow = 0lastRow = 23connectionString = <在此輸入您的連接字符串>‘----------------------------------------------------------------‘‘ 顯示與最佳選擇/搜索詞匹配的簡(jiǎn)單主鍵/排位 ‘‘----------------------------------------------------------------‘Private Sub SearchNPage()Dim p ‘ 循環(huán)通過(guò)行時(shí)的計數器Dim numRows ‘ 緩沖/結果集中的總行數if (useCache <> "1") then ‘ 獲取最佳選擇/結果并將其緩存Dim queryArg ‘ 傳入的查詢(xún)詞if (request.Form("searchTerm") <> "") thenqueryArg = request.Form("searchTerm")elseif (request.QueryString("searchTerm") <> "") thenqueryArg = request.QueryString("searchTerm")elseresponse.Write("未提供搜索詞" & VbCrLF)exit subend if‘ 理想情況下,應該在此清理查詢(xún)詞...‘ 添加自定義的清理邏輯,以防止‘ 隨意執行 SQL‘ 調用 CleanString(queryArg)‘ 建立與 SQL 的連接Set cn = Server.CreateObject("ADODB.Connection")cn.Open connectionString‘ 從傳入的干凈字符串中獲取最佳選擇匹配項set rs = cn.Execute("exec BBSearch ‘" & queryArg & "‘")‘ 如果有最佳選擇,則獲取最佳選擇if not(rs.EOF) thenbbData = rs.GetRowsend if‘ 現在從傳入的干凈字符串中獲取普通匹配項set rs = cn.Execute("exec FTSearch ‘" & queryArg & "‘")‘ 如果未返回任何結果,則結束if (rs.EOF and IsEmpty(bbdata)) thenresponse.Write("沒(méi)有匹配的行" & VbCrLF)call ConnCloseexit subend if‘ 否則,獲取行if not(rs.EOF) thenalldata = rs.GetRowsSession("results") = alldataend ifcall ConnCloseelse ‘ 從緩存加載 (usecache=1)alldata = Session("results")‘ 在此獲取要使用的行范圍if (request.Form("firstRow") <> "") thenfirstRow = request.Form("firstRow")lastRow = firstRow+pageSizeelseif (request.QueryString("firstRow") <> "") thenfirstRow = request.QueryString("firstRow")lastRow = firstRow+pageSizeend ifend if ‘ useCache<>TRUE‘ 對于本應用程序,只是打印出所有最佳選擇‘ (可能比頁(yè)面大小大),然后分頁(yè)顯示普通結果‘ 此處假設:在使用緩存時(shí),如果沒(méi)有新的最佳選擇,‘ 則使用以前顯示的最佳選擇if not(IsEmpty(bbdata)) thenresponse.Write("最佳選擇:" & VbCrLf)for p = 0 to ubound(bbdata, 2)response.Write(bbData(0,p) & " " & bbData(1,p) & VbCrLf)nextresponse.Write(VbCrLf)end if‘ 返回搜索結果(可能只有最佳選擇)if not(IsEmpty(alldata)) thenif uBound(alldata, 2) < lastRow thenlastRow = uBound(allData, 2)end ifresponse.Write("搜索結果:" & VbCrLf)for p = firstRow to lastRowresponse.Write(allData(0,p) & " " & allData(1,p) & VbCrLf)nextend if ‘ not(IsEmpty(alldata))End Sub‘----------------------------------------------------------------‘‘ 關(guān)閉并清除連接對象 ‘‘----------------------------------------------------------------‘Private Sub ConnClosers.CloseSet rs = Nothingcn.CloseSet cn = NothingEnd Subcall SearchNPage%>---------------- 測試結束 ----------------<form action="<本頁(yè)>" method="post"><input type=submit value="next <%=pageSize%> rows" NAME="Submit1"><input type=hidden name="useCache" value="1"><input type=hidden name="firstRow" value=<%=lastrow+1%>></form></pre></body></html>一個(gè)簡(jiǎn)單的 HTML 窗體頁(yè)面即可像下面一樣利用上面的腳本:<html><head><title>輸入搜索詞</title></head><body><form action="<搜索 ASP 頁(yè)面>" method="post">搜索詞:<input name="searchTerm"><p><input type="submit" value="Search"></form></body></html>正如以上兩個(gè)代碼示例所示,創(chuàng )建可執行有效全文查詢(xún)(用最佳選擇完成)并緩存和分頁(yè)顯示結果的 Web 應用程序,并不需要花費太多的工夫。只需使用最低的系統開(kāi)銷(xiāo),即可添加用于提供其他數據、增強最佳選擇的外觀(guān)以及在搜索結果中導航的邏輯(此外,強烈建議您實(shí)現其他用于錯誤處理、安全設置和清理傳入數據的嚴密邏輯)。
通過(guò)上面的高級建議和示例,使用 SQL Server 2000 全文搜索設計和實(shí)現快速可縮放的 Web 搜索應用程序就是輕而易舉的事情了。
Full-Text Search Deployment(英文)
是那些初次接觸全文搜索的用戶(hù)的最佳參考。介紹了填充方法及硬件和軟件需求,并為使用 SQL Server 2000 全文搜索提供了提示、技巧和其他文檔。
全文搜索公共新聞組 (microsoft.public.sqlserver.fulltext)
查找有關(guān)全文搜索問(wèn)題的答案以及有用提示和技巧的理想場(chǎng)所。全文搜索新聞組是 SQL Server 開(kāi)發(fā)小組和博學(xué)的 Microsoft MVP 成員經(jīng)常光顧的場(chǎng)所。
聯(lián)系客服