官术网_书友最值得收藏!

5.9 同義詞

同義詞是對象的備選名稱。如果對象存在同義詞,那么任何SQL語句都能通過實際名稱或通過同義詞來訪問該對象。同義詞看上去似乎沒有什么作用,然而實際情況并非如此。使用同義詞意味著應用程序可以對任何用戶起作用,而不考慮哪個模式擁有視圖和表,甚至不考慮表駐留在哪個數據庫中。分析如下語句:

        select * from hr.employees@prod;

發出該語句的用戶必須知道員工表由數據庫鏈接PROD標識的數據庫中的HR模式擁有(不需要關心數據庫連接的細節,它們是一種訪問不是目前所登錄數據庫的其他數據庫中對象的方式)。如果用如下語句創建公有同義詞:

        create public synonym emp for hr.employees@prod;

那么所有用戶都需要輸入如下語句:

        select * from emp;

這個語句既提供了數據無關性,又提供了位置透明性。用戶必須有訪問底層對象的權限,才能成功使用基于同義詞的引用。只要調整同義詞,就可以在不修改代碼的情況下重命名或重定位表和視圖。

與SELECT語句一樣,DML語句可以像訪問同義詞引用的對象一樣訪問同義詞。

私有同義詞是模式對象。它們要么必須在自己的模式中,要么必須用模式名限定。公有同義詞的存在與模式無關。任何具有查看權限的用戶都可以引用公有同義詞,不需要用模式名限定。私有同義詞必須在模式中有唯一的名稱,而公有同義詞可以與模式對象同名。當執行訪問不帶模式限定符的對象的語句時,Oracle會先在局部模式中查找對象,只有在局部模式中找不到時才會在公有模式中查找。因此在前面的示例中,如果用戶碰巧擁有名為EMP的表,它看到的就是這個表——而不是公有同義詞指向的名為EMP的表。

創建同義詞的語法如下:

        CREATE [PUBLIC] SYNONYM synonym FOR object ;

需要為用戶授予創建私有同義詞的權限,并且進一步授予創建公有同義詞的權限。通常,只有數據庫管理員能夠創建(或刪除)公有同義詞。這是因為是否存在公有同義詞會影響每個用戶。

考點:

“公有同義詞”中的“公有”意味著它不是模式對象,因此不能用模式名做前綴。這并不意味著每個人都具有對公有同義詞的訪問權限。

刪除同義詞的語法如下:

        DROP [PUBLIC] SYNONYM synonym ;

如果同義詞引用的對象(表或視圖)被刪除,同義詞仍然存在。這時試圖使用這樣的同義詞會返回一個錯誤。在這一方面,同義詞的行為方式與視圖相同。如果重新創建對象,那么在使用同義詞前必須重新編譯。與視圖一樣,在下次訪問同義詞時自動重新編譯,也可以使用如下語句顯式地完成編譯:

        ALTER SYNONYM synonym COMPILE;

練習5-8 創建和使用同義詞

在本練習中,將用HR模式中的對象創建和使用私有同義詞。可以使用SQL*Plus或SQL Developer。

(1) 作為用戶HR連接到數據庫。

(2) 為練習5-7中創建的三個視圖創建同義詞:

        create synonym emp_s for emp_anon_v;
        create synonym dept_s for dept_anon_v;
        create synonym dsum_s for dep_sum_v;

(3) 確認同義詞等同于底層對象:

        describe emp_s;
        describe emp_anon_v;

(4) 通過對同義詞而不是視圖運行練習5-7中的語句,來確認同義詞有效(甚至到產生相同錯誤的程度):

        select * from dsum_s;
        insert into dept_s values   (99, 'Temp Dept',1800 );
        insert into emp_s values   (sysdate, 'AC_MGR',10000,0,99);
        update emp_s set salary=salary*1.1;
        rollback;
        select max(salaries / staff) from dsum_s;

(5) 刪除兩個視圖:

        drop view emp_anon_v;
        drop view dept_anon_v;

(6) 查詢基于已刪除視圖的復雜視圖:

        select * from dep_sum_v;

可以注意到查詢失敗。

(7) 嘗試重新編譯被破壞的視圖:

        alter view dep_sum_v compile;

此次編譯嘗試也會失敗。

(8) 刪除DEP_SUM_V視圖:

        drop view dep_sum_v;

(9) 查詢已刪除視圖的同義詞:

        select * from emp_s;

該查詢會失敗。

(10) 重新編譯被破壞的同義詞:

        alter synonym emp_s compile;

注意,雖然這樣做不會拋出錯誤,而是從第(9)步開始重新運行查詢。該同義詞無疑仍然處于破壞狀態。

(11) 通過刪除同義詞進行整理:

        drop synonym emp_s;
        drop synonym dept_s;
        drop synonym dsum_s;

5.10 序列

序列是生成唯一整數值的結構。由于只有一個會話能夠讀取下一個值,因此強制該值遞增。這是序列化的要點,因此生成的每個值都將是唯一的。

序列是用來生成主鍵的寶貴工具。很多應用程序都需要自動生成主鍵值。日常業務數據處理的一個示例是客戶編號或訂單編號:業務分析師會指出,每個訂單必須有唯一的編號,編號應連續地遞增。其他應用程序可能沒有這樣的行規要求,但是仍然需要序列來實施關系完整性。以電話賬單系統為例:在行規中的唯一標識符是電話號碼(字符串),一次電話呼叫的標識符將是主叫電話號碼和通話開始時間(時間戳)。這些數據類型太復雜,對于通過電話交換系統的大流量的主鍵來說,沒必要采用這么復雜的類型。盡管使用這些數據類型也能夠記錄信息,但是使用簡單的數值列來定義主鍵和外鍵要快得多。這些列中的值可以作為序列的基礎。

序列機制與表、行鎖機制及提交或回滾過程無關。這意味著序列每分鐘能發出數千個唯一值,這比任何涉及從表中選擇列、更新列并提交修改的方法快得多。

圖5-12顯示了兩個從一個序列SEQ1中選擇值的會話。

圖5-12 兩個會話并發使用序列

注意,圖5-12中SEQ1.NEXTVAL的每個選擇項生成一個唯一數值。這些數值按選擇的時間順序連續發出,數值會全局地遞增,而不是僅在一個會話中遞增。

5.10.1 創建序列

創建序列的完整語法如下:

        CREATE SEQUENCE [schema.]sequencename
        [INCREMENT BY number]
        [START WITH number]
        [MAXVALUE number | NOMAXVALUE]
        [MINVALUE number | NOMINVALUE]
        [CYCLE | NOCYCLE]
        [CACHE number | NOCACHE]
        [ORDER | NOORDER] ;

可以看出,序列的創建可以非常簡單。例如,圖5-12中使用的序列是用如下語句創建的:

        create sequence seq1;

各選項如表5-2所示。

表5-2 創建序列的選項

對INCREMENT BY、START WITH,以及MAXVALUE或MINVALUE的適當設置取決于業務分析師。

CYCLE很少使用,因為它使序列發出重復值。如果用序列來生成主鍵值,那么只有當數據庫中存在刪除原有行比序列重新發出數值快得多的例程時,CYCLE才有意義。

緩存序列值對于性能至關重要。從序列中選擇是應用程序代碼中串行化的要點:一次只能有一個會話產生這個序列值。這種機制非常有效率:相比于先鎖住行,再更新行,然后用COMMIT解鎖行,使用這種機制會快很多。但是即便如此,從序列中選擇仍然會造成會話之間的資源爭用。CACHE關鍵字通知Oracle在緩存中預先生成序號。這意味著如果需要根據要求生成序號,則可以更快速地發出這些序號。

提示:

要緩存的默認序號個數是20。經驗表明這個數量并不夠。如果應用程序每秒鐘從序列中選擇10次,那么要將緩存值設置為50 000。不要羞于承認這一點。

5.10.2 使用序列

為使用序列,會話可以用強制序列遞增的偽列NEXTVAL選擇下一個值,也可以用偽列CURRVAL選擇發送給該會話的上一個(或“當前”)值。NEXTVAL將是全局唯一值:選擇它的每個會話的每個SELECT會得到不同的、遞增的值。直到再次選擇NEXTVAL前,CURRVAL將是某個會話的常量。無法找出序列發出的上一個值是什么值:可以使用NEXTVAL遞增來獲得下一個值,也可以使用CURRVAL來重新調用發送給會話的上一個值,但是無法找到發出的上一個值。

考點:

序列的CURRVAL是發送給當前會話的上一個值,不一定是發出的上一個值。直到選擇了NEXTVAL后才能選擇CURRVAL。

序列的一種典型用途是用于主鍵值。本例使用序列CUST_SEQ生成唯一客戶號,使用序列ORDER_SEQ生成唯一訂單號,使用LINE_ SEQ生成該訂單的行條目的唯一行號。先創建序列,這是只執行一次的操作:

        create sequence order_seq start with 10;
        create sequence line_seq start with 10;

然后插入訂單,每個訂單所在的行作為單個事務:

        insert into orders (order_id, order_date, customer_id)
        values (order_seq.nextval, sysdate, '1000' );
        insert into order_items (order_id, order_item_id, product_id)
        values (order_seq.currval, line_seq.nextval, 'A111');
        insert into order_items (order_id, order_item_id, product_id)
        values (order_seq.currval, line_seq.nextval, 'B111');
        commit;

第一條INSERT語句生成了一個訂單,它具有從客戶編號1000的序列ORDER_SEQ得到的唯一訂單號。第二條和第三條語句插入訂單的兩行:使用前面從ORDER_SEQ發出的訂單號作為將這兩行連接到訂單的外鍵,并使用LINE_SEQ中的下一個值生成每一行的唯一標識符。最后,提交事務。

序列沒有綁定到任何一個表。在前面的示例中,從技術上來說沒有理由不使用一個序列來生成訂單和行的主鍵值。

對于持久遞增序列來說,COMMIT并不是必需的語句:序列遞增是持久的操作,一旦發生就對其余部分可見,并且不能回滾。序列更新的發生與事務管理系統無關。由于這個原因,序列中總是存在間隔。如果數據庫重啟并且使用CACHE子句,那么間隔會比較大。當數據庫關閉時,所有已生成并緩存但還沒有發出的數值會丟失。下次重啟時,序列的當前值是上次生成的數值,而不是上次發出的數值。因此,如果使用默認的CACHE 20,那么每次關閉/啟動會丟失20個數值。

如果業務分析師指出序列中不能有間隔,那么必須使用另一種生成唯一數值的方式。對于前面生成訂單的示例,當前訂單號可以存儲在該表中,并將它初始化為10:

        create table current_on(order_number number);
        insert into current_on values(10);
        commit;

然后創建訂單的代碼將變成:

        update current_on set order_number=order_number + 1;
        insert into orders (order_number, order_date, customer_number)
        values ((select order_number from current_on), sysdate, '1000');
        commit;

作為一種生成唯一訂單號的方式,當然可以采用這樣的代碼,因為訂單號的遞增在插入訂單的事務中,所以必要時可以使用插入回滾它:除非故意刪除訂單,否則訂單號中不會有間隔。但是,這樣做的效率遠不如使用序列,而且眾所周知的是,像這樣的代碼會引起致命的爭用問題。如果有很多會話試圖鎖定并遞增包含當前編號的一行,那么當隊列在等待輪到自己時,整個應用程序就會掛起。

創建和使用序列后,可以對序列進行修改。語法如下:

        ALTER SEQUENCE sequencename
        [INCREMENT BY number]
        [START WITH number]
        [MAXVALUE number | NOMAXVALUE]
        [MINVALUE number | NOMINVALUE]
        [CYCLE | NOCYCLE]
        [CACHE number | NOCACHE]
        [ORDER | NOORDER] ;

ALTER命令與CREATE命令基本相同,只有一點區別:ALTER命令不能設置起始值。如果要重啟該序列,唯一的方法是刪除并重新創建它。為調整默認緩存值以改進前面的訂單條目示例的性能,可以使用如下代碼:

        alter sequence order_seq cache 1000;

然而,如果希望將該序列重置為它的起始值,唯一的方法是先刪除它:

        drop sequence order_seq;

然后創建該序列。

練習5-9 創建和使用序列

在本練習中,創建一些序列并使用它們。需要兩個并發會話,可以使用SQL Developer或SQL*Plus。

(1) 在單獨的會話中作為HR登錄到數據庫兩次。將其中一次登錄看成A會話,另一次看成B會話。

(2) 在A會話中,創建如下所示的序列:

        create sequence seq1 start with 10 nocache maxvalue 15 cycle;

NOCACHE的使用會降低性能。如果指定了MAXVALUE,那么有必要用CYCLE防止到達MAXVALUE時出錯。

(3) 在適當的會話中按正確的順序執行表5-3中的命令,以觀察NEXTVAL和CURRVAL的使用以及序列的循環。

表5-3 執行命令

(4) 創建一個帶主鍵的表:

    create table seqtest(c1 number, c2 varchar2(10));
    alter table seqtest add constraint seqtest_pk primary key (c1);

(5) 創建一個序列來生成主鍵值:

    create sequence seqtest_pk_s;

(6) 在A會話中,向新表中插入一行并提交:

    insert into seqtest values (seqtest_pk_s.nextval, 'first');
    commit;

(7) 在B會話中,向新表中插入一行且不提交:

    insert into seqtest values (seqtest_pk_s.nextval, 'second');

(8) 在A會話中,插入第三行并提交:

    insert into seqtest values (seqtest_pk_s.nextval, 'third');
    commit;

(9) 在B會話中,回滾第二個插入:

    rollback;

(10) 在B會話中,查看表的內容:

    select * from seqtest;

這就演示了在事務控制機制外部遞增序列并立即發布下一個值。

(11) 整理表和序列:

        drop table seqtest;
        drop sequence seqtest_pk_s;
        drop sequence seq1;

(12) 使用SQL Developer或SQL*Plus連接到HR模式,并創建三個將在后續練習中使用的序列。

        create sequence prod_seq;
        create sequence cust_seq;
        create sequence order_seq;

5.11 本章知識點回顧

分類主要的數據庫對象

● 有些對象包含數據,主要是表和索引。

● 編程對象(如存儲過程和函數)是可執行的代碼。

● 視圖和同義詞是能夠訪問其他對象的對象。

● 表是保存使用列定義的行的二維結構。

● 表在模式中。模式名和表名形成唯一標識符。

列舉列可用的數據類型

● 最常見的字符數據類型有VARCHAR2、NUMBER和DATE。

● 還有其他許多數據類型。

創建簡單的表

● 可以從頭開始創建表或者使用子查詢創建表。

● 創建之后,可以添加、刪除或者修改列定義。

● 表定義可以包含列的默認值。

創建和使用臨時表

● 只有插入行的會話才能訪問臨時表中的行。

● 針對臨時表的DML不生成重做數據。

● 臨時表只存在于會話的PGA或臨時段中。

● 臨時表只在會話期間或事務期間(具體取決于創建方式)保存行。

索引

● 要實施唯一約束和主鍵約束,就必須使用索引。

● B*樹索引不包括NULL,但位圖索引包含NULL。

● B*樹索引可為唯一索引,也可為非唯一索引,這些將決定是否接受重復鍵值。

● B*樹索引適用于基數大的列,而位圖索引適用于基數小的列。

● 位圖索引可以采用復合形式,可以基于函數,也可以降序排列。B*樹索引可以是唯一的、壓縮的和反向鍵。

約束

● 可以在創建表時定義約束,也可以在隨后添加。

● 可以與列一起定義約束,或者在列之后的表級別定義約束。

● 表級別約束可能比內聯定義的約束更復雜。

● 表只能有一個主鍵,但可以有許多唯一鍵。

● 主鍵的功能等同于UNIQUE加上NOT NULL。

● 唯一約束不阻止多個空值的插入。

● 外鍵約束定義表之間的關系。

視圖

● 簡單視圖有一個明細表(或基表),既不使用函數也不使用聚合。

● 復雜視圖可以基于任何SELECT語句,不管多么復雜。

● 視圖是模式對象。要在另一個模式中使用視圖,必須用模式名限定視圖名。

● 可以像查詢表一樣查詢視圖。

● 可以將視圖聯接到其他視圖或者聯接到表,它們可以被聚合,在有些情況下它們可以接受DML語句。

● 視圖僅作為數據字典結構存在。每當查詢視圖時,必須運行底層SELECT語句。

同義詞

● 同義詞是視圖或表的另一個名稱。

● 私有同義詞是模式對象;公有同義詞存在于用戶模式之外,不需要指定模式名作為限定符就可以使用。

● 同義詞與視圖和表共享相同的名稱空間,因此可以與它們互換使用。

序列

● 序列生成唯一值——除非指定了MAXVALUE或MINVALUE和CYCLE。

● 遞增序列不需要提交,不能回滾。

● 任何會話都能通過讀取它的下一個值來遞增序列。可以獲得上次發送給會話的值,但不能獲得上次發出的值。

主站蜘蛛池模板: 陈巴尔虎旗| 托克托县| 嘉祥县| 板桥市| 濮阳县| 大渡口区| 临漳县| 延寿县| 大冶市| 红河县| 昭苏县| 武安市| 平和县| 江都市| 临泉县| 广德县| 新竹县| 娄烦县| 沙河市| 三台县| 江西省| 新疆| 绥滨县| 建平县| 海晏县| 民乐县| 台前县| 文安县| 凭祥市| 塔河县| 乌拉特后旗| 阿拉尔市| 连城县| 渝中区| 连城县| 红原县| 夏邑县| 香河县| 齐齐哈尔市| 沙湾县| 太保市|