1. 程式人生 > >Oracle 模式物件

Oracle 模式物件

chap 3 模式物件

資料庫約束有五種:

  •      主鍵約束(PRIMARY KEY)
  •      唯一性約束(UNIQUE)
  •      非空約束(NOT NULL)
  •      外來鍵約束(FOREIGN KEY)
  •      檢查約束(CHECK)

 

constraint   xskc_p1 PRIMARY KEY(XH, KCH)         約束

constraint

把已經存在的表放入表空間中

alter table xs  move tablespace users!!!!

foreign key (cpno) references course(cno)

【例1建立XS表中計算機專業學生的記錄備份。

CREATE TABLE XS_JSJ   AS SELECT *     FROM SCOTT.XS_KC

 WHERE CJ>100

【例2】(再次重複!!)(1) XS中增加2列:JXJ(獎學金等級)DJSM(

獎學金等級說明)

ALTER TABLE SCOTT.XS  ADD ( JXJ NUMBER(1), DJSM VARCHAR2(40) DEFAULT  '獎金1000');

  (2) 在表XS中修改名為DJSM的列的預設值。

 ALTER TABLE SCOTT.XS   MODIFY ( DJSM DEFAULT '獎金800' );

  (3) 在表XS中刪除名為JXJDJSM的列。

  ALTER TABLE SCOTT.XS

    DROP COLUMN JXJ;

  ALTER TABLE SCOTT.XS     DROP COLUMN DJSM;

按照約束的用途可以將表的完整性約束(constraint)分為5

1.NOT NULL   非空約束。指定一列不允許儲存空值。這實際就是一種強制的CHECK約束 

 2.PRIMARY    KEY  主鍵約束。指定表的主鍵

 3. UNIQUE   唯一約束。指定一列或一組列只能儲存唯一的值 

 4. CHECK   檢查約束。指定一列或一組列的值必須滿足某種條件 

5. FOREIGN   KEY    指定表的外來鍵。外來鍵引用另外一個表中的一列,在自引用的情況中,則引用本表中的一列 

例1:  向XSCJ資料庫的表XS中插入如下的一行:

061101  王林  計算機  男  19870201  50

INSERT INTO XS(XH,XM,ZYM,XB,CSSJ,ZXF)     

 VALUES('061101','王林', '計算機','',

TO_DATE('19860210','YYYYMMDD'),50);

例2:select TO_DATE('19860210','YYYYMMDD') from dual

TO_DATE(字串, '格式’)

作用:將字元轉換為日期型別 

!!!  SQL> create table xs1 as select * from xs;

   SQL> truncate table xs1;

 

這個是重點了啊

  1. list(列表) 分割槽     列表分割槽表是基於特定值對錶進行分割槽,其分割槽列的值為非數值型或日期型別,並且分割槽列的取值範圍較少,所以一般為字元型, partition by list子句,列表值相同的行將被儲存到同一分割槽中)

 

create table part_book1

     (  bid number(4),

        bookname VARCHAR2(20),

        bookpress VARCHAR2(30),

      booktime date)

partition by list(bookpress)

  (partition part1 values('清華大學出版社') tablespace system,

  partition part2 values('教育出版社') tablespace users);

alter table part_book1

add partition part3 values(default) tablespace system;

 

 

 

create table part_book

 (  bid number(4),

     bookname  VARCHAR2(20),

     bookpress  VARCHAR2(30),

     booktime date)

      partition by range(booktime)

        (partition part1 values less than(to_date('20100101','yyyymmdd'))  tablespace system,

          partition part2 values less than (to_date('20120101','yyyymmdd'))   tablespace users,

          partition part3 values less than (MAXVALUE)  tablespace users

        );

insert into part_book values(1,'oracle','清華大學出版社',to_date('20110102','yyyymmdd'));

insert into part_book values(2,'oracle','清華大學出版社',to_date('20090101','yyyymmdd'));

 

分割槽表的切割舉例:將part3分割槽切割為兩個新的分割槽,名字為part3、part4,分割槽的的依據值為20140101.

alter table  part_book4

split partition part3 at (to_date('20140101','yyyymmdd'))

into(partition part3,partition part4)

刪除分割槽

舉例:alter table  part_book  drop partition part3;

 

【例1】為KC表的課程名列建立索引。

            CREATE INDEX kc_name_idx

                     ON KC(KCM)

                     TABLESPACE users;

【例1.1】在xs表的xm列上建立基於LOWER函式的索引,如下:

CREATE INDEX name_lower_index

    ON xs(LOWER(xm))

    TABLESPACE users;

 

【例2】建立同義詞。

    (1) 為XSCJ資料庫的XS_KC表建立公用同義詞XS_KC。

    CREATE PUBLIC SYNONYM XS_KC

     FOR SYSTEM.XS_KC;

【例3】刪除公用同義詞CS_XS。

DROP PUBLIC SYNONYM CS_XS;

 

【例4】建立一個降序序列。

Create sequence  stu_sequence

    start with 5000

         increment by   -2

            maxvalue  5000

                minvalue  1

                     nocycle;

Insert into student values(stu_sequence.nextval, '李明');

例如,要刪除S_TEST序列,可使用如下語句:

DROP sequence student_sequence;

 

引用序列是通過偽列nextval完成的,它用的是序列的 下一個值,若引用當前值,則用偽列currval;

 

 

 

 

重點!!!!!

(PLSQL!!)

要求:向學生表中新增 記錄,值為’007’ ‘Jame’ ‘計算機’ 45,並說明是否成功

DECLARE

      v_xm varchar2(8):='Jame';

    v_zym varchar2(10):='計算機';

    v_zxf number(2):=45;    /*定義變數型別*/

  BEGIN

 INSERT INTO XS(XH,XM,ZYM,ZXF)        VALUES('007',v_xm,v_zym,v_zxf);

 IF SQL%FOUND THEN

       DBMS_OUTPUT.PUT_LINE('操作成功');

ELSE

      DBMS_OUTPUT.PUT_LINE('沒有插入該人');

 END IF;

END;

 

2  要求:針對scott.emp表,計算7788號僱員的應交稅金情況,薪金>=3000,應繳稅金為薪金的0.08,薪金在1500和3000之間,應繳薪金的0.06,其它應繳0.04.

  declare

   v_sal  scott.emp.sal%type;

   v_tax   scott.emp.sal%type;

begin

   select sal into v_sal  from  scott.emp  where empno=7788;

   if  v_sal>=3000 then

      v_tax:=v_sal*0.08;

    elsif  v_sal>=1500 then  

       v_tax:=v_sal*0.06;

    else

        v_tax:=v_sal*0.04;

    end if;  

    DBMS_OUTPUT.PUT_LINE('應繳稅金:'||v_tax);

  end;

3  要求:涉及表為scott.emp,輸入一個員工號,修改該員工的工資,如果該員工為10號部門(deptno),則要求工資增加100;若為20號部門,要求工資增加150;若為30號部門,工資增加200;否則增加300。

DECLARE

     v_deptno scott.emp.deptno%type;

     v_zj NUMBER(4);

     v_empno  scott.emp.empno%type;

BEGIN

      v_empno:='7788';

     SELECT deptno INTO v_deptno FROM scott.emp WHERE empno=v_empno;

     IF        v_deptno=10          THEN v_zj:=100;

     ELSIF v_deptno=20          THEN v_zj:=150;

     ELSIF v_deptno=30          THEN v_zj:=200;

     ELSE    v_zj:=300;

  END IF;

  UPDATE scott.emp SET sal=sal+v_zj  WHERE empno='7788';

END;

多分枝:

declare

   v_deptno  scott.emp.deptno%type;

   v_zl  scott.emp.sal%type;

begin

   select deptno into v_deptno  from  scott.emp  where empno=&&a;

   if v_deptno=10 then

       v_zl:=100;

     elsif   v_deptno=20 then

        v_zl:=150;

          elsif   v_deptno=30 then

          v_zl:=200;

     else

     v_zl:=300;

     end if;

     

     UPDATE scott.emp SET sal=sal+v_zl  WHERE empno=&a;

等職比較:

DECLARE

   v_deptno emp.deptno%type;

   v_increment NUMBER(4);

   v_empno  emp.empno%type;

BEGIN

  v_empno:=&x;

 SELECT deptno INTO v_deptno FROM emp WHERE empno=v_empno;

  CASE v_deptno  

    WHEN 10 THEN v_increment:=100;

    WHEN 20 THEN v_increment:=150;

    WHEN 30 THEN v_increment:=200;

    ELSE  v_increment:=300;

END CASE;

UPDATE emp SET sal=sal+v_increment WHERE empno=v_empno;

if SQL%FOUND then

  dbms_output.put_line('更改成功');

   select sal into v_sal from scott.emp where empno='7788';

   dbms_output.put_line(v_sal);

end  if;

END;

 

例: 關於成績等級制和百分制的相互轉換。

---簡單case表示式

declare

  grade  varchar2(4):='良好';

begin

  case grade

    when '優秀' then dbms_output.put_line('大於等於90');

    when '良好' then dbms_output.put_line('大於等於80,小於90');

    when '及格' then dbms_output.put_line('大於等於60,小於80');

    else dbms_output.put_line('不及格');

 end case;

end;    

例: 關於成績等級制和百分制的相互轉換。

---搜尋case表示式

declare

  score  int:=91;

begin

  case

    when score>=90 then dbms_output.put_line('優秀');

    when score>=80 then dbms_output.put_line('良好');

    when score>=60 then dbms_output.put_line('及格');

    else dbms_output.put_line('不及格');

 end case;

end;

 

 

select  xh,xm,zxf,

(case

    when zxf>50 then 'gao'

      when zxf>=40 then 'zhong'

        else '學分不夠,需繼續'

end) as

 huodexuefenqingkuang  from xs;

注意:

  1.  整個case 語句沒有標點符號
  2.  case 語句的結束用end 而非end case
  3.  then 之後沒有dbms_output.put_line().
  4.  as 之後無引號.

 

檢驗圖書是否過期:

select empno,ename,job,hiredate,

(case

    when trunc(sysdate-HIREDATE)>360  then '過期'

    when hiredate is null then '沒借書'

     else  '沒過期'

    end)

   as 是否過期 from scott.emp;

 

 

【例】下面是一個異常處理的例子:

SET SERVEROUTPUT ON;

DECLARE

    x NUMBER;

BEGIN

    x:= 'aa123';

EXCEPTION

   WHEN VALUE_ERROR THEN

     DBMS_OUTPUT.PUT_LINE('資料型別錯誤');

END;

!!!!!

與資料庫有關的一段異常:查詢“李明”同學的學號 ★

DECLARE

    v_result xs.xm%TYPE;

    BEGIN

    SELECT xh INTO v_result

             FROM xs

             WHERE xm='李明';

    DBMS_OUTPUT.PUT_LINE('The student number is '||v_result);

    EXCEPTION

           WHEN TOO_MANY_ROWS THEN

              DBMS_OUTPUT.PUT_LINE('There has TOO_MANY_ROWS error');

           WHEN NO_DATA_FOUND THEN

             DBMS_OUTPUT.PUT_LINE('There has NO_DATA_FOUND error');

          WHEN OTHERS THEN

            DBMS_OUTPUT.PUT_LINE('錯誤情況不明');

END;

 

 

查詢名為SMITH的員工工資,如果該員工不存在,則輸出“There is not such an employee!”;如果存在多個同名的員工,則輸出'There has too_many_rows error!”

declare

 v_sal scott.emp.sal%type;

begin

    select sal into v_sal from scott.emp where ename='SMITH';

   DBMS_OUTPUT.PUT_LINE(v_sal);

  EXCEPTION

       WHEN NO_DATA_FOUND THEN

             DBMS_OUTPUT.PUT_LINE(‘沒有返回資料');

       WHEN TOO_MANY_ROWS THEN

             DBMS_OUTPUT.PUT_LINE(‘返回多行匹配資料');

 end;

 

注意:使用others異常可以藉助兩個函式來說明捕捉到的異常的型別-----SQLCODE和SQLERRM

DECLARE

      v_result number;

    BEGIN

      SELECT xm INTO v_result

        FROM xs

        WHERE xh='010010';

      DBMS_OUTPUT.PUT_LINE('The student name is'||v_result);

      EXCEPTION

           WHEN OTHERS THEN

               DBMS_OUTPUT.PUT_LINE('the sqlcode is'||SQLCODE);

               DBMS_OUTPUT.PUT_LINE('the sqlERRM is'||SQLERRM);

    END;

 

 

修改7844員工的工資(增加1000),保證修改後工資不超過6000。

DECLARE

  e_1 EXCEPTION;

  v_sal scott.emp.sal%TYPE;

BEGIN

     UPDATE scott.emp SET sal=sal+1000 WHERE empno=7844 ;

      select sal into v_sal from scott.emp  where empno=7844;

     --- 取出更新後的工資

     IF v_sal>4000 THEN

           RAISE e_1;

     END IF;

    EXCEPTION

    WHEN e_1 THEN

            DBMS_OUTPUT.PUT_LINE('The salary is too large!');

    ROLLBACK;

END;

 

練習:更新scott.emp中7788員工的工資,若沒有成功,請丟擲異常。

declare

    v_empno  scott.emp.empno%type;

    no_result  EXCEPTION;

Begin

   v_empno:=&a;

    update scott.emp  set sal=sal+100 where empno=v_empno;

    if  SQL%NOTFOUND   then

         raise no_result;

    end if;

exception

    when  no_result then

       dbms_output.put_line('資料沒有更新');

    when others then

       dbms_output.put_line(sqlcode||'   '||sqlerrm);

end;

 

select deptno,empno,sal,hiredate ,(case

  when trunc(sysdate-hiredate)>5000 then 'guoqi'

  when hiredate is null then '沒借書'

    else ''

  1.  as 是否借書,

  (case

  when trunc(sysdate-hiredate)>5000 then  to_char(trunc(sysdate-hiredate)*0.1)

      else ''

      end) as  罰款

 from scott.emp

 

Merge  只看紅色的即可

/*示例程式塊2   重要*/

  DECLARE

    v_xm varchar2(8):='Jame';

v_zym varchar2(10):='計算機';

v_zxf number(2):=45;    /*定義變數型別*/

BEGIN

               UPDATE XS  SET zxf=v_zxf

           WHERE xm=v_xm;

     IF SQL%NOTFOUND THEN

                DBMS_OUTPUT.PUT_LINE('沒有該人,需要插入該人');

      INSERT INTO XS(XH,XM,ZYM,ZXF)          VALUES('007',v_xm,v_zym,v_zxf);

    END IF;

end;  

 

應用場合:對於特定的資料,在一次批量操作過程中,如果資料已經存在,則對存在的資料按照現有情況進行更新,如果不存在,則需要加入資料庫。可以採用 Oracle 的 merge。

 說明:products為目標表,newproducts為源表,則若產品號相匹配,根據源表資訊修改目標表的產品名(product_name)和產品類別(category)

merge into products p

     using newproducts np

          on (p.product_id=np.product_id)

when matched then

           update set

           p.product_name=np.product_name,

           p.category=np.category;

  WHEN NOT MATCHED THEN             INSERT(a.xh,a.xm,a.zym,a.xb,a.cssj,a.zxf)

    Values(b.xh,b.xm,b.zym,b.xb,b.cssj,b.zxf);

 

!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

一個merge例子帶update,delete 和insert 三種操作。

MERGE INTO products p

 USING newproducts np

 ON (p.product_id = np.product_id)

  WHEN MATCHED THEN

     UPDATE

    SET p.product_name = np.product_name,

    p.category = np.category

    DELETE WHERE (p.category = 'ELECTRNCS')

 WHEN NOT MATCHED THEN

    INSERT

VALUES (np.product_id, np.product_name, np.category)

[例]使用UNION ALL操作符,對scott使用者的emp表進行操作,獲得員工編號大於7800或者所在部門編號為10的員工資訊。使用ORDER BY語句將結果集按照deptno列升序排列輸出。

select empno,ename,sal,deptno from scott.emp

  where empno>7800

   union all

  select empno,ename,sal ,deptno from    scott.emp   where deptno=10

  order by deptno ASC

 

 

遊標!!!

 

declare

        cursor my_cursor

             is   select xh from xs;

         v_xh xs.xh%type;

begin

         open my_cursor;

         fetch my_cursor into v_xh;

          dbms_output.put_line(v_xh);

          dbms_output.put_line(my_cursor%rowcount);

          Close my_cursor;  

   exception

       when others then

          dbms_output.put_line(sqlcode||sqlerrm);

 

【例2】下面介紹一個完整的遊標應用例項:

DECLARE

     varId  NUMBER;

     varName VARCHAR2(50);   

        CURSOR MyCur(v_xb  xs.xb%type)   IS

           SELECT xh, xm FROM xs

          WHERE xb=v_xb;

Varxh=xs.xh%type;

Varname=xs.xm%type;

BEGIN

      OPEN MyCur(‘男’); --開啟遊標,引數為‘男’,表示讀取資訊為男同學資訊

      FETCH MyCur INTO varId, varName;

      dbms_output.put_line('學生編號:'|| varxh||'學生名:'||varName ) ;

      CLOSE MyCur;

END;

 

遊標的%isopen 屬性練習

declare

   cursor  c_1 is select * from xs;

   v_1 c_1%rowtype;

begin

  if c_1%isopen=false then

         open c_1;

  end if;

  fetch c_1 into v_1;

  dbms_output.put_line(v_1.xh||v_1.xm||v_1.zxf);

  close c_1;

end;

 

 

最麻煩的

DECLARE

   TYPE emp_record_type IS RECORD(

        f_name   scott.emp.ename%TYPE,

        h_date   scott.emp.hiredate%TYPE);

   v_1   EMP_RECORD_TYPE;

   CURSOR c3(v_deptno NUMBER,v_job VARCHAR2)

 --宣告遊標,有引數有返回值

          RETURN EMP_RECORD_TYPE

   IS

      SELECT ename, hiredate FROM scott.emp

      WHERE deptno=v_deptno AND job =v_job;

BEGIN

   OPEN c3(v_job=>'MANAGER', v_deptno=>10);

 --開啟遊標,傳遞引數值

   LOOP

      FETCH c3 INTO v_1;    --提取遊標

      IF c3%FOUND THEN

         DBMS_OUTPUT.PUT_LINE(v_1.f_name||'的僱傭日期是' ||v_1.h_date);

      ELSE

         DBMS_OUTPUT.PUT_LINE('已經處理完結果集了');

         EXIT;

      END IF;

   END LOOP;

   CLOSE c3;   --關閉遊標

END;

 

 

大概率會考到

使用遊標分別遍歷xs表中的xh,zxf

DECLARE

      v_xh char(6);

       v_zxf number(2);

       CURSOR    XS_CUR3

            IS SELECT XH,ZXF FROM XS;

BEGIN

      OPEN XS_CUR3;

       FETCH XS_CUR3 INTO v_xh,v_zxf;

 WHILE XS_CUR3%FOUND

 LOOP

   dbms_output.put_line(v_xh||v_zxf);

   FETCH XS_CUR3 INTO v_xh,v_zxf;

  END LOOP;

  CLOSE XS_CUR3;

  END;

 

 

利用WHILE迴圈檢索遊標

DECLARE

    CURSOR cursor_name IS SELECT…;

BEGIN

    OPEN cursor_name;

    FETCH…INTO…;

    WHILE cursor_name%FOUND

     LOOP

           ……   

           FETCH…INTO…;

     END LOOP;

    CLOSE cursor;

END;

注:在開啟遊標後用fetch語句先取一行到變數,然後再用while對該遊標進行判斷,而不是開啟後就立即用while進行判斷 。

 

 

利用遊標WHILE迴圈統計並輸出scott.emp表各個部門的平均工資;若平均工資大於3000,則輸出“該部門平均工資較高。”

DECLARE

  CURSOR c_dept_stat IS SELECT deptno,avg(sal) avgsal FROM scott.emp GROUP BY deptno;

  v_dept c_dept_stat%ROWTYPE;

BEGIN

  OPEN c_dept_stat;

  FETCH c_dept_stat INTO v_dept;

  WHILE c_dept_stat%FOUND LOOP

       DBMS_OUTPUT.PUT_LINE('部門號為'||v_dept.deptno||' '||'平均工資為'||trunc(v_dept.avgsal,1));

        FETCH c_dept_stat INTO v_dept;

  END LOOP;

  CLOSE c_dept_stat;

END;

 

利用FOR迴圈統計並輸出各個部門的平均工資。

DECLARE

    CURSOR c_1 IS SELECT deptno,avg(sal) avgsal FROM scott.emp GROUP BY deptno;

    V_dept  c_1%ROWTYPE;

BEGIN

    FOR   v_dept    IN c_1

LOOP

    DBMS_OUTPUT.PUT_LINE(v_dept.deptno||' '||v_dept.avgsal);

  END LOOP;

END;

 

在For迴圈中直接使用select 子查詢代替遊標名

BEGIN

  FOR v_dept IN (SELECT deptno,avg(sal) avgsal FROM scott.emp GROUP BY deptno)

  LOOP

    DBMS_OUTPUT.PUT_LINE(v_dept.deptno||' '||v_dept.avgsal);

  END LOOP;

END;

DECLARE

   v_empno  scott.emp.empno%TYPE;

   v_sal      scott.emp.sal%TYPE;

   CURSOR c_cursor IS SELECT empno,sal FROM scott.emp where sal<1200 for update;

 begin

      open c_cursor;

      LOOP

      FETCH c_cursor INTO v_empno, v_sal;

      EXIT WHEN c_cursor%NOTFOUND;

             UPDATE scott.emp SET Sal=Sal+50 WHERE current of c_cursor;

            DBMS_OUTPUT.PUT_LINE('編碼為'||v_empno||'工資已更新!');

      DBMS_OUTPUT.PUT_LINE('記錄數:'|| c_cursor%ROWCOUNT);

   END LOOP;

   CLOSE c_cursor;

END;

修改scott.emp表員工的工資,如果員工的部門號為10,工資提高100;部門號為20,工資提高150;部門號為30,工資提高200;否則工資提高250。

DECLARE

  CURSOR c_emp IS SELECT * FROM scott.emp FOR UPDATE      of zl nowait;

   v_zl NUMBER;

   v_emp c_emp%rowtype;

BEGIN

     FOR v_emp IN c_emp LOOP

        CASE v_emp.deptno

             WHEN 10 THEN v_zl:=100;

             WHEN 20 THEN v_zl:=150;

             WHEN 30 THEN v_zl:=200;

             ELSE                v_zl:=250;

       END CASE;

    UPDATE scott.emp SET sal=sal+v_zl WHERE CURRENT OF c_emp;

  END LOOP;

END;

 

不懂!

declare

       type t_dept is REF CURSOR  return scott.emp%rowtype;

       c_1 t_dept;

       v_row  scott.emp%rowtype;

    begin

       open c_1 for select * from scott.emp where  deptno=10;

       fetch c_1 into v_row;

       dbms_output.put_line(v_row.empno||' ' ||v_row.job);

       close c_1;

       

       open c_1 for select * from scott.emp where sal>=2000;

       fetch c_1 into v_row;

       dbms_output.put_line(v_row.deptno||' ' ||v_row.job);

       close c_1;

     end;

 

 

儲存過程

 

create or replace procedure update_emp

 as

 begin

  update scott.emp set ename='candy' where empno=7876;

end update_emp;