達夢數據庫中包含各種數據庫對象,主要分為兩大類型:基本數據庫對象和復雜數據庫對象。下面分別進行介紹。
視頻講解如下 |
---|
【趙渝強老師】達夢數據庫的數據庫對象 |
一、 基本數據庫對象
常見的基本數據庫對象有:表、索引、視圖、序列、同義詞等。之所以叫做基本數據庫對象是因為這些對象直接使用一條DDL(Data Definition Language,數據定義語言)語句即可創建和管理。
1.1 表
表是一種非常重要的數據庫對象,DM數據庫的數據都是存儲在表中。DM數據庫的表是一種二維結構,由行和列組成。表有列組成,列有列的數據類型。達夢數據庫支持的數據類型主要有常規數據類型、位串數據類型、日期時間數據類型和多媒體數據類型。
這里值得說明的是,char(n)和varchar(n)中括號中n代表字符的個數,并不代表字節個數,比如char(30) 就可以存儲30個字符。char和varchar 類型類似,但它們保存和檢索的方式不同。它們的最大長度和是否尾部空格被保留等方面也不同。在存儲或檢索過程中不進行大小寫轉換。
下面通過一個例子來說明char和varchar在存儲字符的時候的區別。
(1)創建表一張新的表
SQL> create table test1(v1 char(5),v2 varchar(5));
(2)往表中插入數據
SQL> insert into test1 values('abc ','abc ');
(3)查詢表中的數據
SQL> select concat(v1,'*'),concat(v2,'*') from test1;# 輸出的結果如下:
行號 CONCAT(V1,'*') CONCAT(V2,'*')
---------- -------------- --------------
1 abc * abc*# 從輸出的結果可以看出,char同varchar 的區別在于前者長度不足時,系統自動填充空格,
# 而后者只占用實際的字節空間。另外,實際插入表中的列長度要受到記錄長度的約束,
# 每條記錄總長度不能大于頁面大小的一半。
1.2 索引
索引是與表相關的可選的結構(聚簇索引除外),它能使對應于表的SQL語句執行得更快,因為有索引比沒有索引能更快地定位信息。DM8數據庫的索引能提供訪問表的數據的更快路徑,可以不用重寫任何查詢而使用索引,其結果與不使用索引是一樣的,但速度更快。DM數據庫官方對索引的定義為:索引(Index)是幫助DM數據庫高效獲取數據的數據結構。DM數據庫中默認索引類型是B+樹索引。達夢數據庫提供了幾種最常見類型的索引,對不同場景有不同的功能,它們是:
- 聚集索引:每一個普通表有且只有一個聚集索引;
- 唯一索引:索引數據根據索引鍵唯一;
- 函數索引:包含函數/表達式的預先計算的值;
- 位圖索引:對低基數的列創建位圖索引;
- 位圖連接索引:針對兩個或者多個表連接的位圖索引,主要用于數據倉庫中;
- 全文索引:在表的文本列上而建的索引。
提示:索引在邏輯上和物理上都與相關的表的數據無關,作為無關的結構,索引需要存儲空間。
創建或刪除一個索引,不會影響基本的表、數據庫應用或其他索引。當插入、更改和刪除相關的表的行時,
達夢數據庫會自動管理索引。如果刪除索引,所有的應用仍繼續工作,
但訪問以前被索引了的數據時速度可能會變慢。
可以用create index語句明確地創建索引。下面通過一個簡單的示例來演示如何創建普通的索引。
(1)在emp表的ename列上創建一個名為index_emp_ename的索引。
SQL> create index index_emp_ename on emp(ename);
(2)獲取索引的ID號。
SQL> select name,id from sysobjects where name='INDEX_EMP_ENAME' and subtype$='INDEX';# 輸出的信息如下:
行號 NAME ID
---------- --------------- -----------
1 INDEX_EMP_ENAME 33555654
(3)通過indexdef系統函數查看索引的定義。
SQL> select indexdef(33555654, 0);# 輸出的信息如下:
行號 INDEXDEF(33555654,0)
---------- ----------------------------------------------
1 CREATE INDEX "INDEX_EMP_ENAME" ON "EMP"("ENAME" ASC) STORAGE(ON "MAIN", CLUSTERBTR) ;# 函數indexdef的定義如下:indexdef(index_id int, preflag int);
# 其中:
# index_id:索引id
# preflag:對象前綴個數。1表示導出模式名;0表示只導出對象名
(4)查詢名叫KING的員工數據,并輸出對應的SQL執行計劃。
SQL> explain select * from emp where ename='KING';# 輸出的信息如下:
1 #NSET2: [1, 1, 172]
2 #PRJT2: [1, 1, 172]; exp_num(9), is_atom(FALSE)
3 #BLKUP2: [1, 1, 172]; INDEX_EMP_ENAME(EMP)
4 #SSEK2: [1, 1, 172]; scan_type(ASC), INDEX_EMP_ENAME(EMP), scan_range['KING','KING']# 從輸出的執行計劃可以看出,該查詢使用了索引index_emp_ename。
(5)使用系統函數sp_rebuild_index重建索引。
SQL> sp_rebuild_index('SCOTT', 33555660);# 提示:當一個表經過大量的增刪改操作后,表的數據在物理文件中可能存在大量碎片,從而影響訪問速度。
# 另外,當刪除表的大量數據后,若不再對表執行插入操作,索引所處的段可能占用了大量并不使用的簇,
# 從而浪費了存儲空間。可以使用重建索引來對索引的數據進行重組,使數據更加緊湊,并釋放不需要的空間,
# 從而提高訪問效率和空間效率。DM8提供的重建索引的系統函數為:
# sp_rebuild_index(schema_name varchar(256), index_id int);
# 其中:
# scheam_name為索引所在的模式名
# index_id為索引id。# 使用說明:
# 水平分區子表,臨時表和系統表上建的索引不支持重建。
# 虛索引和聚集索引不支持重建。
1.3 視圖
視圖(VIEW)是一種虛表,其本身并不包含數據。它將作為一個select語句保存在數據字典中的。視圖依賴的表叫做基表。通過視圖可以展現基表的部分數據;視圖數據來自定義視圖的查詢中使用的基表。在了解的視圖的作用后,下面通過具體的步驟來演示如何使用視圖。
(1)基于員工表emp創建視圖。
SQL> create or replace view view1
as
select * from dmhr.employee where department_id=1001;# 視圖也可以基于多表進行創建,例如:
SQL> create or replace view view2
as
select employee.employee_name,employee.salary,department.department_name
from dmhr.employee,dmhr.department
where employee.department_id=department.department_id;
(2)從視圖中查詢數據。
SQL> select employee_name,salary from view1;# 輸出的信息如下:
行號 EMPLOYEE_NAME SALARY
---------- ------------- -----------
1 龔順超 8000
2 陳偉婷 15853
3 何杭菊 15868
4 歐鋒利 15878
5 薛輝明 15888SQL> select * from view2 where rownum<=10;
行號 EMPLOYEE_NAME SALARY DEPARTMENT_NAME
------ ------------------ ----------- ---------------
1 馬學銘 12345 總經理辦
2 程擎武 9000 行政部
3 鄭吉群 15000 開發部
4 陳仙 12000 市場部
5 金緯 10000 技術支持部
6 李慧軍 10000 總經理辦
7 常鵬程 5000 行政部
8 謝俊人 5000 市場部
9 蘇國華 30000 總經理辦
10 強潔芳 10000 行政部10 rows got# 這里使用了行號rownum只顯示前10條記錄。
(3)通過視圖執行DML操作,例如:給1001號部門員工漲100塊錢工資。
SQL> update view1 set salary=salary+100;# 并不是所有的視圖都可以執行DML操作。在視圖定義時含義以下內容,視圖則不能執行DML操作:
# (1)查詢子句中包含distinct和組函數
# (2)查詢語句中包含group by子句和order by子句
# (3)查詢語句中包含union 、union all等集合運算符
# (4)where子句中包含相關子查詢
# (5)from子句中包含多個表
# (6)如果視圖中有計算列,則不能執行update操作
# (7)如果基表中有某個具有非空約束的列未出現在視圖定義中,則不能做insert操作
(4)創建視圖時使用WITH CHECK OPTION約束。
SQL> create or replace view view3asselect * from dmhr.employee where salary <5000with check option;# 提示:WITH CHECK OPTION表示對視圖所做的DML操作,不能違反視圖的WHERE條件的限制。
(5)在view3上執行update操作。
SQL> update view3 set salary=10000;# 此時將出現下面的錯誤信息:
update view3 set salary=10000;
[-6603]:違反視圖[VIEW3]CHECK約束.
已用時間: 4.332(毫秒). 執行號:0.
1.4 序列
序列(sequence)是DM數據庫中的數據庫實體之一。通過使用序列,多個用戶可以產生和使用一組不重復的有序整數值。比如可以用序列來自動地生成主關鍵字值。序列通過提供唯一數值的順序表來簡化程序設計工作。例如,下面的語句將創建一個序列。
SQL> create sequence myseq;
一旦序列生成,用戶就可以在SQL語句中用以下偽列來存取序列的值:
- currval:返回當前的序列值;
- nextval:如果為升序序列,序列值增加并返回增加后的值;如果為降序序列,序列值減少并返回減少后的值。
例如:
SQL> select myseq.nextval;# 輸出的信息如下:
行號 NEXTVAL
---------- --------------------
1 1SQL> select myseq.currval; # 輸出的信息如下:
行號 CURRVAL
---------- --------------------
1 1# 提示:序列可以是循環的,當序列的值達到最大值/最小值時,序列將從最小值/最大值計數。
# 使用一個序列時,不保證將生成一串連續不斷遞增的值。例如,如果查詢一個序列的
# 下一個值供insert使用,則該查詢是能使用這個序列值的唯一會話。
# 如果未能提交事務處理,則序列值就不被插入表中,以后的insert將繼續使用該序列隨后的值。
1.5 同義詞
同義詞(Synonym)讓用戶能夠為數據庫的一個模式下的對象提供別名。同義詞通過掩蓋一個對象真實的名字和擁有者,并且對遠程分布式的數據庫對象給予了位置透明特性以此來提供了一定的安全性。同時使用同義詞可以簡化復雜的SQL語句。同義詞可以替換模式下的表、視圖、序列、函數、存儲過程等對象。創建同義詞的語法格式如下:
create [or replace] [public] synonym [<模式名>.]<同義詞名> for [<模式名>.]<對象名>
例如,創建一個同義詞代表員工表。
SQL> create synonym emp for dmhr.employee;
接下來就可以通過同義詞查詢員工數據了。
SQL> select count(*) from emp;# 輸出的信息如下:
行號 COUNT(*)
---------- --------------------
1 858
關于同義詞的使用需要注意以下兩點:
- 同義詞分為全局同義詞(public synonym)和非全局同義詞。用戶在自己的模式下創建同義詞,必須有create synonym權限。用戶要創建全局同義詞(public synonym),必須有create public synonym權限;
- 同義詞創建時,并不會檢查他所指代的同義詞對象是否存在,用戶使用該同義詞時候,如果不存在指代對象或者對該指代對象不擁有權限,則會報錯。
二、復雜數據庫對象
達夢數據庫提供的復雜數據庫對象主要包括:存儲過程、存儲函數和觸發器。之所以叫做復雜數據庫對象,是因為這些對象需要使用DMSQL語言進行開發和管理。DMSQL程序是達夢數據庫對標準SQL語言的擴展,是一種過程化SQL語言。在DMSQL程序中,包括一整套數據類型、條件結構、循環結構和異常處理結構等,DMSQL程序中可以執行SQL語句,SQL語句中也可以使用DMSQL函數。
2.1 存儲過程和存儲函數
存儲過程(Stored Procedure)和存儲函數(Stored Function)指存儲在數據庫中供所有用戶調用的子程序,它們事先經過經編譯后存儲在數據庫系統中。因此,調用存儲過程和存儲函數來完成業務邏輯,是可以提高性能的。存儲過程和存儲函數的結構類似,但是存儲函數必須要有一個return子句用于返回函數的值;而存儲過程沒有return子句。盡管存儲過程沒有return子句,但卻可以通過指定一個或者多個out參數來指定返回值。
創建存儲過程的語法格式如下:
create [or replace] procedure 存儲過程名稱(參數列表)
asDMSQL子程序體;
創建存儲函數的基本語法格式如下:
create [or replace] function 存儲函數名稱(參數列表)
return 函數返回值類型
asDMSQL子程序體;
2.1.1 【實戰】創建和使用存儲過程
下面通過具體的步驟來演示如何創建存儲過程,以及如何在DM數據庫中調用它。
(1)創建第一個存儲過程sayhelloworld,輸出Hello World字符串。
SQL> create or replace procedure dmhr.sayhelloworld
as--說明部分
begindbms_output.put_line('Hello World');
end;
/
(2)存儲過程創建成功后,可以在PL/SQL程序中調用它。例如下面的代碼調用了兩次存儲過程dmhr.sayhelloworld()。
SQL> begindmhr.sayhelloworld();dmhr.sayhelloworld();
end;
/# 輸出的信息如下:
Hello World
Hello WorldDMSQL 過程已成功完成
已用時間: 0.909(毫秒). 執行號:3528.
(3)存儲過程也可以是exec命令單獨進行調用,例如:
SQL> exec dmhr.sayhelloworld;# 輸出的信息如下:
Hello WorldDMSQL 過程已成功完成
已用時間: 0.455(毫秒). 執行號:3530.
2.1.2 【實戰】創建和使用存儲函數
存儲函數與存儲過程的最大區別就在于存儲函數可以通過reture子句返回函數的值,而存儲過程沒有return子句。下面將通過一個具體的示例來演示如何使用存儲函數,并且如何調用它。
(1)創建存儲函數dmhr.queryEmpTotalIncome查詢指定員工的年收入。
SQL> create or replace function dmhr.queryEmpTotalIncome(eno in number)
return number
as--定義引用型變量保存月薪和獎金psal dmhr.employee.salary%type;pcomm dmhr.employee.commission_pct%type;
begin-- 查詢指定員工的薪水和獎金,并賦值給變量。select salary,commission_pct into psal,pcomm from dmhr.employee where employee_id=eno;--返回年收入return psal*12+nvl(pcomm,0);
end;
/
(2)調用存儲函數dmhr.queryEmpTotalIncome查詢員工號是1001的年收入。
SQL> select dmhr.queryEmpTotalIncome(1001) "該員工的年收入"; # 輸出的信息如下:
行號 該員工的年收入
---------- ---------------------
1 361200
2.2 觸發器
觸發器是一種特殊的存儲過程,它在創建后就存儲在數據庫中。觸發器的特殊性在于它是建立在某個具體的表或視圖之上的,或者是建立在各種事件前后的,而且是自動激發執行的,如果用戶在這個表上執行了某個DML操作(insert、delete、update),觸發器就被激發執行。
觸發器常用于自動完成一些數據庫的維護工作。例如,觸發器可以具有以下功能:
- 可以對表自動進行復雜的安全性、完整性檢查;
- 可以在對表進行DML操作之前或者之后進行其它處理;
- 進行審計,可以對表上的操作進行跟蹤;
- 實現不同節點間數據庫的同步更新。
DM數據庫觸發器分為兩種不同的類型語句級觸發器和元組級觸發器,這兩種不同類型的觸發器在定義是通過for each row進行區分。
- 語句級觸發器
語句級觸發器是指在指定的操作語句之前或者之后執行一次,不管這個操作影響了多少行記錄。換句話說,語句級觸發器針對是表。 - 元組級觸發器
元組級觸發器是指觸發語句作用的每一條記錄都被觸發。換句話說,行級級觸發器就是針對是表中的每一行。在行級觸發器中可以使用:old和:new關鍵字來表示同一行數據在操作之前和操作之后的值。以員工表(dmhr.employee)為例,:old.salary操作該行之前員工的薪水,而:new.salary表示操作該行之后員工的薪水。
# 提示:old和:new表示的表中同一行。區別是:old表示操作之前,而:new表示操作之后。
# 注意這里的冒號不能少。
下面是利用數據庫的觸發器可以實現安全性的檢查。這里的需求是:禁止在非工作時間往員工表中插入數據。例如:今天如果是星期天就不允許在員工表上執行insert操作。
(1)創建語句級觸發器dmhr.securityemp禁止在非工作時間往員工表中插入數據。
SQL> create or replace trigger dmhr.securityemp
before insert
on dmhr.employee
begin-- 判斷當前時間是否是非工作時間if to_char(sysdate,'day') in ('星期六','星期日') orto_number(to_char(sysdate,'hh24')) not between 9 and 18 then--如果是在非工作時間,禁止insert操作拋出異常raise_application_error(-20001,'此時不允許插入員工數據');end if;
end;
/# 觸發器dmhr.securityemp中指定的非工作時間有兩個:一個是周末;另一個是早9點前和晚18點后。
(2)非工作時間在員工表上執行insert操作。
SQL> insert into dmhr.employee
(employee_id,employee_name,email,hire_date,job_id,salary,department_id)
values(1234,'Tom','tom@126.com',sysdate,42,7000,706);# 輸出的信息如下:
[-20001]:此時不允許插入員工數據
-20001: SECURITYEMP line 9 .
已用時間: 2.673(毫秒). 執行號:0.
(3)刪除觸發器。
SQL> drop trigger dmhr.securityemp;