18.4.1.2 數據庫結構介紹
IBLOCAL數據庫的結構都是由InterBase服務器工具交互式SQL工具(ISQL)定義的。
用ISQL定義數據庫,首先要用Create Database命令建立數據庫,建立的新數據庫一般是以GDB為擴展名。建立好後,就可以用SQL語言定義數據庫表,例如建立EMPLOYEE表的SQL語句如下:
定義域名數據類型:
CREATE DOMAIN FIRSTNAME AS VARCHAR(15);
CREATE DOMAIN LASTNAME AS VARCHAR(20);
CREATE DOMAIN COUNTRYNAME AS VARCHAR(15);
CREATE DOMAIN EMPNO AS SMALLINT;
CREATE DOMAIN DEPTNO AS CHAR(3)
CHECK (VALUE = '000' OR (VALUE > '0' AND VALUE <= '999') OR VALUE IS NULL);
CREATE DOMAIN JOBCODE AS VARCHAR(5)
CHECK (VALUE > '99999');
CREATE DOMAIN JOBGRADE AS SMALLINT
CHECK (VALUE BETWEEN 0 AND 6);
CREATE DOMAIN SALARY AS NUMERIC(15, 2)
DEFAULT 0
CHECK (VALUE > 0);
建立EMPLOYEE表:
CREATE TABLE EMPLOYEE (EMP_NO EMPNO NOT NULL,
FIRST_NAME FIRSTNAME NOT NULL,
LAST_NAME LASTNAME NOT NULL,
PHONE_EXT VARCHAR(4),
HIRE_DATE DATE DEFAULT 'NOW' NOT NULL,
DEPT_NO DEPTNO NOT NULL,
JOB_CODE JOBCODE NOT NULL,
JOB_GRADE JOBGRADE NOT NULL,
JOB_COUNTRY COUNTRYNAME NOT NULL,
SALARY SALARY NOT NULL,
FULL_NAME COMPUTED BY (last_name || ', ' || first_name),
PRIMARY KEY (EMP_NO));
CHECK語句是給數據庫字段取值范圍加約束條件。PRIMARY_KEY語句是給表建立關鍵字索引。
如法炮制,就可以定義IBLOCAL中的所有表。
IBLOCAL中的表包括:
EMPLOYEE CUSTOMER DEPARTMENT EMPLOYEE_PROJECT
PROJECT SALES SALARY_HISCORY
各數據庫表中的內容如下:
表18.14 EmployeeDemoDB中各數據庫表的內容
━━━━━━━━━━━━━━━━━━━━━━━━━━━
數據庫表名 表中內容
───────────────────────────
EMPLOYEE 雇員信息
CUSTOMER 客戶信息
DEPARTMENT 部門信息
EMPLOYEE_PROJECT 雇員負責的工程
PROJECT 工程信息
SALES 銷售信息
SALARY_HISTORY 雇員薪水調整的歷史信息
━━━━━━━━━━━━━━━━━━━━━━━━━━━
每個數據庫表中都定義了關鍵字段。關於數據庫表中的字段名、類型、大小,這裡不再贅述。
18.4.2 應用程序分析
18.4.2.1 TDatabase部件的使用
CSDEMO程序中定義了一個數據庫模塊部件——TDmEmployee,它是繼承於TDataModule。TDataModule是在Delphi2.0中才出現的專門放置數據訪問部件(如TDatabase、TTable和TQuery等)的框架。其它涉及數據庫訪問的窗體,只要在uses語句中插入數據庫模塊所在的庫單元,該窗體上的數據庫部件就可引用相應的數據庫訪問部件。
在TDmEmployee中定義了一個TDatabase類型的部件──EmployeeDatabase。EmployeeDatagase的主要屬性及屬性值如下:
表18.15 EmployeeDatabase部件主要屬性的取值
━━━━━━━━━━━━━━━━━━━━━━━
屬性 屬性值
───────────────────────
AliasName IBLOCAL
DatabaseName EmployeeDemoDB
KeepConnection True
LoginPrompt False
TransIsolation tiReadCommitted
Params USERNAME = SYSDBA
PASSWORD = masterkey
Connected True
━━━━━━━━━━━━━━━━━━━━━━━
AliasName屬性所指定的IBLOCAL,必須已經在BDE中配置好,DatabaseName屬性指定要使用的數據庫名,該數據庫名是由應用程序自己定義的,因此不反應到BDE中,該屬性值被TTable、TQuery等DataSet部件引用,並且出現在DataSet部件的DatabaseName 下拉式列表框中。本例中的“EmployeeDemoDB”,被EmployeeTable,SalesTable等所有DataSet部件引用。
Connected為True表明,應用程序與數據庫將保持聯接。
KeepConnection屬性為True,表明多次打開和關閉EmployeeDemoDB數據庫中的任意表,應用程序將始終與數據庫保持聯接,這省卻了重復注冊的開銷。
LoginPrompt 屬性為False,表明應用程序自動處理與數據庫的聯接注冊,因此,Params屬性中定義了注冊的用戶名和口令:
USERNAME = SYSDBA
PASSWORD = masterkey
TransIsolation屬性為tiReadCommitted表明,如果存在多個同時事務,則某一事務只允許讀由其它事務提交了的數據。
程序中EmployeeDatabase的應用還與事務控制等有關。下文中會介紹這方面的內容。
18.4.2.2 不同數據庫表的切換
在許多數據庫應用中都要在不同數據庫表之間相互切換,以響應用戶輸入條件或系統狀態的變化。這時,往往需要特別的處理,例如改變光標形狀或隱藏數據改變等,尤其是在客戶/服務器應用程序中。因為是用SQL語句訪問遠程數據庫,有時還要在服務器端執行計算任務,所以客戶端的數據變化會有一定的間隔,因此應該讓用戶明白發生了什麼。下面是CSDEMO在數據庫表切換時的處理辦法:
procedure TFrmViewDemo.ShowTable( ATable: string );
begin
Screen.Cursor := crHourglass; { 向用戶提示當前操作狀態 }
VaryingTable.DisableControls; { 隱藏數據變化 }
VaryingTable.Active := FALSE; { 關閉原來的數據庫表 }
VaryingTable.TableName := ATable; { 更新數據庫表名 }
VaryingTable.Open; { 打開數據庫表 }
VaryingTable.EnableControls; { 顯示所作的修改 }
Screen.Cursor := crDefault; { 重新設置光標形狀 }
end;
crHourglass型光標表明正在執行SQL查詢。DisableControls和EnableControls的作用是隱藏和顯示數據變化。
18.4.2.3 InterBase觸發器(Trigger)的應用
在CSDEMO應用程序中,演示觸發器應用的窗體是TFromTriggerDemo;
在該窗體中包含兩個TDBGrid對象。DBGrid1顯示EmployeeTable中的數據,DBGrid2顯示SalaryHistoryTable中的數據。它們的主要屬性及屬性值如下:
表18.16 EmlpoyeeTable部件主要屬性的取值
━━━━━━━━━━━━━━━━━━━━━
屬 性 屬 性 值
─────────────────────
DatabaseName EmployeeDemoDB
IndexFieldName Emp_No
TableName EMPLOYEE
━━━━━━━━━━━━━━━━━━━━━
表18.17 SalaryHistoryTable部件主要屬性的取值
━━━━━━━━━━━━━━━━━━━━━
屬 性 屬 性 表
─────────────────────
DatabaseName EmployeeDemoDB
IndexFieldName Emp_No
MasterFields Emp_No
MasterSource EmployeeSource
TableName SALARY_HISTORY
━━━━━━━━━━━━━━━━━━━━━
這兩個表之間存在兩種關系:
● 連接關系
EmployeeTable的記錄變化時,SalaryHistoryTable的數據要作相應的變化。這種連接關系是通過索引來實現的。
● 數據一致性
對EmployeeTable中的Salary字段的值作修改必須反映到SalaryHistoryTable中,SalaryHistoryTable維護的是Salary變化的歷史信息。這種數據一致性要求在本程序中是通過觸發器實現的。
觸發器是在SQL服務器端執行的一段程序,它在服務器端被觸發執行完成一定的數據計算任務。
下面是InterBase服務器上與Employee表相關的觸發器程序:
Triggers on Table EMPLOYEE:
SAVE_SALARY_CHANGE, Sequence: 0, Type: AFTER UPDATE, Active AS
BEGIN
IF (old.salary <> new.salary) THEN
INSERT INTO salary_history
(emp_no, change_date, updater_id, old_salary, percent_change)
VALUES (
old.emp_no,
'now',
user,
old.salary,
(new.salary - old.salary) * 100 / old.salary);
END
因為觸發器是相應於EMPLOYEE表上的數據修改由服務器自動觸發執行的,所以在客戶應用程序上沒有顯式的調用。在客戶端有打開並顯示數據庫表內容的程序和當SALARY_HISTORY表中數據變化時的更新顯示的操作。
procedure TFrmTriggerDemo.FormShow(Sender: TObject);
begin
DmEmployee.EmployeeTable.Open;
DmEmployee.SalaryHistoryTable.Open;
end;
procedure TDmEmployee.EmployeeTableAfterPost(DataSet: TDataSet);
begin
{ 一個雇員的薪水變化將觸發薪水調整歷史記錄的變化,
因此,如果SalaryHistory打開的話,就需要更新顯示 }
with SalaryHistoryTable do if Active then Refresh;
end;