Oracle 模式物件
chap 3 模式物件
資料庫約束有五種:
- 主鍵約束(PRIMARY KEY)
- 唯一性約束(UNIQUE)
- 非空約束(NOT NULL)
- 外來鍵約束(FOREIGN KEY)
- 檢查約束(CHECK)
constraint xskc_p1 PRIMARY KEY(XH, KCH) 約束
把已經存在的表放入表空間中
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中刪除名為JXJ和DJSM的列。
ALTER TABLE SCOTT.XS
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;
這個是重點了啊
- (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;
注意:
- 整個case 語句沒有標點符號
- case 語句的結束用end 而非end case
- then 之後沒有dbms_output.put_line().
- 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 ''
- 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;