Oracle基础

一、基本查询
1、查询结构

(5)select * |列名|表达式 – 表示表的所有列名称;多个列或者多个表达式用逗号分隔

(1)from 表名|结果集 – 查看的对象为表或者结果集

(2)where 条件 – 多个条件用and/or/not 连接

(3)group by 列名 – 多个列用逗号分隔,分组

(4)having 条件 – 一般为聚合函数表达式,分组后过滤,前面要有GROUP BY,多个表达式 and/or/not 连接

(6)order by 列名 [ asc / desc ] – asc 升序(默认),desc 降序

2、条件查询
  • 运算符

    • > 、>= 、< 、<= 、 = 、<=> 、 != 、<>
  • 逻辑运算符

    • and、or、not
  • 模糊运算符

    • like :字符匹配操作可以使用通配符’%‘和’_‘, ‘%’ 代表零个或多个字符,’_’ 代表任意一个字符

    • between and 、 in:查询包含在指定区间内(包含边界)的 值

    • not between and

    • not in

    • is null

    • is not null

    • --查询名字中包含'A'd
      select * from emp where ename like '%A%'
      
      --按时间查询
      select * from emp 
      where hiredate between date'1980-01-01' and date'1982-01-01'
      
  • 分页查询

    • --使用子查询在原表加上一列rownum来实现分页
      SELECT * FROM (SELECT E.*, ROWNUM RN FROM EMP E) T
      WHERE T.RN <5
      
3、子查询
  • 单行子查询:不向外部返回结果,或者只返回一行结果

    • SELECT * FROM EMP WHERE SAL >(SELECT SAL FROM EMP WHERE ENAME = ‘FORD’)
  • 多行子查询:向外部返回零行、一行或者多行结果

    • SELECT * FROM EMP WHERE (DEPTNO,SAL) IN (SELECT DEPTNO,MAX(SAL) FROM EMP GROUP BY DEPTNO)
4、多表查询 (结果集增加列)
  • 内连接
    • 隐式内连接
      • SELECT * FROM EMP E,DEPT D WHERE E.DEPTNO = D.DEPTNO;
    • 显式内连接
      • SELECT * FROM EMP E INNER JOIN DEPT D ON E.DEPTNO = D.DEPTNO;
  • 左外连接
    • 隐式左外连接
      • SELECT * FROM EMP E,DEPT D WHERE E.DEPTNO = D.DEPTNO(+);
    • 显式左外连接
      • SELECT * FROM EMP E LEFT JOIN DEPT D ON E.DEPTNO = D.DEPTNO;
  • 右外连接
    • 隐式左外连接
      • SELECT * FROM EMP E,DEPT D WHERE E.DEPTNO(+) = D.DEPTNO;
    • 显式左外连接
      • SELECT * FROM EMP E RIGHT JOIN DEPT D ON E.DEPTNO = D.DEPTNO;
  • 全外连接
    • SELECT * FROM EMP E FULL JOIN DEPT D ON E.DEPTNO = D.DEPTNO;
  • 交叉连接
  • 隐式交叉连接
    • SELECT * FROM EMP,DEPT
  • 显式交叉连接
    • SELECT * FROM EMP E CROSS JOIN DEPT D
4、集合运算 (结果集增加行)

查询所返回的列数以及列的类型必须匹配,列名可以不同,只有UNION ALL不会去重。其他三个都需要排序后去重,性能比较差

  • 并集运算:将两个查询结果进行合并

    • UNION : 去重,排序
      • SELECT * FROM EMP WHERE DEPTNO = 10 UNION SELECT * FROM EMP WHERE SAL <2000;
    • UNION ALL : 不去重,不排序
      • SELECT * FROM EMP WHERE DEPTNO = 10 UNION ALL SELECT * FROM EMP WHERE SAL <2000;
  • 交集运算:两者共有部分 INTERSECT

    • SELECT * FROM EMP WHERE SAL>1500 INTERSECT SELECT * FROM EMP WHERE DEPTNO =20;
  • 差集运算:前者有后者无 MINSU

    • SELECT * FROM EMP WHERE DEPTNO =20 MINUS SELECT * FROM EMP WHERE SAL>1500
二、数据定义(DDL)

数据定义语言(DDL),包括CREATE(创建)命令、ALTER(修改)命令、DROP(删除对象)、TRUNCATE (删除表数据)命令等。

1、数据类型
  • CHAR(length) --存储固定长度的字符串。参数length指定了长度,如果存储的字符串长度小于length,用空格填充。默认长度是1,最长不超过2000字节。
  • VARCHAR2(length) --存储可变长度的字符串。length指定了该字符串的最大长度。默认长度是1,最长不超过4000字符。
  • NUMBER(p,s) --既可以存储浮点数,也可以存储整数,p表示数字的最大位数(如果是小数包括整数部分和小数部分,p默认是38位),s是指小数位数。
  • DATE --存储日期和时间,存储纪元、4位年、月、日、时、分、秒,存储时间从公元前4712年1月1日到公元后4712年12月31日。
  • NULL
    • 在查询条件中NULL值用IS NULL作条件,非NULL值用 IS NOT NULL做条件
    • 空值跟任何值进行算术运算,得到的结果都为空值
    • 空值跟任何值进行关系运算,得到的结果都为不成立
    • 空值不参与任何聚合运算
    • 排序的时候,空值永远是最大的
2、CREATE(创建)命令
  • -- 语法格式
    create table 表名 (列名1 数据类型, 列名2 数据类型,...);
    
    --加注释
    comment on table 表名 is '表注释名';	
    comment on table 表名.列名1 is '字段注释名';
    
  • --创建一张学生表
    create table student_info                             --学生信息表
          (id number(4) primary key                       --主键
          ,sno number(4)                                  --学号
          ,sname varchar2(20) not null                    --姓名
          ,sex varchar2(2) check(sex = '男'or sex = '女')  --性别
          ,graduated_year number(4)                       --毕业年龄
          ,major varchar2(40)                             --专业
          ,province varchar2(20)                          --省份
          ,dnum number constraint pk_1 references emp(empno) --外键
          );
    
3、ALTER 更改表结构
  • 修改的列没有数据时,可以随便更改类型;修改的列有数据时,只能在同一种类型上增加长度

-- 修改列数据类型
alter table 表名 modify 列名 新的数据类型;
alter table student_info modify sno varchar2(20);
--修改表名
alter table 表名 rename to 新表名;
alter table sutdent_info to student_info_new;
-- 修改列名
alter table 表名 rename column 旧列名 to 新列名;
alter table student_info rename column sno to snumber;
-- 增加列
alter table 表名 add 列名 数据类型;
alter table student_info add age number(3);
-- 删除列
alter table 表名 drop column 列名;
alter table student_info drop column sex;
4、DROP 删除表对象
  • -- 语法格式
    drop table 表名;
    drop table student_info;
    
5、truncate(对象是表)删除表数据
  • 清空整张表数据, 不会将清除的数据写入日志,不能跟where

  • -- 语法格式
    truncate table 表名;
    truncate table student_info;
    
6、表的约束

定义

  • 表的约束是Oracle数据库中应用在表数据上的一系列强制性规则。当向已创建的表中插入数据或修改表中的数据时,必须满足表的完整性约束所规定的条件
  • 例如,学生的性别必须是“男”或“女”,各个学生的学号不得相同等。
  • 在设计表的结构是,应该充分考虑在表上需要施加的完整性约束。
  • 表的完整性约束既可以在创建表时制定,也可以在表创建之后再指定。可以对一个或多个字段进行约束。

分类

  • PRIMARY KEY:主键约束
  • FOREIGN KEY:外键约束
  • CHECK:检查约束
  • UNIQUE:唯一约束
  • NOT NULL:非空约束

创建命令

  • 一张表只有一个主键,主键非空且唯一

  • 外键只能依赖于另一张表的主键(例如EMP表中的DEPTNO依赖于DEPT表中的DEPTNO)

  • 约束不能修改,只能删除重建

  • --添加主键约束
    alter table 主表名 add constraint 约束名 primary key(列名1[,列名2...])
    alter table student_info add constraint p_sno primary key(sno);
    --添加外键约束
    alter table 主表名 add constraint 约束名 foreign key(列名1[,列名2...]) references 从表名(列名1[,列名2...])
    --添加CHECK约束
    ALTER TABLE 表名 ADD CONSTRAINT 约束名 CHECK(条件)
    --添加唯一约束
    ALTER TABLE 表名 ADD CONSTRAINT 约束名 UNIQUE(列名)
    --添加非空约束
    ALTER TABLE 表名 MODIFY 列名 NOT NULL
    
7、创建分区表
  • create table students_info(
    id number(4) primary key,
    name  varchar2(30),
    dnum number,
    datatime date )
    partition by range(datatime)
    (partition part1 values less than(to_date('1999-4-1','yyyy/mm/dd')),
    partition part2 values less than(to_date('1999-4-2','yyyy/mm/dd')),
    partition part3 values less than(to_date('2000-4-2','yyyy-mm-dd'))
    )
    --添加分区
    alter table students_info add partition part4 values less than(to_date('2000-4-3','yyyy/mm/dd'));
    
    --查询分区数据
    select * from students_info partition(part4);
    
8、创建自增序列
  • 因为是首次查询序列的当前值,内存中之前并没有缓存序列的任何值,所以需要先查询 一下序列的下一个值(此时,Oracle会自动缓存已查询的序列值),再查询序列的当前值。

  • create sequence SEQ_ID
    minvalue 1        ---最小者=值
    maxvalue 99999999  ---最大值 
    start with 1   -- 从1开始计数
    increment by 1  -- 每次加几个
    nocache         -- cache/nocache:nocache不缓存。cache缓存。开启缓存,效率高
    ;
    select SEQ_ID.currval from dual;----查看序列当前值
    select SEQ_ID.nextval from dual;--查看序列当前下个值,每次运行语句都会增加
    
9、索引
  • 类型:唯一索引(主键、唯一)、组合索引、基于函数的索引、反向键索引、位图索引
  • 索引失效
    • 查询不规范: IN EXISTS NOT EXISTS NOT IN
    • 全表扫描、算术运算
    • 解决方法:分库分表分区加索引
  • 优点:加快检索速度;减少分组合排序的时间;唯一性;
  • 缺点:占用空间;占用时间;
三、数据操纵(DML)

数据操纵语言(DML),包括INSERT(插入)命令、UPDATE(更新)命令、DELETE(删除)命令、SELECT(查找)命令等

对数据表进行DML操作后,一定要主要对数据进行提交(commit)或者回滚(rollback)

1.INSERT INTO 插入数据到表中
  • --语法格式
    INSERT INTO 表名(列名1,列名2,……) VALUES (值1,值2……)
    INSERT INTO student_2(ename,age,birthday) VALUES ('张三',20,date'2000-12-01');
    INSERT INTO student_2(ename,age,birthday) VALUES ('李四',22,date'1998-12-01');
    
2.UPDATE 更新表数据
  • --语法格式
    UPDATE 表名 SET 列名1=值, 列名2=值,…… WHERE 条件
    
3.DELETE 删除表数据
  • --语法格式
    DELETE FROM 表名 WHERE 条件; 删除的数据可以从日志中找回
    
四、函数
1、字符函数
  • CONCAT(x,y) --连接字符串x和y
  • LENGTH(x) --返回x的长度
  • LOWER(x) --x转换为小写
  • UPPER(x) --x转换为大写
  • TRIM(x) --去掉字符串两端的多余的空格
  • REPLACE(x,old,new) --在x中查找old,并替换为new
  • SUBSTR(x,start[,length]) --返回x的字串,从staart处开始,截取length个字符,缺省length,默认到结尾
2、数字函数
  • ABS(x) x绝对值 ABS(-3)=3
  • MOD(x,y) x除以y的余数 MOD(8,3)=2
  • POWER(x,y) x的y次幂 POWER(2,3)=8
  • ROUND(x[,y]) x在第y位四舍五入 ROUND(3.456,2)=3.46
  • TRUNC(x[,y]) x在第y位截断,直接截取,不四舍五入 TRUNC(3.456,2)=3.45
3、日期函数
  • add_months(d,n) --在某一个日期d上,加上指定的月数n,返。d表示日期,n表示要加的月数

    • select sysdate,add_months(sysdate,3) from dual;
  • last_day(d) --返回指定日期当月的最后一天

    • select sysdate,last_day(sysdate) from dual;
  • round(d,‘fmt’) --返回一个以fmt为格式的四舍五入日期值,d是日期,fmt是格式模型。默认fmt为DDD,即月中的某一天。

  • trunc(d,‘fmt’)对日期的操作,TRUNC与ROUND非常相似,只是不对日期进行舍入,直接截取到对应格式的第一天。
    TRUNC(date[,fmt]):将date截取到fmt指定的形式,如果fmt省略,则截取到最近的

  • select sysdate,         --22/4/25 12:14:34
    round(sysdate),         --22/4/26
    round(sysdate,'day'),   --22/4/24
    round(sysdate,'month'), --22/5/1
    round(sysdate,'year')   --22/1/1
    from dual;
    
4、转换函数
  • 转换为字符串:to_char
    • select to_char(sysdate,'yyyymmdd') from dual;
  • 转换为日期:to_date
    • select to_date('20170731123245','yyyymmdd hh24:mi:ss') from dual;
  • 转换为数字:to_number
    • select to_number('123456') from dual;
5、其他常用函数
  • nvl(x,value) --如果x为空,返回value,否则返回x。

    • select ename,comm,nvl(comm,0)+100 c from emp where sal<2000
  • nvl2(x,value1,value2) --如果x非空,返回value1,否则返回value2

    • select ename,nvl2(comm,comm+100,200) from emp;
  • decode(列|表达式,值1,value1,值2,value2,…,默认值) --当参数的值为判断值1,则返回value1……当参数的值匹配不到时,则返回默认值

    • SELECT E.ENAME, DECODE(E.JOB, 'CLERK','业务员', 'SALESMAN','销售员', 'MANAGER','经理', 'ANALYST','分析员', 'PRESIDENT','总裁') FROM EMP E
  • case when end --适用于多条件判断

    -- 第一种语法
    CASE 参数
    WHEN 判断值1 THEN 返回值1
    WHEN 判断值2 THEN 返回值2
    …… 
    ELSE 默认值 END
    --例
    Select E.Ename,
           case e.job
           WHEN 'CLERK' THEN'业务员'
           WHEN 'SALESMAN' THEN'销售员'
           WHEN 'MANAGER' THEN'经理'
           WHEN 'ANALYST' THEN'分析员'
           WHEN 'PRESIDENT' THEN'总裁'
           END
    FROM EMP E
    --第二种语法
    CASE
    WHEN 条件1 THEN 返回值1
    WHEN 条件2 THEN 返回值2
    ……
    ELSE 默认值 END
    --例
    select e.*,
           case 
           when sal>3000 then '工资很高'
           when sal>1000 then '工资一般'
           else '工资很低'
           end
    from emp e
    
  • 行列转换

    • --创建表 emp014
      create table emp014(id number,q number,amt number)
      id   q   AMT
      01   1   100
      01   4   100
      02   1   200
      01   2   110
      02   2   150
      02   3   100
      01   3   130
      02   4   200
      --创建表 emp015
      create table emp015(y number,q1 number,q2 number,q3 number,q4 number)
      insert into emp015( Y,Q1,Q2, Q3, Q4)values(01,100,110,130,100);
      insert into emp015( Y,Q1,Q2, Q3, Q4)values(02 ,200,150,100,200);
      
    • --emp014 转 emp015 
      with aa as
      (select id y,
      max(case when q =1 then amt end) q1,
        max(case when q =2 then amt end) q2,
          max(case when q =3 then amt end) q3,
            max(case when q =4 then amt end) q4
              from emp014 
              group by id)
      select * from aa
      
    • --emp015 转 emp014
      with aa as 
      (select  id, 
              max(case when q=1 then amt end) as q1,
                max(case when q=2 then amt end) as q2,
                  max(case when q=3 then amt end) as q3,
                    max(case when q=4 then amt end) as q4
      from emp014
      group by id  )
      select id , 1 as q1,q1 as amt
      from aa union all
      select id , 2 as q1,q2 as amt
      from aa union all
      select id , 3 as q1,q3 as amt
      from aa union all
      select id , 4 as q1,q4 as amt
      from aa
      
  • 伪列 rowid

    • 表中的每一行在数据文件中都有一个物理地址,ROWID伪列返回的就是该行的物理地址
    • 使用ROWID可以快速的定位表中的某一行
    • ROWID值可以唯一的标识表中的一行
    • DELETE FROM EMP E WHERE ROWID NOT IN (SELECT MIN(ROWID) FROM EMP E GROUP BY EMPNO); ROLLBACK; – 去重,删除重复数据
  • 伪列rownum

    • 在查询的结果集中,ROWNUM为结果集中每一行标识一个行号,第一行返回1,第二行返回2,以此类推。
    • 通过ROWNUM伪列可以限制查询结果集中返回的行数
    • rownum 必须从1开始查询
    • select ename,sal,deptno,rn from (select e.*,rownum rn from emp e where rownum<8) where rn >2; --查询emp表第3-7列数据
6、分析函数
  • 分析函数与聚合函数不同,聚合函数用group by分组,每个分组返回一个统计值,而分析函数采用partition by 分组,并且每组每行都可以返回一个统计值

  • 函数()over(partition by 字段 order by 字段 )

  • --求每个部门工资高于部门平均工资的员工数量
    select deptno,count(1)
    from (SELECT E.*, AVG(E.SAL) OVER(PARTITION BY E.DEPTNO) AVG FROM EMP E) A 
    where sal>avg
    group by deptno
    
  • MAX(),MIN(),SUM(),AVG(),COUNT() --加了ORDER BY 是累计求值

  • 排序分析函数 100 90 90 80 70

    • row_number() --派号码排序, 1 2 3 4 5

    • rank() --跳跃排序派号码 1 2 2 4 5

    • dense_rank() --不跳跃排序派号码 1 2 2 3 4

    • -- 各部门工资排名前3的员工的所有信息
      select *
      from
      (select e.* ,dense_rank()over(partition by deptno order by sal desc) rn
      from emp e)
      where rn<4
      
  • 聚类分析函数

    • MAX(),MIN(),SUM(),AVG(),COUNT() --加了ORDER BY 是累计求值

    • select t.*,sum(sal)over(partition by deptno order by empno desc) sum_sal from emp t;
      select t.*,avg(sal)over(partition by deptno) avg_sal from emp t;
      select t.*,max(sal)over(partition by deptno) max_sal from emp t;
      select t.*,min(sal)over(partition by deptno) min_sal from emp t;
      select t.*,count(sal)over(partition by deptno) count_sal from emp t;
      
  • 最大值、最小值分析函数

    • --语法格式
      MAX()/MIN() KEEP (DENSE_RANK FIRST/LAST  ORDER BY  )
      --奖金最大、最小
      select min(empno) keep(dense_rank first order by sum(NVL2(comm,comm+500,200)) desc) first,
                 min(empno) keep(dense_rank last order by sum(NVL2(comm,comm+500,200)) desc) last
      from emp
      group by empno;
      --工资最大、最小
      select max(empno) keep(dense_rank first order by sum(sal) desc) first,
      max(empno) keep(dense_rank last order by sum(sal) desc) last
      from emp
      group by empno; 
      
  • 按层次查询

    • 平均分配,如果不能平均分配,则较小层次分配额外的行,并且各个层次能放的行数最多相差1
    • select t.*, ntile(5) over(order by sal desc) ntile from Emp t
  • 位移分析函数

    • --LAG(取值字段,N)OVER(ORDER BY  排序字段)    --N 表示步长,表示取前 N 个的结果
      --LEAD(取值字段,N)OVER(ORDER BY  排序字段)   --N 表示步长,表示取后 N 个的结果
      select t.*,
                 lag(sal,1,0) over(order by sal) a ,
                 lead(sal,2,1) over(order by sal) b ,
                 lag(sal,1) over(partition by deptno order by sal) as lag,
                 lead(sal,1) over(partition by deptno order by sal) as lead 
                 from emp t;
      
五、程序块
1、语法结构
  • DECLARE  
    	V_EMPNO  number;   -- 声明变量
    	v_emp varchar2(20):='hello world';  --声明变量并赋值
    	v_num varchar2(20):=&请输入;         --用户输入变量。
    	V_ENAME  EMP.ename%TYPE;    -- 列名%type ,指定变量的数据类型。
    	V_NSAL   EMP%ROWTYPE;	
    BEGIN 
      V_EMPNO  :=&请输入员工编号 ;
      SELECT E.ename ,12*( E.sal + NVL(E.comm,0))
      INTO V_ENAME ,V_NSAL.sal
      FROM EMP E
      WHERE E.empno = V_EMPNO;
      IF V_NSAL.DEPTNO  = 10       --IF判断
         THEN V_NSAL.SAL := V_SAL1  *1.3 ;
         ELSIF V_NSAL.DEPTNO = 20 
         THEN V_NSAL.SAL := V_SAL1 *1.2 ;
      ELSE 
         V_NSAL.SAL := V_SAL1 ;
      END IF;
      dbms_output.put_line(V_ENAME|| '的年薪是: '||V_NSAL.sal);
    EXCEPTION     ----异常处理,比如输入1010编号,就会输出下面的没有这个编号
      WHEN no_data_found THEN  ---是否有数据
        dbms_output.PUT_LINE('没有这个编号');   ----打印语句
    END ; 
    
2、循环
  • -- 一写法
    DECLARE 
       X  NUMBER(5) := 1 ;
       Y  NUMBER(5) := 0 ;
    BEGIN 
      LOOP 
       Y := X + Y;
       X := X +1 ;
        IF X >10  THEN    -- 当x>10 才会退出循环
          EXIT;  -- 退出循环  
        END IF;
        DBMS_OUTPUT.PUT_LINE(X||' '||Y);
         END LOOP;
      DBMS_OUTPUT.PUT_LINE('1~10的累加结果为:'||Y);
    END ;
    
    -- 二写法
    DECLARE
       X   NUMBER(5) := 0;
       Y   NUMBER(5) := 0;
    BEGIN
      WHILE X < 10 LOOP    ---判断并做循环
        X := X + 1;
        Y := X + Y;
       -- DBMS_OUTPUT.PUT_LINE(X||' '|| Y);
       -- DBMS_OUTPUT.PUT_LINE('RESULT IS :' || Y);
      END LOOP;
      DBMS_OUTPUT.PUT_LINE('RESULT IS :' || Y);
    END;
    
    -- 三写法
    DECLARE
      X    NUMBER(3) := 0;
      Y    NUMBER    := 0;
    BEGIN
      FOR X IN 1.. 10 LOOP   --- 判断现在1--10之间就做循环;
        Y := Y + X;
      
      END LOOP;
      DBMS_OUTPUT.PUT_LINE('RESULT IS :' || Y);
    END;
    
3、动态SQL
  • DDL(数据定义语言,Data Definition Language)不能直接在程序块中运行,需使用动态SQL

  • Oracle中提供了Execute immediate [ɪˈmiːdiət] 语句来执行动态SQL

  • --语法
    DECLARE
      V_SQL VARCHAR2(2000) := '';
      -- V_NUM NUMBER;
    BEGIN
      --EXECUTE IMMEDIATE用法1:立刻执行
      V_SQL := 'create or replace view mytest as select* from emp';
      EXECUTE IMMEDIATE V_SQL;
      或者  
      EXECUTE IMMEDIATE 'create or replace view mytest as select* from emp';
    
    END PRO_TEST;
    
六、游标
1、定义及作用
  • 游标是SQL的一个内存工作区,由系统或用户以变量的形式定义。作用是用于临时存储从数据库中提取的数据块。

  • 指定结果集中特定行的位置 >>基于当前的结果集位置检索一行或连续的几行>>在结果集

2、显式游标的写法
DECLARE
  CURSOR C_EMP -- 定义一个游标   C_EMP
  IS --游标指向 一个结果集  IS 后面的查询结果集
    SELECT E.EMPNO, E.ENAME, E.JOB, E.SAL
      FROM EMP E
     WHERE E.JOB = 'MANAGER';

  V_CEMP C_EMP%ROWTYPE; -- 定义一个游标的变量
BEGIN
  OPEN C_EMP; -- 打开游标
  LOOP
    FETCH C_EMP  ---读取游标的结果集
      INTO V_CEMP; -- 将游标结果集中的数据 FETCH INTO 到游标的变量中,每次变量只接收一行数据
    EXIT WHEN C_EMP%NOTFOUND; -- 当游标结果集中找不到数据时,退出循环
    DBMS_OUTPUT.PUT_LINE(V_CEMP.ENAME || ' ' || V_CEMP.EMPNO || '  ' ||
                         V_CEMP.JOB || '  ' || V_CEMP.SAL);
  END LOOP;
  CLOSE C_EMP; -- 关闭游标 
END;
3、隐式游标的写法
DECLARE
  V_JOB  VARCHAR2(10)  :=&输入一个岗位;
  CURSOR C_EMP -- 定义一个游标   C_EMP
  IS --游标指向 一个结果集  IS 后面的查询结果集
    SELECT E.EMPNO, E.ENAME, E.JOB, E.SAL
      FROM EMP E
     WHERE E.JOB = V_JOB;

  V_CEMP C_EMP%ROWTYPE; -- 定义一个游标的变量
BEGIN
  FOR V_CEMP IN C_EMP LOOP    -- 隐式游标,不需要打开和关闭游标,直接通过for循环遍历游标的结果集
  
    DBMS_OUTPUT.PUT_LINE(V_CEMP.ENAME || ' ' || V_CEMP.EMPNO || '  ' ||
                         V_CEMP.JOB || '  ' || V_CEMP.SAL);
  END LOOP;
END;
七、存储过程和自定义函数
1、区别
  • 功能不同,存储过程主要用于数据同步,而自定义函数主要用于做计算
  • 调用方式不同,存储过程一般使用 BEGIN END 调用,而自定义函数是放在 SELECT 语句后面调用的
  • 返回值不同, 存储过程不需要返回结果,而自定义函数必须返回一个计算结果
  • 定义时不同,存储过程不需要指明返回值的数据类型,自定义函数必须指定返回值的数据类型
2、创建存储过程
  • --语法
    CREATE OR REPLACE PROCEDURE SP_过程名(参数1 [IN|OUT|IN OUT] 数据类型,参数2 [IN|OUT|IN OUT] 数据类型……)
    IS   /*|AS*/
    BEGIN  
      执行体
    END ;
    
3、创建自定义函数
  • CREATE OR REPLACE FUNCTION FUN_函数名(参数1 数据类型,参数2[IN|OUT|IN OUT] 数据类型……)
    RETURN 返回的数据类型,不写长度
    IS   /*|AS*/
    BEGIN  
       执行体
       RETURN  结果; --里面必须要有一个RETURN子句
    END ; 
    
    
4、参数类型
  • IN 类型为 输入参数,默认参数,可以不写。【不能重新赋值】

  • OUT 类型为 输出参数。【可以被重新赋值,但是不会接受输入的参数】

  • IN OUT 类型为输入输出参数,既可以接受传入的值,也可以作为输出参数输出结果。【可以被重新赋值】

  • 在过程或者函数中定义了参数,就必须传值

  • IN 类型的参数,不能通过变量传值,必须直接传值[可以是数字 字符 日期]

  • 带了 OUT 类型的参数,必须通过变量传参,无论是 OUT 或者 IN OUT

  • OUT 类型的参数,虽然不接收传入的参数,但是跟第一条罗一样,必须给它一个变量,哪怕变量的值是空的

  • IN OUT 类型的参数,也需要通过变量传参,同时 IN OUT 类型的参数,可以像变量一样输出或者被重新赋值

5、增量抽取
  • 只需抽取新增的或修改的数据。此方法性能好,但容易遗漏。目标表中有,但是源表中没有的话,更新不了。以时间戳取增量,对源表删除的数据无能为力。

  • 通过源表更新目标表的时候,通常是先判断 源表中的数据在目标表中是否存在(通过主键判断),如果存在,那么就用源表的数据,更新目标表的数据,如果不存在,那么就直接从源表中插入数据到目标表。

  • --  使用游标进行增量数据同步。       
    CREATE OR REPLACE PROCEDURE SP_EMP_BACK2 IS
      CURSOR C_EMP IS
      SELECT * FROM EMP;
      V_EMP  C_EMP%ROWTYPE;
      V_CT   NUMBER(5);
      V_MARK NUMBER(5);
    BEGIN
      --初始化变量
      V_MARK := SEQ_ID.NEXTVAL;
      FOR V_EMP IN C_EMP LOOP
        SELECT COUNT(1) INTO V_CT 
    	FROM EMP_BACK1 
    	WHERE EMPNO = V_EMP.EMPNO; -- 比较字段,通过主键 empno 判断员工信息是否存在于目标表
        -- 判断源表中的数据在目标表中存在,存在则用源表中的数据更新目标表
    	---  V_MARK := SEQ_ID.NEXTVAL;  放在循环里面就会递增
        IF V_CT = 1 THEN
          UPDATE EMP_BACK1 M
             SET --  这里更新的时候,不能更新 比较字段。
                 M.ENAME     = V_EMP.ENAME,
                 M.JOB       = V_EMP.JOB,
                 M.MGR       = V_EMP.MGR,
                 M.HIREDATE  = V_EMP.HIREDATE,
                 M.SAL       = V_EMP.SAL,
                 M.COMM      = V_EMP.COMM,
                 M.DEPTNO    = V_EMP.DEPTNO,
                 M.DATA_DATE = SYSDATE,
                 M.MARK      = V_MARK
           WHERE EMPNO       = V_EMP.EMPNO;
          -- 判断源表中的数据,在目标表中不存在,那么就插入数据
        ELSIF V_CT = 0 THEN
          INSERT INTO EMP_BACK1
            (EMPNO,
             ENAME,
             JOB,
             MGR,
             HIREDATE,
             SAL,
             COMM,
             DEPTNO,
             DATA_DATE,
             MARK)
          VALUES
            (V_EMP.EMPNO,
             V_EMP.ENAME,
             V_EMP.JOB,
             V_EMP.MGR,
             V_EMP.HIREDATE,
             V_EMP.SAL,
             V_EMP.COMM,
             V_EMP.DEPTNO,
             SYSDATE,
             V_MARK);
        END IF;
      END LOOP;
      COMMIT;
    END;
    
  • --使用 merge into 更新目标表  EMP_BACK1
    CREATE OR REPLACE PROCEDURE SP_EMP_BACK3 IS
    
    BEGIN
    MERGE INTO EMP_BACK1 M     
    USING (SELECT * FROM EMP) E  -- 使用 () 内的查询结果
    ON (M.EMPNO = E.EMPNO)    -- 通过 on ()里面的条件比较
    -- 判断源表中数据在目标表中存在,则更新
      WHEN MATCHED THEN            -- 当 on 后面的条件比较 匹配上数据,then 更新
        UPDATE
           SET -- 比较字段 empno  不能更新
               M.ENAME     = E.ENAME,
               M.JOB       = E.JOB,
               M.MGR       = E.MGR,
               M.HIREDATE  = E.HIREDATE,
               M.SAL       = E.SAL,
               M.COMM      = E.COMM,
               M.DEPTNO    = E.DEPTNO,
               M.DATA_DATE = SYSDATE,
               M.MARK      = V_MARK      -- 这里不加分号 ,分号表示程序执行到这里结束
        -- 判断源表中的数据在目标表不存在,则插入
      WHEN NOT MATCHED THEN         -- 当 on 后面的条件比较 匹配不到数据,then 插入
        INSERT 
          (M.EMPNO,
           M.ENAME,
           M.JOB,
           M.MGR,
           M.HIREDATE,
           M.SAL,
           M.COMM,
           M.DEPTNO,
           M.DATA_DATE,
           M.MARK)
        VALUES
          (E.EMPNO,
           E.ENAME,
           E.JOB,
           E.MGR,
           E.HIREDATE,
           E.SAL,
           E.COMM,
           E.DEPTNO,
           SYSDATE,
           V_MARK);
      COMMIT;
    END;
    
    --- 调用存储过程 
    BEGIN 
      SP_EMP_BACK3;
    END ;
    
6、全量抽取
  • 将目标表的数据全部删除,再将源系统的数据全部插入目标表。

  • 此方法保证了数据的质量,但是对于数据量大的表而言,性能太差。

  • CREATE OR REPLACE PROCEDURE SP_EMP_BACK2
    IS 
       -- V_MARK  NUMBER(10);
    BEGIN 
      -- 初始化变量
      -- V_MARK  :=  seq_test2.nextval;
      
      -- 使用动态SQL 清空目标表,让存储过程支持重跑
      /*EXECUTE IMMEDIATE 'TRUNCATE TABLE EMP_BACK1 ' ;  */-- ①  删除效率比较高,但是必须使用动态SQL执行
    
       DELETE FROM EMP_BACK2 WHERE 1 = 1;                   -- ②  直接使用delete 语句删除,记得加条件。
      --EXECUTE IMMEDIATE 'TRUNCATE TABLE EMP_BACK1' ; 
      INSERT INTO EMP_BACK2(empno,
                            ename,
                            job,
                            mgr,
                            hiredate,
                            sal,
                            comm,
                            deptno,
                            DATA_DATE
                            -- MARK
    						)
                         SELECT 
                         empno,
                         ename,
                         job,
                         mgr,
                         hiredate,
                         sal,
                         comm,
                         deptno,
                         SYSDATE
                         --V_MARK
                         FROM EMP_BACK1  ; 
       COMMIT;                                
    END ;
    
九、包
1、创建包头
  • CREATE OR REPLACE PACKAGE PK_TEST1
    IS
    procedure sp_emp(p_sal1 in number,p_sal2 in out  number);
    
    END PK_TEST1;
    
    
    
2、创建包体
  • CREATE OR REPLACE PACKAGE BODY PK_TEST1 
    IS
    procedure sp_emp(p_sal1 in number,p_sal2 in out  number)
    is
      v_emp emp%rowtype;
    begin
      select ename,sal,hiredate
      into  v_emp.ename,v_emp.sal,v_emp.hiredate
      from emp
      where  sal>p_sal1 and sal<=p_sal2;
      dbms_output.put_line('员工姓名:'||v_emp.ename||' 工资:'||v_emp.sal|| 
                           ' 入职日期:'||to_char(v_emp.hiredate,'yyyy/mm/dd'));
    end;
    
    
    END PK_TEST1;
    
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值