Oracle文档

基础知识

关于数据库语言的分类

DDL:数据库定义语言:create、drop
DML:数据库的操作语言:insert、update、delete
DQL:数据库的查询语言:select
DCL:数据库的控制语言:grant、revoke

数据类型

VARCHAR2(size)

可变长字符数据。VARCHAR2(n)数据类型用于定义可变长度的字符串,其中,n用于指定字符串的最大长度,n的值必须是正整数且不超过32767。

CHAR(size)

定长字符数据。CHAR(n)数据类型用于定义固定长度的字符串,其中,n用于指定字符串的最大长度,n的值必须是正整数且不超过32767。

NUMBER(p,s)

可变长数值数据。NUMBER(precision,scale)数据类型用于定义固定长度的整数和浮点数,其中,precision表示精度,用于指定数字的总位数;scale表示标度,用于指定小数点后的数字位数,默认值为0,即没有小数位数。

DATE

日期型数据。DATE数据类型用于定义日期时间类型的数据,分别描述年、月、日、时、分、秒。

TIMESTAMP

TIMESTAMP数据类型也用于定义日期时间数据,但与DATE仅显示日期不同,TIMESTAMP类型数据还可以显示时间和上下午标记,如“11-9月-2007 11:09:32.213 AM”。

LONG

可变长字符数据,最大可达到2G。LONG数据类型在其它的数据库系统中常被称为备注类型,它主要用于存储大量的可以在稍后返回的文本内容。

CLOB

字符数据,最大可达到4G。

BLOB

二进制数据,最大可达到4G。

ROWID

行地址,十六进制串,表示行在所在的表中唯一的行地址,该数据类型主要用于返回ROWID伪列,常用在可以将表中的每一条记录都加以唯一标识的场合。

提交回滚

在每次执行完数据变更的语句(insert\update\delete)后,需要提交数据

可以将执行数据变更的语句后跟上commit,进行提交

注意:执行变更数据(执行提交)之前,已经要检查语句,是否变更逻辑正确。

别名

as

SELECT count(1) as sjl FROM test1;SELECT t.name FROM test1 as t;

sjl就是count(1)的别名

别名是为了方便计算和防止

通配符

‘%’(百分号): 用来表示任意数量的字符,或者可能根本没有字符。

‘_’(下划线): 表示确切的未知字符。

‘?’(问号): 用来表示确切的未知字符。

‘#’(井号): 用来表示确切的阿拉伯数字,0到9。

‘[a-d]’(方括号):用来表示字符范围,在这里是从a到d。

用户和表空间

创建新用户

CREATE USER usename IDENTIFIED BY password;

授权

GRANT CONNECT, RESOURCE, DBA TO usename;

DBA: 拥有全部特权,是系统最高权限,只有DBA才可以创建数据库结构。
RESOURCE:拥有Resource权限的用户只可以创建实体,不可以创建数据库结构。
CONNECT:拥有Connect权限的用户只可以登录Oracle,不可以创建实体,不可以创建数据库结构。

创建表空间

CREATE TABLESPACE spacename DATAFILE '表空间文件存放路径' SIZE 表空间大小 AUTOEXTEND ON;
-- 例
CREATE TABLESPACE dragon DATAFILE 'C:\Tablespace\dragon.dbf' SIZE 1000M AUTOEXTEND ON;

表和视图

创建表

CREATE

CREATE TABLE tablename(
num1 varchar2(200) null,
num2 varchar2(200) null,
num3 number(10,0) null,
num4 DATE null
)
tablespace spacename -- 指定表空间
comment on table tablename is '表注释';
comment on column tablename.num1 is '字段注释';

tablename:表名

num:字段名

create table test1 (
id varchar2(10) primary key,
name varchar2(200) not null,
phone varchar2(11) null,
adress varchar2(200) null
)

primary key:主键,主键的特性,不能重复且不为空

查询

SELECT

查询

-- 全字段显示
SELECT * FROM tablename;
SELECT num1,num2,num3,num4 FROM tablename;
-- 查询单一字段
SELECT num1 FROM tablename;
-- 查询多个字段
SELECT rowid,num1,num2 FROM tablename;

插入

INSERT INTO

插入

-- 全字段插入
INSERT INTO tablename(num1,num2,num3,num4)VALUES('10001','AT',20,'2022-04-03 00:00:00');
-- 指定字段插入
INSERT INTO tablename(num1,num2)VALUES('10001','AT');

更新

UPDATE

-- 全量更新
UPDATE tablename SET num2 = 'AG';
-- 条件更新
UPDATE tablename SET num2 = 'AG' WHERE num2 = 'AT';
UPDATE tablename SET num2 = 'AG' WHERE num1 = '10001';

FOR UPDATE

SELECT * FROM test1 FOR UPDATE;

*,rowid

SELECT t.*,t.rowid FROM test1 t;

删除表

DELETE

删除数据

-- 全量删除
-- 注1:delete只会删除数据,不会删表
-- 注2:delete删除数据可以回滚
-- 注3:如表数据量大,delete全量删除慢
DELETE FROM tablename;
-- 条件删除
DELETE FROM tablename WHERE num1 = '10001';

DROP

删除表

-- 注:无法回滚
-- 执行前请注意
DROP TABLE tablename;

TRUNCATE

清空表

-- 注:无法回滚
-- 执行速度极快
TRUNCATE TABLE tablename;

创建视图

视图(view),也称虚表, 不占用物理空间,这个也是相对概念,因为视图本身的定义语句还是要存储在数据字典里的。视图只有逻辑定义。每次使用的时候,只是重新执行SQL。

视图是从一个或多个实际表中获得的,这些表的数据存放在数据库中。那些用于产生视图的表叫做该视图的基表。一个视图也可以从另一个视图中产生。

CREATE [OR REPLACE] VIEW v_test1 AS
select t.name,t.phone,t.adress,e.wq,e.sf,e.zx from test1 t inner join test2 e on t.name = e.name;

语法:

create [or replace] [force] view view_name[(column1,column2,...)]as select ...[ with read only ];
  1. or replace: 如果存在同名的视图, 则使用新视图"替代"已有的视图
  2. force: "强制"创建视图,不考虑基表是否存在,也不考虑是否具有使用基表的权限
  3. with read only:创建的视图只能用于查询数据, 而不能用于更改数据.
视图的作用
  1. 提供各种数据表现形式, 可以使用各种不同的方式将基表的数据展现在用户面前, 以便符合用户的使用习惯(主要手段: 使用别名);
  2. 隐藏数据的逻辑复杂性并简化查询语句, 多表查询语句一般是比较复杂的, 而且用户需要了解表之间的关系, 否则容易写错; 如果基于这样的查询语句创建一个视图, 用户就可以直接对这个视图进行"简单查询"而获得结果. 这样就隐藏了数据的复杂性并简化了查询语句.这也是oracle提供各种"数据字典视图"的原因之一,all_constraints就是一个含有2个子查询并连接了9个表的视图(在catalog.sql中定义);
  3. 执行某些必须使用视图的查询. 某些查询必须借助视图的帮助才能完成. 比如, 有些查询需要连接一个分组统计后的表和另一表, 这时就可以先基于分组统计的结果创建一个视图, 然后在查询中连接这个视图和另一个表就可以了;
  4. 提供某些安全性保证. 不会让对方看到表结构,视图提供了一种可以控制的方式, 即可以让不同的用户看见不同的列, 而不允许访问那些敏感的列, 这样就可以保证敏感数据不被用户看见;
  5. 简化用户权限的管理. 可以将视图的权限授予用户, 而不必将基表中某些列的权限授予用户, 这样就简化了用户权限的定义。

条件过滤

WHERE

指定过滤的条件返回符合查询条件的行记录

SELECT * FROM tablename where num2 = 'AB';
num1 num2
10001 AB
10001 AB

AND

并且

-- 查询无结果
SELECT * FROM tablename WHERE num2 = 'AB' AND num2 = 'AC'
SELECT * FROM tablename WHERE num2 = 'AC' AND num2 = '10002'
num1 num2
10002 AC

OR

或者

SELECT * FROM tablename WHERE num2 = 'AB' OR num2 = 'AC'

Fetch

-- 获取前10行的数据
SELECT * FROM test1 fetch next 10 rows only;
SELECT * FROM test1 fetch first 10 rows only;
SELECT * FROM test1 fetch next 10 rows with ties;-- 获取整表前10%的数据
SELECT * FROM test1 fetch next 10 percent rows only;

OFFSET

-- 获取10行之后的数据
SELECT * FROM test1 offset 10 rows

IN

等于值列表中的任何值

SELECT * FROM tablename WHERE num1 in ('10001','10002');
num1 num2
10001 AB
10001 AB
10002 AC

BETWEEN AND

两者之间

SELECT * FROM tablename WHERE num1 BETWEEN 10001 AND 10002;
-- 结果同上
-- 结果等同于num1 >= ’10001‘ and num1 <= ’10002‘

LIKE

模糊查询

-- 以B结尾
SELECT * FROM tablename WHERE num2 LIKE '%B';
-- 以A开头
SELECT * FROM tablename WHERE num2 LIKE 'A%';
-- 包含C的
SELECT * FROM tablename WHERE num2 LIKE '%C%';

CASE WHEN

条件判断

SELECT name,case when sex = '1'  then '男'  when sex = '0'  then '女'  end 性别 FROM test6;

分组聚合

group by

分组函数,根据by后面的一定的规则进行分组

count()

计算数据量

count(*)

聚合计算时,包含了所有列,相当于行数,不会忽略NULL值

count(1)

在聚合计算时,忽略所有列,用数字1代表代码行,不会忽略NULL值

count(列名)

在聚合计算时,会忽略列为空(NULL)的值,只算有值的行数

注:在对表作分析计算时,使用count(1)比count(*)用时要少,不过相差不大、

​ 如果表中存在主键,count(主键)效率最高,如果表中只有一列(非主键),则count(*)效率最高,如果表中有多列,则count(1)效率最高

​ 所有执行,相差不大

MAX()

最大

MIN()

最小

SUM()

求和

AVG()

平均值

having

通常用在聚合函数前面,对聚合函数进行筛选

order by

排序

SELECT * FROM test1 ORDER BY id;
SELECT * FROM test1 ORDER BY id ASC;
SELECT * FROM test1 ORDER BY id DESC;

注:

  1. order by 默认正序排列
  2. asc 正序排列
  3. desc 倒序排列

聚合函数注意事项

  1. where条件后面不能跟聚合函数
  2. where、group by 、having、order by可以组合使用(例:select name,count(1) as sl from test1 where id is not null group by name having count(1) > 1 order by sl)
  3. 执行的优先顺序为:where > group by > having > order by

函数

字符串处理函数

LENGTH(S)

计算字符串S长度

LOWER(S)

S转换成小写

UPPER(S)

S转换成大写

CONCAT(X,Y)

连接字符串X和Y

INSTR(X,S,A)

从X中查找S,可以指定从A开始

LTRIM(X,T)

把X的左边截去T字符串,缺省截去空格

RTRIM(X,T)

把X的右边截去T字符串,缺省截去空格

TRIM([T FROM]X)

把X的两边截去T字符串,缺省截去空格

REPLACE(X,O,N)

在X中查找O,并替换成N

SUBSTR(X,start[,length])

返回X的字串,从start处开始,截取length个字符,缺省length,默认到结尾

数值处理函数

ROUND()

四舍五入

TRUNC()

直接截取,不四舍五入。

时间函数

sysdate

返回当前日期

current_date

返回当前时区中的当前日期

add_months(d,m)

增加月份,返回在日期d基础上再加m个月后新的日期。

last_day(d)

返回日期d所在月份最后一天的日期。

months_between(d1,d2)

返回日期d1到日期d2之间的月数。

trunc(d,[c])

返回日期d1所在期间(参数c,年月周范围)的第一天日期

SELECT sysdate,trunc(sysdate,'year'),trunc(sysdate,'month'),trunc(sysdate,'day') FROM test;
-- 结果 2022-04-05 2022-01-01(本年第一天)    2022-04-01(本月第一天) 2022-04-03(本周第一天)
next_day(d,[c])

返回日期d在下周,星期几(参数c)的日期

SELECT sysdate,next_day(sysdate,'星期一') FROM test3
-- 结果为下周一的日期

类型转换函数

TO_DATE

将字符串转换为时间类型

SELECT to_date('2022-04-05 19:20:00','yyyy-MM-dd hh24:mi:ss') FROM test;
TO_CHAR

将数值、时间等格式转换为字符串类型

SELECT to_char(sysdate,'yyyy-MM-dd hh24:mi:ss') FROM test;
TO_NUMBER

将字符串转换为数值类型

SELECT to_number('9999999','$999,999,999') FROM test;

注:

  1. 当char类型转换date类型时需要用到to_date函数,
  2. 当date类型转换char类型时需要用到to_char函数
  3. 当char类型转换number类型时需要用到to_number函数
  4. 当number类型转换char类型时需要用到to_char函数

分析函数和开窗函数

开窗函数指定了函数所能影响的窗口范围,也就是说在这个窗口范围中都可以受到函数的影响

与聚合函数的区别:

普通的聚合函数用group by分组,每个分组返回一个统计值,而分析函数采用partition by分组,并且每组每行都可以返回一个统计值。

over()
  • 用于标识分析函数
  • over关键字后的括号中的选项为空,则开窗函数会对结果集中的所有行进行聚合运算
  • over关键字后的括号中的选项为不为空,则按照括号中的范围进行聚合运算
row_number()over()

从1开始,为每一条分组记录返回一个数字

SELECT id,row_number()over(order by id desc) rn FROM test1
row_number()over(partition by order by )
SELECT name,cxxx,gxsj,row_number()over(partition by name order by gxsj desc) rn FROM test5

partition by:分区子句,根据分区表达式的条件逻辑将单个结果集进行分组

order by:排序子句,用于对分区中的数据进行排序

count(*)over()

查询结果的每一行都返回所有符合条件行的条数

SELECT id,name,phone,count(*)over() FROM test1 WHERE name = '猪八戒'
sum()over()
SELECT id,name,sum(phone)over() FROM test1;
range

用来指定时间范围进行统计

SELECT id,name,sum(phone)over(order by phone range between unbounded preceding and current row) FROM test1;
-- 查询从第一行到当前行的手机号总和

unbounded preceding:包含当前行之前的所有行

current row:当前行

rank( )

返回一个唯一的值,当碰到相同的数据时,此时所有相同数据的排名是一样的,同时会在最后一条相同记录和下一条不同记录的排名之间空出排名

SELECT id,name,rank()over(order by name) FROM test1;
dense_rank( )

返回一个唯一的值,当碰到相同数据时,此时所有相同数据的排名都是一样的,同时会在最后一条相同记录和下一条不同记录的排名之间紧邻递增。

SELECT id,name,dense_rank()over(order by name) FROM test1;

其他函数

DISTINCT

去掉多余的重复记录

NVL(X,VALUE)

空值处理,当x值为空,则返回value

NVL2(x,value1,value2)

空值条件返回,当x值为空,则返回value1,否则则返回value2

EXISTS

在oracle中,exists的作用就是检查子查询的结果是否为真,如果子查询为true则执行外面的SQL语句,如果返回为false则不执行外面的SQL语句,语法为“select * from daul where exists(条件)”。

子查询

SELECT * FROM (SELECT name,count(1) as sjl FROM test1 GROUP BY name)
SELECT  name,city,salary,(SELECT count(salary) FROM student WHERE salary < 4000) FROM  student WHERE  salary < 4000

关联

left join

左联接:结果包括左表(出现在JOIN子句最左边)中的所有行,不包括右表中的不匹配行。

select t.name,t.phone,t.adress,e.wq,e.sf,e.zx from test1 t left join test2 e on t.name = e.name

right join/right outer join

右联接:结果包括右表(出现在JOIN子句最右边)中的所有行,不包括有左表中的不匹配的行。

select e.name,t.phone,t.adress,e.wq,e.sf,e.zx from test1 t right join test2 e on t.name = e.name

inner join/inner outer join

内联接,结果为两个联接表中的匹配行的联接

select t.name,t.phone,t.adress,e.wq,e.sf,e.zx from test1 t inner join test2 e on t.name = e.name

full join/full outer join

全联接:结果包括所有联接中的所有行,不论他们是否匹配。

select t.name,t.phone,t.adress,e.wq,e.sf,e.zx from test1 t full join test2 e on t.name = e.name

except

EXCEPT 返回两个结果集的差(即从左查询中返回右查询没有找到的所有非重复值)。

intersect

INTERSECT 返回 两个结果集的交集(即两个查询都返回的所有非重复值)。

交叉关联

生成笛卡尔积——直接将一个数据源中的每个行和另一个数据源中的每个行,一一匹配

SELECT a.name,b.name FROM test1 a,test2 b

(+)

在oracle数据库中 (+)是一种特殊用法,表示外连接,(+)放在那一边,就标识,那边可以为空

/*此SQL相当于左关联*/
select t.name,t.phone,t.adress,e.wq,e.sf,e.zx from test1 t,test2 e where t.name = e.name(+)

集合操作符

union

不包含重复值,默认按第一个查询的第一列升序排列。

select t.id,t.name from test1 t union select e.id,e.name from test2 e

union all

完全并集包含重复值。不排序。

select t.id,t.name from test1 t union all select e.id,e.name from test2 e

minus

不包含重复值,不排序。

索引约束(索引理论)

概念

**Oracle索引(index)最大的作用是用来优化数据库查询的效率,提升数据库的查询性能。**就好比书的目录一样,可以通过目录来直接定位所需内容存在的页数,大大提高检索效率。

Oracle数据库中如果某列出现在查询的条件中,而该列的数据是无序的,查询时只能从第一行开始一行一行的匹配。创建索引就是对某些特定列中的数据进行排序或归类,生成独立的索引表。在某列上创建索引后,如果该列出现在查询条件中,Oracle 会自动的引用该索引,先从索引表中查询出符合条件记录的 ROWID,由于 ROWID 是记录的物理地址,因此可以根据 ROWID 快速的定位到具体的记录,当表中的数据非常多时,引用索引带来的查询效率非常可观 。

索引创建

创建标准索引:

CREATE INDEX 索引名 ON 表名 (列名) TABLESPACE 表空间名;

创建唯一索引:

CREATE unique INDEX 索引名 ON 表名 (列名) TABLESPACE 表空间名;

创建位图索引:

CREATE bitmap INDEX 索引名 ON 表名(列名) 表空间

创建组合索引:

CREATE INDEX 索引名 ON 表名 (列名1,列名2) TABLESPACE 表空间名;

创建反向键索引:

CREATE INDEX 索引名 ON 表名 (列名) reverse TABLESPACE 表空间名;

索引操作

给列名增加一个唯一约束

alter table 表名 add constraint 索引名 unique(列名)

删除唯一约束

alter table 表名 drop unique (列名)

删除索引

alter table 表名drop constraint 索引名

drop index 索引名;

禁用和激活索引

alter table 表名 disable/enable unique 唯一约束

alter table 表名 disable/enable constraint 索引名

alter table 表名 modify constraint 唯一约束 disable/enable

alter table 表名 modify constraint 索引名 disable/enable

清理索引碎片
1.合并索引(只是简单的将B树叶结点中的存储碎片合并在一起,并不会改变索引的物理组织结构)
alter index 索引名 coalesce;

2.重建索引(不仅能够消除存储碎片,还可以改变索引的全部存储参数设置,并且可以将索引移动到其它的表空间中,重建索引
实际上就是再指定的表空间中重新建立一个新的索引,然后删除原来的索引)
alter index 索引名 rebuild;

索引的类别

1、b-tree索引:Oracle数据中最常见的索引,就是b-tree索引,create index创建的normal就是b-tree索引,没有特殊的必须应用在哪些数据上。

2、bitmap位图索引:位图索引经常应用于列数据只有几个枚举值的情况,比如上面说到过的性别字段,或者我们经常开发中应用的代码字段。这个时候使用bitmap位图索引,查询效率将会最快。

3、函数索引:比如经常对某个字段做查询的时候经常是带函数操作的,那么此时建一个函数索引就有价值了。例如:trim(列名)或者substr(列名)等等字符串操作函数,这个时候可以建立函数索引来提升这种查询效率。

4、hash索引:hash索引可能是访问数据库中数据的最快方法,但它也有自身的缺点。创建hash索引必须使用hash集群,相当于定义了一个hash集群键,通过这个集群键来告诉oracle来存储表。因此,需要在创建HASH集群的时候指定这个值。存储数据时,所有相关集群键的行都存储在一个数据块当中,所以只要定位到hash键,就能快速定位查询到数据的物理位置。

5、reverse反向索引:这个索引不经常使用到,但是在特定的情况下,是使用该索引可以达到意想不到的效果。如:某一列的值为{10000,10001,10021,10121,11000,…},假如通过b-tree索引,大部分都密集分布在某一个叶子节点上,但是通过反向处理后的值将变成{00001,10001,12001,12101,00011,…},很明显的发现他们的值变得比较随机,可以比较平均的分部在各个叶子节点上,而不是之前全部集中在某一个叶子节点上,这样子就可大大提高检索的效率。

6、分区索引和分区表的全局索引:这两个索引是应用在分区表上面的,前者的分区索引是对分区表内的单个分区进行数据索引,后者是对分区表的全表进行全局索引。分区表的介绍,可以后期再做单独详解,这里就不累述了。

索引使用原则

索引字段建议建立NOT NULL约束
经常与其他表进行连接的表,在连接字段上应该建立索引;
经常出现在Where子句中的字段且过滤性很强的,特别是大表的字段,应该建立索引;
复合索引的建立需要进行仔细分析;尽量考虑用单字段索引代替:
A、正确选择复合索引中的第一个字段,一般是选择性较好的且在where子句中常用的字段上;
B、复合索引的几个字段经常同时以AND方式出现在Where子句中可以建立复合索引;否则单字段索引;
C、如果复合索引中包含的字段经常单独出现在Where子句中,则分解为多个单字段索引;
D、如果复合索引所包含的字段超过3个,那么仔细考虑其必要性,考虑减少复合的字段;
E、如果既有单字段索引,又有这几个字段上的复合索引,一般可以删除复合索引;
频繁DML的表,不要建立太多的索引;
不要将那些频繁修改的列作为索引列;

索引失效

  1. .没有where条件或者条件中的列不是索引列
    众所周知,添加索引的字段必需要在where条件后适当使用才会生效,如果连查询条件都没有,那肯定不会用到索引的。
  2. 使用 IS NULL 和 IS NOT NULL
    select … from emp where colnum is null; colnum列的索引会失效
  3. WHERE 子句中使用函数
    如果没有使用基于函数的索引,那么 where 子句中对存在索引的列使用函数时,会使优化器忽略掉这些索引。例如:
    select * from staff where trunc(birthdate) = ‘01-MAY-82’;
    但是把函数应用在条件上,索引是可以生效的,把上面的语句改成下面的语句,就可以通过索引进行查找。
    select * from staff where birthdate < (to_date(‘01-MAY-82’) + 0.9999);
    注意:对于 MIN, MAX 函数,Oracle 仍然使用索引。
  4. 使用 LIKE ‘%T’ 进行模糊查询
    select * from student where name like ‘aaa%’ ; // ‘aaa%’ 会用到索引
    select * from student where name like ‘%aaa’ ; //’%aaa’ 或者 ‘_aaa’ 不会使用索引
  5. WHERE 子句中使用不等于操作
    不等于操作包括:<>, !=, NOT colum >= ?, NOT colum <= ?
    对于这个限制条件可以通过 OR 替代,例如: colum <> 0 ===> colum>0 OR colum<0
  6. 等于和范围索引不会被合并使用
    SELECT emp_id, emp_m, salary_q … FROM emp WHERE job=‘manager’ AND deptno>10
    job 和 deptno 都是非唯一索引,这种条件下 oracle 不会合并索引,它只会使用第一个索引。
  7. 比较不匹配数据类型
    dept_id是一个varchar2型的字段,在这个字段上有索引,但是下面的语句会执行全表扫描。
    select * from temp where dept_id = 100101;
    这是因为 oracle 会自动把 where 子句转换成 to_number(dept_id)=900198,相当于使用函数,这样就限制了索引的使用。正确写法如下:
    select * from temp where dept_id = ‘100101’;

索引的优缺点

优点:

  1. 创建唯一性索引,保证数据库表中每一行数据的唯一性
  2. 大大加快数据的检索速度,这也是创建索引的最主要的原因
  3. 加速表和表之间的连接,特别是在实现数据的参考完整性方面特别有意义。
  4. 在使用分组和排序子句进行数据检索时,同样可以显著减少查询中分组和排序的时间。

缺点:

  1. 创建索引和维护索引要耗费时间,这种时间随着数据量的增加而增加
  2. 索引需要占物理空间,除了数据表占数据空间之外,每一个索引还要占一定的物理空间,如果要建立聚簇索引,那么需要的空间就会更大
  3. 当对表中的数据进行增加、删除和修改的时候,索引也要动态的维护,降低了数据的维护速度

事务(ACID)

  • 原子性(Atomicity):一个事务里面所有包含的SQL语句都是一个整体,是不可分割的,要么不做,要么都做。
  • 一致性(Consistency):事务开始时,数据库中的数据是一致的,事务结束时,数据库的数据也应该是一致的。
  • 隔离性(Isolation):数据库允许多个并发事务同时对其中的数据进行读写和修改的能力,隔离性可以防止事务在并发执行时,由于他们的操作命令交叉执行而导致的数据不一致状态。
  • 持久性 (Durability) :当事务结束后,它对数据库中的影响是永久的,即便系统遇到故障的情况下,数据也不会丢失。

一组SQL语句操作要成为事务,数据库管理系统必须保证这组操作的原子性(Atomicity)、一致性(consistency)、隔离性(Isolation)和持久性(Durability),这就是ACID特性。

事务控制语句

commit;提交

rollback;回滚

事务的过程

事务开始于:

DDL语句的执行

事务结束于:

commit;或rollback;

用户退出数据库(正常退出则会后台运行完提交,不正常退出则会回滚)

系统崩溃(回滚)

事务锁

DDL锁:保护数据库对象结构(表、索引)的完整性

DML锁:保护数据的完整性

对某表进行DML语句执行的过程中,会对该表进行DDL锁。

DDL锁是为了防止在事务结束之前,其他用户对该表进行DDL操作

对某表进行DDL语句执行的过程中,会对该表进行DML锁。

DML锁是为了在修改表结构时,其他用户会对该表进行DML操作

DML锁:表级锁™、行锁(TX)

TM锁,面向对象,并且锁定了数据库中的一个对象(表),在锁定期间中,不允许对该对象进行DDL操作

TX锁,面向事务,表示锁定了数据库中的一个事务。

当update语句执行时,Oracle优先申请TM锁,然后在申请TX锁,并标定数据行锁的位置

锁表

锁表的概念;

  1. A程序执行了对 tableA 的 insert ,并还未 commite时,B程序也对tableA 进行insert 则此时会发生资源正忙的异常 就是锁表;
  2. 锁表常发生于并发而不是并行

减少锁表的概率:

  1. 减少insert 、update 、delete 语句执行 到 commite 之间的时间。具体点批量执行改为单个执行、优化sql自身的非执行速度;
  2. 如果异常对事物进行回滚

死锁:

两个事务更新同一个表,并且同时每个事务要更新的数据正在被另外一个事务所锁定。因为两个事务在相互等待资源,两个事务都无法继续下去直到ORACLE产生一个DEADLOCK的错误。

查询锁表:

select s.username,l.object_id, l.session_id,s.serial#, s.lockwait,s.status,s.machine,s.program from v s e s s i o n s , v session s,v sessions,vlocked_object l where s.sid = l.session_id;

杀进程:

alter system kill session ‘301,16405’;

PL/SQL(程序SQL语言)

变量

变量类型

普通变量类型(char、varchar2、date、number、boolean、long、integer)

特殊变量类型 (引用型变量、记录型变量)

变量声明方式

变量名 变量类型(长度)

name varchar2(20);

普通变量赋值

  1. 直接赋值 :=
  2. 语句赋值 select … into …

输出示例

DECLARE-- 姓名name varchar2(20) := '张三';    -- 声明变量直接赋值-- 薪水sal number;-- 地址address varchar2(200);
BEGIN-- 薪水直接赋值sal := 5000;-- 地址语句赋值SELECT '北京市长安大街' INTO address FROM dual;-- 输出dbms_output.put_line('姓名:'||name||' 薪水:'||sal||' 地址:'||address)
END;

引用型变量赋值

变量的类型和长度取决于表中字段的类型和长度

通过表名.列名%TYPE指定变量的类型和长度

p_name test.name%TYPE;

输出示例

DECLARE-- 姓名c_name test.name%TYPE := '张三';    -- 声明变量直接赋值-- 手机c_phone test.phone%TYPE;
BEGIN-- 语句赋值SELECT phone into c_phone FROM test WHERE id = '10001';-- 输出dbms_output.put_line('姓名:'||c_name||' 手机:'||c_phone)
END;

记录型变量赋值

接收表中的一整行数据记录

如果表里边需要引用的字段过多,可以用记录型变量解决复杂操作的问题

变量名称 表名%ROWTYPE

c_test   test%ROWTYPE

输出示例

DECLAREc_test test%ROWTYPE;BEGIN-- 语句赋值SELECT * into c_test FROM test WHERE id = '10001';-- 输出dbms_output.put_line('姓名:'||c_test.c_name||' 手机:'||c_test.c_phone)
END;

流程控制

条件分支

语法

BEGINIF 条件1 THEN 执行1ELSEIF 条件2 THEN 执行2ELSE 执行3END IF;
END;

输出示例

DECLAREv_count integer;
BEGIN-- 语句赋值SELECT count(1) into v_count FROM test;IF v_count > 20 THEN dbms_output.put_line('test表中的记录数超过了20条,记录数是:'||v_count);ELSEIF v_count>=10 THEN dbms_output.put_line('test表中的记录数在10-20条,记录数是:'||v_count);ELSE dbms_output.put_line('test表中的记录数在10条以下,记录数是:'||v_count);END IF;
END;

循环

LOOP循环

语法

BEGINLOOPEXIT WHEN -- 退出循环条件END LOOP;
END;

输出示例

DECLAREv_num integer := 1;
BEGINLOOPEXIT WHEN v_num > 10-- 输出dbms_output.put_line(v_num);v_num := v_num + 1;END LOOP;
END;
WHILE循环
DECLAREi number;
BEGINi:=0;WHILE i<5 LOOPi:=i+1;dbms_output.put_line(i);END LOOP;
END;
FOR循环
DECLAREi number;
BEGINi:=0;FOR i IN 1..5 LOOPdbms_output.put_line(i);END LOOP;
END;
DECLAREuserRow t_user%rowtype;cursor userRows is select * from t_user;
BEGINFOR userRow IN userRows LOOPdbms_output.put_line(userRow.Id||','||userRow.Name||','||userRows%rowcount);END LOOP;
END;

游标

概念

用于临时存储一个查询返回的多行数据的结果集,通过遍历游标,可以逐行访问处理该结果集的数据

游标使用方式:声明—打开—读取—关闭

语法

CURSOR 游标名[(游标参数)] IS 查询语句   -- 声明游标
OPEN 游标名;   -- 打开游标
FETCH 游标名 INTO 变量列表;    -- 游标取值
CLOSE 游标名;  -- 关闭游标

游标的属性

属性 返回值类型 说明
%ROWCOUNT 整型 获取FETCH语句的数据行数
%FOUND 布尔型 最近的FETCH语句的返回一行数据则为真,否则为假
%NOTFOUND 布尔型 与%FOUND相反
%ISOPEN 布尔型 游标已经打开为真,否则为假

输出示例

DECLARECURSOR v_csr IS SELECT name,phone FROM TEST;v_name test.name%TYPE;v_phone test.phone%TYPE;
BEGINOPEN v_csr;LOOP --遍历游标FETCH v_csr INTO v_name,v_phone; --获取游标中的数据EXIT WHEN v_csr%NOTFOUND; -- 退出循环条件dbms_output.put_line(v_name||','||v_phone);END LOOP;CLOSE v_csr;
END;

带参游标

为游标添加参数(类似形参)

输出示例

DECLARECURSOR v_csr(v_id test.id%TYPE) IS SELECT name,phone FROM TEST WHERE id = v_id;v_name test.name%TYPE;v_phone test.phone%TYPE;
BEGINOPEN v_csr(10001);LOOP --遍历游标FETCH v_csr INTO v_name,v_phone; --获取游标中的数据EXIT WHEN v_csr%NOTFOUND; -- 退出循环条件dbms_output.put_line(v_name||','||v_phone);END LOOP;CLOSE v_csr;
END;

注意:循环中(LOOP之后)要加上FETCH,否则在利用EXIT WHEN v_csr%NOTFOUND进行判断时,会默认它有值,然后在执行FETCH后,会带着FETCH之后的结果,进入循环,造成多输出最后一条数据

EXECUTE IMMEDIATE

在PL/SQL中,是可以直接执行DML语句的,同时也可以用execute immediate ‘sql’ 去执行SQL语句

利用execute去执行DML或者DDL语句是比较灵活的,因为它可以执行动态的SQL语句。

DECLAREv_sql   VARCHAR2(200);v_sname stu_info.sname%TYPE;v_num   PLS_INTEGER;
BEGIN-- 用法1 : ddl 语句v_sql := 'create or replace view vw_stu_info as select sno, sname from stu_info';EXECUTE IMMEDIATE v_sql;-- 用法2 :dml 语句 + 动态 sqlv_sql := 'insert into stu_info(sno, sname) values(:b1, :b2)';EXECUTE IMMEDIATE v_sqlUSING 3, '倩倩';-- 用法3:赋值给某个变量v_sql := 'select count(1) from stu_info';EXECUTE IMMEDIATE v_sqlINTO v_num;dbms_output.put_line('总记录条数:' || v_num);-- 记得 commit(同 Oracle 语法)COMMIT;
END;

存储过程

概念

存储过程(Stored Procedure)是在大型数据库系统中,一组为了完成特定功能的SQL 语句集,存储在数据库中,经过第一次编译后再次调用不需要再次编译,用户通过指定存储过程的名字并给出参数(如果该存储过程带有参数)来调用存储过程。

存储过程包含三部分:过程声明,执行过程部分,存储过程异常

创建存储的优点

  1. 效率高

    存储过程编译一次后,就会存到数据库,每次调用时都直接执行。而普通的sql语句我们要保存到其他地方(例如:记事本 上),都要先分析编译才会执行。所以想对而言存储过程效率更高。

  2. 降低网络流量

    存储过程编译好会放在数据库,我们在远程调用时,不会传输大量的字符串类型的sql语句。

  3. 复用性高

    存储过程往往是针对一个特定的功能编写的,当再需要完成这个特定的功能时,可以再次调用该存储过程。

  4. 可维护性高

    当功能要求发生小的变化时,修改之前的存储过程比较容易,花费精力少。

  5. 安全性高

    完成某个特定功能的存储过程一般只有特定的用户可以使用,具有使用身份限制,更安全。

创建存储过程

CREATE [OR REPLACE] PROCEDURE 存储过程名称 [(输入参数或输出参数)] IS -- 声明变量和游标-- 如果程序中无声明可省略
BEGIN-- 执行语句-- [异常处理]
END;

执行存储过程方式

  1. Call 存储过程名称(参数);
  2. Execute (exec)存储过程名称(参数);
  3. PL/SQL内执行

注意:

  1. 在oracle 数据库中,call命令任何窗口都能使用,但是execute只能在命令窗口使用,否则会报无效的SQL语句的异常。
  2. sqlplus客户端的输出选项默认是关闭的,需要用set serveroutput on来打开

在存储过程中需要注意事项

1、在oracle数据库存储过程中,表别名不能用as

2、在oracle数据库存储过程中,select某一字段时,后面必须紧跟into,如果select整个记录则必须使用游标处理

3、在使用select…into…时必须保证数据库有该数据,否则报”no data found”异常

4、在存储过程中,别名不能和字段名相同,否则虽然编译能通过,但是运行结果会报错

无参存储过程

语法
CREATE OR REPLACE PROCEDURE 过程名称 IS
BEGINEND [过程名称];

注:无DECLARE声明,IS后可直接跟上声明

输出示例
CREATE OR REPLACE PROCEDURE p_test IS
BEGIN
dbms_output.put_line('Hello Word');
END [过程名称];

执行示例

call p_test(); -- 所有窗口均可执行
exec p_test; -- 命令窗口执行DECLARE
BEGINp_test();
END;

带参存储过程

语法
CREATE OR REPLACE PROCEDURE 过程名称[(参数列表)]
BEGINEND [过程名称];

输入参数

CREATE OR REPLACE PROCEDURE p_test(p_id IN test.id%TYPE) ISv_name test.name%TYPE;v_phone test.phone%TYPE;
BEGINSELECT name,phone INTO v_name,v_phone FROM test WHERE id = p_iddbms_output.put_line(v_name||','||v_phone);
END;

执行示例

call p_test2('10001');
exec p_test2('10001');DECLARE
BEGINP_TEST('10001');
END;

输出参数

CREATE OR REPLACE PROCEDURE p_test(p_id IN test.id%TYPE,p_name OUT test.name%TYPE) IS
BEGINSELECT name INTO v_name FROM test WHERE id = p_idEND;

执行示例

DECLAREv_name test.name%type; -- 生成一个新的变量去接收存储过程的返回值
BEGINP_TEST('10001',v_name);dbms_output.put_line(v_name);
END;

IN, OUT , IN OUT 用来修饰参数。

IN 表示这个变量必须被调用者赋值然后传入到 PROCEDURE 进行处理。

OUT 表示 PRCEDURE 通过这个变量将值传回给调用者。

IN OUT 则是这两种的组合。

创建函数

语法

create [or replace] function 函数名
([p1,p2...pn])
return datatype
is|as
--声明部分
begin
--PL/SQL程序块
end

函数示例

CREATE OR REPLACE FUNCTION F_MSG(NAME_U IN VARCHAR2,NAME_P IN VARCHAR2
)RETURN NUMBER IS ALL_T NUMBER;BEGIN SELECT SUM(A.PRODUCT_NUM*A.PRICE) INTO ALL_T FROM PRODUCT_RELEASE A,USER_M BWHERE A.USER_ID=B.USER_ID AND B.USER_NAME=NAME_U AND A.PRODUCT_NAME=NAME_PGROUP BY B.USER_NAME,A.PRODUCT_NAME;RETURN ALL_T;END F_MSG;

执行函数的方法

SELECT F_MSG('甲','男人') AS"总额" FROM DUAL;

函数和存储过程的区别

  1. 可以理解函数是存储过程的一种
  2. 函数可以没有参数,但是一定需要一个返回值,存储过程可以没有参数,不需要返回值
  3. 函数return返回值没有返回参数模式,存储过程通过out参数返回值, 如果需要返回多个参数则建议使用存储过程
  4. 在sql数据操纵语句中只能调用函数而不能调用存储过程

触发器

触发器是在事件发生时隐式地自动运行的PL/SQL程序块,不能接收参数,不能被调用。

语法

CREATE [OR REPLACE] TRIGGER trigger_name
{BEFORE | AFTER }
{INSERT | DELETE | UPDATE [OF column [, column …]]}
[OR {INSERT | DELETE | UPDATE [OF column [, column …]]}...]
ON [schema.]table_name | [schema.]view_name
[REFERENCING {OLD [AS] old | NEW [AS] new| PARENT as parent}]
[FOR EACH ROW ]
[WHEN condition]
PL/SQL_BLOCK | CALL procedure_name;