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中删除名为JXJ和DJSM的列。
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;
这个是重点了啊
- (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;