Oracle

基础 #

安装
    运行services.msc
    找到OracleServiceORCL 服务 (最后是数据库名)改为手动
    sqlplus / as sysdba 运行oracle测试是否安装成功
    解锁scott用户
        sqlplus / as sysdba
        alter user scott account unlock;
        alter user scott identified by tiger;
启动
    OracleServiceORCL
    OracleOraDb11g_home1TNSListener
端口
    1521    # 停了以后再开会变
权限
    系统权限:创建数据库,创建用户
    对象权限:表操作
    sys create database权限
    system  没有create database权限
    scott
    hr
    角色  # 超管以角色进入,普通用户输入用户名密码
        dba
        dbaoperator
服务器的组成
    oracle数据库
        存储位置 oracle安装位置/oradata/
            每个数据库文件夹中有多个二进制文件(*.dbf)
    oracle实例(内存概念)
        一个数据库进程可以有多个实例
        一个实例可以有多个数据库进程和一个内存池(缓存)    # 不同于windows进程,每个进程对应一个用户访问
        服务名为OracleService数据库名
    集群(多实例)
        连接失败转移
        负载均衡
连接类型
    normal sysdba sysoper
分布式
认证
    开发技术认证
    Java认证
                数据库开发语言SQL和PL/SQL认证
    数据库技术认证
        OCM【大师】
        OCP【专家】
        OCA【初级】
    中间件技术认证
        OracleServer认证,WEB服务器认证
    专业领域技术认证
        ERP
        CRM
        HR
        OA
支持的事务隔离
    只支持读提交与序列化两种
版本
    8i 9i 10i   # i是internet
    11g         # 基于jdk6, g是grid网格(一个请求多个服务器运行)
    12c         # c是cloud
SQL99标准     # 不区分大小写
    PLSQL是Oracle对SQl99标准的扩展
    结构代查询语言
        DML select,insert,update,delete
        DDL create table,alter table, drop table
        DCL grant, revoke
        TCL(杜撰) commit, rollback,rollback to savepoint
加载过程
     启动oracle服务
        oradim -startup -sid 数据库名                 # 相当于windows服务中启动该服务,会从windows注册表中加载配置
        oradim -shutdown -sid orcl -shuttype srvc    # 停止oracle 服务
        普通登录
            sqlplus system/asdf                 # 从注册表中查找默认的数据库名称进行登录
            sqlplus system/asdf@orcl            # 指定数据库名登录,必须有监听的时候该命令可以执行,监听的进程是独立于oracle之外的进程
        管理员登录
            sqlplus / as sysdba                 # windows管理员的身份进行登录,不需要用户名密码,可以在配置文件中禁用它
        无连接登录
            sqlplus /nolog                      #用来设置sql/plus
    启动实例
        cmd> sqlplus / as sysdba                    # 连接到数据库的空闲实例
        sql> select status from v$instance          # 查看实例状态
        sql> shutdown abort                         # 立即终止当前的实例,实例结束后用户仍然登录状态,但没有连接实例
        sql> startup nomount                        # 启动默认实例(不加载数据库)
        sql> startup open                           # 启动、装载、打开默认的数据库
        cmd> set oracle_sid=orcl                    # 在windows下设置oracle的默认登录数据库

        启动过程
            加载参数文件database/init数据库名
            分配sga (system global area)到内存空间,用于缓存数据库信息
            创建后台进程
    挂载数据库
        sql> startup mount

        挂载过程
            装载数据库文件夹中的文件    # sga中有已经初始化了ctl文件的路径,通过ctl文件装载数据库
            ctl(从中得到数据库文件名) -> dbf
                    # 3种数据库文件: .ctl(控制文件) .dbf(数据库文件) .log(日志文件)
            这时,数据库还是不能访问,但是管理员可以访问(debug数据库)
    打开数据库       # 使外界可以访问
        sql> alter database 数据库名 open                # 数据库文件夹中记录日志

类型 #

varchar2(7)        # 可变字符串
char                # 字符
number(precision [, scale])    # presision是有效位(从左边第一个不为0的数算起,小数点和负号不计入有效位数),scale是精确位(正数为小数的精确位,负数为整数的精确位)
constant number                # 常量

数据字典 #

dual                # dual表只有一行,用于执行一些特殊操作
dba_sequences
        select SEQUENCE_OWNER,SEQUENCE_NAME from dba_sequences
                # 查询序列
dba_users
        select username,password from dba_users;
                # 查询用户和密码
tab
        select * from tab
                # 查看自己的可用表,视图,等
v$session
        select count(*) from v$session
                # 查看当前数据库的连接

存储过程 #

过程与函数
    返回值
        函数有且只一个返回值
        过程没有或多个返回值用(out参数)
        过程可返回函数,函数只能返回值或表对象
    过程和函数不能重名
    函数可嵌入sql执行,过程不行
语法
    IF .. THEN
            null;
    ELSIF .. THEN
            null;
    ELSE
            null;
    END IF;

    select into给变量赋值

    raise 异常名   # 抛异常
函数
    create or replace function calculate_score
    ( cat in varchar2
    , score in number
    , weight in number
    ) return number as
    begin
    return null;
    end calculate_score;


    例子1     # in参数 和返回值的函数
        create or replace function findEmpIncome(pempno in number) return number
        as
            income number(10);
        begin
            select sal*12+NVL(comm,0) into income from emp where empno = pempno;
            return income;
        end;
        /
    例子2     # in、out 参数和返回值的函数
        create or replace function findEmpNameAndSal (pempno in number, psal out number)return varchar2
        as
        pename emp.ename%type;
        begin
        select ename , sal into pename,psal from emp where empno = pempno;
        return pename;
        end;
        /
        执行
        declare
        psal emp.sal%type;
        pename emp.ename%type;
            begin
                pename := findEmpNameAndSal(7788,psal);
                dbms_output.put_line(pename||'的工资是'||psal);
            end;
            /
过程
    # 参数默认in
    create or replace procedure add_evaluation
    ( evaluation_id in number
    , employee_id in number
    , evaluation_date in date
    , job_id in varchar2
    , manager_id in number
    , department_id in number
    ) as            # as 变量 类型 (值范围);
    begin
    null;
    exception
        when others then
            rollback;
    end add_evaluation;

    exec  add_evaluation

    drop procedure add_evaluation;

    例子1                # in 参数的使用
        create or replace procedure raiseSalary(pempno in emp.empno%type)
        as
        begin
            update emp set sal = sal * 1.1 where empno = pempno;
        end;
        /
        exec raiseSalary(7369);
    例子2                # out参数的使用                select into
        create or replace procedure findEmpNameAndSalAndJob (pempno in emp.empno%type,
        pename out emp.ename%type,
        pjob out emp.job%type,
        psal out emp.sal%type)
        as
        begin
            select ename,job,sal into pename,pjob,psal from emp where empno = pempno;
        end;
        /
        declare
            pename emp.ename%type;
            pjob  emp.job%type;
            psal  emp.sal%type;
        begin
            findEmpNameAndSalAndJob(7788,pename,pjob,psal);
            dbms_output.put_line('7788号员工的姓名是'||pename||',职位是'||pjob||',薪水是'||psal);
        end;
        /
包
    声明
        create or replace PACKAGE emp_eval AS
        PROCEDURE eval_department(department_id IN NUMBER);
        FUNCTION calculate_score(evaluation_id IN NUMBER , performance_id IN NUMBER)
            RETURN NUMBER;
        END emp_eval;
    定义
        CREATE OR REPLACE PACKAGE BODY emp_eval AS

        PROCEDURE eval_department(department_id IN NUMBER) AS
        BEGIN
            /* TODO implementation required */
            NULL;
        END eval_department;

        FUNCTION calculate_score(evaluation_id IN NUMBER , performance_id IN NUMBER)
            RETURN NUMBER AS
        BEGIN
            /* TODO implementation required */
            RETURN NULL;
        END calculate_score;

        END emp_eval;

触发器 #

分类
    # select 没有触发器
    语句级insert / delete           # 只在语句执行时触发
    行级(for each row)update        # 每一行都执行,出错后不会继续执行并且回滚
语法
    create [or replace] trigger 触发器名
    {before|after}
    {insert|delete|update of 列名}
    on 表名
    for each row
    plsql块

    行级触发器中
        :new 代表更新后那一行整行的值
        :old 则是旧的整行的值
        update of 列名 for each row是连起来用的。语句级触发器没有for each row

    drop trigger 触发器名

例子(语句级)
    # 当在休息日与非9点到17点之间的时候,禁止对emp表进行插入操作
    create or replace trigger securityTrigger
    before insert on emp
    declare
    pday varchar2(10);
    phour number(20);
    begin
    select to_char(sysdate,'day') into pday from dual;
    select to_char(sysdate,'hh24') into phour from dual;
    if(pday in ('星期六','星期日') or (phour not between 9 and 17)) then
    raise_application_error('-20666','禁止操作');
    end if;
    end;
    /
例子(行级):当对每一行的工资进行修改的时候,新的工资不能小于原来的工资
    create or replace trigger checkSalayTrigger
    before update of sal on emp for each row
    begin
    if :new.sal < :old.sal then
    raise_application_error('-20555','工资不能减少');
    end if;
    end;
    /

函数及常量 #

常量
    sysdate 当前日期

单行函数
    nvl(comm,0)                        # 替换comm列中的空值为0
    nvl2(comm,comm,0)                # 替换comm列中的空值为0,非空时为comm
    select lower('AAA') from dual;                        # 取小写
    upper('')                # 取大写
    initcap('www.itcast.cn')        # 每一段字符串的首字符大写
    concat('','')                        # 拼接字符串
    substr('itcast',1,3)        # 取第一个和第三个字符,脚标从1开始
    length('')                # 字符的长度
    lengthb('')                # 字节的长度
    instr('itcast','t')      # 查找第一个匹配字符串的位置,区分大小写
    lpad('a',10,'*')                # 左填充,一直到10位,也可以截取左边的10位字节(并非字符)
    rpad('a',10,'*')                # 右填充,一直到10位,也可以截取右边的10位字节(并非字符)
    trim('x' from 'xxhelloxsx')                # 增强版trim(),去掉字符串中的所有'x'
    replace('hello','l','o')                # 替换字符串中的'l' 为 'o'
    round(3.45,2)                # 四舍五入,2代表小数点后的位数  -1代表个位,-2代表十位
            round(sysdate,'month')      # 四舍五入到月(15号前后判断)
    round(sysdate,'year')      # 四舍五入到年(6月30号前后判断)
    trunc(3.142,1)                # 截取小数点后1位
    mod(10,3)                        # 10mod3取余
    nullif(10,20)                # 比较两个数值,相同时返回空,不同时返回第一个数值
    (job,'analyst',sal + 1000,"manager",sal + 800, sal + 400) "新工资" from emp;
    decode(...)函数:例子
        select ename "姓名" , sal "原工资" , decode(job, 'analyst',sal + 1000, 'manager',sal + 800, sal + 400) "新工资" from emp;
        select ename "姓名" , sal "原工资" , case job when 'analyst' then sal + 1000 when 'manager' then sal + 800 else sal + 400 end "新工资" from emp
            #sql99语法
    日期函数
        sysdate +/- 1                # 增加或减少1天
        sysdate - hiredate                # 日期减日期,得到天数
        to_char(sysdate,'yyyy-mm-dd hh24:mi:ss')                # 格式化显示时间
        months_between('12-2月-13',sysdate)        # 日期之间的月数
        add_months(sysdate,1)                # 给当前日期增加1个月
        next_day(sysdate,'星期一')        # 下一个星期一的日期
        last_day(sysdate)                        # 反回这个月的最后一天
    类型转换:
        隐式转换                # 要求 1,格式正确。2,内容合理
            字符串与Date,number相互转换
        显式转换
            到字符串:
                日期到字符串:to_char(sysdate,'yyyy "年" mm "月" dd "日" day hh12:mi:ss:am')
                    # day是星期、hh12是12进制计时(hh24代表24进制计时)、am是个变量,上午时是am,下午是pm
                    ## 双引号中的内容直接显示
                货币值到字符串:to_char(1234,'L9,999')
                    # L可以小写,代表当地货币,','可以省略
            字符串到Date: to_date('1980-12-17','yyyy-mm-dd')
            字符串转换到数字:        to_number('123')
多行函数    # 多行函数分为接收多个输入,返回一个输出。
           ## 多行函数本身不会统计值为null的记录
           ## 多表的多行函数的统计多会用到group by ,因为where 只是筛选出了不符合条件的部分
           ## ,剩下的部分分列在不同的表中,只有按照列名分组以后才能按列名执行多行函数
    having
    count([distinct] deptno)                # 统计deptno【不重复的】记录数
    sum(sal)                # 统计数值的和
    avg(sal)                # 统计平均值
    max(sal)                # 统计最大值,可以用在日期上
    min(sal)                # 统计最小值 ,可以用在日期上

游标 #

分类
    显示
        cursor is
    隐式
        执行insert, delete, update,返回单条记录的select时,pl/sql自动定义

闪回技术 #

可以闪回的操作
        dml 
        ddl操作(回收站中存在)

闪回记录
        1.获取当前时间与改变号
            select to_char(sysdate,'yyyy-mm-dd:mi:ss') "系统时间", timestamp_to_scn(sysdate) "系统改变号" from dual;
                    # scn 是 系统改变号,每秒钟都会变,oracle根据它来进行闪回,如1216687
    2.给普通用户授于闪回的权限
        grant flashback any table to scott;
    3.alter table emp enable row movement;      # 更改rowid号可以更改
    4.flashback table emp to SCN 1216687;      # 闪回表到时间改变号
        # 如果没有做第三步,这里会出错 ,原因是 还原表记录时,递增的rowid后面不能插入数据。在rowid之间插入数据必须改变rowid的状态为可以'移动',即还原记录后面的表记录可以'移动'。 

闪回表(回收站中才可以)
        1.确定回收站中有该表,可以用show recyclebin命令查看
        2.flashback table emp to before drop;
                # flachback table "BIN$Pu9C2euHQ96xySmn08e5aQ==$0" to before drop;        也是可以的
        3.如果回收站中有两个相同的表需要闪回时,设置一个表名
                flashback table emp to before drop rename to newemp;
                        # 两张同名的表首先恢复最近删除的表

审计 #

审计(Audit)
        用于监视用户所执行的数据库操作,并且Oracle会将审计跟踪结果存放到OS文件(默认位置为$ORACLE_BASE/admin/$ORACLE_SID/adump/)
        或数据库(存储在system表空间中的SYS.AUD$表中,可通过视图dba_audit_trail查看)中。
        默认情况下审计是没有开启的。
        不管你是否打开数据库的审计功能,以下这些操作系统会强制记录:用管理员权限连接Instance;启动数据库;关闭数据库。
        
两个参数                # cmd> set xxx=xx
                        ## 这两个参数是static参数,需要重新启动数据库才能生效。
        Audit_sys_operations
                默认为false,当设置为true时,所有sys用户(包括以sysdba,sysoper身份登录的用户)的操作都会被记录,
                audit trail不会写在aud$表中,因为数据库还没有启动,conn / as sysdba 这样的命令是没法记录的
                windows平台记录在windows的事件管理中,unix平台记录在audit_file_dest参数指定的文件中
        Audit_trail
                None:是默认值,不做审计;
                DB:将audit trail 记录在数据库的审计相关表中,如aud$,审计的结果只有连接信息;
                DB,Extended:这样审计结果里面除了连接信息还包含了当时执行的具体语句;
                OS:将audit trail 记录在操作系统文件中,文件名由audit_file_dest参数指定;
                XML:10g里新增的。
                
审计级别
        Statement(语句)、Privilege(权限)、object(对象)。
        
        Statement        按语句来审计,比如audit table 会审计数据库中所有的create table,drop table,truncate table语句。
        Privilege        按权限来审计,当用户使用了该权限则被审计,如执行grant select any table to a,
        object                按对象审计,只审计on关键字指定对象的相关操作,如aduit alter,delete,drop,insert on cmy.t by scott; 

工具 #

sqldeveloper
isqlplus
dbconsole
toad

sqlplus #

命令
    # ;或/结束

    /               # 执行最近一次的sql命令(不执行工具命令)
    host cls        # 清屏
    edit            # 用本地编辑器编辑
    设置显示
        column deptno format 9999       # deptno是列名,9999 代表4个数字宽度
        column ename format a8          # a8代表8个字符宽度(date也用a)
            col ename for a8            # 简写
        set pagesize 40                 # 设置一页显示40条记录
        spool d:/a.sql                  # 假脱机,开始记录控制台信息
            spool off                   # 正式保存记录信息到文件
            @ d:/a.sql                  # 从脱机文件中导入命令并执行
    监查时间
        set timing on / set timing off  # 每执行一段语句显示语句的执行时间
        set time on / set time off      # 在输入标记前面显示当前时间

pl/sql #

procedure language
oracle对sql99的扩展
    增加了类型定义
    判断
    循环
    指针/游标/光标
    输出语句
    异常
语法
    符号
        /   # 结束标记
        :=                # 赋值号
        &   # 进行定义运行时赋值
    语句
        declare    分号结束
        ;
        begin  dml语句/tcl语句,以分号结束 # 必写
        ;
        [exception]
        ;
        end;        # 必写
        /

        set serveroutput on;        # 设置plsql的输出打开,默认是off 的

        dbms_output.put_line('');  # 只能在 plsql 的执行语句中使用,dbms_output的输出方法,会自动换行

        emp.ename%type;        # 同emp表中的ename的类型一样的类型
        emp_record emp%rowtype      # 匹配一行类型
            emp_record.ename输出一个字段的数据


        if 条件1.1 and 条件1.2 then 语句1;
        elsif 条件2 then 语句2;
        else 语句3;
        end if;

        loop
        exit [when];
        end loop;

        while 条件
        loop
        ;
        end loop;

        for i in 1..3                # 不可以在循环中更改i的值
        loop
        ;
        end loop;

    cursor      # 多行数据 ,相当于resultset
        cursor c1 查询语句
        open c1;
        loop
        fetch                        # 先判断再下移,最后一条记录打印两次
                                        ## 先下移再判断 ,正常显示
        exit when 条件;
        end loop;
        close c1;

异常
        内置异常
        no_data_found  # 没有查到数据,游标中使用的时候异常不抛出
            select ename into pename from emp where deptno = 100;      # select into 插入的数据找不到的时候
        too_many_rows  #
        zero_divide    # 除零异常
        value_error
        timeout_on_resource
                例子
                        declare
                    i number(2) := 10;
                    s number(2);
                begin
                    s:= i/0;
                exception
                    when zero_divide then dbms_output.put_line('除0异常');
                end;
                        /
        自定义异常
                declare
                no_emp_found exception;
            begin
                if()then
                raise no_emp_found;
                end if;
            exception
                when no_emp_found then dbms_output.put_line('查无数据');
            end;
            /
        抛出异常的函数
                raise_application_error('-20666','禁止操作');
                        # begin语句中的相关地方调用此函数即可
                        # 20000-20999错误编号范围,是负数

例1                # dbms_output.put_line('');
    declare
        mysum number(3);
    begin
        mysum := 10 + 100;      # :=就是赋值
        dbms_output.put_line('结果为' || mysum);
    end;
    /
例2      # emp.ename%type
        ## select .. into ..
    declare
        x emp.ename%type;
        y emp.sal%type;
    begin
        --select ename,sal from emp where empno = 7369        # sql语句可以单独执行
        select ename,sal into x,y from emp where empno = 7369      # plsql语句只能整体执行
        dbms_output.put_line(x || '是' || y);
    end;
    /
例3                # emp%rowtype
    declare
        emp_record emp%rowtype;
    begin
        select * into emp_record from emp where empno = 7788;
        dbms_output.put_line(emp_record.ename || emp_record.sal);
    end;
    /
例4                        # 运行时赋值符号与if判断语句
    declare
    num number(2);
    begin
    num := &num;
    if num<5 then dbms_output.put_line(num || '<5');
    elsif num=5 then ..
    else ..
    end if;
    end;
    /
例5                        # loop 循环
    declare
        i number(2) := 1;      # 声明的时候可以赋值
    begin
        loop
            exit when i > 10;
            dbms_output.put_line(i);
            i := i + 1;
        end loop;
    end;
    /
例6                        # while循环
    declare
        i number(2) := 10;
    begin
        while i <= 20
        loop
            dbms_output.put_line(i);
            i := i + 1;
        end loop;
    end;
    /
例7                        # for循环
    declare
        i number(2)
    begin
            loop
        for i in 20..30    # 一个一个增加,循环中不能再对i进行操作
            dbms_output.put_line(i);
        end loop;
    end;
    /
例8                        # cursor
    declare
        cursor cemp is select ename,sal from emp;
        pename emp.ename%type;
        psal emp.sal%type;
    begin
        open cemp;
        loop
            exit when cemp%notfound;
            fetch cemp into pename,psal;
            dbms_output.put_line(pename || '的薪水是' || psal);
        end loop;
        close cemp;
    end;
    /
例9 # 有参游标
    declare
        cursor cemp(pdeptno emp.deptno%type) is select ename,sal from emp where deptno=pdeptno;
        pename emp.ename%type;
        psal emp.sal%type;
    begin
        open cemp(&deptno);
        loop
            fetch cemp into pename,psal;
            exit when cemp%notfound;
            dbms_output.put_line(pename ||'的薪水是' || psal);
    end loop;
            close cemp;
            end;
            /
例10 # 输入&emptno没有的时候,输出"查无员工"                # 综合if loop 与cursor
    declare
        cursor cemp(pdeptno emp.deptno%type) is select ename,sal from emp where deptno=pdeptno;
        pename emp.ename%type;
        psal emp.sal%type;
                    pdeptno emp.deptno%type := &deptno;
    begin
            if pdeptno in (10,20,30) then dbms_output.put_line('输入的值正确');
        open cemp(pdeptno);
        loop
            fetch cemp into pename,psal;
            exit when cemp%notfound;
            dbms_output.put_line(pename ||'的薪水是' || psal);
    end loop;
            close cemp;
            else dbms_output.put_line('输入的值不正确');
            end if;
            end;
            /

例11 # 给所有ANALYST加工资        # 综合cursor if loop ,循环之后执行了tcl 事务控制语言
    declare
        cursor cemp is select empno,ename,job,sal from emp;
        pempno emp.empno%type;
        pename emp.ename%type;
        pjob emp.job%type;
        psal emp.sal%type;
    begin
        open cemp;
        loop
            fetch cemp into pempno,pename,pjob,psal;
            exit when cemp%notfound;
            if pjob='ANALYST' then
                update emp set sal = sal+1000 where empno = pempno;
            end if;
        end loop;
        commit;
        close cemp;
    end;
    /