第九章 查詢數據庫
這一章介紹如何用TQuery構件查詢數據庫,如何經過SQL語句檢索、插入、更新和刪除數據。SQL是符合工業標準的關係數據庫語言,既能夠用於遠程的基於服務器的數據庫,如Sybase、Oracle、InterBase和Microsoft SQL Server,也能夠用於本地數據庫如Paradox、dBASE、FoxPro和Access以及符合ODBC的數據庫。
Array.1 有效地使用查詢
要有效地使用查詢,必須熟悉標準的SQL語言以及所使用的服務器對SQL-Array2的限制和擴展,同時還要熟悉BDE。
Array.1.1 查詢桌面數據庫
做爲一個桌面開發者,應對錶格、記錄和字段的概念有所瞭解,又能熟練地使用TTable構件訪問數據集中的每一條記錄和每個字段。
還可使用TTable的範圍和過濾功能在數據集中選擇一部分記錄,前者用於選擇一塊連續的記錄,這些記錄的值在一個特定的範圍內; 後者用於選擇非連續的記錄,這些記錄符合特定的條件。
所謂查詢,很是相似於過濾,不一樣的是,查詢要用到TQuery構件和SQL屬性,有時候可能還要用到Params屬性。從功能上講,查詢要比過濾複雜和強大些,這主要體如今:
.能夠同時查詢幾個表格
.可讓查詢結果中只包含部分字段,而過濾將返回全部字段。
查詢也能夠帶參數,此時稱爲參數化查詢。所謂參數,相似於變量,它的實際的值由BDE在執行SQL語句以前賦值。參數化查詢的好處是,不須要修改SQL語句,只要修改參數的值,就能執行不一樣的查詢功能。
大部分狀況下,使用TQuery構件是爲了在數據集中選擇一部分字段和記錄,但也可使用SQL語句實現更新、插入和刪除記錄的功能,這是與TTable構件的一個區別。
Array.1.2 查詢遠程數據庫
要查詢遠程數據庫,必須熟悉SQL語句以及服務器對標準SQL的限制和擴展。
TQuery構件的SQL屬性用於指定要執行的SQL語句, Params屬性用於提供參數。TQuery構件的功能並不僅限於SQL語句和參數,它仍是BDE與應用程序之間的接口。
應用程序能夠經過TQuery構件的屬性和方法來操縱SQL語句和參數。TQuery構件最終仍是經過SQL Links與遠程服務器進行通信的,遠程服務器把查詢結果返回給BDE,再由BDE返回給應用程序。
Array.2 能夠查詢哪些數據庫
使用TQuery構件能夠查詢下列數據庫:
一是Paradox或dBASE,這是經過BDE內置的Local SQL實現的。Local SQL是SQL-Array2標準的一個子集,支持大部分DML和DDL。
二是Local InterBase Server,這是經過InterBase引擎實現的。
三是遠程數據庫,如Oracle、Sybase、MS-SQL Server、InFormix、DB2和InterBase,不過,必須安裝了相應的SQL Links驅動程序。不一樣的服務器對標準SQL都有不一樣的限制和擴展,要查詢遠程數據庫以前,務必要查閱它的有關文檔。
Delphi 4還支持異構查詢,也就是說,能夠同時查詢幾個不一樣類型的數據庫。
Array.3 使用TQuery構件的通常步驟
第一步是把一個TQuery構件放到數據模塊上,設置它的DatabaseName屬性指定要訪問的數據庫。對於Paradox和dBASE來講,DatabaseName屬性能夠設爲BDE別名如DBEMOS、DefaultDD、IBLOCAL等,也能夠是自定義的別名或者表所在的路徑。
對於SQL表來講,DatabaseName屬性只能設爲BDE別名。若是應用程序使用TDatabase構件來鏈接數據庫,DatabaseName屬性也能夠設爲應用程序專用的別名。
第二步是設置SQL屬性指定要執行的SQL語句,有必要的話還能夠設置Params屬性爲SQL語句設置參數。
第三步是把TDataSource構件放到數據模塊上,設置它的DataSet屬性指定TQuery構件。再把TDBGrid構件放到窗體上,設置它的DataSource屬性指定TDataSource構件。
第四步是執行SQL語句。要執行SQL語句有兩種方式,一是在設計期把Active屬性設爲True,程序啓動時將自動執行SQL語句。另外一種方式是在運行期調用Open或ExecSQL執行SQL語句。若是但願返回查詢結果,調用Open,若是不須要返回查詢結果,調用ExecSQL。在調用Open或ExecSQL以前,最好先調用Prepare通知服務器做好準備。
執行SQL語句所返回的查詢結果其實是數據集中知足特定條件的記錄所組成的子集,數據庫柵格中只顯示符合特定條件的記錄。
Array.4 指定要執行的SQL語句
能夠設置SQL屬性以指定要執行的SQL語句。在設計期,只要把Active屬性設爲True,就會自動執行SQL語句。在運行期,首先要調用Prepare通知服務器準備好,而後調用Open或ExecSQL執行SQL函數語句。
Array.4.1 概述
SQL屬性是一個典型的TStrings對象。SQL屬性通常只包含一條完整的SQL語句,但能夠分紅幾行寫,TQuery構件會自動把幾行字符串合併成一條SQL語句。
把SQL語句分紅幾行寫的好處是,SQL語句的邏輯結構比較清楚,有利於從此維護和調試。因此,SQL語句的SELECT部分和WHERE部分通常都不在同一行上。
SQL語句能夠不帶參數,把字段名稱和值固定在SQL語句中,例如,下面這個SQL語句就是硬寫(Hard-Coded)的:
SELECT * FROM Customer WHERE CustNo = 1231
注意:若是要查詢的是本地數據庫,若是SQL語句中的字段名包含空格或其餘特殊符號,必須用引號括起來,前面還要加上表格名和小圓點。
若是用參數的話,查詢就靈活得多,應用程序不須要改寫SQL語句自己,只要修改參數的值,就能使SQL語句執行不一樣的查詢功能。在執行SQL語句以前,TQuery構件會自動把實際的值替換SQL語句中的參數,即便並無顯式地調用Prepare函數。
下面這條SQL語句是典型的參數化查詢:
SELECT * FROM Customer WHERE CustNo = :Number
其中,Number就是一個參數,它的前面必須加冒號。在運行期,應用程序必須提供Number參數的值,每次執行SQL語句時,Number參數的值能夠不一樣。
參數的值是經過TQuery的Params屬性提供的。
Array.4.2 在設計期指定SQL語句
在設計期,要指定SQL語句,能夠在對象觀察器中單擊SQL屬性邊上的省略號按鈕,彈出一個字符串列表編輯器,如圖Array.1所示。
圖Array.1 在設計期指定SQL語句
SQL語句能夠分紅幾行寫,但同一單詞不能分開。通常狀況下,SQL屬性只能包含一條完整的SQL語句,但有些服務器容許同時執行幾條SQL語句,這種狀況下,能夠輸入多條SQL語句。
若是使用Delphi 4的Client/Server版本或Enterprise版本,也能夠用SQLBuilder這個實用工具來創建SQL語句。要使用SQL Builder,在TQuery構件上單擊鼠標右鍵,在彈出的菜單中選擇「SQL Builder」命令。
Array.4.3 在運行期指定SQL語句
在運行期,要指定SQL屬性有三種方式,一是直接設置SQL屬性,二是調用LoadFromFile從文件中讀取SQL語句,或者從另外一個TStrings對象中得到SQL語句。
在直接設置SQL屬性以前,首先要調用Close函數。若是SQL屬性中原本已經有了SQL語句,還要調用Clear把原來的SQL語句清除。
下面的代碼演示了怎樣在運行期直接設置SQL屬性:
With CustomerQuery Do
Begin
Close;
With SQL Do
Begin
Clear;
Add(SELECT * FROM Customer);
Add(WHERE Company = Light Diver);
End;
Open;
End;
有時候,可能想在原來的SQL語句的基礎上修改或增長一行,這時候就不能調用Clear把原來的SQL語句清掉,例如:
CustomerQuery.SQL[1] := WHERE Company = "Light Diver";
也能夠調用LoadFromFile從文件中獲取SQL語句,這主要是由於TStrings對象支持文件操做。LoadFromFile會自動把原來的SQL語句清掉。
下面的代碼是調用LoadFromFile的例子:
CustomerQuery.Close;
CustomerQuery.SQL.LoadFromFile(c:\orders.txt);
CustomerQuery.Open;
還能夠從另外一個TStrings對象中獲取SQL語句,這就要調用TStrings的Assign函數。Assign 會自動把原來的SQL語句清空。
下面的代碼是調用Assign的例子:
CustomerQuery.Close;
CustomerQuery.SQL.Assign(Memo1.Lines);
CustomerQuery.Open;
Array.5 參 數
要使用參數化查詢,必須在SQL語句中加入參數,例如:
INSERT INTO Country (Name, Capital, Population)
VALUES (:Name, :Capital, :Population)
其中,Name、Capital和Population是三個參數。
在執行上述SQL語句以前,應用程序應當調用Prepare函數通知BDE和服務器預先分配好資源,以加快查詢速度。程序示例以下:
With Query1 Do
Begin
Close;
Unprepare;
ParamByName(Name).AsString := China;
ParamByName(Capital).AsString := Beijing;
ParamByName(Population).AsInteger := 120000;
Prepare;
Open;
End;
Array.5.1 在設計期提供參數
要在設計期提供參數,單擊Params屬性邊上的省略號按鈕,彈出如圖Array.2所示的編輯器。
圖Array.2 在設計期設置Params屬性
若是SQL語句中沒有包含任何參數,圖Array.2所示的編輯器就是空白的。這個編輯器的工具欄老是禁止的,這意味着只能在SQL語句中加入參數。
選擇其中一個參數(TParam對象),就能夠在對象觀察器中設置它的屬性,或者創建事件句柄。TParam的主要屬性有:
DataType屬性用於指定參數的數據類型,它的初始值老是ftUnknown,必須設置每一個參數的數據類型。
ParamType屬性用於指定參數的使用類型,它的初始值也是ptUnknown。
Value屬性用於給出參數的值。固然,也能夠在運行期給出參數的值。
Array.5.2 在運行期提供參數
要在運行期訪問參數,有三種方式:
一是經過ParamByName函數按名稱訪問參數。
二是經過Params屬性按序號訪問參數。
三是經過TParams對象的ParamValues屬性按名稱訪問參數。
假設一條SQL語句有三個參數:
INSERT INTO "COUNTRY.DB"
(Name, Capital, Continent)
VALUES (:Name, :Capital, :Continent)
下面這行代碼經過ParamByName函數來訪問其中的Capital參數:Query1.ParamByName(Capital).AsString := Edit1.Text;
下面這行代碼經過Params屬性來訪問其中的Name參數:
Query1.Params[0].AsString := Edit1.Text;
下面這行代碼經過TParams對象的ParamValues屬性來同時訪問三個參數:
Query1.Params.ParamValues[Country;Capital;Continent] :=VarArrayOf([Edit1.Text, Edit2.Text, Edit3.Text]);
Array.5.3 從另外一個數據集得到參數
TQuery構件的DataSource屬性用於指定一個數據源(TDataSource構件),若是應用程序既沒有在設計期也沒有在運行期給參數賦值,它就在這個數據源中查找與參數名匹配的字段,而後用這個字段的值做爲參數的值。
假設一個數據模塊叫LinkModule,上面有一個TQuery構件叫OrdersQuery,它的SQL語句以下:
SELECT CustNo, OrderNo, SaleDate
FROM Orders WHERE CustNo = :CustNo
另外,數據模塊上還有下列構件:
.一個TTable構件叫CustomersTable,它的TableName屬性設爲CUSTOMER.DB。
.一個TDataSource構件叫OrdersSource,它的DataSet屬性設爲OrdersQuery。
.一個TDataSource構件叫CustomersSource,它的DataSet屬性設爲CustomersTable。
.OrdersQuery的DataSource屬性也設爲CustomersSource。
假設應用程序有一個窗體叫LinkedQuery,窗體上有兩個TDBGrid構件,它們的DataSource屬性分別指定CustomersSource和OrdersSource。
若是編譯和運行這個應用程序,將看到如圖Array.3所示的效果:
圖Array.3 從另外一個數據集得到參數
這裏簡單解釋一下圖Array.3。若是沒有對SQL語句中的:CustNo參數賦值,OrdersQuery將試圖從CustomersSource指定的數據集中查找匹配的字段。因爲CustomersSource是從CUSTOMER.DB中獲取數據的,而CUSTOMER.DB中剛好有一個CustNo字段,因此,:CustNo參數的值就是CustNo字段的值。若是您在顯示CUSTOMER.DB的柵格中選擇了另外一條記錄,將致使:CustNo參數的值跟着變化。
Array.6 執 行 查 詢
當指定了SQL語句而且提供了參數後,就能夠執行查詢了。若是是第一次執行查詢,最好調用Prepare通知BDE或服務器作好準備,這樣能加快查詢的速度。
既能夠在設計期執行查詢,也能夠在運行期執行查詢。
要在設計期執行查詢,只要把Active屬性設爲True。不過,在設計期能執行的SQL語句,僅限於SELECT語句,不能是INSERT、UPDATE或DELETE語句。
要在運行期執行查詢,能夠調用Open或ExecSQL函數,其中,Open適合於執行SELECT語句,而ExecSQL適合於執行INSERT、UPDATE或DELETE語句,後者不返回結果。
在調用Open或ExecSQL以前,首先要調用Close。程序示例以下:
CustomerQuery.Close;
CustomerQuery.Open;
若是在編程的時候沒法肯定是否要返回查詢結果,能夠用Try...Except結構把這兩個過程都寫進去,通常Open在Try部分調用,而ExecSQL在Except部分調用,這樣,即便Open調用失敗,也能執行到ExecSQL。程序示例以下:
Try
Query2.Open;
Except
On E: Exception Do
If not (E is ENoResultSet) then Raise;
End;
前面屢次提到,在執行查詢前最好先調用Prepare,儘管這並非必須的。預先調用Prepare可以改善應用程序的性能。程序示例以下:
CustomerQuery.Close;
If not (CustomerQuery.Prepared) then
CustomerQuery.Prepare;
CustomerQuery.Open;
上述程序首先調用Close,而後檢查Prepared屬性,若是這個屬性返回True,表示已經準備好,若是這個屬性返回False,表示沒有準備好,此時就要調用Prepare。
Array.7 異 構 查 詢
所謂異構查詢,就是同時查詢幾個不一樣的數據庫。這些數據庫的類型能夠不一樣。例如,能夠同時查詢Oracle數據庫、Sybase數據庫和本地的dBASE表。當程序執行異構查詢的時候,BDE經過Local SQL來分析和處理這個查詢,而不是用與服務器相關的特定的SQL語法。
創建一個異構查詢的通常步驟是這樣的:
第一步,把一個TQuery構件放到窗體或數據模塊上,讓DatabaseName屬性空着。
第二步,爲要查詢的每個數據庫創建一個單獨的BDE別名。
第三步,設置SQL屬性以指定要執行的SQL語句。在SQL語句中,表的名字前要加別名和冒號,而且用雙引號括起來。字段名前要加表名和小圓點。例如:
SELECT Customer.CustNo, Orders.OrderNo
FROM "Oracle1:CUSTOMER"
JOIN "Sybase1:ORDERS"
ON (Customer.CustNo = Orders.CustNo)
WHERE (Customer.CustNo = 1503)
第四步,設置Params屬性提供參數。
第五步,調用Prepare通知BDE或服務器作好準備,而後調用Open或ExecSQL執行查詢。若是顯式地使用TDatabase構件鏈接數據庫,而且設置了它的DatabaseName屬性定義了應用程序專用的別名,在SQL語句中能夠用專用的別名代替BDE別名。
Array.8 查 詢 結 果
默認狀況下,查詢結果是隻讀的。應用程序能夠用數據控件去顯示查詢結果,但用戶不能編輯數據。怎樣才能使用戶可以編輯數據呢?
要使用戶可以編輯數據,必須把TQuery構件的RequestLive屬性設爲True。不過,把RequestLive屬性設爲True並不能保證查詢結果必定是能夠修改的,由於這還取決於查詢使用的是Local SQL仍是與服務器相關的SQL。
像查詢Paradox或dBASE以及異構查詢都是使用Local SQL,而查詢遠程服務器則使用與服務器相關的SQL。即便RequestLive屬性設爲True,並且查詢的是本地數據庫,但因爲SELECT語句的文法不合適,BDE也將返回只讀的查詢結果。
所以,在編輯數據以前,先要訪問CanModify屬性。只有當這個屬性返回True時,才表示查詢結果是可編輯的。 數據庫