Oracle数据库知识点整理和复习

select 标识 选择哪些列.
from  标识从哪个表中选择.
as 列的别名可以省略,别名使用双引号,可以在别名中包含空格或特殊字符并区分大小写.
|| 把列与列,列与字符连接在起一起
select ename||'_'||job as "Employees" from emp;
字符串  日期和字符只能在单引号中出现
select ename||' is a '||job
       as "Employee Details"
from emp;
distinct  删除重复行
select distinct deptno
from emp;

sql:
一种语言  ANSI标准  关键字不能缩写  使用语句控制数据库中的表定义信息和表中的数据
sql*puls
一种环境  Oracle的特性之一  关键字可以缩写  命令不能改变数据库中的数据值  集中运行

desc 显示表的结构

where 将不满足条件的行过滤掉. where字句紧跟随from字句  解析顺序右-->左
select empno,ename,job,deptno
from emp
where deptno=10;

字符和日期要包含在单引号中,字符大小写敏感,日期格式敏感.默认的日期格式是DD-MON-RR.

比较运算符

操作符 = > >= > <= <>
含义 等于 大于 大于等于 大于 小于等于 不等于(也可以是!=)

赋值使用:=符号


操作符 含义
between...and... 在两个值之间(包含边界)
in(set) 等于值列表中的一个
like 模糊查询((%任意字符)(_任意一个字符)(\转译字符))
is null 空值

操作符 and or not
含义 逻辑并 逻辑或 逻辑否
order by 子句
asc 升序
desc 降序
order by 子句在select语句的结尾.
select ename,job,deptno,hiredate
from emp
order by deptno,hiredate desc;

host cls  w清屏  host clear  l清屏
设置行宽  show linesize   set linesize 120
设置列宽  col ename for a8  col sal for 9999

sql 优化的原则:
1.尽量使用列名
2.where解析顺序: 右 ---> 左
3.尽量使用where
4.尽量使用多表查询

修改书写错误语句
c命令 change
c /form/form

ed也是修改书写错误的语句

sql 中的null
1.包含null的表达式都为null
2.null永远!=null
3.如果集合中含有null,不能使用not in;但可以使用in;
4.null排序  null默认最大
select * 
from emp 
order by comm desc 
nulls last
5.组函数会自动滤空

dual表:伪表

单行函数:(字符,通用,转换,数值,日期)
操作数据对象
接受参数返回一个结果
只对一行进行变换
每行返回一个结果
可以转换数据类型
可以嵌套
参数可以是一列或一个值


字符函数

大小写控制函数  (lower,upper,initcap)
lower:全部小写
upper:全部大写
initcap:首字母大写

字符控制函数  (concat,substr,length/lengthb,instr,lpad | rpad,trim,replace)
concat: concat(str1,str2,...)连接字符
substr: substr(a,b)从a中,第b位开始取  substr(a,b,c)从a中,第b位开始取,取c位
length: 字符数 lengthb: 字节数
instr:  instr(a,b)在a中,查找b
lpad | rpad: lpad 左填充  rpad 右填充  select lpad('abcd',10,'*')  ******abcd
trim:  trim 去掉前后指定的字符  select trim('H' from 'Hello WorldH') from dual;
replace:  替换字符select replace('Hello World','l','*') from dual;


数字函数

round:  四舍五入 round(45.926,2)  45.93  round(45.926,-1) 50
trunc:  截断  trunc( 45.926,2 ) 45.92   trunc( 45.926,-1 ) 40
mod:  求余 mod(1600,300)  100


日期
函数sysdate返回日期时间
select ename,(sysdate-hiredate)/7 as weeks
from emp
where deptno=10;


日期函数

函数 描述
months_between 两个日期相差的月数
add_months 向指定日期中加上若干个月数
next_day 指定日期的下一个日期
last_day 本月的最后一天
round 日期四舍五入
trunc 日期截断

下一个星期四
select next_day(sysdate,'星期四') from dual;



格式 说明 举例
YYYY Full year in numbers 2011
YEAR Year spelled out(年的英文全称) twenty eleven
MM Two-digit value of month月份(两位数字) 04
MONTH Full name of the month(月的全称) 4月
DY Three-letter abbreviation of the day of the week(星期几) 星期一
DAY Full name of the day of the week 星期一
DD Numeric day of the month 02

时间格式

HH24:MI:SS AM 15:45:32 PM
to_char 函数对日期的转换
select ename,
       to_char(hiredate,'DD Month YYYY')
       as hiredate
from emp;

2016-09-29 12:18:12今天是星期四
select to_char(sysdate,'yyyy-mm-dd hh24:mi:ss"今天是"day') from dual;

to_char 函数对数字的转换


9 数字
0
$ 美元符
L 本地货币符号
. 小数点
, 千分符
select to_char(sal,'$99,999,00') salary
from emp
where ename = 'KING';


to_number函数将字符转换成数字
to_date函数将字符转换成日期

nvl:  nvl(a,b) 如果a=null的时候返回b   nvl2(a,b,c) 当a=null的时候,返回c;否则返回b
nullif:  nullif(a,b) 当a=b的时候,返回null;否则返回a
coalesce:  从左到右 找到第一个不为null的值
select comm,sal,coalesce(comm,sal) "第一个不为null的值" from emp;

decode
select ename,job,sal 涨前
       decode(job,'PRESIDENT',sal+1000,
                  'MANAGER',sal+800,
                            sal+400)涨后
from emp;


case
select ename,job,sal 涨前,
       case job when 'PRESIDENT' then sal+1000
                    when 'MANAGER' then sal+800
                    else sal+400
       end 涨后
from emp;


分组函数
avg
count
max
min
sum

group by
多个列的分组
select deptno,job,sum(sal)
from emp
group by deptno,jop
order by 1;

where和having的区别:where不能使用多行函数

group by 的增强


SQL> /*
SQL> group by 的增强
SQL> select deptno,job,sum(sal) from emp group by deptno,job
SQL> +
SQL> select deptno,sum(sal) from emp group by deptno
SQL> +
SQL> select sum(sal) from emp
SQL>
SQL> ====
SQL> select deptno,job,sum(sal) from emp group by rollup(deptno,job)
SQL>
SQL> 抽象
SQL> group by rollup(a,b)
SQL> =
SQL> group by a,b
SQL> +
SQL> group by a
SQL> +
SQL> 没有group by
SQL> */
SQL> select deptno,job,sum(sal) from emp group by rollup(deptno,job);


DEPTNO JOB         SUM(SAL)                                                                                                                                                                         
---------- --------- ----------                                                                                                                                                                         
        10 CLERK               1300                                                                                                                                                                         
        10 MANAGER         2450                                                                                                                                                                         
        10 PRESIDENT        5000                                                                                                                                                                         
        10                          8750                                                                                                                                                                         
        20 CLERK                1900                                                                                                                                                                         
        20 ANALYST            6000                                                                                                                                                                         
        20 MANAGER         2975                                                                                                                                                                         
        20                         10875                                                                                                                                                                         
        30 CLERK                950                                                                                                                                                                         
        30 MANAGER         2850                                                                                                                                                                         
        30 SALESMAN        5600                                                                                                                                                                         
    DEPTNO JOB             SUM(SAL)                                                                                                                                                                         
---------- --------- ----------                                                                                                                                                                         
        30                         9400                                                                                                                                                                         
                                    29025

break on deptno skip 2
break on null

多表查询

等值连接
select e.empno,e.ename,e.sal,d.dname
from emp e,depr d
where e.deptno=d.deptno

不等值连接
select e.empno,e.ename,e.sal,s.grade
from emp e,salgrade s
where e.sal between s.losal  and s.hisal;

外链接
左外连接: 当where e.deptno=d.deptno不成立的时候,等号左边的表任然被包含在最后的结果中
         写法:where e.deptno=d.deptno(+)
右外连接: 当where e.deptno=d.deptno不成立的时候,等号右边的表任然被包含在最后的结果中
         写法:where e.deptno(+)=d.deptno
select d.deptno 部门号,d.dname 部门名称,count(e.empno) 人数
from emp e,dept d
where e.deptno(+)=d.deptno
group by d.deptno,d.dname;

自连接
自连接不适合操作大表
select e.ename 员工姓名,b.ename 老板姓名
from emp e,emp b
where e.mgr=b.empno;

层次连接
select level,empno,ename,mgr
from emp
connect by prior empno=mgr
start with mgr is null
order by 1;


子查询
注意的问题
1.括号
2.合理的书写风格
3.可以在主查询的where select having from 后面使用子查询
4.不可以在group by使用子查询
5.强调from后面的子查询
6.主查询和子查询可以不是同一张表;只要子查询返货的结果 主查询可以使用 即可
7.一般不在子查询中排序;但在top-n分析问题中 必须对子查询排序
8.一般先执行子查询,在执行主查询;但相关子查询列外
9.单行子查询只能使用单行操作符;多行子查询只能使用多行操作符
10.子查询中的null

多行子查询

操作符 含义
IN 等于列表中的任何一个
ANY 和子查询返回的任意一个值比较
ALL 和子查询返回的所有值比较

oracel分页

select e2.*
from (select rownum r1,e1.*
     from (select * from emp order by sal) e1
     where rownum <=8
    ) e2
where r1>=5;

SQL的类型
1.DML(Data Manipulation Language 数据操作语言): select insert update delete
2.DDL(Data Definition Language 数据定义语言): create table, alter table, truncate table, drop table
                                                                        create/drop view,sequnece,index,synonym(同义词)
3.DCL(Data Control Language 数据控制语言): grant(授权) revoke(撤销授权)

插入insert  &地址符
一次插入多条记录
create table emp10 as select * from emp where 1=2;
 
海量插入数据:
1.数据泵(PLSQL程序)  dbms_datapump(程序包)
2.SQL*Loader
3.外部表

delect 和truncate 的区别
1.delete逐条删除;truncate先摧毁表  在重建2
2.delect是DML(可以回滚)   truncate是DDL(不可以回滚)
3.delete不会释放空间  truncate会
4.delete会产生碎片  truncate不会
5.delete可以闪回(flashback)  truncate不可以

undo数据(还原数据)

打开消耗时间
set timing on
关闭消耗时间
set timing off

Oracel中的事务
1.起始标志:  事务中的第一条DML语句
2.结束标志:  提交:  显示  commit  隐式:  正常退出 DDL DCL
                  回滚:  显示  rollback  隐式:  非正常退出  掉电  宕机

设置保存点
savepoint a;
返回保存点
rollback to savepoint a;


行号 rownum
1、rownum永远按照默认的顺序生成
2、rownum只能使用< <=;不能使用> >=

临时表
1. create global temporary table  ****
2. 自动:排序
特点:当事务或者会话结束的时候,表中的数据自动删除

行转列
wm_concat(varchar2) 多行函数
select deptno,wm_concat(ename) nameslist
from emp
group by deptno;

创建表
create table test1
        (tid number,tname varchar2(20));


数据类型 描述
VARCHAR2(size) 可变长字符数据
CHAR(size) 定长字符数据
NUMBER(p,s) 可变长数值数据
DATE 日期型数据
LONG 可变长字符数据,最大可达到2G
CLOB 字符数据,最大可达到4G
RAW and LONG RAW 原始的二进制数据
BLOB 二进制数据,最大可达到4G
BFILE 存储外部文件的二进制数据,最大可达到4G
ROWID 行地址

rowid 行地址
select rowid,empno,ename,sal from emp;

select * from emp where rowid='AAAMfPAAEAAAAAgAAJ';

修改表:增加新列,修改列,删除列,重命名列,重命名表

增加新行
alter table test1 add photo blob;

修改列
alter table test1 modify tname varchar(40);

删除列
alter table test1 drop column photo;

重命名列
alter table test1 rename column tname to username;

重命名表
rename test1 to test2;


删除表
drop table TESTDELETE;

查看回收站
show recyclebin;


清空回收站
purge recyclebin;

管理员没有回收站

闪回删除     回收站
flashback table TESTSAVEPOINT to before drop;


集合运算

并集 union/union all
union运算符返回两个集合去掉重复元素后的所有记录.
union all 返回两个集合的所有记录,包括重复的

并集 intersect
intersect运算符返回同时属于两个集合的记录

差集 minus
minus返回属于第一个集合,但不属于第二个集合的记录.

约束
非空约束 NOT NULL;
唯一性约束 UNIQUE;
主键约束 PRIMARY KEY;
外键约束
FOREIGN KEY:在子表中,定义了一个表级的约束
REFERENCES:指定表和父表中的列
ON DELETE CASCADE:当删除父表时,级联删除子表记录
ON DELETE SET NULL:将子表的相关依赖记录的外键置为null
检查约束 check 定义每一行记录所必须满足的条件

Oracel 默认读以提交

管理其他数据库对象
视图:封装了一条复杂查询的语句.视图是一个虚表
优点:简单化复杂的查询
创建一个视图
create view empinfoview
as
select e.empno,e.ename,e.sal,e.sal*12 annsal,d.dname
from emp e,dept d
where e.deptno=d.deptno;

不建议通过视图对表中的数据进行修改,因为会受到很多的限制.

序列
创建序列
create sequence myseq;

nextval:取得序列的下一个内容
currval:取得序列的当前内容
例如
select myseq.nextval from dual;
select myseq.currval from dual;
insert into  testseq values(myseq.nextval,'aaa');

序列可能产生裂缝的原因:
回滚   系统异常  多个表共用一个序列

index索引:  是用于加速数据存取的数据对象.


同义词:synonym
为hr.employees起别名
create synonym hremp for hr.employees


pl/sql编程语言
语法
declare
    说明部分(变量说明,光标申明,例外说明)
begin
    语句序列(DML语句)
exception
    例外处理语句
End;
/

helloword
declare
  --说明部分
begin
  --程序
  dbms_output.put_line('Hello World');
end;
/

常量和变量的定义
引用变量
declare
  --定义变量保存姓名和薪水
  --pename varchar2(20);
  --psal   number;
  pename emp.ename%type;
  psal   emp.sal%type;
begin
  --得到7839的姓名和薪水
  select ename,sal into pename,psal from emp where empno=7839;
  --打印
  dbms_output.put_line(pename||'的薪水是'||psal);
end;
/

记录型变量
--记录型变量: 查询并打印7839的姓名和薪水
declare
  --定义记录型变量:代表一行
  emp_rec emp%rowtype;
begin
  select * into emp_rec from emp where empno=7839;
  
  dbms_output.put_line(emp_rec.ename||'的薪水是'||emp_rec.sal);
end;
/

if语句
语法:
1.
if    条件    then    语句1;
语句2;
end if;
2.
if    条件    then    语句序列1;
else    语句序列2;
end if;
3.
if    条件    then    语句;
elseif    语句    then    语句;
else    语句;
end if;

-- 判断用户从键盘输入的数字
--接受键盘输入
--变量num:是一个地址值,在该地址上保存了输入的值
accept num prompt '请输入一个数字';
declare
  --定义变量保存输入 的数字
  pnum number := #
begin
  if pnum = 0 then dbms_output.put_line('您输入的是0');
     elseif pnum = 1 then dbms_output.put_line('您输入的是1');
     elseif pnum = 2 then dbms_output.put_line('您输入的是2');
     else dbms_output.put_line('其他数字');
  end if;
end;
/

循环
语法:
1.
while total<=25000
loop
...
total:=total+salary;
end loop;
2.
loop
exit[when 条件];
...
end loop;
3.
for i in 1..3
loop
语句序列;
end loop;

-- 打印1~10
declare
  -- 定义变量
  pnum number := 1;
begin
  loop
    --退出条件
    exit when pnum > 10;
    --打印
    dbms_output.put_line(pnum);
    --加一
    pnum := pnum + 1;
  end loop;
end;
/

游标(光标Cursor)
打开游标:    open  c1;(打开游标执行查询)
取一行游标的值:    fetch  c1  into  pjob;(取一行到变量中)
关闭游标:    close  c1;(关闭游标释放资源)
游标的结束方式    exit  when  c1%notfound
注意:上面的pjop必须与emp表中的job列类型一致;
    定义:pjop  emp.empjob%type;

-- 查询并打印员工的姓名和薪水
/*
光标的属性: %isopen   %rowcount(影响的行数)
             %found    %notfound
*/
declare
   --定义光标(游标)
   cursor cemp is select ename,sal from emp;
   pename emp.ename%type;
   psal   emp.sal%type;
begin
  --打开
  open cemp;
  loop
       --取当前记录
       fetch cemp into pename,psal;
       --exit when 没有取到记录;
       exit when cemp%notfound;
       
       dbms_output.put_line(pename||'的薪水是'||psal);
  end loop;
  --关闭
  close cemp;
end;
/

-- 查询某个部门的员工姓名
declare
   --形参
   cursor cemp(dno number) is select ename from emp where deptno=dno;
   pename emp.ename%type;
begin
   --实参
   open cemp(20);
   loop
        fetch cemp into pename;
        exit when cemp%notfound;
        
        dbms_output.put_line(pename);
   end loop;
   close cemp;
end;
/


例外
no_data_found    (没有找到数据)
too_many_rows    (select...into语句匹配多个行)
zero_divide    (被零除)
value_error    (算术或转换错误)
timeout_on_resource    (在等待资源时发生超时)

系统例外
-- 被0除
declare
   pnum number;
begin
  pnum := 1/0;
  
exception
  when zero_divide then dbms_output.put_line('1:0不能做分母');
                        dbms_output.put_line('2:0不能做分母');
  when value_error then dbms_output.put_line('算术或者转换错误');                      
  when others then dbms_output.put_line('其他例外');
end;
/

自定义列外
-- 查询50号部门的员工
declare
  cursor cemp  is select ename from emp where deptno=50;
  pename emp.ename%type;
  
  --自定义例外
  no_emp_found exception;
begin
  open cemp;
  
  --取第一条记录
  fetch cemp into pename;
  if cemp%notfound then
    --抛出例外
    raise no_emp_found;
  end if;
  
  --进程:pmon进程(proccesss monitor)
  close cemp;
exception
  when no_emp_found then dbms_output.put_line('没有找到员工');
  when others then dbms_output.put_line('其他例外');
end;
/


存储过程
语法
create [or replace] procedure 过程名[(参数名 in/out 数据类型)]
as
begin
    plsql子程序体;
end;

创建一个存储过程
create or replace procedure sayhelloworld
as
   --说明部分
begin
   dbms_output.put_line('Hello World');
end;
/
调用存储过程
exec sayhelloworld();
begin
sayhelloworld();
sayhelloworld();
end;
/

带参数的存储过程
--给指定的员工涨100,并且打印涨前和涨后的薪水
create or replace procedure raiseSalary(eno in number)
is
       --定义变量保存涨前的薪水
       psal emp.sal%type;
begin
       --得到涨前的薪水
       select sal into psal from emp where empno=eno;
       
       --涨100
       update emp set sal=sal+100 where empno=eno;
       
       --要不要commit?
       --不需要
       dbms_output.put_line('涨前:'||psal||'   涨后:'||(psal+100));
end raiseSalary;
/


存储函数
语法:
create or replace function 函数名(Neme in type,name out type,...) return 数据类型 is
    结果变量 数据类型;
begin
    
    return(结果变量);
end[函数名];

例如
--查询某个员工的年收入
create or replace function queryEmpIncome(eno in number)
return number
is
       --定义变量保存月薪和奖金
       psal emp.sal%type;
       pcomm emp.comm%type;
begin
       --得到月薪和奖金
       select sal,comm into psal,pcomm from emp where empno=eno;
       
       --返回年收入
       return psal*12+nvl(pcomm,0);
end queryEmpIncome;
/

out参数
--查询某个员工的姓名 薪水和职位
/*
1、查询某个员工的所有信息 ---> out参数太多
2、查询某个部门中的所有员工信息 ----> 返回的是集合
*/
create or replace procedure queryEmpInformation(eno in number,
                                                pename out varchar2,
                                                psal   out number,
                                                pjob   out varchar2)
is
begin
  
   select ename,sal,job into pename,psal,pjob from emp where empno=eno;                                             
end queryEmpInformation;
/

查询光标
--2、查询某个部门中的所有员工信息 ----> 返回的是集合
create or replace package mypackage is
       type empcursor is ref cursor;
       procedure queryEmpList(dno in number,empList out empcursor);
end mypackage;
/
create or replace package body mypackage is
       procedure queryEmpList(dno in number,empList out empcursor)
       as
       begin
         
          open empList for select * from emp where deptno=dno;
       
       end;
end mypackage;
/


j ava连接Oracle
public class TestOracle {
/*
* create or replace procedure queryEmpInformation(eno in number,
                                                pename out varchar2,
                                                psal   out number,
                                                pjob   out varchar2)
*/
    @Test
    public void testProcedure(){
        //{call [(,, ...)]}
        String sql = "{call queryEmpInformation(?,?,?,?)}";
        
        Connection conn = null;
        CallableStatement call = null;
        try {
            conn = JDBCUtils.getConnection();
            call = conn.prepareCall(sql);
            
            //对于in参数,赋值
            call.setInt(1,7839);
            
            //对于out参数,申明
            call.registerOutParameter(2, OracleTypes.VARCHAR);
            call.registerOutParameter(3, OracleTypes.NUMBER);
            call.registerOutParameter(4, OracleTypes.VARCHAR);
            
            //执行
            call.execute();
            
            //输出
            String name = call.getString(2);
            double sal = call.getDouble(3);
            String job = call.getString(4);
            
            System.out.println(name+"\t"+sal+"\t"+job);
        } catch (Exception e) {
            e.printStackTrace();
        }finally{
            JDBCUtils.release(conn, call, null);
        }
    }
/*
* create or replace function queryEmpIncome(eno in number)
return number
*/
    @Test
    public void testFunction(){
        //{?= call [(,, ...)]}
        String sql = "{?=call queryEmpIncome(?)}";
        
        Connection conn = null;
        CallableStatement call = null;
        try {
            conn = JDBCUtils.getConnection();
            call = conn.prepareCall(sql);
            
            call.registerOutParameter(1, OracleTypes.NUMBER);
            call.setInt(2, 7839);
            
            //执行
            call.execute();
            
            //取出年收入
            double income = call.getDouble(1);
            
            System.out.println(income);
        } catch (Exception e) {
            e.printStackTrace();
        }finally{
            JDBCUtils.release(conn, call, null);
        }        
    }
    @Test
    public void testCursor(){
        String sql = "{call mypackage.QUERYEMPLIST(?,?)}";
        
        Connection conn = null;
        CallableStatement call = null;
        ResultSet rs = null;
        try {
            conn = JDBCUtils.getConnection();
            call = conn.prepareCall(sql);
            
            //对于in参数,赋值
            call.setInt(1,20);
            
            //对于out参数,申明
            call.registerOutParameter(2, OracleTypes.CURSOR);
            
            //执行
            call.execute();
            
            //取出结果
            rs = ((OracleCallableStatement)call).getCursor(2);
            while(rs.next()){
                //取出一个员工
                String name = rs.getString("ename");
                double sal = rs.getDouble("sal");
                System.out.println(name+"\t"+sal);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }finally{
            JDBCUtils.release(conn, call, rs);
        }        
        
    }
}

触发器
触发器的类型
语句级触发器:在指定的操作语句操作之前或之后执行一次,不管这条语句影响了多少行.
行级触发器:触发语句作用的每一条记录都被触发.在行级触发器中使用:old和:new伪记录变量,识别值得状态.

语法:
CREATE [OR REPLACE] TRIGGER 触发器名
{BEFORE|AFTER}
{DELETE|INSERT|UPDATE[OF列名]}
ON 表名
[FOR EACH ROW [WHEN(条件)]]
PLSQL块
每当成功插入新员工后,自动打印“成功插入新员工”
create trigger firsttrigger
after insert
on emp
declare
begin
  dbms_output.put_line('成功插入新员工');
end;
/
强制审计
标准审计(配置)
基于值的审计
细粒度审计
管理员审计



/*
实施复杂的安全性检查
禁止在非工作时间 插入新员工
1、周末:  to_char(sysdate,'day') in ('星期六','星期日')
2、上班前 下班后:to_number(to_char(sysdate,'hh24')) not between 9 and 17
*/
create or replace trigger securityemp
before insert
on emp
begin
   if to_char(sysdate,'day') in ('星期六','星期日','星期五') or
      to_number(to_char(sysdate,'hh24')) not between 9 and 17 then
      --禁止insert
      raise_application_error(-20001,'禁止在非工作时间插入新员工');
   end if;
  
end securityemp;
/


/*
数据的确认
涨后的薪水不能少于涨前的薪水
*/
create or replace trigger checksalary
before update
on emp
for each row
begin
    --if 涨后的薪水 < 涨前的薪水 then
    if :new.sal < :old.sal then
       raise_application_error(-20002,'涨后的薪水不能少于涨前的薪水。涨前:'||:old.sal||'   涨后:'||:new.sal);
    end if;
end checksalary;
/


你可能感兴趣的:(Oracle数据库知识点整理和复习)