• oracle 开发手册


    开发中快速查找的好帮手,覆盖面全,查找方便,有实例:

    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;
  • 相关阅读:
    Centos7上安装docker
    centos如何查看某一目录下每个目录或文件占用磁盘空间大小
    大学毕设参考文献在什么网站搜索
    Spring MVC使用jstl 标签c:forEach 遍历输出双层嵌套List的数据
    JQuery选择器通过click事件获取当前点击对象的id,name,value属性
    利用ajax和servlet实现前后端数据交互(json)
    Java软件工程的弹幕调试原则
    周周总结——时时更新(第4学期,第8周)
    团队软件的NABCD——星遇
    周周总结——时时更新(第4学期,第7周)
  • 原文地址:https://www.cnblogs.com/kingwangzhen/p/1820758.html
Copyright © 2020-2023  润新知