开发中快速查找的好帮手,覆盖面全,查找方便,有实例:
Oracle函数
单行函数
字符函数
LOWER: 将字符转化成大写:对非字符无影响
UPER : 将字符转化成小写:对非字符无影响
CONCAT: 将字符串连接,相当于||
SUBSTR : 取得字串
LENGTH: 求长度
INITCAP : 将每个单词的第一个字母大写其它字母小写返回
INSTR : 求出现的位置
SELECT E.ENAME, LOWER(E.ENAME)
FROM EMP E
WHERE LOWER(E.ENAME)='smith'//转化成小写
SELECT MIN(SAL)//求最小值 (基于数值型的)
FROM EMP;
SELECT *
FROM EMP E
WHERE INITCAP(E.ENAME)='Ward'//首字母大写,其余小写
SELECT E.EMPNO,E.ENAME,E.JOB, CONCAT(E.ENAME,E.JOB) AS CON ,//连接(基于任
何类型的)
LENGTH(E.ENAME) AS LEN ,INSTR(E.JOB,'S') AS "IN" //求出现的位置
FROM EMP E
WHERE SUBSTR(E.JOB,1,5)='SALES';//取子串
使用数字函数
round//进行四舍五入
trunc:// 将值截断到指定的小数位
MOD//返回相除后的余数
SELECT TRUNC(49.536 ,1) AS "小数点后一位" ,TRUNC(49.536 ,0)AS "个位",TRUNC(49.536 ,-1) "十位"
FROM SYS.DUAL
//trunc: 将值截断到指定的小数位
SELECT E.ENAME, E.SAL, MOD(E.SAL,300) AS "除以300后的余数"
FROM EMP E
WHERE E.SAL IS NOT NULL;
MOD//返回相除后的余数
SELECT ROUND(45.945,2) "小数点后两位",
ROUND(45.945,0) "个位",
ROUND(45.945,-1) "十位"
FROM SYS.DUAL ;
round//进行四舍五入
使用日期函数
ADD_MONTHS(,<i>)
返回日期d加上i个月后的结果。i可以使任意整数。如果i是一个小数,那么数据库将隐式的他转换成整数,
将会截去小数点后面的部分。
LAST_DAY()
函数返回包含日期d的月份的最后一天
缺省的日期格式是 DD-MON-YY
SYSDATE
函数没有参数,返回当前日期和时间。
SELECT SYSDATE AS "时间"
FROM SYS.DUAL;
-- 日期-日期得到两个日期的差(是一个数值类型)
SELECT E.ENAME , (SYSDATE-E.HIREDATE)/7 AS "工作的周数"
FROM EMP E
WHERE E.ENAME IS NOT NULL;
SYSDATE//返回当前日期和时间的函数
SELECT E.ENAME , ROUND ( (SYSDATE-E.HIREDATE)/7,0) AS "工作的周数"
FROM EMP E
WHERE E.ENAME IS NOT NULL;
Round//取得按年或月四舍五入得到的新日期
日期+数值得到一个新的日期
SELECT E.ENAME,E.HIREDATE 雇用日期,(E.HIREDATE + 90) AS "转正日期"
FROM EMP E
WHERE E.ENAME IS NOT NULL;
SELECT E.ENAME ,MONTHS_BETWEEN(SYSDATE,E.HIREDATE) AS "工作的月数"
FROM EMP E;
MONTHS_BETWEEN//两日期相差多少月
转换函数和日期型
SELECT E.ENAME ,E.HIREDATE
FROM EMP E
WHERE E.HIREDATE > TO_DATE('23-9-1982','DD-MM-YYYY')
-- 日期类型转化为文本类型 ,TO_CHAR()函数的使用
SELECT E.ENAME ,e.hiredate, TO_CHAR(E.HIREDATE,'YYYY-MM-DD:DAY') AS "日期"
FROM EMP E
WHERE E.ENAME='SMITH'
SELECT TO_CHAR(SYSDATE,'YYYY-MM-DD HH24:MI:SS AM') 时间
FROM SYS.DUAL;
-- 使用9的时候,如果该位没有数字,则该位不显示
-- 使用0的时候,如果该位没有数字,则该位显示为0
SELECT E.ENAME ,e.sal,TO_CHAR(E.SAL,'$99,999.99') AS "薪水"
FROM EMP E
WHERE E.ENAME IS NOT NULL;
SELECT E.ENAME ,e.sal,TO_CHAR(E.SAL,'L00,000.99') AS "薪水" ,TO_CHAR(E.SAL,'$99,999.99') "薪水2"
FROM EMP E
WHERE E.ENAME IS NOT NULL;
SELECT TO_DATE('1999-3-31','YYYY-MM-DD') AS 日期
FROM SYS.DUAL
SELECT *
FROM EMP E
WHERE E.HIREDATE < TO_DATE('1981-12-17','YYYY-MM-DD')
SELECT *
FROM EMP E
WHERE E.HIREDATE < '17-12月-1981'
SELECT TO_NUMBER('$123,456.98','$999,999.99') AS "NUMBER"
FROM
SYS.DUAL
SELECT * FROM EMP for update
SELECT E.ENAME ,E.SAL , e.comm ,(E.SAL * 12 + E.COMM) AS "年收入"
FROM EMP E
WHERE E.ENAME IS NOT NULL;
-- 使用NVL函数,如果该字段为null,就用后面给的0替换该null值
SELECT E.ENAME ,E.SAL ,E.SAL * 12 ,E.COMM,(E.SAL * 12 + NVL(E.COMM,0)) AS "年收入"
FROM EMP E
WHERE E.ENAME IS NOT NULL;
空值的应用NVL NVL2 NULL
SELECT NVL(E.ENAME,'无名氏') as "姓名",E.SAL
FROM EMP E
NVL(COMM,0) //如果comm字段为null,就用0替换该null
SELECT * FROM EMP E FOR UPDATE;
-- 如果E.ENAME是null,就显示无名氏
-- 如果E.ENAME不是null,就显示E.ENAME
SELECT E.ENAME, NVL2(E.ENAME,E.ENAME,'无名氏') AS "NAME"
FROM EMP E
ORDER BY E.ENAME;
NVL2//如果expr不为Null,返回expr1, 为Null,返回expr2
SELECT * FROM EMP FOR UPDATE;
SELECT ENAME, LENGTH(ENAME) "expr1",
ENAME, LENGTH(ENAME) "expr2",
NULLIF(LENGTH(ENAME), LENGTH(JOB)) result
FROM EMP;
NULLIF//比较两个表达式,如果相等返回空值,如果不等返回第一个表达式。
Case decode语句
SELECT E.ENAME ,E.JOB,E.SAL,
CASE E.JOB
WHEN 'CLERK' THEN 1.10 * E.SAL
WHEN 'MANAGER' THEN 1.3 * E.SAL
WHEN 'SALESMAN' THEN 1.45 * E.SAL
ELSE E.SAL
END AS "修订工资数"
FROM EMP E
WHERE E.ENAME='SMITH';
SELECT E.ENAME,E.JOB ,E.SAL ,
DECODE(E.JOB, 'CLERK',E.SAL * 1.1,
'SALESMAN',E.SAL * 1.2,
'MANAGER' ,E.SAL * 1.4,
E.SAL)
AS "工资修订数"
FROM EMP E
SELECT E.ENAME,E.JOB,NVL(TO_CHAR(E.JOB), '还没有工作') AS "结果"
FROM EMP E
WHERE E.JOB IS NULL;
多行函数
-- AVG,SUM只能针对数值类型
SELECT MIN(E.SAL) "最低工资",
MAX(E.SAL) "最高工资",
AVG(E.SAL) "平均工资" ,
SUM(E.SAL) "工资总和"
FROM EMP E;
MIN,MAX可以用于任何数据类型
SELECT MAX(E.HIREDATE) ,MIN(E.HIREDATE)
FROM EMP E
SELECT MAX(E.HIREDATE),MIN(E.HIREDATE)
FROM EMP E;
SELECT * FROM EMP FOR UPDATE;
WHERE COMM IS NOT NULL;
SELECT * FROM EMP;
Count 统计数目
SELECT COUNT(*) FROM EMP;
COUNT(*)求出所有符 合条件的记录条数,包含有重复的
SELECT COUNT(COMM) FROM EMP;
COUNT(字段)这是求出所有符合条件并且字段值是
非空的记录数,包含有重复的
SELECT COUNT(DISTINCT E.JOB) FROM EMP E;
SELECT COUNT(COMM)
FROM EMP;
SELECT JOB FROM EMP;
-- 查询job字段非空的数据的总数
SELECT COUNT(JOB)
FROM EMP;
-- 查询job字段非空的并且数据不重复的总数
SELECT COUNT(DISTINCT (JOB))
FROM EMP;
SELECT * FROM EMP FOR UPDATE;
SELECT SUM(E.COMM) "佣金总和" ,count(E.COMM) "总条数", AVG(E.COMM) "平均佣金" FROM EMP E
SELECT AVG(E.COMM), SUM(E.COMM) ,COUNT(E.COMM)
FROM EMP E
SELECT AVG(E.COMM)
FROM EMP E
SELECT sum(E.COMM)
FROM EMP E ;
SELECT AVG(NVL(E.COMM,0))
FROM EMP E ;
SELECT * FROM EMP FOR UPDATE;
分组函数GROUP BY
使用GROUP BY子句将表中的数据分成多个小组。分组后的数据执行组函数计算,结果返回给客户。最终的结果自动按照分组字段进行升序排列
-- 出现在查询列表中的字段,要么出现在组函数中,要么出现在GROUP BY字句中
-- (另一种情况,可以只出现在GROUP BY字句中)
SELECT E.DEPTNO,AVG(E.SAL) AS "AVG"
FROM EMP E
GROUP BY E.DEPTNO
ORDER BY "AVG" ASC;
SELECT * FROM EMP;
SELECT AVG(E.SAL) AS "AVG"
FROM EMP E
GROUP BY E.DEPTNO
ORDER BY "AVG"
SELECT E.DEPTNO,E.JOB,SUM(E.SAL)
FROM EMP E
GROUP BY E.DEPTNO,E.JOB
order BY E.DEPTNO “AVG”/“DESC”;
-- 出现查询列表中的字段,要末出现在组函数中,要末出现在GROUP BY 子句中(必需要出现一次,不能都不出现)
SELECT E.DEPTNO,COUNT(E.ENAME)
FROM EMP E
GROUP BY E.DEPTNO
-- 原意是给分组函数加上限制条件,但是不能使用Where子句
SELECT E.DEPTNO,AVG(E.SAL)
FROM EMP E
WHERE AVG(E.SAL) > 3000
GROUP BY E.DEPTNO
SELECT E.DEPTNO,COUNT(E.ENAME)
FROM EMP E
GROUP BY E.DEPTNO;
SELECT E.DEPTNO,AVG(E.SAL)
FROM EMP E
WHERE AVG(E.SAL) > 3000
GROUP BY E.DEPTNO;
SELECT E.DEPTNO,MAX(E.SAL)
FROM EMP E
GROUP BY E.DEPTNO
HAVING MAX(E.SAL) >=3000;
SELECT E.JOB,SUM(E.SAL) AS "工资总和"
FROM EMP E
WHERE E.JOB IN('SALESMAN','MANAGER','CLERK')
GROUP BY E.JOB
HAVING SUM(E.SAL) > 3000
ORDER BY SUM(E.SAL);
对组的过滤不能出现在WHERE子句中,而是要使用查询语句的另一个子句:HAVING
SELECT AVG(E.SAL)
FROM EMP E
GROUP BY DEPTNO;
SELECT MAX(AVG(E.SAL))
FROM EMP E
GROUP BY E.DEPTNO
SELECT E.ENAME, E.JOB ,NVL(TO_CHAR(E.JOB),'还没有工作') AS "工作"
FROM EMP E
组函数忽略空值,可以使用NVL,NVL2,COALESCE 函数处理空值
SELECT MAX(E.SAL) AS "MAXSAL",MIN(E.SAL) AS "MINSAL",AVG(E.SAL) AS "AVGSQL" ,SUM(E.SAL) AS "SUMSAY"
FROM EMP E
SELECT MAX(E.ENAME) AS "MAXNAME" ,MIN(E.ENAME) AS "MINNAME"
FROM EMP E
SELECT COUNT(*)
FROM EMP E
SELECT COUNT(DISTINCT (E.JOB))
FROM EMP E
SELECT AVG(E.COMM) ,COUNT(E.COMM),SUM(E.COMM)
FROM EMP E
SELECT AVG(NVL(E.COMM,0))
FROM EMP E
完整的SELECT查询语句的语法及执行顺序
SELECT column, group_function
FROM table
[WHERE condition]
[GROUP BY group_by_expression]
[HAVING group_condition]
[ORDER BY column];
在整个语句执行的过程中,首先执行的是WHERE语句,
对表中的数据进行过滤;符合条件的数据通过GROUP BY
进行分组;分组的数据通过HAVING子句进行组函数过滤;
最终的结果通过ORDER BY 进行排序,排序后的结果返
回给客户
SELECT AVG(E.SAL) AS "AVG"
FROM EMP E
GROUP BY E.DEPTNO
--ORDER BY "AVG"/“DESC”
SELECT E.DEPTNO,E.JOB ,SUM(E.SAL)
FROM EMP E
GROUP BY E.DEPTNO,E.JOB
SELECT E.DEPTNO, COUNT(E.ENAME)
FROM EMP E
GROUP BY E.DEPTNO
SELECT E.DEPTNO,AVG(E.SAL)
FROM EMP E
WHERE AVG(E.SAL) > 3000
GROUP BY E.DEPTNO
SELECT E.DEPTNO, MAX(E.SAL)
FROM EMP E
GROUP BY E.DEPTNO
HAVING MAX(E.SAL) > 3000
SELECT E.JOB,SUM(E.SAL)
FROM EMP E
WHERE E.JOB IN ('CLERK','SALESMAN','MANAGER')
GROUP BY E.JOB
HAVING SUM(E.SAL) > 3000
ORDER BY SUM(E.SAL);
函数的嵌套
SELECT MAX(AVG(E.SAL))
FROM EMP E
GROUP BY E.DEPTNO ;
-- 查询所有的数据
SELECT * FROM EMP
QUERY查询语句
Oracle 8i以前的老标准的多表连接
等值连接
非等值连接
外连接
自连接
为了连接n个表,至少需要n-1个连接条件
当多个表中有重名列时,必须在列的名字前加上表名作为前缀,以便能够清晰的表明字段来自那个表
等值连接(=)
SELECT E.ENAME,E.DEPTNO,D.DNAME
FROM EMP E, DEPT D
WHERE E.DEPTNO=D.DEPTNO AND E.ENAME='ALLEN';
SELECT E.ENAME,E.DEPTNO,D.DNAME,D.LOCATION_ID,L.LOCNAME
FROM EMP E,DEPT D,LOCATIONS L
WHERE E.DEPTNO=D.DEPTNO AND D.LOCATION_ID=L.LOCID AND E.ENAME='FORD';
非等值练级(<,>,<>,<=,>=,between\and,in,like)
SELECT E.EMPNO,E.SAL,S.GRADE,S.LOSAL,S.HISAL
FROM EMP E,SALGRADE S
WHERE E.SAL BETWEEN S.LOSAL AND S.HISAL
外连接(+)
--右连接,显示左边的表
SELECT E.ENAME,E.DEPTNO,D.DNAME
FROM EMP E, DEPT D
WHERE E.DEPTNO = D.DEPTNO (+);
--左连接,显示右边的表
SELECT E.ENAME,E.DEPTNO,D.DNAME
FROM EMP E, DEPT D
WHERE E.DEPTNO (+) = D.DEPTNO ;
自连接
SELECT E.EMPNO,E.ENAME,M.EMPNO "经理编号",M.ENAME "经理名称"
FROM EMP E,EMP M
WHERE E.MGR = M.EMPNO ;
Oracle 9i以后的新标准多表连接
CROSS JOIN——它在两个表格中创建了一个笛卡尔积,就象是在Oracle8i中没写WHERE时一样
NATURAL JOIN——这是一个很有用的Oracle9i的句法,它通过从WHERE子句中自动连接标准来改善SQL的稳定性。表示作自然连接
USING子句——它可以通过名字来具体指定连接
ON子句——这个句法允许在两个表中为连接具体指定列名
LEFT OUTER JOIN——它返回表格中左边的行和右边的数值,如果没有搭配的行的话,则返回空
RIGHT OUTER JOIN——它返回表格中右边的行和左边的数值,如果没有搭配的行的话,则返回空
FULL OUTER JOIN——它返回的是两个表格中所有的行,用空填满每一个空格。这在Oracle8i中则没有相应的此种句法
交叉连接(cross join)
SELECT E.ENAME,E.DEPTNO,D.DNAME
FROM EMP E
CROSS JOIN DEPT D;
自然连接(natural join)
SELECT E.ENAME,DEPTNO,D.DNAME
FROM EMP E
NATURAL JOIN DEPT D;
SELECT D.DEPTNO,D.DNAME,L.LOCNAME
FROM DEPT D
NATURAL JOIN LOCATIONS L ;
INNER内链接(inner join..on…)即等值连接
SELECT E.EMPNO,E.ENAME, D.DEPTNO,D.DNAME,D.LOCATION_ID,L.LOCNAME
FROM DEPT D
INNER JOIN LOCATIONS L ON(D.LOCATION_ID=L.LOCID)
INNER JOIN EMP E ON(E.DEPTNO=D.DEPTNO) ;
Join..using
SELECT E.EMPNO,E.ENAME,DEPTNO,D.DNAME
FROM EMP E
JOIN DEPT D USING(DEPTNO);
左外连接(left outer join...on..)
SELECT E.ENAME,E.DEPTNO,D.DNAME
FROM EMP E
LEFT OUTER JOIN DEPT D ON(E.DEPTNO = D.DEPTNO);
右外连接(right outer join ..on..)
SELECT E.ENAME,E.DEPTNO,D.DNAME
FROM EMP E
right OUTER JOIN DEPT D ON(E.DEPTNO = D.DEPTNO);
全外连接(full outer join)
SELECT E.ENAME,E.DEPTNO,D.DNAME
FROM EMP E
FULL OUTER JOIN DEPT D ON(E.DEPTNO = D.DEPTNO);
联合查询(Union)
--去掉重复的记录
SELECT D.DEPTNO, D.DNAME,D.LOCATION_ID FROM DEPT D
UNION
SELECT BK.DEPTNO , BK.DNAME,BK.LOCATION_ID FROM DEPT_BK BK;
--UNION 不去掉重复的记录
SELECT D.DEPTNO, D.DNAME,D.LOCATION_ID FROM DEPT D
UNION ALL
SELECT BK.DEPTNO , BK.DNAME,BK.LOCATION_ID FROM DEPT_BK BK;
子查询
为了给主查询(外部查询)提供数据而首先执行的查询(内部查询)称为子查询
可以给自查询起个别名:
单行子查询(<,>,<>,<=,>=,between\and,in,like)
SELECT E.*
FROM EMP E
WHERE E.SAL < (SELECT SAL FROM EMP WHERE ENAME='ALLEN');
SELECT E.EMPNO,E.ENAME,E.JOB
FROM EMP E
WHERE E.JOB = ( SELECT E.JOB
FROM EMP E
WHERE E.EMPNO=7566
) ;
SELECT E.ENAME,E.SAL
FROM EMP E
WHERE E.SAL > (SELECT MIN(E.SAL)
FROM EMP E
GROUP BY DEPTNO
);
SELECT E.EMPNO,E.ENAME,E.JOB
FROM EMP E
WHERE E.JOB = (SELECT E.JOB
FROM EMP E
WHERE E.EMPNO=8566
) ;
多行子查询(in,all,any)
--in
SELECT E.*
FROM EMP E
WHERE E.JOB IN (SELECT SAL FROM EMP WHERE SAL >2000 )
< ALL 是小于子查询的最小值
-- > ALL 大于子查询的最大值
SELECT E.SAL
FROM EMP E
WHERE E.SAL > ALL (SELECT SAL FROM EMP WHERE DEPTNO=30)
< ANY 是小于子查询的最大值
-- > ANY 大于子查询的最小值
SELECT E.SAL
FROM EMP E
WHERE E.SAL < ANY (SELECT SAL FROM EMP WHERE DEPTNO=30)
SELECT E.DEPTNO,MIN(E.SAL)
FROM EMP E
GROUP BY E.DEPTNO
HAVING MIN(E.SAL) > (SELECT MIN(E.SAL)
FROM EMP E
WHERE E.DEPTNO='20' );
SELECT * FROM DEPT
FOR UPDATE
DML记录操作语句(insert,delete,update,merge)
INSERT(插入记录)
INSERT INTO DEPT (DEPTNO,DNAME,LOCATION_ID) VALUES(41,'技术部',1000);
INSERT INTO DEPT (DEPTNO,DNAME,LOCATION_ID) VALUES(42,'软件部',null);
INSERT INTO DEPT (DEPTNO,DNAME) VALUES(43,'软件一部');
INSERT INTO DEPT_BK VALUES (44,'人事一部',1000);
DELETE(删除记录)
DELETE [FROM] DEPT_BK WHERE DEPTNO > 40;
DELETE EMP;//删除所有的记录
INSERT INTO DEPT_BK SELECT * FROM DEPT;
UPDATE(修改记录)
UPDATE DEPT SET DNAME='人事部',LOCATION_ID = 1000 WHERE DEPTNO=30;
SELECT * FROM DEPT_BK;
INSERT INTO EMP (EMPNO,HIREDATE) VALUES (8899,'12-6月-1984');
INSERT INTO EMP (EMPNO,HIREDATE) VALUES (8890, TO_DATE('1980-12-23','YYYY-MM-DD'));
MERGE
根据条件在表中执行修改或插入数据的功能,如果插入的数据行在目的表中存在就执行UPDATE,如果是不存在则执行INSERT:
MERGE INTO DEPT_BK D
USING DEPT S ON(D.DEPTNO=S.DEPTNO)
WHEN MATCHED THEN
UPDATE SET D.DNAME = S.DNAME,D.LOCATION_ID = S.LOCATION_ID
WHEN NOT MATCHED THEN
INSERT VALUES (S.DEPTNO,S.DNAME,S.LOCATION_ID);
事物控制语句(commit/rollback)
INSERT INTO DEPT VALUES (50,'开发',NULL);
SAVEPOINT A;
DELETE FROM DEPT D WHERE D.DEPTNO > 30;
SAVEPOINT B;
UPDATE DEPT D SET D.DNAME='人事部' where d.deptno=10;
SAVEPOINT C;
ROLLBACK TO B;
COMMIT;
ROLLBACK TO A;
DDL语句 (create/drop/alter)
drop(表)
DROP TABLE TEST;
create (表)
CREATE TABLE TEST(
N1 NUMBER(4),
N2 NUMBER(4,2),
DATE1 DATE
);
子查询建表(AS)
CREATE TABLE EMP_BK
AS
SELECT * FROM EMP;
CREATE TABLE DEPT_BK
(D_ID,D_NAME)
AS
SELECT DEPTNO,DNAME FROM DEPT;
alter(表字段)
增加字段(add)
ALTER TABLE TEST ADD SEX CHAR(1);
修改字段(modify)
ALTER TABLE TEST MODIFY SEX char(10) default '男' ;
删除字段(drop)
ALTER TABLE emp DROP COLUMN AGE;
truncate(清除表中所有的记录)
--是DDL语句,效率高,不可以回滚,而DELETE语句可以ROLLBACK
TRUNCATE TABLE TEST;
rename(改变对象名称)
--对象的所有者才能修改对象的名字
RENAME TEST_1 TO TEST;
完整性约束和数据对象
对象名称 描述
表 基本的数据存储对象,以行和列的形式存在
约束 执行数据校验,保证数据完整性的对象
视图 一个或多个表数据的显示
索引 用于提高查询的速度
同义词 对象的别名
Oracle 支持下面五类完整性约束:
NOT NULL 非空
UNIQUE Key 唯一键
PRIMARY KEY 主键
FOREIGN KEY 外键
CHECK 检察
非空、惟一性约束(not null / unique)
CREATE TABLE STUDENT(
ID NUMBER(4) PRIMARY KEY,
NAME VARCHAR2(20) CONSTRAINTS NAME_NN NOT NULL,
EMAIL_1 VARCHAR2(50) ,
EMAIL_2 VARCHAR2(50) ,
CONSTRAINTS EMAIL_UNIQUE UNIQUE (EMAIL_1,EMAIL_2)
);
联合主键约束(primary key)
CREATE TABLE STU(
FIRST_NAME VARCHAR2(20),
LAST_NAME VARCHAR2(20),
CONSTRAINTS STU_PK PRIMARY KEY (FIRST_NAME,LAST_NAME)
)
外键约束(foreign key)
--在外键约束下,在建表的时候,先建主表,然后建立字表
CREATE TABLE DEPT_1(
DEPT_ID NUMBER(4) PRIMARY KEY,
DEPT_NAME VARCHAR2(20)
);
CREATE TABLE EMP_1(
E_ID NUMBER(4) PRIMARY KEY,
E_NAME VARCHAR2(20),
D_ID NUMBER(4),
CONSTRAINTS DEPT_1_EMP_1_FK FOREIGN KEY (D_ID) REFERENCES DEPT_1 (DEPT_ID)
);
--在外键约束下,在删除表的时候,先删除子表,然后删词主表
DROP TABLE EMP_1;
DROP TABLE DEPT_1;
--在外键约束下,在添加数据的时候,先添加主表的数据,在添加字表的数据
INSERT INTO EMP_1 VALUES (4000,'张三',1000);
INSERT INTO EMP_1 VALUES (4001,'张四',1000);
INSERT INTO EMP_1 VALUES (4002,'张五',null);
INSERT INTO DEPT_1 VALUES (1000,'人事部');
SELECT * FROM EMP_1;
SELECT * FROM DEPT_1;
--在外键约束下,在删除数据的时候,先删除字表的数据,在删除主表的数据
DELETE FROM EMP_1 ;
DELETE FROM DEPT_1;
check约束
CREATE TABLE EMPL(
E_ID NUMBER PRIMARY KEY,
E_NAME VARCHAR2(20) NOT NULL,
E_SEX VARCHAR2(4) ,
E_SAL NUMBER(8,2),
E_GRADE VARCHAR2(30) NOT NULL,
CONSTRAINT EMPL_MIN_SAL CHECK (E_SAL >0),
CONSTRAINT AVAL_SEX CHECK (E_SEX IN ('男','女'))
--CHECK (E_SEX IN ('男','女'))
);
INSERT INTO EMPL VALUES (1,'张三','男',2000);
INSERT INTO EMPL VALUES (2,'张四','女',-22000);
INSERT INTO EMPL VALUES (3,'张五','女',-100);
--可增加或删除约束,但不能直接修改
--增加约束
Alter Table emp_s
Add Constraint email_uk Unique (email);
--删除约束
ALTER TABLE DEPT_1 DROP PRIMARY KEY CASCADE;
索引(Index)
1. 当在表上定义一个PRIMARY KEY 或者UNIQUE 约束条件时,Oracle数据库自动创建一个对应的唯一索引.
CREATE INDEX EMP_INDEX_ENAME
ON EMP (ENAME);
视图(view)
--在CREATE VIEW 语句中字段与子查询中的字段必须匹配.
CREATE OR REPLACE VIEW V_EMP_SAL
AS
SELECT E.EMPNO,E.ENAME,E.SAL
FROM EMP E
WHERE E.ENAME IS NOT NULL
WITH READ ONLY;
--通过设置WITH READ ONLY选项可以禁止对视图执行DML操作.
CREATE OR REPLACE VIEW V_EMP_INFO
AS
SELECT E.EMPNO,E.ENAME,E.JOB,E.MGR,E.HIREDATE
FROM EMP E
WHERE E.ENAME IS NOT NULL
--删除视图(drop)
DROP VIEW V_EMP_SAL;
INSERT INTO V_EMP_SAL VALUES (9003,'AAA','2000.00');
TOP-N ( rownum / rowid )和行内视图
--rownum来说它是oracle系统顺序分配为从查询返回的行的编号
--rowid是物理地址,用于定位oracle中具体数据的物理存储位置
SELECT ROWNUM ,ENAME,SAL
FROM (SELECT E.ENAME,E.SAL
FROM EMP E
WHERE E.ENAME IS NOT NULL AND E.SAL IS NOT NULL
ORDER BY E.SAL ASC)
WHERE ROWNUM <=3 ;
SELECT ROWNUM ,ENAME, HIREDATE
FROM (SELECT E.ENAME,E.HIREDATE
FROM EMP E
WHERE E.ENAME IS NOT NULL AND E.SAL IS NOT NULL
ORDER BY E.HIREDATE ASC)
WHERE ROWNUM <=3 ;
--给rownum起个别名可以对其进行大于(>)操作
SELECT aa ,ENAME, HIREDATE
FROM (SELECT rownum aa,E.ENAME,E.HIREDATE
FROM EMP E
WHERE E.ENAME IS NOT NULL AND E.SAL IS NOT NULL
ORDER BY E.HIREDATE ASC)
WHERE aa>3and aa<5;
PL/SQL程序块
主要有四类:
过程 执行特定操作
函数 进行复杂计算,返回计算的结果
包 将逻辑上相关的过程和函数组织在一起
触发器 事件触发,执行相应操作
PL/SQL子程序
PL/SQL子程序主要有两种类型
1. 存储过程(PROCEDUER):用来完成某些操作的任务
2. 函数(FUNCTION):用来作复杂的计算
PL/SQL子程序,是基于PL/SQL块的结构的,只是比PL/SQL块多了子程序头部的定义.使用PL/SQL子程序,使的PL/SQL程序易于维护,而且可以重复的使用
声明部分不再以DECLARE作为开头,而是以IS开始,不需要再使用DECLARE开始声明部分,IS就表示了声明部分的开始
匿名块
语法:
DECLARE
变量、常量声明;
BEGIN
pl/sql_block;
EXCEPTIONS
异常捕捉;
END;
1.使用SELECT 命令,可以从数据库中取出单行数据,语法略有变化select..into
2.使用DML(insert/delete/update)命令,修改数据库中的行,没有变化
3.通过EXECUTE IMMEDIATE,执行DDL和DCL语句
declare
v_1 varchar2(20) ;
begin
--v_1 := USER||': '||TO_CHAR(SYSDATE);
v_1 := USER||': '||SYSDATE;
dbms_output.put_line(v_1);
end;
%type/%rowtype
declare
v_id employees.empl_id%type := 1005;
v_hiredate employees.hire_date%type := '23-4月-1978';
v_sal employees.salary%type := 3000;
v_deptid employees.department_id%type := 2000;
BEGIN
insert into employees values(v_id,v_hiredate,v_sal,v_deptid);
commit;
END;
declare
v_emp employees%rowtype;
begin
select * into v_emp from employees where empl_id = 1003;
dbms_output.put_line(v_emp.empl_id || '----' || v_emp.hire_date ||
'----' || v_emp.salary || '----' ||
v_emp.department_id);
end;
嵌套块
declare
v_weight number(3) := 100;
begin
-----------------嵌套块---------------------
declare
v_weight number(3) := 1;
begin
v_weight := v_weight +1;
dbms_output.put_line('在嵌套块中v_weight的值是:'|| v_weight);
end;
--------------------------------------------
v_weight := v_weight +1;
dbms_output.put_line('在嵌套块外v_weight的值是:' || v_weight);
end;
select..into
declare
v_hire_date employees.hire_date%type;
v_department_id employees.department_id%type;
begin
select hire_date,department_id into v_hire_date,v_department_id
from employees where empl_id =1000;
dbms_output.put_line (v_hire_date ||'----' || v_department_id );
end;
declare
v_avg_salary employees.salary%type;
begin
select avg(salary) into v_avg_salary from employees;
dbms_output.put_line('平均工资是' || ':' || v_avg_salary);
end;
使用EXECUTE IMMEDIATE执行DDL语句
begin
execute immediate 'create table temp(
id number(4) primary key,
name varchar2(20)
)';
execute immediate 'drop table temp';
end;
流程控制语句
条件控制结构(IF语句)
循环控制语句
基本循环
FOR循环
WHILE循环
EXIT语句
if-then..elsif-then..else..
declare
v1 number(4) := 100;
v2 number(4) := 101;
v3 boolean := (v1=v2);
begin
if(v3) then
dbms_output.put_line('true');
else
dbms_output.put_line('false');
end if;
end;
declare
V_SAL EMP.SAL%TYPE;
begin
SELECT SAL INTO V_SAL FROM EMP WHERE EMPNO = 7698;
IF (V_SAL < 1000) THEN
UPDATE EMP SET COMM = V_SAL * 0.8 WHERE EMPNO = 7698;
ELSIF (V_SAL < 2000) THEN
UPDATE EMP SET COMM = V_SAL * 1.0 WHERE EMPNO = 7698;
ELSE
UPDATE EMP SET COMM = V_SAL * 1.2 WHERE EMPNO = 7698;
END IF;
COMMIT;
end;
循环语句
简单循环(loop..exit when..end loop;)
FOR 循环(for index in lower_bound..upper_bound LOOP....end loop; )
WHILE 循环(while..loop..end loop;)
loop简单循环
BEGIN
DELETE FROM TEST;
FOR V_COUNT IN REVERSE 1 .. 10 LOOP
INSERT INTO TEST VALUES (V_COUNT, 'aaaa');
END LOOP;
COMMIT;
END;
DROP TABLE TEST;
CREATE TABLE TEST(
ID NUMBER(4) PRIMARY KEY,
NAME VARCHAR2(10)
);
DECLARE
V_COUNT NUMBER(2) := 0;
BEGIN
DELETE FROM TEST;
LOOP
INSERT INTO TEST VALUES (V_COUNT, 'AAAA');
V_COUNT := V_COUNT + 1;
EXIT WHEN V_COUNT >= 10;
END LOOP;
COMMIT;
END;
DECLARE
V_COUNT NUMBER(3) := 0;
BEGIN
DELETE FROM TEST;
LOOP
INSERT INTO TEST VALUES (V_COUNT, 'bbb');
V_COUNT := V_COUNT + 1;
exit when v_count<10;
END LOOP;
COMMIT;
END;
for循环
BEGIN
DELETE FROM TEST;
FOR V_COUNT IN REVERSE 1 .. 10 LOOP
INSERT INTO TEST VALUES (V_COUNT, 'aaaa');
END LOOP;
COMMIT;
END;
WHILE循环
DECLARE
V_COUNT NUMBER(3) := 0;
BEGIN
DELETE FROM TEST;
WHILE V_COUNT < 10 LOOP
INSERT INTO TEST VALUES (V_COUNT, 'bbb');
V_COUNT := V_COUNT + 1;
END LOOP;
COMMIT;
END;
SQL 游标
隐式游标(SQL%ROWCOUNT /SQL%FOUND/SQL%NOTFOUND/SQL%ISOPEN)
显式游标(%ROWCOUNT /%FOUND/%NOTFOUND/%ISOPEN)
隐式SQL 游标
DECLARE
V_COUNT NUMBER(3);
BEGIN
DELETE FROM EMP E WHERE E.DEPTNO = 30;
V_COUNT := SQL%ROWCOUNT;
DBMS_OUTPUT.put_line('总共删除数据:' || v_count || ' 条');
COMMIT;
END;
/
显式游标
--取一条
DECLARE
V_EMP_RECORD EMP%ROWTYPE;
CURSOR EMP_CUR IS
SELECT * FROM EMP;
BEGIN
OPEN EMP_CUR;
FETCH EMP_CUR
INTO V_EMP_RECORD;
DBMS_OUTPUT.put_line(V_EMP_RECORD.EMPNO || V_EMP_RECORD.ENAME);
CLOSE EMP_CUR;
END;
/
--loop
DECLARE
V_EMP_RECORD EMP%ROWTYPE;
CURSOR EMP_CUR IS SELECT * FROM EMP;
BEGIN
OPEN EMP_CUR;
LOOP
FETCH EMP_CUR INTO V_EMP_RECORD;
EXIT WHEN EMP_CUR%NOTFOUND;
DBMS_OUTPUT.put_line('----'||V_EMP_RECORD.EMPNO ||V_EMP_RECORD.ENAME);
END LOOP;
CLOSE EMP_CUR;
DBMS_OUTPUT.put_line('总共有数据:' ||EMP_CUR%rowcount );
END;
/
--while
DECLARE
V_EMP_RECORD EMP%ROWTYPE;
CURSOR EMP_CUR IS
SELECT * FROM EMP;
BEGIN
OPEN EMP_CUR;
FETCH EMP_CUR
INTO V_EMP_RECORD;
WHILE EMP_CUR%FOUND LOOP
DBMS_OUTPUT.put_line(V_EMP_RECORD.EMPNO || V_EMP_RECORD.ENAME);
FETCH EMP_CUR
INTO V_EMP_RECORD;
END LOOP;
DBMS_OUTPUT.put_line('共有数据:' || EMP_CUR%ROWCOUNT);
CLOSE EMP_CUR;
END;
/
--for循环
DECLARE
CURSOR EMP_CUR IS SELECT * FROM EMP ;
BEGIN
FOR V_EMP IN EMP_CUR LOOP //不用声明V_EMP
DBMS_OUTPUT.put_line(V_EMP.EMPNO|| V_EMP.ENAME);
END LOOP;
END;
--带参数的游标
在调用时,通过给定不同的参数得到不同的结果集
DECLARE
V_EMP_RECORD EMP%ROWTYPE;
CURSOR EMP_CUR( V_DEPTNO NUMBER ) IS SELECT * FROM EMP
WHERE DEPTNO=V_DEPTNO;
BEGIN
OPEN EMP_CUR(30);
LOOP
FETCH EMP_CUR INTO V_EMP_RECORD;
EXIT WHEN EMP_CUR%NOTFOUND;
DBMS_OUTPUT.put_line(V_EMP_RECORD.EMPNO ||
V_EMP_RECORD.ENAME);
END LOOP;
CLOSE EMP_CUR;
END;
-- WHERE CURRENT OF
DECLARE
CURSOR EMP_CUR IS
SELECT EMPNO, SAL FROM EMP WHERE COMM IS NULL FOR UPDATE;
V_COMM NUMBER(8, 2);
BEGIN
FOR V_EMP_RECORD IN EMP_CUR LOOP
IF V_EMP_RECORD.SAL < 1000 THEN
V_COMM := V_EMP_RECORD.SAL * 0.15;
ELSIF V_EMP_RECORD.SAL < 2000 THEN
V_COMM := V_EMP_RECORD.SAL * 0.25;
ELSIF V_EMP_RECORD.SAL < 3000 THEN
V_COMM := V_EMP_RECORD.SAL * 0.30;
ELSE
V_COMM := V_EMP_RECORD.SAL * 0.35;
END IF;
UPDATE EMP SET COMM = V_COMM WHERE CURRENT OF EMP_CUR;
END LOOP;
COMMIT;
END;
存储过程(procedure..is..begin..end;)
-- 准备数据
drop table empl;
create table empl(e_id number(5), e_name varchar2(20), e_salary number(8,2) );
创建存储过程
--
drop procedure insert_empl;
CREATE OR REPLACE PROCEDURE insert_empl(V_ID [in] NUMBER,
V_NAME VARCHAR2,
V_SAL IN NUMBER) IS
v_1 number(4);--声明的变量
BEGIN
v_1 := 1000;
INSERT INTO EMPL VALUES (V_ID, V_NAME, V_SAL);
COMMIT;
DBMS_OUTPUT.put_line('数据插入成功!' || v_1);
EXCEPTION
WHEN OTHERS THEN
DBMS_OUTPUT.put_line('发生异常');
END;
-- 执行存储过程
INSERT_EMPL(1,'AAA',3300); -- 在command窗口指向
BEGIN
INSERT_EMPL(2,'BBB',4000);
END;
不同参数模式的存储过程
IN OUT IN-OUT
默认 必须指定 必须指定
值被:
传递给子程序 返回到调用环境 传递给子程序,返回到调用环境
参数形式:
常量 未初始化的变量 初始化的变量.
可以是表达式, 必须是一个变量 必须是一个变量
常量,或者是
初始化的变量
create or replace procedure pararm_test(v_in in varchar2,
v_out out varchar2,
v_in_out in out varchar2) is
v_localtion varchar2(20);
begin
v_localtion := v_in;
dbms_output.put_line(v_in_out);
v_out := '从存储过程中返回的'; ----out类型的--- 参数’;
v_in_out := '从存储过程中返回的' ;----int_out类型的--- 参数’;
dbms_output.put_line(v_in);
end;
/ --调用存储过程
set serveroutput on; --在command windows 执行dbms_output.put_line语句中可以打印出来数据
declare
v_in_par varchar2(20) := 'in类型参数';
v_in_out_par varchar2(255);
v_out_par varchar2(255) := 'in_out类型参数';-- 也可以不初始化
begin
pararm_test(v_in_par, v_out_par, v_in_out_par);
dbms_output.put_line(v_in_out_par);
dbms_output.put_line(v_out_par);
end;
2.
CREATE OR REPLACE PROCEDURE insert_out(v_name varchar2,
v_age integer,
v_sex char,
mes in out varchar2,flag out varchar2) IS
in_out varchar2(20);
BEGIN
INSERT INTO STUDENT VALUES (STU_PK.NEXTVAL, v_name, v_age, v_sex);
in_out := mes;
mes := '已经插入成功';
dbms_output.put_line(in_out);
flag := '已经返回';
COMMIT;
END;
declare
in_out_mes varchar2(20) := '已经传值进去了';
out_flag varchar2(10);
begin
insert_out('rrr',13,'女',in_out_mes,out_flag);
dbms_output.put_line(in_out_mes);
dbms_output.put_line(out_flag);
end;
银行转账业务的存储过程:
Create or replace procedure change_count(id1 in number,
id2 in number,
money in number,
out_ret out varchar2) is
id1_exist number;
id2_exist number;
id1_price_temp counts.price%type;
id2_price_temp counts.price%type;
begin
select count(*) into id1_exist from counts where id = id1;
select count(*) into id2_exist from counts where id = id2;
if (id1_exist = 1 and id2_exist = 1) then
select price into id1_price_temp from counts where id = id1;
select price into id2_price_temp from counts where id = id2;
if (id1_price_temp >= money) then
update counts set price = id1_price_temp - money where id = id1;
update counts set price = id2_price_temp + money where id = id2;
commit;
else
--转出帐号余额不足
out_ret := '400';
end if;
else
--id1 或者id2帐号不存在
out_ret := '300';
end if;
end change_count;
函数(function)
--创建
CREATE OR REPLACE FUNCTION tax
(v_value IN NUMBER)
RETURN NUMBER
IS
BEGIN
IF v_value < 1000 THEN
RETURN (v_value * .10);
ELSE
RETURN (v_value * .15);
END IF
END tax;
--调用
DECLARE
V_VALUES_OUT NUMBER(10);
BEGIN
V_VALUES_OUT := tax(10000);
dbms_output.put_line(V_VALUES_OUT);
END;
/
SELECT sal, tax(SAL) as "税金" FROM EMP;
包(package)
-----------包的声明和使用1-------------------------
DROP TABLE employees;
CREATE TABLE employees(
id number(5) primary key,
name varchar2(30),
commission_pct number(3,2 )
);
insert into employees values (1,'张一',0.13);
insert into employees values (2,'张二',0.23);
insert into employees values (3,'张三',0.33);
insert into employees values (4,'张四',0.43);
commit;
select * from employees;
--创建包头
CREATE OR REPLACE PACKAGE comm_package IS
g_comm NUMBER := 0.10;
PROCEDURE reset_comm (p_comm IN NUMBER);
END comm_package;
/
--创建包体
CREATE OR REPLACE PACKAGE BODY comm_package
IS
-------------在包体中定义的局部函数--------------
/*
如果输入的参数p_comm大于employees表中最大的commission_pct
字段,则函数返回FALSE,否则函数返回TRUE
*/
FUNCTION validate_comm (p_comm IN NUMBER)
RETURN BOOLEAN
IS
v_max_comm NUMBER;
BEGIN
SELECT MAX(commission_pct)
INTO v_max_comm
FROM employees;
IF p_comm > v_max_comm THEN
RETURN FALSE ;
ELSE
RETURN TRUE ;
END IF;
END validate_comm;
---------在包体中定义的局部函数:结束------------
------------完成在包体中声明的过程--------------
PROCEDURE reset_comm (p_comm IN NUMBER)
IS
BEGIN
IF validate_comm(p_comm) THEN
g_comm:=p_comm;
ELSE
RAISE_APPLICATION_ERROR(-20210, '不合理的表达式');
END IF;
END reset_comm;
----------完成在包体中声明的过程:结束------------
END comm_package;
--测试包
EXECUTE comm_package.reset_comm(1);
EXECUTE comm_package.reset_comm(.33);
-- 测试包里定义的公共变量
begin
comm_package.reset_comm(0.15);
dbms_output.put_line('g_comm = ' || comm_package.g_comm );
end;
/
--以scott登陆,测试包
EXECUTE test.comm_package.reset_comm(0.15);
begin
test.comm_package.reset_comm(0.15);
dbms_output.put_line('g_comm = ' || test.comm_package.g_comm );
end;
/
-----------包的声明和使用2-------------------------
drop table employee;
create table employee(
id number (5),
name varchar2(30),
salary number(8,2)
);
insert into employee values(1,'张一',3000);
insert into employee values(2,'张二',3400);
insert into employee values(3,'张三',5600);
commit;
create or replace package tax_pkg as
function tax(v_value in number) return number;
end tax_pkg;
/
create or replace package body tax_pkg
as
-------------包体中的函数执行部分---------------
function tax(v_value in number) return number
is
begin
if v_value < 1000 then
return (v_value * 0);
elsif v_value < 5000 then
return (v_value * 0.10);
elsif v_value <10000 then
return (v_value * 0.15);
else
return (v_value * 0.20);
end if;
end tax;
-------------包体中的函数:结束-----------------
end tax_pkg;
/
-- 测试包中定义的函数
select salary,tax_pkg.tax(salary) from employee;
--删除包
包头和包体是两种数据库对象,可以独立的存在
在删除时,可以分别删除;在删除包体时,包头不会受到影响。但是在删除包头时,相关的包体也会被删除
DROP PACKAGE packae_name;
DROP PACKAGE BODY packae_name;
触发器(trigger)
在Oracle数据库中主要有二种触发器类型:
DML触发器(INSERT,UPDATE,DELETE三种触发器)
触发的时机包括:对表来说有before或aftet触发,对视图来说有INSTEAD OF
系统触发器
对表操作时间限制的触发器
CREATE OR REPLACE TRIGGER secure_emp_1 --这里不能有IS
BEFORE INSERT ON employees -- 这里没有分号
BEGIN
IF (TO_CHAR (SYSDATE,'DY') IN ('STA','SUN') OR (TO_CHAR(SYSDATE,'HH24:MI') NOT BETWEEN '08:00' AND '18:00' )) THEN
RAISE_APPLICATION_ERROR(-20500,'你只能在工作时间对表进行操作');
END IF;
END;
语句级DML触发器
CREATE OR REPLACE TRIGGER secure_emp_2
BEFORE INSERT OR UPDATE OR DELETE ON employees
BEGIN
--如果当前时间是周六或周日 或者时间不在8:00-18:00之间
IF (TO_CHAR(SYSDATE, 'DY') IN ('SAT', 'SUN')) OR
(TO_CHAR(SYSDATE, 'HH24') NOT BETWEEN '08' AND '18') THEN
IF DELETING THEN
RAISE_APPLICATION_ERROR(-20501, '你只能在工作时间删除员工表的数据');
ELSIF INSERTING THEN
RAISE_APPLICATION_ERROR(-20500, '你只能在工作时间插入员工表的数据.');
ELSIF UPDATING('SALARY') THEN
RAISE_APPLICATION_ERROR(-20503, '你只能在工作时间更新员工表的数据');
ELSE
RAISE_APPLICATION_ERROR(-20504, '你只能在工作事件操作员工表的数据.');
END IF;
END IF;
END;
行级DML触发器(for each row)
FOR EACH ROW:表明对表中的每行数据操作时都会处分这个触
发器REFERENCING子句是说明触发器替换值的前缀名,默认替换
前的前缀名为old,替换后的前缀名为NeW。也可以自己声明替换前
后变量的的前缀规则
2.
在行级触发器中,可以通过old和new这两种前缀来引用DML操作前后的两种值。
在插入事件中可以使用new,但是不能使用old
在更新时间中可以使用old来引用老的书籍,使用new来引用新的数据
在删除时间中,只能使用old前缀
--1、编写一个数据库触发器以显示当任何时候雇员加薪时的加薪情况--
CREATE OR REPLACE TRIGGER EMP_SAL
AFTER UPDATE OF SAL ON EMP FOR EACH ROW--语句级触发器
BEGIN
DBMS_OUTPUT.put_line(:OLD.ENAME||'更新前的工资为:'||:OLD.SAL||'更新后的工资情况为:'||:NEW.SAL);
END;
UPDATE EMP E SET E.SAL=2200 WHERE E.JOB='MANAGER' ;--只有在update sal的时候才会触动触发器
UPDATE EMP E SET E.EMPNO=11 WHERE E.ENAME='CLACK' ;--不会触动触发器
SELECT * FROM EMP;
--2、编写一个数据库触发器,它允许用户只在上午9:00到下午5:00之间执行dml任务-----
CREATE OR REPLACE TRIGGER EMP_DML
BEFORE INSERT OR DELETE OR UPDATE ON EMP
FOR EACH ROW
DECLARE—-可以出现declare 语句
V_TIME VARCHAR2(10);
BEGIN
V_TIME := TO_CHAR(SYSDATE, 'HH24');
IF (TO_NUMBER(V_TIME) < 9 OR TO_NUMBER(V_TIME) > 17) THEN
raise_application_error(-20008, '现在不允许执行DML任务');--编码可变
END IF;
END;
DELETE FROM EMP;
--3、编写一个数据库触发器,当任何时候某个部门从"dept"中删除时,
-----该触发器将从"emp"表中删除该部门的所有雇员
create or replace trigger dept_emp_delete
before delete on dept
for each row
declare
d_depno dept.deptno%type;
begin
d_depno :=:old.deptno;
delete from emp where emp.deptno=d_depno;
end;
delete from dept where dept.deptno=20;
instead of 对视图操作
create or replace trigger new_view_emp_dept
instead of insert on v_empl for each row—视图
begin
if inserting then
insert into departments values(:new.department_id,:new.dept_name);
insert into employees –转换成对表的操作
values(:new.employee_id,:new.employee_name,:new.department_id);
end if;
end;
管理触发器
启用或者禁用某个触发器
ALTER TRIGGER trigger_name DISABLE | ENABLE
启用或者禁用某个对象上的所有触发器
ALTER TABLE table_name DISABLE | ENABLE ALL TRIGGERS
重编译触发器
ALTER TRIGGER trigger_name COMPILE
数据库系统级触发器(登录/退出触发器)
CREATE OR REPLACE TRIGGER logon_trig
AFTER LOGON ON SCHEMA
BEGIN
INSERT INTO log_trig_table(user_id, log_date, action)
VALUES (USER, SYSDATE, 'Logging on');
END;
CREATE OR REPLACE TRIGGER logoff_trig
BEFORE LOGOFF ON SCHEMA
BEGIN
INSERT INTO log_trig_table(user_id, log_date, action)
VALUES (USER, SYSDATE, 'Logging off');
END;
PL/SQL 中的异常处理
捕获异常:语法
1.预定义的oracle 数据库错误
2.非预定义的oracle 数据库错误
3.用户定义的错误
EXCEPTION
WHEN exception1 THEN..WHEN OTHERS THEN..
预定义异常
预定义异常就是Oracle中已经预先定义好名称的异常
DECLARE
V_EMP_RECORD EMP%ROWTYPE;
V_1 NUMBER(4,2);
BEGIN
V_1 := 12; --这里会发生一个异常
SELECT * INTO V_EMP_RECORD FROM EMP WHERE DEPTNO=10; --这里会发生一个数据过多的异常 ,但不被执行
DBMS_OUTPUT.put_line('---------------------');
EXCEPTION
WHEN NO_DATA_FOUND THEN
DBMS_OUTPUT.put_line('没有合适的数据异常');
WHEN TOO_MANY_ROWS THEN
DBMS_OUTPUT.put_line('数据过多的异常');
WHEN VALUE_ERROR THEN
DBMS_OUTPUT.put_line('赋值异常');
WHEN OTHERS THEN
DBMS_OUTPUT.put_line('遇到其他的异常');
END;
非预定义异常
为了捕获非预定义的异常,必须先创建一个异常名称,然后将错误编号和刚创建的异常关联起来
DECLARE
V_EMP_REMAINING EXCEPTION;
PRAGMA EXCEPTION_INIT(V_EMP_REMAINING, -02292);
BEGIN
DELETE FROM DEPT WHERE DEPTNO =20;
EXCEPTION
WHEN V_EMP_REMAINING THEN
DBMS_OUTPUT.put_line('违反完整性约束');
WHEN OTHERS THEN
DBMS_OUTPUT.put_line('其他错误');
END;
用户定义异常(raise exception)
而用户定义异常是对数据库的操作不符合用户的业务时,人为定义的异常.这类异常不是数据库的错误,所以没有对应的错误代码.而且数据库在执行时不会主动的认为是异常.
drop table sm_emp;
CREATE TABLE sm_emp(
no char(4),
name char(10),
salary number(6,2),
phone char(8)
);
--insert TOM
INSERT INTO sm_emp VALUES ('001','TOM',999.99,'62543678');
INSERT INTO sm_emp VALUES ('002','TOM2',999.99,'62543678');
INSERT INTO sm_emp VALUES ('003','TOM3',999.99,NULL);
commit;
--如果用户的电话为null,则认为发生一个异常
DECLARE
CURSOR C_SM IS
SELECT * FROM SM_EMP;
PHONE_EXCEPTION EXCEPTION;
V_NAME SM_EMP.NAME%TYPE;
BEGIN
FOR V_EMP IN C_SM LOOP
IF (V_EMP.PHONE IS NULL) THEN
V_NAME := V_EMP.NAME;
RAISE PHONE_EXCEPTION;
END IF;
END LOOP;
EXCEPTION
WHEN PHONE_EXCEPTION THEN
DBMS_OUTPUT.PUT_LINE(V_NAME || '的电话不能为空');
END;
--为了能够记录发生的异常信息,Oracle提供了两个函数
1.SQLCODE
返回错误代码,NUMBER类型
2. SQLERRM
返回与错误代码关联的消息;VARCHAR2类型
建立一个错误日志表(SQLERRM\ SQLCODE)
DROP TABLE ERR_LOG;
CREATE TABLE ERR_LOG (
CODE NUMBER(10),
MESSAGE VARCHAR2(255),
ERRDATE DATE
);
drop table test;
create table test (
id number(5) primary key ,
name varchar2(20),
salary number(8,2)
);
DECLARE
V_CODE ERR_LOG.CODE%TYPE;
V_MESSAGE ERR_LOG.MESSAGE%TYPE;
V_DATE ERR_LOG.ERRDATE%TYPE;
BEGIN
INSERT INTO TEST VALUES (1, 'AAA', 3000);
INSERT INTO TEST VALUES (1, 'BBB', 2000); --这里将会出现主键重复异常
EXCEPTION
WHEN OTHERS THEN
V_CODE := SQLCODE;
V_MESSAGE := SQLERRM;
V_DATE := SYSDATE;
INSERT INTO ERR_LOG VALUES (V_CODE, V_MESSAGE, V_DATE);
COMMIT;
DBMS_OUTPUT.PUT_LINE('发生错误');
DBMS_OUTPUT.PUT_LINE(SQLCODE);
DBMS_OUTPUT.PUT_LINE(SQLERRM);
END;
SELECT * FROM DEPT;
SELECT * FROM ERR_LOG;
Oracle函数
单行函数
字符函数
LOWER: 将字符转化成大写:对非字符无影响
UPER : 将字符转化成小写:对非字符无影响
CONCAT: 将字符串连接,相当于||
SUBSTR : 取得字串
LENGTH: 求长度
INITCAP : 将每个单词的第一个字母大写其它字母小写返回
INSTR : 求出现的位置
SELECT E.ENAME, LOWER(E.ENAME)
FROM EMP E
WHERE LOWER(E.ENAME)='smith'//转化成小写
SELECT MIN(SAL)//求最小值 (基于数值型的)
FROM EMP;
SELECT *
FROM EMP E
WHERE INITCAP(E.ENAME)='Ward'//首字母大写,其余小写
SELECT E.EMPNO,E.ENAME,E.JOB, CONCAT(E.ENAME,E.JOB) AS CON ,//连接(基于任
何类型的)
LENGTH(E.ENAME) AS LEN ,INSTR(E.JOB,'S') AS "IN" //求出现的位置
FROM EMP E
WHERE SUBSTR(E.JOB,1,5)='SALES';//取子串
使用数字函数
round//进行四舍五入
trunc:// 将值截断到指定的小数位
MOD//返回相除后的余数
SELECT TRUNC(49.536 ,1) AS "小数点后一位" ,TRUNC(49.536 ,0)AS "个位",TRUNC(49.536 ,-1) "十位"
FROM SYS.DUAL
//trunc: 将值截断到指定的小数位
SELECT E.ENAME, E.SAL, MOD(E.SAL,300) AS "除以300后的余数"
FROM EMP E
WHERE E.SAL IS NOT NULL;
MOD//返回相除后的余数
SELECT ROUND(45.945,2) "小数点后两位",
ROUND(45.945,0) "个位",
ROUND(45.945,-1) "十位"
FROM SYS.DUAL ;
round//进行四舍五入
使用日期函数
ADD_MONTHS(,<i>)
返回日期d加上i个月后的结果。i可以使任意整数。如果i是一个小数,那么数据库将隐式的他转换成整数,
将会截去小数点后面的部分。
LAST_DAY()
函数返回包含日期d的月份的最后一天
缺省的日期格式是 DD-MON-YY
SYSDATE
函数没有参数,返回当前日期和时间。
SELECT SYSDATE AS "时间"
FROM SYS.DUAL;
-- 日期-日期得到两个日期的差(是一个数值类型)
SELECT E.ENAME , (SYSDATE-E.HIREDATE)/7 AS "工作的周数"
FROM EMP E
WHERE E.ENAME IS NOT NULL;
SYSDATE//返回当前日期和时间的函数
SELECT E.ENAME , ROUND ( (SYSDATE-E.HIREDATE)/7,0) AS "工作的周数"
FROM EMP E
WHERE E.ENAME IS NOT NULL;
Round//取得按年或月四舍五入得到的新日期
日期+数值得到一个新的日期
SELECT E.ENAME,E.HIREDATE 雇用日期,(E.HIREDATE + 90) AS "转正日期"
FROM EMP E
WHERE E.ENAME IS NOT NULL;
SELECT E.ENAME ,MONTHS_BETWEEN(SYSDATE,E.HIREDATE) AS "工作的月数"
FROM EMP E;
MONTHS_BETWEEN//两日期相差多少月
转换函数和日期型
SELECT E.ENAME ,E.HIREDATE
FROM EMP E
WHERE E.HIREDATE > TO_DATE('23-9-1982','DD-MM-YYYY')
-- 日期类型转化为文本类型 ,TO_CHAR()函数的使用
SELECT E.ENAME ,e.hiredate, TO_CHAR(E.HIREDATE,'YYYY-MM-DD:DAY') AS "日期"
FROM EMP E
WHERE E.ENAME='SMITH'
SELECT TO_CHAR(SYSDATE,'YYYY-MM-DD HH24:MI:SS AM') 时间
FROM SYS.DUAL;
-- 使用9的时候,如果该位没有数字,则该位不显示
-- 使用0的时候,如果该位没有数字,则该位显示为0
SELECT E.ENAME ,e.sal,TO_CHAR(E.SAL,'$99,999.99') AS "薪水"
FROM EMP E
WHERE E.ENAME IS NOT NULL;
SELECT E.ENAME ,e.sal,TO_CHAR(E.SAL,'L00,000.99') AS "薪水" ,TO_CHAR(E.SAL,'$99,999.99') "薪水2"
FROM EMP E
WHERE E.ENAME IS NOT NULL;
SELECT TO_DATE('1999-3-31','YYYY-MM-DD') AS 日期
FROM SYS.DUAL
SELECT *
FROM EMP E
WHERE E.HIREDATE < TO_DATE('1981-12-17','YYYY-MM-DD')
SELECT *
FROM EMP E
WHERE E.HIREDATE < '17-12月-1981'
SELECT TO_NUMBER('$123,456.98','$999,999.99') AS "NUMBER"
FROM
SYS.DUAL
SELECT * FROM EMP for update
SELECT E.ENAME ,E.SAL , e.comm ,(E.SAL * 12 + E.COMM) AS "年收入"
FROM EMP E
WHERE E.ENAME IS NOT NULL;
-- 使用NVL函数,如果该字段为null,就用后面给的0替换该null值
SELECT E.ENAME ,E.SAL ,E.SAL * 12 ,E.COMM,(E.SAL * 12 + NVL(E.COMM,0)) AS "年收入"
FROM EMP E
WHERE E.ENAME IS NOT NULL;
空值的应用NVL NVL2 NULL
SELECT NVL(E.ENAME,'无名氏') as "姓名",E.SAL
FROM EMP E
NVL(COMM,0) //如果comm字段为null,就用0替换该null
SELECT * FROM EMP E FOR UPDATE;
-- 如果E.ENAME是null,就显示无名氏
-- 如果E.ENAME不是null,就显示E.ENAME
SELECT E.ENAME, NVL2(E.ENAME,E.ENAME,'无名氏') AS "NAME"
FROM EMP E
ORDER BY E.ENAME;
NVL2//如果expr不为Null,返回expr1, 为Null,返回expr2
SELECT * FROM EMP FOR UPDATE;
SELECT ENAME, LENGTH(ENAME) "expr1",
ENAME, LENGTH(ENAME) "expr2",
NULLIF(LENGTH(ENAME), LENGTH(JOB)) result
FROM EMP;
NULLIF//比较两个表达式,如果相等返回空值,如果不等返回第一个表达式。
Case decode语句
SELECT E.ENAME ,E.JOB,E.SAL,
CASE E.JOB
WHEN 'CLERK' THEN 1.10 * E.SAL
WHEN 'MANAGER' THEN 1.3 * E.SAL
WHEN 'SALESMAN' THEN 1.45 * E.SAL
ELSE E.SAL
END AS "修订工资数"
FROM EMP E
WHERE E.ENAME='SMITH';
SELECT E.ENAME,E.JOB ,E.SAL ,
DECODE(E.JOB, 'CLERK',E.SAL * 1.1,
'SALESMAN',E.SAL * 1.2,
'MANAGER' ,E.SAL * 1.4,
E.SAL)
AS "工资修订数"
FROM EMP E
SELECT E.ENAME,E.JOB,NVL(TO_CHAR(E.JOB), '还没有工作') AS "结果"
FROM EMP E
WHERE E.JOB IS NULL;
多行函数
-- AVG,SUM只能针对数值类型
SELECT MIN(E.SAL) "最低工资",
MAX(E.SAL) "最高工资",
AVG(E.SAL) "平均工资" ,
SUM(E.SAL) "工资总和"
FROM EMP E;
MIN,MAX可以用于任何数据类型
SELECT MAX(E.HIREDATE) ,MIN(E.HIREDATE)
FROM EMP E
SELECT MAX(E.HIREDATE),MIN(E.HIREDATE)
FROM EMP E;
SELECT * FROM EMP FOR UPDATE;
WHERE COMM IS NOT NULL;
SELECT * FROM EMP;
Count 统计数目
SELECT COUNT(*) FROM EMP;
COUNT(*)求出所有符 合条件的记录条数,包含有重复的
SELECT COUNT(COMM) FROM EMP;
COUNT(字段)这是求出所有符合条件并且字段值是
非空的记录数,包含有重复的
SELECT COUNT(DISTINCT E.JOB) FROM EMP E;
SELECT COUNT(COMM)
FROM EMP;
SELECT JOB FROM EMP;
-- 查询job字段非空的数据的总数
SELECT COUNT(JOB)
FROM EMP;
-- 查询job字段非空的并且数据不重复的总数
SELECT COUNT(DISTINCT (JOB))
FROM EMP;
SELECT * FROM EMP FOR UPDATE;
SELECT SUM(E.COMM) "佣金总和" ,count(E.COMM) "总条数", AVG(E.COMM) "平均佣金" FROM EMP E
SELECT AVG(E.COMM), SUM(E.COMM) ,COUNT(E.COMM)
FROM EMP E
SELECT AVG(E.COMM)
FROM EMP E
SELECT sum(E.COMM)
FROM EMP E ;
SELECT AVG(NVL(E.COMM,0))
FROM EMP E ;
SELECT * FROM EMP FOR UPDATE;
分组函数GROUP BY
使用GROUP BY子句将表中的数据分成多个小组。分组后的数据执行组函数计算,结果返回给客户。最终的结果自动按照分组字段进行升序排列
-- 出现在查询列表中的字段,要么出现在组函数中,要么出现在GROUP BY字句中
-- (另一种情况,可以只出现在GROUP BY字句中)
SELECT E.DEPTNO,AVG(E.SAL) AS "AVG"
FROM EMP E
GROUP BY E.DEPTNO
ORDER BY "AVG" ASC;
SELECT * FROM EMP;
SELECT AVG(E.SAL) AS "AVG"
FROM EMP E
GROUP BY E.DEPTNO
ORDER BY "AVG"
SELECT E.DEPTNO,E.JOB,SUM(E.SAL)
FROM EMP E
GROUP BY E.DEPTNO,E.JOB
order BY E.DEPTNO “AVG”/“DESC”;
-- 出现查询列表中的字段,要末出现在组函数中,要末出现在GROUP BY 子句中(必需要出现一次,不能都不出现)
SELECT E.DEPTNO,COUNT(E.ENAME)
FROM EMP E
GROUP BY E.DEPTNO
-- 原意是给分组函数加上限制条件,但是不能使用Where子句
SELECT E.DEPTNO,AVG(E.SAL)
FROM EMP E
WHERE AVG(E.SAL) > 3000
GROUP BY E.DEPTNO
SELECT E.DEPTNO,COUNT(E.ENAME)
FROM EMP E
GROUP BY E.DEPTNO;
SELECT E.DEPTNO,AVG(E.SAL)
FROM EMP E
WHERE AVG(E.SAL) > 3000
GROUP BY E.DEPTNO;
SELECT E.DEPTNO,MAX(E.SAL)
FROM EMP E
GROUP BY E.DEPTNO
HAVING MAX(E.SAL) >=3000;
SELECT E.JOB,SUM(E.SAL) AS "工资总和"
FROM EMP E
WHERE E.JOB IN('SALESMAN','MANAGER','CLERK')
GROUP BY E.JOB
HAVING SUM(E.SAL) > 3000
ORDER BY SUM(E.SAL);
对组的过滤不能出现在WHERE子句中,而是要使用查询语句的另一个子句:HAVING
SELECT AVG(E.SAL)
FROM EMP E
GROUP BY DEPTNO;
SELECT MAX(AVG(E.SAL))
FROM EMP E
GROUP BY E.DEPTNO
SELECT E.ENAME, E.JOB ,NVL(TO_CHAR(E.JOB),'还没有工作') AS "工作"
FROM EMP E
组函数忽略空值,可以使用NVL,NVL2,COALESCE 函数处理空值
SELECT MAX(E.SAL) AS "MAXSAL",MIN(E.SAL) AS "MINSAL",AVG(E.SAL) AS "AVGSQL" ,SUM(E.SAL) AS "SUMSAY"
FROM EMP E
SELECT MAX(E.ENAME) AS "MAXNAME" ,MIN(E.ENAME) AS "MINNAME"
FROM EMP E
SELECT COUNT(*)
FROM EMP E
SELECT COUNT(DISTINCT (E.JOB))
FROM EMP E
SELECT AVG(E.COMM) ,COUNT(E.COMM),SUM(E.COMM)
FROM EMP E
SELECT AVG(NVL(E.COMM,0))
FROM EMP E
完整的SELECT查询语句的语法及执行顺序
SELECT column, group_function
FROM table
[WHERE condition]
[GROUP BY group_by_expression]
[HAVING group_condition]
[ORDER BY column];
在整个语句执行的过程中,首先执行的是WHERE语句,
对表中的数据进行过滤;符合条件的数据通过GROUP BY
进行分组;分组的数据通过HAVING子句进行组函数过滤;
最终的结果通过ORDER BY 进行排序,排序后的结果返
回给客户
SELECT AVG(E.SAL) AS "AVG"
FROM EMP E
GROUP BY E.DEPTNO
--ORDER BY "AVG"/“DESC”
SELECT E.DEPTNO,E.JOB ,SUM(E.SAL)
FROM EMP E
GROUP BY E.DEPTNO,E.JOB
SELECT E.DEPTNO, COUNT(E.ENAME)
FROM EMP E
GROUP BY E.DEPTNO
SELECT E.DEPTNO,AVG(E.SAL)
FROM EMP E
WHERE AVG(E.SAL) > 3000
GROUP BY E.DEPTNO
SELECT E.DEPTNO, MAX(E.SAL)
FROM EMP E
GROUP BY E.DEPTNO
HAVING MAX(E.SAL) > 3000
SELECT E.JOB,SUM(E.SAL)
FROM EMP E
WHERE E.JOB IN ('CLERK','SALESMAN','MANAGER')
GROUP BY E.JOB
HAVING SUM(E.SAL) > 3000
ORDER BY SUM(E.SAL);
函数的嵌套
SELECT MAX(AVG(E.SAL))
FROM EMP E
GROUP BY E.DEPTNO ;
-- 查询所有的数据
SELECT * FROM EMP
QUERY查询语句
Oracle 8i以前的老标准的多表连接
等值连接
非等值连接
外连接
自连接
为了连接n个表,至少需要n-1个连接条件
当多个表中有重名列时,必须在列的名字前加上表名作为前缀,以便能够清晰的表明字段来自那个表
等值连接(=)
SELECT E.ENAME,E.DEPTNO,D.DNAME
FROM EMP E, DEPT D
WHERE E.DEPTNO=D.DEPTNO AND E.ENAME='ALLEN';
SELECT E.ENAME,E.DEPTNO,D.DNAME,D.LOCATION_ID,L.LOCNAME
FROM EMP E,DEPT D,LOCATIONS L
WHERE E.DEPTNO=D.DEPTNO AND D.LOCATION_ID=L.LOCID AND E.ENAME='FORD';
非等值练级(<,>,<>,<=,>=,between\and,in,like)
SELECT E.EMPNO,E.SAL,S.GRADE,S.LOSAL,S.HISAL
FROM EMP E,SALGRADE S
WHERE E.SAL BETWEEN S.LOSAL AND S.HISAL
外连接(+)
--右连接,显示左边的表
SELECT E.ENAME,E.DEPTNO,D.DNAME
FROM EMP E, DEPT D
WHERE E.DEPTNO = D.DEPTNO (+);
--左连接,显示右边的表
SELECT E.ENAME,E.DEPTNO,D.DNAME
FROM EMP E, DEPT D
WHERE E.DEPTNO (+) = D.DEPTNO ;
自连接
SELECT E.EMPNO,E.ENAME,M.EMPNO "经理编号",M.ENAME "经理名称"
FROM EMP E,EMP M
WHERE E.MGR = M.EMPNO ;
Oracle 9i以后的新标准多表连接
CROSS JOIN——它在两个表格中创建了一个笛卡尔积,就象是在Oracle8i中没写WHERE时一样
NATURAL JOIN——这是一个很有用的Oracle9i的句法,它通过从WHERE子句中自动连接标准来改善SQL的稳定性。表示作自然连接
USING子句——它可以通过名字来具体指定连接
ON子句——这个句法允许在两个表中为连接具体指定列名
LEFT OUTER JOIN——它返回表格中左边的行和右边的数值,如果没有搭配的行的话,则返回空
RIGHT OUTER JOIN——它返回表格中右边的行和左边的数值,如果没有搭配的行的话,则返回空
FULL OUTER JOIN——它返回的是两个表格中所有的行,用空填满每一个空格。这在Oracle8i中则没有相应的此种句法
交叉连接(cross join)
SELECT E.ENAME,E.DEPTNO,D.DNAME
FROM EMP E
CROSS JOIN DEPT D;
自然连接(natural join)
SELECT E.ENAME,DEPTNO,D.DNAME
FROM EMP E
NATURAL JOIN DEPT D;
SELECT D.DEPTNO,D.DNAME,L.LOCNAME
FROM DEPT D
NATURAL JOIN LOCATIONS L ;
INNER内链接(inner join..on…)即等值连接
SELECT E.EMPNO,E.ENAME, D.DEPTNO,D.DNAME,D.LOCATION_ID,L.LOCNAME
FROM DEPT D
INNER JOIN LOCATIONS L ON(D.LOCATION_ID=L.LOCID)
INNER JOIN EMP E ON(E.DEPTNO=D.DEPTNO) ;
Join..using
SELECT E.EMPNO,E.ENAME,DEPTNO,D.DNAME
FROM EMP E
JOIN DEPT D USING(DEPTNO);
左外连接(left outer join...on..)
SELECT E.ENAME,E.DEPTNO,D.DNAME
FROM EMP E
LEFT OUTER JOIN DEPT D ON(E.DEPTNO = D.DEPTNO);
右外连接(right outer join ..on..)
SELECT E.ENAME,E.DEPTNO,D.DNAME
FROM EMP E
right OUTER JOIN DEPT D ON(E.DEPTNO = D.DEPTNO);
全外连接(full outer join)
SELECT E.ENAME,E.DEPTNO,D.DNAME
FROM EMP E
FULL OUTER JOIN DEPT D ON(E.DEPTNO = D.DEPTNO);
联合查询(Union)
--去掉重复的记录
SELECT D.DEPTNO, D.DNAME,D.LOCATION_ID FROM DEPT D
UNION
SELECT BK.DEPTNO , BK.DNAME,BK.LOCATION_ID FROM DEPT_BK BK;
--UNION 不去掉重复的记录
SELECT D.DEPTNO, D.DNAME,D.LOCATION_ID FROM DEPT D
UNION ALL
SELECT BK.DEPTNO , BK.DNAME,BK.LOCATION_ID FROM DEPT_BK BK;
子查询
为了给主查询(外部查询)提供数据而首先执行的查询(内部查询)称为子查询
可以给自查询起个别名:
单行子查询(<,>,<>,<=,>=,between\and,in,like)
SELECT E.*
FROM EMP E
WHERE E.SAL < (SELECT SAL FROM EMP WHERE ENAME='ALLEN');
SELECT E.EMPNO,E.ENAME,E.JOB
FROM EMP E
WHERE E.JOB = ( SELECT E.JOB
FROM EMP E
WHERE E.EMPNO=7566
) ;
SELECT E.ENAME,E.SAL
FROM EMP E
WHERE E.SAL > (SELECT MIN(E.SAL)
FROM EMP E
GROUP BY DEPTNO
);
SELECT E.EMPNO,E.ENAME,E.JOB
FROM EMP E
WHERE E.JOB = (SELECT E.JOB
FROM EMP E
WHERE E.EMPNO=8566
) ;
多行子查询(in,all,any)
--in
SELECT E.*
FROM EMP E
WHERE E.JOB IN (SELECT SAL FROM EMP WHERE SAL >2000 )
< ALL 是小于子查询的最小值
-- > ALL 大于子查询的最大值
SELECT E.SAL
FROM EMP E
WHERE E.SAL > ALL (SELECT SAL FROM EMP WHERE DEPTNO=30)
< ANY 是小于子查询的最大值
-- > ANY 大于子查询的最小值
SELECT E.SAL
FROM EMP E
WHERE E.SAL < ANY (SELECT SAL FROM EMP WHERE DEPTNO=30)
SELECT E.DEPTNO,MIN(E.SAL)
FROM EMP E
GROUP BY E.DEPTNO
HAVING MIN(E.SAL) > (SELECT MIN(E.SAL)
FROM EMP E
WHERE E.DEPTNO='20' );
SELECT * FROM DEPT
FOR UPDATE
DML记录操作语句(insert,delete,update,merge)
INSERT(插入记录)
INSERT INTO DEPT (DEPTNO,DNAME,LOCATION_ID) VALUES(41,'技术部',1000);
INSERT INTO DEPT (DEPTNO,DNAME,LOCATION_ID) VALUES(42,'软件部',null);
INSERT INTO DEPT (DEPTNO,DNAME) VALUES(43,'软件一部');
INSERT INTO DEPT_BK VALUES (44,'人事一部',1000);
DELETE(删除记录)
DELETE [FROM] DEPT_BK WHERE DEPTNO > 40;
DELETE EMP;//删除所有的记录
INSERT INTO DEPT_BK SELECT * FROM DEPT;
UPDATE(修改记录)
UPDATE DEPT SET DNAME='人事部',LOCATION_ID = 1000 WHERE DEPTNO=30;
SELECT * FROM DEPT_BK;
INSERT INTO EMP (EMPNO,HIREDATE) VALUES (8899,'12-6月-1984');
INSERT INTO EMP (EMPNO,HIREDATE) VALUES (8890, TO_DATE('1980-12-23','YYYY-MM-DD'));
MERGE
根据条件在表中执行修改或插入数据的功能,如果插入的数据行在目的表中存在就执行UPDATE,如果是不存在则执行INSERT:
MERGE INTO DEPT_BK D
USING DEPT S ON(D.DEPTNO=S.DEPTNO)
WHEN MATCHED THEN
UPDATE SET D.DNAME = S.DNAME,D.LOCATION_ID = S.LOCATION_ID
WHEN NOT MATCHED THEN
INSERT VALUES (S.DEPTNO,S.DNAME,S.LOCATION_ID);
事物控制语句(commit/rollback)
INSERT INTO DEPT VALUES (50,'开发',NULL);
SAVEPOINT A;
DELETE FROM DEPT D WHERE D.DEPTNO > 30;
SAVEPOINT B;
UPDATE DEPT D SET D.DNAME='人事部' where d.deptno=10;
SAVEPOINT C;
ROLLBACK TO B;
COMMIT;
ROLLBACK TO A;
DDL语句 (create/drop/alter)
drop(表)
DROP TABLE TEST;
create (表)
CREATE TABLE TEST(
N1 NUMBER(4),
N2 NUMBER(4,2),
DATE1 DATE
);
子查询建表(AS)
CREATE TABLE EMP_BK
AS
SELECT * FROM EMP;
CREATE TABLE DEPT_BK
(D_ID,D_NAME)
AS
SELECT DEPTNO,DNAME FROM DEPT;
alter(表字段)
增加字段(add)
ALTER TABLE TEST ADD SEX CHAR(1);
修改字段(modify)
ALTER TABLE TEST MODIFY SEX char(10) default '男' ;
删除字段(drop)
ALTER TABLE emp DROP COLUMN AGE;
truncate(清除表中所有的记录)
--是DDL语句,效率高,不可以回滚,而DELETE语句可以ROLLBACK
TRUNCATE TABLE TEST;
rename(改变对象名称)
--对象的所有者才能修改对象的名字
RENAME TEST_1 TO TEST;
完整性约束和数据对象
对象名称 描述
表 基本的数据存储对象,以行和列的形式存在
约束 执行数据校验,保证数据完整性的对象
视图 一个或多个表数据的显示
索引 用于提高查询的速度
同义词 对象的别名
Oracle 支持下面五类完整性约束:
NOT NULL 非空
UNIQUE Key 唯一键
PRIMARY KEY 主键
FOREIGN KEY 外键
CHECK 检察
非空、惟一性约束(not null / unique)
CREATE TABLE STUDENT(
ID NUMBER(4) PRIMARY KEY,
NAME VARCHAR2(20) CONSTRAINTS NAME_NN NOT NULL,
EMAIL_1 VARCHAR2(50) ,
EMAIL_2 VARCHAR2(50) ,
CONSTRAINTS EMAIL_UNIQUE UNIQUE (EMAIL_1,EMAIL_2)
);
联合主键约束(primary key)
CREATE TABLE STU(
FIRST_NAME VARCHAR2(20),
LAST_NAME VARCHAR2(20),
CONSTRAINTS STU_PK PRIMARY KEY (FIRST_NAME,LAST_NAME)
)
外键约束(foreign key)
--在外键约束下,在建表的时候,先建主表,然后建立字表
CREATE TABLE DEPT_1(
DEPT_ID NUMBER(4) PRIMARY KEY,
DEPT_NAME VARCHAR2(20)
);
CREATE TABLE EMP_1(
E_ID NUMBER(4) PRIMARY KEY,
E_NAME VARCHAR2(20),
D_ID NUMBER(4),
CONSTRAINTS DEPT_1_EMP_1_FK FOREIGN KEY (D_ID) REFERENCES DEPT_1 (DEPT_ID)
);
--在外键约束下,在删除表的时候,先删除子表,然后删词主表
DROP TABLE EMP_1;
DROP TABLE DEPT_1;
--在外键约束下,在添加数据的时候,先添加主表的数据,在添加字表的数据
INSERT INTO EMP_1 VALUES (4000,'张三',1000);
INSERT INTO EMP_1 VALUES (4001,'张四',1000);
INSERT INTO EMP_1 VALUES (4002,'张五',null);
INSERT INTO DEPT_1 VALUES (1000,'人事部');
SELECT * FROM EMP_1;
SELECT * FROM DEPT_1;
--在外键约束下,在删除数据的时候,先删除字表的数据,在删除主表的数据
DELETE FROM EMP_1 ;
DELETE FROM DEPT_1;
check约束
CREATE TABLE EMPL(
E_ID NUMBER PRIMARY KEY,
E_NAME VARCHAR2(20) NOT NULL,
E_SEX VARCHAR2(4) ,
E_SAL NUMBER(8,2),
E_GRADE VARCHAR2(30) NOT NULL,
CONSTRAINT EMPL_MIN_SAL CHECK (E_SAL >0),
CONSTRAINT AVAL_SEX CHECK (E_SEX IN ('男','女'))
--CHECK (E_SEX IN ('男','女'))
);
INSERT INTO EMPL VALUES (1,'张三','男',2000);
INSERT INTO EMPL VALUES (2,'张四','女',-22000);
INSERT INTO EMPL VALUES (3,'张五','女',-100);
--可增加或删除约束,但不能直接修改
--增加约束
Alter Table emp_s
Add Constraint email_uk Unique (email);
--删除约束
ALTER TABLE DEPT_1 DROP PRIMARY KEY CASCADE;
索引(Index)
1. 当在表上定义一个PRIMARY KEY 或者UNIQUE 约束条件时,Oracle数据库自动创建一个对应的唯一索引.
CREATE INDEX EMP_INDEX_ENAME
ON EMP (ENAME);
视图(view)
--在CREATE VIEW 语句中字段与子查询中的字段必须匹配.
CREATE OR REPLACE VIEW V_EMP_SAL
AS
SELECT E.EMPNO,E.ENAME,E.SAL
FROM EMP E
WHERE E.ENAME IS NOT NULL
WITH READ ONLY;
--通过设置WITH READ ONLY选项可以禁止对视图执行DML操作.
CREATE OR REPLACE VIEW V_EMP_INFO
AS
SELECT E.EMPNO,E.ENAME,E.JOB,E.MGR,E.HIREDATE
FROM EMP E
WHERE E.ENAME IS NOT NULL
--删除视图(drop)
DROP VIEW V_EMP_SAL;
INSERT INTO V_EMP_SAL VALUES (9003,'AAA','2000.00');
TOP-N ( rownum / rowid )和行内视图
--rownum来说它是oracle系统顺序分配为从查询返回的行的编号
--rowid是物理地址,用于定位oracle中具体数据的物理存储位置
SELECT ROWNUM ,ENAME,SAL
FROM (SELECT E.ENAME,E.SAL
FROM EMP E
WHERE E.ENAME IS NOT NULL AND E.SAL IS NOT NULL
ORDER BY E.SAL ASC)
WHERE ROWNUM <=3 ;
SELECT ROWNUM ,ENAME, HIREDATE
FROM (SELECT E.ENAME,E.HIREDATE
FROM EMP E
WHERE E.ENAME IS NOT NULL AND E.SAL IS NOT NULL
ORDER BY E.HIREDATE ASC)
WHERE ROWNUM <=3 ;
--给rownum起个别名可以对其进行大于(>)操作
SELECT aa ,ENAME, HIREDATE
FROM (SELECT rownum aa,E.ENAME,E.HIREDATE
FROM EMP E
WHERE E.ENAME IS NOT NULL AND E.SAL IS NOT NULL
ORDER BY E.HIREDATE ASC)
WHERE aa>3and aa<5;
PL/SQL程序块
主要有四类:
过程 执行特定操作
函数 进行复杂计算,返回计算的结果
包 将逻辑上相关的过程和函数组织在一起
触发器 事件触发,执行相应操作
PL/SQL子程序
PL/SQL子程序主要有两种类型
1. 存储过程(PROCEDUER):用来完成某些操作的任务
2. 函数(FUNCTION):用来作复杂的计算
PL/SQL子程序,是基于PL/SQL块的结构的,只是比PL/SQL块多了子程序头部的定义.使用PL/SQL子程序,使的PL/SQL程序易于维护,而且可以重复的使用
声明部分不再以DECLARE作为开头,而是以IS开始,不需要再使用DECLARE开始声明部分,IS就表示了声明部分的开始
匿名块
语法:
DECLARE
变量、常量声明;
BEGIN
pl/sql_block;
EXCEPTIONS
异常捕捉;
END;
1.使用SELECT 命令,可以从数据库中取出单行数据,语法略有变化select..into
2.使用DML(insert/delete/update)命令,修改数据库中的行,没有变化
3.通过EXECUTE IMMEDIATE,执行DDL和DCL语句
declare
v_1 varchar2(20) ;
begin
--v_1 := USER||': '||TO_CHAR(SYSDATE);
v_1 := USER||': '||SYSDATE;
dbms_output.put_line(v_1);
end;
%type/%rowtype
declare
v_id employees.empl_id%type := 1005;
v_hiredate employees.hire_date%type := '23-4月-1978';
v_sal employees.salary%type := 3000;
v_deptid employees.department_id%type := 2000;
BEGIN
insert into employees values(v_id,v_hiredate,v_sal,v_deptid);
commit;
END;
declare
v_emp employees%rowtype;
begin
select * into v_emp from employees where empl_id = 1003;
dbms_output.put_line(v_emp.empl_id || '----' || v_emp.hire_date ||
'----' || v_emp.salary || '----' ||
v_emp.department_id);
end;
嵌套块
declare
v_weight number(3) := 100;
begin
-----------------嵌套块---------------------
declare
v_weight number(3) := 1;
begin
v_weight := v_weight +1;
dbms_output.put_line('在嵌套块中v_weight的值是:'|| v_weight);
end;
--------------------------------------------
v_weight := v_weight +1;
dbms_output.put_line('在嵌套块外v_weight的值是:' || v_weight);
end;
select..into
declare
v_hire_date employees.hire_date%type;
v_department_id employees.department_id%type;
begin
select hire_date,department_id into v_hire_date,v_department_id
from employees where empl_id =1000;
dbms_output.put_line (v_hire_date ||'----' || v_department_id );
end;
declare
v_avg_salary employees.salary%type;
begin
select avg(salary) into v_avg_salary from employees;
dbms_output.put_line('平均工资是' || ':' || v_avg_salary);
end;
使用EXECUTE IMMEDIATE执行DDL语句
begin
execute immediate 'create table temp(
id number(4) primary key,
name varchar2(20)
)';
execute immediate 'drop table temp';
end;
流程控制语句
条件控制结构(IF语句)
循环控制语句
基本循环
FOR循环
WHILE循环
EXIT语句
if-then..elsif-then..else..
declare
v1 number(4) := 100;
v2 number(4) := 101;
v3 boolean := (v1=v2);
begin
if(v3) then
dbms_output.put_line('true');
else
dbms_output.put_line('false');
end if;
end;
declare
V_SAL EMP.SAL%TYPE;
begin
SELECT SAL INTO V_SAL FROM EMP WHERE EMPNO = 7698;
IF (V_SAL < 1000) THEN
UPDATE EMP SET COMM = V_SAL * 0.8 WHERE EMPNO = 7698;
ELSIF (V_SAL < 2000) THEN
UPDATE EMP SET COMM = V_SAL * 1.0 WHERE EMPNO = 7698;
ELSE
UPDATE EMP SET COMM = V_SAL * 1.2 WHERE EMPNO = 7698;
END IF;
COMMIT;
end;
循环语句
简单循环(loop..exit when..end loop;)
FOR 循环(for index in lower_bound..upper_bound LOOP....end loop; )
WHILE 循环(while..loop..end loop;)
loop简单循环
BEGIN
DELETE FROM TEST;
FOR V_COUNT IN REVERSE 1 .. 10 LOOP
INSERT INTO TEST VALUES (V_COUNT, 'aaaa');
END LOOP;
COMMIT;
END;
DROP TABLE TEST;
CREATE TABLE TEST(
ID NUMBER(4) PRIMARY KEY,
NAME VARCHAR2(10)
);
DECLARE
V_COUNT NUMBER(2) := 0;
BEGIN
DELETE FROM TEST;
LOOP
INSERT INTO TEST VALUES (V_COUNT, 'AAAA');
V_COUNT := V_COUNT + 1;
EXIT WHEN V_COUNT >= 10;
END LOOP;
COMMIT;
END;
DECLARE
V_COUNT NUMBER(3) := 0;
BEGIN
DELETE FROM TEST;
LOOP
INSERT INTO TEST VALUES (V_COUNT, 'bbb');
V_COUNT := V_COUNT + 1;
exit when v_count<10;
END LOOP;
COMMIT;
END;
for循环
BEGIN
DELETE FROM TEST;
FOR V_COUNT IN REVERSE 1 .. 10 LOOP
INSERT INTO TEST VALUES (V_COUNT, 'aaaa');
END LOOP;
COMMIT;
END;
WHILE循环
DECLARE
V_COUNT NUMBER(3) := 0;
BEGIN
DELETE FROM TEST;
WHILE V_COUNT < 10 LOOP
INSERT INTO TEST VALUES (V_COUNT, 'bbb');
V_COUNT := V_COUNT + 1;
END LOOP;
COMMIT;
END;
SQL 游标
隐式游标(SQL%ROWCOUNT /SQL%FOUND/SQL%NOTFOUND/SQL%ISOPEN)
显式游标(%ROWCOUNT /%FOUND/%NOTFOUND/%ISOPEN)
隐式SQL 游标
DECLARE
V_COUNT NUMBER(3);
BEGIN
DELETE FROM EMP E WHERE E.DEPTNO = 30;
V_COUNT := SQL%ROWCOUNT;
DBMS_OUTPUT.put_line('总共删除数据:' || v_count || ' 条');
COMMIT;
END;
/
显式游标
--取一条
DECLARE
V_EMP_RECORD EMP%ROWTYPE;
CURSOR EMP_CUR IS
SELECT * FROM EMP;
BEGIN
OPEN EMP_CUR;
FETCH EMP_CUR
INTO V_EMP_RECORD;
DBMS_OUTPUT.put_line(V_EMP_RECORD.EMPNO || V_EMP_RECORD.ENAME);
CLOSE EMP_CUR;
END;
/
--loop
DECLARE
V_EMP_RECORD EMP%ROWTYPE;
CURSOR EMP_CUR IS SELECT * FROM EMP;
BEGIN
OPEN EMP_CUR;
LOOP
FETCH EMP_CUR INTO V_EMP_RECORD;
EXIT WHEN EMP_CUR%NOTFOUND;
DBMS_OUTPUT.put_line('----'||V_EMP_RECORD.EMPNO ||V_EMP_RECORD.ENAME);
END LOOP;
CLOSE EMP_CUR;
DBMS_OUTPUT.put_line('总共有数据:' ||EMP_CUR%rowcount );
END;
/
--while
DECLARE
V_EMP_RECORD EMP%ROWTYPE;
CURSOR EMP_CUR IS
SELECT * FROM EMP;
BEGIN
OPEN EMP_CUR;
FETCH EMP_CUR
INTO V_EMP_RECORD;
WHILE EMP_CUR%FOUND LOOP
DBMS_OUTPUT.put_line(V_EMP_RECORD.EMPNO || V_EMP_RECORD.ENAME);
FETCH EMP_CUR
INTO V_EMP_RECORD;
END LOOP;
DBMS_OUTPUT.put_line('共有数据:' || EMP_CUR%ROWCOUNT);
CLOSE EMP_CUR;
END;
/
--for循环
DECLARE
CURSOR EMP_CUR IS SELECT * FROM EMP ;
BEGIN
FOR V_EMP IN EMP_CUR LOOP //不用声明V_EMP
DBMS_OUTPUT.put_line(V_EMP.EMPNO|| V_EMP.ENAME);
END LOOP;
END;
--带参数的游标
在调用时,通过给定不同的参数得到不同的结果集
DECLARE
V_EMP_RECORD EMP%ROWTYPE;
CURSOR EMP_CUR( V_DEPTNO NUMBER ) IS SELECT * FROM EMP
WHERE DEPTNO=V_DEPTNO;
BEGIN
OPEN EMP_CUR(30);
LOOP
FETCH EMP_CUR INTO V_EMP_RECORD;
EXIT WHEN EMP_CUR%NOTFOUND;
DBMS_OUTPUT.put_line(V_EMP_RECORD.EMPNO ||
V_EMP_RECORD.ENAME);
END LOOP;
CLOSE EMP_CUR;
END;
-- WHERE CURRENT OF
DECLARE
CURSOR EMP_CUR IS
SELECT EMPNO, SAL FROM EMP WHERE COMM IS NULL FOR UPDATE;
V_COMM NUMBER(8, 2);
BEGIN
FOR V_EMP_RECORD IN EMP_CUR LOOP
IF V_EMP_RECORD.SAL < 1000 THEN
V_COMM := V_EMP_RECORD.SAL * 0.15;
ELSIF V_EMP_RECORD.SAL < 2000 THEN
V_COMM := V_EMP_RECORD.SAL * 0.25;
ELSIF V_EMP_RECORD.SAL < 3000 THEN
V_COMM := V_EMP_RECORD.SAL * 0.30;
ELSE
V_COMM := V_EMP_RECORD.SAL * 0.35;
END IF;
UPDATE EMP SET COMM = V_COMM WHERE CURRENT OF EMP_CUR;
END LOOP;
COMMIT;
END;
存储过程(procedure..is..begin..end;)
-- 准备数据
drop table empl;
create table empl(e_id number(5), e_name varchar2(20), e_salary number(8,2) );
创建存储过程
--
drop procedure insert_empl;
CREATE OR REPLACE PROCEDURE insert_empl(V_ID [in] NUMBER,
V_NAME VARCHAR2,
V_SAL IN NUMBER) IS
v_1 number(4);--声明的变量
BEGIN
v_1 := 1000;
INSERT INTO EMPL VALUES (V_ID, V_NAME, V_SAL);
COMMIT;
DBMS_OUTPUT.put_line('数据插入成功!' || v_1);
EXCEPTION
WHEN OTHERS THEN
DBMS_OUTPUT.put_line('发生异常');
END;
-- 执行存储过程
INSERT_EMPL(1,'AAA',3300); -- 在command窗口指向
BEGIN
INSERT_EMPL(2,'BBB',4000);
END;
不同参数模式的存储过程
IN OUT IN-OUT
默认 必须指定 必须指定
值被:
传递给子程序 返回到调用环境 传递给子程序,返回到调用环境
参数形式:
常量 未初始化的变量 初始化的变量.
可以是表达式, 必须是一个变量 必须是一个变量
常量,或者是
初始化的变量
create or replace procedure pararm_test(v_in in varchar2,
v_out out varchar2,
v_in_out in out varchar2) is
v_localtion varchar2(20);
begin
v_localtion := v_in;
dbms_output.put_line(v_in_out);
v_out := '从存储过程中返回的'; ----out类型的--- 参数’;
v_in_out := '从存储过程中返回的' ;----int_out类型的--- 参数’;
dbms_output.put_line(v_in);
end;
/ --调用存储过程
set serveroutput on; --在command windows 执行dbms_output.put_line语句中可以打印出来数据
declare
v_in_par varchar2(20) := 'in类型参数';
v_in_out_par varchar2(255);
v_out_par varchar2(255) := 'in_out类型参数';-- 也可以不初始化
begin
pararm_test(v_in_par, v_out_par, v_in_out_par);
dbms_output.put_line(v_in_out_par);
dbms_output.put_line(v_out_par);
end;
2.
CREATE OR REPLACE PROCEDURE insert_out(v_name varchar2,
v_age integer,
v_sex char,
mes in out varchar2,flag out varchar2) IS
in_out varchar2(20);
BEGIN
INSERT INTO STUDENT VALUES (STU_PK.NEXTVAL, v_name, v_age, v_sex);
in_out := mes;
mes := '已经插入成功';
dbms_output.put_line(in_out);
flag := '已经返回';
COMMIT;
END;
declare
in_out_mes varchar2(20) := '已经传值进去了';
out_flag varchar2(10);
begin
insert_out('rrr',13,'女',in_out_mes,out_flag);
dbms_output.put_line(in_out_mes);
dbms_output.put_line(out_flag);
end;
银行转账业务的存储过程:
Create or replace procedure change_count(id1 in number,
id2 in number,
money in number,
out_ret out varchar2) is
id1_exist number;
id2_exist number;
id1_price_temp counts.price%type;
id2_price_temp counts.price%type;
begin
select count(*) into id1_exist from counts where id = id1;
select count(*) into id2_exist from counts where id = id2;
if (id1_exist = 1 and id2_exist = 1) then
select price into id1_price_temp from counts where id = id1;
select price into id2_price_temp from counts where id = id2;
if (id1_price_temp >= money) then
update counts set price = id1_price_temp - money where id = id1;
update counts set price = id2_price_temp + money where id = id2;
commit;
else
--转出帐号余额不足
out_ret := '400';
end if;
else
--id1 或者id2帐号不存在
out_ret := '300';
end if;
end change_count;
函数(function)
--创建
CREATE OR REPLACE FUNCTION tax
(v_value IN NUMBER)
RETURN NUMBER
IS
BEGIN
IF v_value < 1000 THEN
RETURN (v_value * .10);
ELSE
RETURN (v_value * .15);
END IF
END tax;
--调用
DECLARE
V_VALUES_OUT NUMBER(10);
BEGIN
V_VALUES_OUT := tax(10000);
dbms_output.put_line(V_VALUES_OUT);
END;
/
SELECT sal, tax(SAL) as "税金" FROM EMP;
包(package)
-----------包的声明和使用1-------------------------
DROP TABLE employees;
CREATE TABLE employees(
id number(5) primary key,
name varchar2(30),
commission_pct number(3,2 )
);
insert into employees values (1,'张一',0.13);
insert into employees values (2,'张二',0.23);
insert into employees values (3,'张三',0.33);
insert into employees values (4,'张四',0.43);
commit;
select * from employees;
--创建包头
CREATE OR REPLACE PACKAGE comm_package IS
g_comm NUMBER := 0.10;
PROCEDURE reset_comm (p_comm IN NUMBER);
END comm_package;
/
--创建包体
CREATE OR REPLACE PACKAGE BODY comm_package
IS
-------------在包体中定义的局部函数--------------
/*
如果输入的参数p_comm大于employees表中最大的commission_pct
字段,则函数返回FALSE,否则函数返回TRUE
*/
FUNCTION validate_comm (p_comm IN NUMBER)
RETURN BOOLEAN
IS
v_max_comm NUMBER;
BEGIN
SELECT MAX(commission_pct)
INTO v_max_comm
FROM employees;
IF p_comm > v_max_comm THEN
RETURN FALSE ;
ELSE
RETURN TRUE ;
END IF;
END validate_comm;
---------在包体中定义的局部函数:结束------------
------------完成在包体中声明的过程--------------
PROCEDURE reset_comm (p_comm IN NUMBER)
IS
BEGIN
IF validate_comm(p_comm) THEN
g_comm:=p_comm;
ELSE
RAISE_APPLICATION_ERROR(-20210, '不合理的表达式');
END IF;
END reset_comm;
----------完成在包体中声明的过程:结束------------
END comm_package;
--测试包
EXECUTE comm_package.reset_comm(1);
EXECUTE comm_package.reset_comm(.33);
-- 测试包里定义的公共变量
begin
comm_package.reset_comm(0.15);
dbms_output.put_line('g_comm = ' || comm_package.g_comm );
end;
/
--以scott登陆,测试包
EXECUTE test.comm_package.reset_comm(0.15);
begin
test.comm_package.reset_comm(0.15);
dbms_output.put_line('g_comm = ' || test.comm_package.g_comm );
end;
/
-----------包的声明和使用2-------------------------
drop table employee;
create table employee(
id number (5),
name varchar2(30),
salary number(8,2)
);
insert into employee values(1,'张一',3000);
insert into employee values(2,'张二',3400);
insert into employee values(3,'张三',5600);
commit;
create or replace package tax_pkg as
function tax(v_value in number) return number;
end tax_pkg;
/
create or replace package body tax_pkg
as
-------------包体中的函数执行部分---------------
function tax(v_value in number) return number
is
begin
if v_value < 1000 then
return (v_value * 0);
elsif v_value < 5000 then
return (v_value * 0.10);
elsif v_value <10000 then
return (v_value * 0.15);
else
return (v_value * 0.20);
end if;
end tax;
-------------包体中的函数:结束-----------------
end tax_pkg;
/
-- 测试包中定义的函数
select salary,tax_pkg.tax(salary) from employee;
--删除包
包头和包体是两种数据库对象,可以独立的存在
在删除时,可以分别删除;在删除包体时,包头不会受到影响。但是在删除包头时,相关的包体也会被删除
DROP PACKAGE packae_name;
DROP PACKAGE BODY packae_name;
触发器(trigger)
在Oracle数据库中主要有二种触发器类型:
DML触发器(INSERT,UPDATE,DELETE三种触发器)
触发的时机包括:对表来说有before或aftet触发,对视图来说有INSTEAD OF
系统触发器
对表操作时间限制的触发器
CREATE OR REPLACE TRIGGER secure_emp_1 --这里不能有IS
BEFORE INSERT ON employees -- 这里没有分号
BEGIN
IF (TO_CHAR (SYSDATE,'DY') IN ('STA','SUN') OR (TO_CHAR(SYSDATE,'HH24:MI') NOT BETWEEN '08:00' AND '18:00' )) THEN
RAISE_APPLICATION_ERROR(-20500,'你只能在工作时间对表进行操作');
END IF;
END;
语句级DML触发器
CREATE OR REPLACE TRIGGER secure_emp_2
BEFORE INSERT OR UPDATE OR DELETE ON employees
BEGIN
--如果当前时间是周六或周日 或者时间不在8:00-18:00之间
IF (TO_CHAR(SYSDATE, 'DY') IN ('SAT', 'SUN')) OR
(TO_CHAR(SYSDATE, 'HH24') NOT BETWEEN '08' AND '18') THEN
IF DELETING THEN
RAISE_APPLICATION_ERROR(-20501, '你只能在工作时间删除员工表的数据');
ELSIF INSERTING THEN
RAISE_APPLICATION_ERROR(-20500, '你只能在工作时间插入员工表的数据.');
ELSIF UPDATING('SALARY') THEN
RAISE_APPLICATION_ERROR(-20503, '你只能在工作时间更新员工表的数据');
ELSE
RAISE_APPLICATION_ERROR(-20504, '你只能在工作事件操作员工表的数据.');
END IF;
END IF;
END;
行级DML触发器(for each row)
FOR EACH ROW:表明对表中的每行数据操作时都会处分这个触
发器REFERENCING子句是说明触发器替换值的前缀名,默认替换
前的前缀名为old,替换后的前缀名为NeW。也可以自己声明替换前
后变量的的前缀规则
2.
在行级触发器中,可以通过old和new这两种前缀来引用DML操作前后的两种值。
在插入事件中可以使用new,但是不能使用old
在更新时间中可以使用old来引用老的书籍,使用new来引用新的数据
在删除时间中,只能使用old前缀
--1、编写一个数据库触发器以显示当任何时候雇员加薪时的加薪情况--
CREATE OR REPLACE TRIGGER EMP_SAL
AFTER UPDATE OF SAL ON EMP FOR EACH ROW--语句级触发器
BEGIN
DBMS_OUTPUT.put_line(:OLD.ENAME||'更新前的工资为:'||:OLD.SAL||'更新后的工资情况为:'||:NEW.SAL);
END;
UPDATE EMP E SET E.SAL=2200 WHERE E.JOB='MANAGER' ;--只有在update sal的时候才会触动触发器
UPDATE EMP E SET E.EMPNO=11 WHERE E.ENAME='CLACK' ;--不会触动触发器
SELECT * FROM EMP;
--2、编写一个数据库触发器,它允许用户只在上午9:00到下午5:00之间执行dml任务-----
CREATE OR REPLACE TRIGGER EMP_DML
BEFORE INSERT OR DELETE OR UPDATE ON EMP
FOR EACH ROW
DECLARE—-可以出现declare 语句
V_TIME VARCHAR2(10);
BEGIN
V_TIME := TO_CHAR(SYSDATE, 'HH24');
IF (TO_NUMBER(V_TIME) < 9 OR TO_NUMBER(V_TIME) > 17) THEN
raise_application_error(-20008, '现在不允许执行DML任务');--编码可变
END IF;
END;
DELETE FROM EMP;
--3、编写一个数据库触发器,当任何时候某个部门从"dept"中删除时,
-----该触发器将从"emp"表中删除该部门的所有雇员
create or replace trigger dept_emp_delete
before delete on dept
for each row
declare
d_depno dept.deptno%type;
begin
d_depno :=:old.deptno;
delete from emp where emp.deptno=d_depno;
end;
delete from dept where dept.deptno=20;
instead of 对视图操作
create or replace trigger new_view_emp_dept
instead of insert on v_empl for each row—视图
begin
if inserting then
insert into departments values(:new.department_id,:new.dept_name);
insert into employees –转换成对表的操作
values(:new.employee_id,:new.employee_name,:new.department_id);
end if;
end;
管理触发器
启用或者禁用某个触发器
ALTER TRIGGER trigger_name DISABLE | ENABLE
启用或者禁用某个对象上的所有触发器
ALTER TABLE table_name DISABLE | ENABLE ALL TRIGGERS
重编译触发器
ALTER TRIGGER trigger_name COMPILE
数据库系统级触发器(登录/退出触发器)
CREATE OR REPLACE TRIGGER logon_trig
AFTER LOGON ON SCHEMA
BEGIN
INSERT INTO log_trig_table(user_id, log_date, action)
VALUES (USER, SYSDATE, 'Logging on');
END;
CREATE OR REPLACE TRIGGER logoff_trig
BEFORE LOGOFF ON SCHEMA
BEGIN
INSERT INTO log_trig_table(user_id, log_date, action)
VALUES (USER, SYSDATE, 'Logging off');
END;
PL/SQL 中的异常处理
捕获异常:语法
1.预定义的oracle 数据库错误
2.非预定义的oracle 数据库错误
3.用户定义的错误
EXCEPTION
WHEN exception1 THEN..WHEN OTHERS THEN..
预定义异常
预定义异常就是Oracle中已经预先定义好名称的异常
DECLARE
V_EMP_RECORD EMP%ROWTYPE;
V_1 NUMBER(4,2);
BEGIN
V_1 := 12; --这里会发生一个异常
SELECT * INTO V_EMP_RECORD FROM EMP WHERE DEPTNO=10; --这里会发生一个数据过多的异常 ,但不被执行
DBMS_OUTPUT.put_line('---------------------');
EXCEPTION
WHEN NO_DATA_FOUND THEN
DBMS_OUTPUT.put_line('没有合适的数据异常');
WHEN TOO_MANY_ROWS THEN
DBMS_OUTPUT.put_line('数据过多的异常');
WHEN VALUE_ERROR THEN
DBMS_OUTPUT.put_line('赋值异常');
WHEN OTHERS THEN
DBMS_OUTPUT.put_line('遇到其他的异常');
END;
非预定义异常
为了捕获非预定义的异常,必须先创建一个异常名称,然后将错误编号和刚创建的异常关联起来
DECLARE
V_EMP_REMAINING EXCEPTION;
PRAGMA EXCEPTION_INIT(V_EMP_REMAINING, -02292);
BEGIN
DELETE FROM DEPT WHERE DEPTNO =20;
EXCEPTION
WHEN V_EMP_REMAINING THEN
DBMS_OUTPUT.put_line('违反完整性约束');
WHEN OTHERS THEN
DBMS_OUTPUT.put_line('其他错误');
END;
用户定义异常(raise exception)
而用户定义异常是对数据库的操作不符合用户的业务时,人为定义的异常.这类异常不是数据库的错误,所以没有对应的错误代码.而且数据库在执行时不会主动的认为是异常.
drop table sm_emp;
CREATE TABLE sm_emp(
no char(4),
name char(10),
salary number(6,2),
phone char(8)
);
--insert TOM
INSERT INTO sm_emp VALUES ('001','TOM',999.99,'62543678');
INSERT INTO sm_emp VALUES ('002','TOM2',999.99,'62543678');
INSERT INTO sm_emp VALUES ('003','TOM3',999.99,NULL);
commit;
--如果用户的电话为null,则认为发生一个异常
DECLARE
CURSOR C_SM IS
SELECT * FROM SM_EMP;
PHONE_EXCEPTION EXCEPTION;
V_NAME SM_EMP.NAME%TYPE;
BEGIN
FOR V_EMP IN C_SM LOOP
IF (V_EMP.PHONE IS NULL) THEN
V_NAME := V_EMP.NAME;
RAISE PHONE_EXCEPTION;
END IF;
END LOOP;
EXCEPTION
WHEN PHONE_EXCEPTION THEN
DBMS_OUTPUT.PUT_LINE(V_NAME || '的电话不能为空');
END;
--为了能够记录发生的异常信息,Oracle提供了两个函数
1.SQLCODE
返回错误代码,NUMBER类型
2. SQLERRM
返回与错误代码关联的消息;VARCHAR2类型
建立一个错误日志表(SQLERRM\ SQLCODE)
DROP TABLE ERR_LOG;
CREATE TABLE ERR_LOG (
CODE NUMBER(10),
MESSAGE VARCHAR2(255),
ERRDATE DATE
);
drop table test;
create table test (
id number(5) primary key ,
name varchar2(20),
salary number(8,2)
);
DECLARE
V_CODE ERR_LOG.CODE%TYPE;
V_MESSAGE ERR_LOG.MESSAGE%TYPE;
V_DATE ERR_LOG.ERRDATE%TYPE;
BEGIN
INSERT INTO TEST VALUES (1, 'AAA', 3000);
INSERT INTO TEST VALUES (1, 'BBB', 2000); --这里将会出现主键重复异常
EXCEPTION
WHEN OTHERS THEN
V_CODE := SQLCODE;
V_MESSAGE := SQLERRM;
V_DATE := SYSDATE;
INSERT INTO ERR_LOG VALUES (V_CODE, V_MESSAGE, V_DATE);
COMMIT;
DBMS_OUTPUT.PUT_LINE('发生错误');
DBMS_OUTPUT.PUT_LINE(SQLCODE);
DBMS_OUTPUT.PUT_LINE(SQLERRM);
END;
SELECT * FROM DEPT;
SELECT * FROM ERR_LOG;