ORACLE日常操作手册
转发自:http://blog.csdn.net/lichangzai/article/details/7955766
以前为开发人员编写的oracle基础操作手册,都基本的oracle操作和SQL语句写法,适合初学者。
因是很久之前写的,文章中可能会存在不准确的地方,希望指正。
ORACLE日常操作手册
目录
1、 以DBA用户登录数据库(如system,sys)...5
2、 delete和truncate 、drop的区别...7
Ø Exists和not exists与in和not in的比较...10
Ø 取得当前日期是一个星期中的第几天,注意星期日是第一天...15
Ø 如果一个表在一个date类型的字段上面建立了索引,如何使用...16
Ø 选择最有效率的表名顺序(只在基于规则的优化器中有效)20
Ø 用EXISTS替代IN和用NOT EXISTS替代NOT IN..24
Ø 避免在索引列上使用IS NULL和IS NOT NULL.28
本文档约定:
1、 文中的数据库主要用到了公司cms、pms库结构
2、 所有SQL都实际的测试通过,放心使用。
3、 如果有再需要了解的部分以后可以再做补充。
l 以DBA的身份登录数据库(要在oracle安装用户下执行sqlplus)
[oracle@DB1 ~]$sqlplus “/as sysdba”
l 执行启动数据库命令
SQL>startup
ORACLE instance started.
Total System Global Area 285212672 bytes
Fixed Size 1218968 bytes
Variable Size 88082024 bytes
Database Buffers 188743680 bytes
Redo Buffers 7168000 bytes
Database mounted.
Database opened.
[oracle@DB1 ~]$lsnrctl start
[oracle@DB1 ~]$lsnrctl stop
2. 数据库的正常关闭步骤
l 同样以DBA的身份登录数据库
[oracle@DB1 ~]$sqlplus “/as sysdba”
l 执行数据库关闭命令
SQL>shutdown immediate;
Database closed.
Database dismounted.
ORACLE instance shut down.
SHUTDOWN有四个参数:NORMAL、TRANSACTIONAL、IMMEDIATE、ABORT。缺省不带任何参数时表示是NORMAL。
SHUTDOWN NORMAL:不允许新的连接、等待会话结束、等待事务结束、做一个检查点并关闭数据文件。启动时不需要实例恢复,这种方法往往不能关闭数据库或等待很长时间。
SHUTDOWN TRANSACTIONAL:不允许新的连接、不等待会话结束、等待事务结束、做一个检查点并关闭数据文件。启动时不需要实例恢复。
SHUTDOWN IMMEDIATE:不允许新的连接、不等待会话结束、不等待事务结束、做一个检查点并关闭数据文件。没有结束的事务是自动rollback的。启动时不需要实例恢复。最常用的方法。
SHUTDOWN ABORT:不允许新的连接、不等待会话结束、不等待事务结束、不做检查点且没有关闭数据文件。启动时自动进行实例恢复。一般不推荐采用,只有在数据库无法关闭时使用,可能造成数据库的不一致。
SQL>conn system/oracle@orcl
注:如果在本地服务器登录@orcl可以去掉
CREATE USER user_name IDENTIFIED BY user_passwordDefaultTablespace tbs_users;
l user_name为数据库用户的用户名
l user_password为数据库用户的密码
l tbs_users为用户使用的表空间,默认是users表空间。
例如:
CREATE USER cmsuser IDENTIFIED BY passwordDefaultTablespace users;
alter user user_name quota unlimited on user_tablespace quota unlimited on user_tablespace;
GRANT connect, resource TO cmsuser;
l Connect用户能登录数据库的权限
l Resource用户能创建一些数据库对像的权限,表、视图,存储过程,一般是授予开发人员的
DropUser cmsuser Cascade;
l 使用cascade参数可以删除该用户的全部objects
l INTEGER存储整数,整数不包括浮点数;它是一个整数数字,如:1、10、15
l NUMBER,是以十进制格式进行存储的,它便于存储,但是在计算上,系统会自动的将它转换成为二进制进行运算的。它的定义方式是NUMBER(P,S),P是精度,最大38位,S是刻度范围,可在-84...127间取值。例如:NUMBER(5,2)可以用来存储表示-999.99...999.99间的数值。P、S可以在定义是省略,例如:NUMBER(5)、NUMBER等;
l CHAR,描述定长的字符串,如果实际值不够定义的长度,系统将以空格填充。它的声明方式如下CHAR(L),L为字符串长度,缺省为1,作为变量最大32767个字符,作为数据存储在ORACLE8中最大为2000。
l VARCHAR2(VARCHAR),描述变长字符串。它的声明方式如下VARCHAR2(L),L为字符串长度,没有缺省值,作为变量最大32767个字节,作为数据存储在ORACLE8中最大为4000。在多字节语言环境中,实际存储的字符个数可能小于L值,例如:当语言环境为中文(SIMPLIFIED CHINESE_CHINA.ZHS16GBK)时,一个VARCHAR2(200)的数据列可以保存200个英文字符或者100个汉字字符。
l NCHAR、NVARCHAR2,国家字符集,与环境变量NLS指定的语言集密切相关,使用方法和CHAR、VARCHAR2相同。不过最大参数为NCHAR(2000)、NVARCHAR2(2000)
l DATE唯一的一种日期类型--,用来存储时间信息,站用7个字节(从世纪到秒)
l LOB(oracle8以前叫long)变量主要是用来存储大量数据的数据库字段,最大可以存储4G字节的内容,CLOB:存储单字节字符数据(如英文)NCLOB:用来存储定宽多字节字符数据(如汉字),BLOB:用来存储无结构的二进制数据(word、pdf文档)。
rowid是Oracle数据库中的每一行都有一个唯一的行标识符,称为rowid,它是一个18位数字,以64为基数,该徝包含了该行在oracle数据库中的物理位置,查询rowid如下:
SQL> Select rowid,id From infobase Where Rownum < 5;
ROWID ID
------------------ ---------
AAAYKRAAEAAGGpcAAI 1000000
AAAYKRAAEAAGGpcAAJ 1000001
AAAYKRAAEAAGGpcAAK 1000002
AAAYKRAAEAAGGpcAAL 1000003
Rowid应用实例:
DeleteFrom Infobase a
Where Rowid < (Select Max(Rowid)From Infobase Where Id = a.Id);
使用下面的语句可以使表处于可编辑状态,可手工添加、删除、更改记录。然后分别点击pl/sql Dev的按
Select t.Rowid,t.*From infobase t;
rownum 被称为“伪数列”,是事实上不存在一个数列,它的特点是按照顺序标记,而且是逐次递加,换句话说只有存在rownum=1的记录,才有可能有rownum=2的记录。查询如下:
SQL> Select Rownum,id From infobase Where Rownum < 5;
ROWNUM ID
---------- ---------
1 1000000
2 1000001
3 1000002
4 1000003
rownum应用实例:
如果要删除的数据量很大,一次删除可能需要占用系统大量的内存,给数据库带来很大的压力,可以进行分步批量删除并提交,避免这种情况
createor replace procedure del_data
as --创建过程并执行
begin
for i in 1..1000loop
delete from cmsuser_zbxy.infobase Where posterid='Servlet提交'and rownum < 100;
commit;
end loop;
End del_data;
Select *
From (Select * From InfobaseOrder By Originaltime Desc)
Where Rownum <= 10;
TRUNCATE TABLE 在功能上与不带WHERE子句的DELETE语句相同:二者均删除表中的全部行。但 TRUNCATE TABLE比 DELETE速度快,且使用的系统和事务日志资源少。
Drop 则是删除整个表,与TRUNCATE操作类型相同,都是DDL操作(数据定义语言)
DeleteFrom infobase Id = 1;
Commit;
--或
Delete infobase Where Id = 1;
Commit;
TruncateTable infobase;
DropTable infobase;
根据连接中使用操作符的不同,连接条件可以分为两类:
l 等连接:在连接中使用等于操作符(=)
l 不等连接:在连接中使用除等号之外的操作符,如:<、>、between等
除连接条件区分之外,连接本身也有3种不同的类型:
l 内连接:只有当连接中的列包含满足连接条件的值时才会返回一行。这就是说,如果某一行的连接条件中的一列是空值,那么这行就不返回。
l 外连接:即使连接条件中的一列包含空值也会返回一行。
l 自连接:返回连接的同一个表中的行。
Select e.first_name,e.title,e.salary,sg.salary_grade_id
From employees e,salary_grades sg
Where e.salary Between sg.low_salary And sg.high_salary;
--employees员工表,salary_grades工资等级表
Select a.Id, b.Name, a.Title, a.Content
From Infobase a
Join Class b On a.Classid = '(' || b.Id || ')';
--或
Select a.Id, b.Name, a.Title, a.Content
From Infobase a Class b
Where a.Classid = '(' || b.Id ||')';
Update Infobase a
Set a.Title = (Select Title From Infobase_TempWhere Id = a.Id);
Commit;
Delete From Infobase a
Where Exists ( Select 1 From Class bWhere a.classid = b.id);
Commit;
Select a.*
From Infobase a
Left Join Attachment b On a.Id = b.Infoid
Where b.Infoid Is Null;
--这是典型两表相减查询,也可用not in,但是种写法效率会高些
--这是一个左外连接例子,右外连接跟左外连接一样,只是表的位置不同
Select w.Last_Name ||' works for ' || m.Last_Name
From Employees m, Employees w
Where w.Employee_Id = m.Manager_Id;
下面的语法也可以看做成一个隐含的自连接查询,它是一个字列和父列的递归查询
Select *
From Class
Start With Parentid = 0001
Connect By Prior Id = Parentid
--id,parentid那么通过表示每一条记录的parent是谁,就可以形成一个树状结构
--Parentid = 0001指定树的根从哪个节点开始
子查询有两种基本类型:
l 单行子查询:不向外部的SQL返回结果,或者只返回一行。
l 多行子查询:向外部的SQL返回一行或多行。
另外子查询还有三种子类型:
l 多列子查询:向外部的SQL语句返回多列。
l 关联子查询:引用外的SQL语句中的一钱或多列。
l 嵌套子查询:位于另外一个子查询中。子查询最多可以嵌套255层。
Select *
From Infobase
Where Contentsize > (Select Avg(Contentsize) From Infobase);
检索那些平均价格低于同类产品平均价格最大值的产品的product_type_id和平均价格:
Select Product_Type_Id,Avg(Price)
From Products
Group By Product_Type_Id
Having Avg(Price) < (Select Max(Avg(Price))
From Products
Group By Product_Type_Id);
就外部查询的from子句而言,子查询的输出仅仅是另外一个数据源。
检索Productid大于100的产品
Select Productid
From (Select Productid From ProductWhere Productid < 100);
在外部查询中从products表中检索product_id和price列,在子查询中检索一种产品已经被购买的次数:
Select a.Product_Id, a.Price, b.Product_Count
From Products a,
(Select Product_Id, Count(Product_Id) Product_Count
From Purchases
Group By Product_Id) b
Where a.Product_Id = b.Product_Id;
(1)、单行查询最多返回一行
SQL> Select Productid, Productname
2 From Product
3 Where Productid =
4 (Select Productid From Product Where Productname Like '恒泰%');
Select Productid, Productname
ORA-01427:单行子查询返回多于一个行
(2)、子查询不能包含order by子句,必须在外查询中进行任何排序
检索信息表里符合classid条件的记录:
Select *
From Infobase
Where Classid In
(Select '(' ||Id || ')'From Class Where Name Like '营业部%')
检查是否有任何员工的工资低于salary_grades表中任何一级的最低工资:
Select e.Employee_Id, e.Last_Name
From Employees e
Where e.Salary < Any (Select Sg.Low_SalaryFrom Salary_Grades Sg);
检查是否有任何员工的工资高于salary_grades表中所有级别的最高工资:
Select e.Employee_Id, e.Last_Name
From Employees e
Where e.Salary > All (Select sg.high_salaryFrom Salary_Grades Sg);
Select *
From Products
Where (Product_Type_Id, Price) In
(Select Product_Type_Id, Min(Price)
From Products
Group By Product_Type_Id);
--上面的写法也如同下面的写法,返回结果一样
Select *
From Products a
Where Price = (Select Min(Price)
From Products
Where Product_Type_Id = a.Product_Type_Id);
--注意:这个例子是日常的开发很典型的例子,会经常用到,一定要学会应用
检索那些负责管理其它员工的员工记录:
Select Employee_Id, Last_Name
From Employees
Outer Where Exists
(Select Employee_Id
From Employees Inner
Inner Where Inner.Manager_Id = Outer.Employee_Id);
检索从未购买过的产品
Select Product_Id,Name
From Products a
Where Not Exists (Select 1 From Purchases Where Product_Id = a.Product_Id)
-- 子句的1是个虚拟列,没有意义,改成其它值也可以
Ø Exists和not exists与in和not in的比较
Exists与in不同,Exists只检查行的存在性,而in则要检查实际值的存在性。
通常来讲,Exists的性能要比in要高一些,因此应该尽可能地使用Exists,而不用in。
在编写使用Not Exists和Not in的查询时必须要谨慎。当一个值列表包含一个空值时,Not Exists就返回true,而Not in 则返回false。考虑下面这个例子:本例使用了Not Exists,检索那些在products表中没有任何产品的产品类型:
Select Product_Type_Id,Name
From Product_Types a
Where Not Exists
(Select 1From Products Where Product_Type_Id = a.Product_Type_Id);
PRODUCT_TYPE_ID NAME
---------------------------------- ----------
5 Magazine
注意上面这个例子返回了一行记录。下面这个例子使用Not in重写了上面这个例子,而此时没有返回任何行:
Select Product_Type_Id,Name
From Product_Types a
Where Product_Type_Id Not In (Select Product_Type_IdFrom Products);
PRODUCT_TYPE_ID NAME
---------------------------------- ----------
这所以没有返回行,就是因为子查询返回Product_Type_Id值的列表,其中包含一个空值。而产品#12的Product_Type_Id是空值。因此外部查询中的Not in操作符返回false,因此没有任何行。这个问题可以使用Nvl()函数将空值转换成一个值解决。
下面的例子中,Nvl()函数将空值的Product_Type_Id转换成0:
Select Product_Type_Id,Name
From Product_Types a
Where Product_Type_Id Not In (Select nvl(Product_Type_Id,0)From Products);
PRODUCT_TYPE_ID NAME
---------------------------------- ----------
5 Magazine
这次返回了我们要得到的那行记录。
在子查询内部可以嵌套其它子查询,嵌套层次最多为255。在编写时应该尽量少使用嵌套子查询技术,因为使用表连接时,查询性能会更高。下面的例子包含了一个嵌套子查询,子查询包含了另外一个子查询,而它自己又被包含在一个外部查询中:
Select Product_Type_Id,Avg(Price)
From Products
Group By Product_Type_Id
Having Avg(Price) < (Select Max(Avg(Price))
From Products
Where Product_Type_Id In
(Select Product_Id
From Purchases
Where Quantity >= 1)
Group By Product_Type_Id);
这个查询包含了3个查询:一个嵌套子查询、一个子查询和一个外部查询。可以由里到外自己逐步分析,得到运行结果。
集合操作符可以将两个或多个查询返回的行组合起来,当使用集合操作符的时候,必须牢记下列的限制条件:所有查询所返回的列数以及列的类型必须匹配,列名可以不同。
集合操作符主要有:
l Union all 返回各个查询检索出的所有行,包括重复的行。
l Union 返回各个查询检索出的所有行,不包括重复的行。
l Intersect 返回两个查询共有行。
l Minus 返回第二个查询检索出的行从第一个查询检索出的行中减去之后剩余的记录。
Union all 返回各个查询检索出的所有行,包括重复的行
SelectId,classid From infobase Where Rownum <5
Union All
Select Id,classid From infobase_temp;
ID CLASSID
---------- --------------------------------------------------------------------------------
1000000 (000100010002)
1000001 (000200020003000100010002)
1000002 (000100010003)
1000005 (00010001000400030007)
1000000 (000100010002)
1000001 (000200020003000100010002)
可以使用order by子句根据两个查询中的列的位置对列进行排序。
SelectId,classid From infobase Where Rownum <5
Union All
Select Id,classid From infobase_temp
Order By 1;
ID CLASSID
---------- --------------------------------------------------------------------------------
1000000 (000100010002)
1000000 (000100010002)
1000001 (000200020003000100010002)
1000001 (000200020003000100010002)
1000002 (000100010003)
1000005 (00010001000400030007)
返回各个查询检索出的所有行,不包括重复的行。因为Union查询时要有排重操作,所以Union all要比Union操作效率要高一些。
SelectId,classid From infobase Where Rownum <5
Union
Select Id,classid From infobase_temp;
ID CLASSID
---------- --------------------------------------------------------------------------------
1000000 (000100010002)
1000001 (000200020003000100010002)
1000002 (000100010003)
1000005 (00010001000400030007)
Intersect 返回两个查询共有行
只检索出那些infobase与infobase_temp共有的行
SelectId,classid From infobase Where Rownum <5
intersect
Select Id,classid From infobase_temp;
ID CLASSID
---------- --------------------------------------------------------------------------------
1000000 (000100010002)
1000001 (000200020003000100010002)
Minus 返回第二个查询检索出的行从第一个查询检索出的行中减去之后剩余的记录.
下例是从infobase返回的行中减去从infobase_temp中返回的行,然后返回剩余的行:
SelectId,classid From infobase Where Rownum <5
Minus
Select Id,classid From infobase_temp;
ID CLASSID
---------- --------------------------------------------------------------------------------
1000002 (000100010003)
1000005 (00010001000400030007)
Decode(value,search_value,result,default_value)对value与search_value进行比较,如果两个值相等,Decode()返回result,否则返回default_value。Decode()允许if-then-else类型的逻辑处理,而不需要使用pl/sql。
下面是个简单的例子:
SQL> Select decode(1,1,2,3) From dual;
DECODE(1,1,2,3)
---------------
2
因为对1与1进行比较,由于两者相等,所以返回2(否则返回3)
Decode通常在写SQL时与dual表结合给变量赋值。
下面这个例子对more_products中的available列进行比较。如果available等于Y,返回字符串Product is available,否则返回字符串Product is not available:
Select Prd_Id,
Available,
Decode(Available,
'Y',
'Product is available',
'Product is not available')
From More_Products;
PRD_ID AVAILABLE DECODE(AVAILABLE,'Y','PRODUCTI
--------------------------------------- --------- ------------------------------
1 Y Product is available
2 Y Product is available
3 N Product is not available
4 N Product is not available
5 Y Product is available
可以向Decode()传递多个搜索和结果参数,如下例:
Select Product_Id,
Product_Type_Id,
Decode(Product_Type_Id, 1,'Book', 2, 'Video',3, 'Dvd','CD')
From Products;
如果Product_Type_Id=1,返回Book
如果Product_Type_Id=2,返回Video
如果Product_Type_Id=3,返回Dvd
如果Product_Type_Id等于其它值,返回CD
case允许if-then-else类型的逻辑处理,而不需要使用pl/sql。Case的工作方式与Decode()类似,通常我们在有较少的判断时使用decode,因为条件多话会看着很混乱;所以尽量使用case,它与ANSI兼容。
有两种类型的case表达式:
l 简单case表达式,使用表达式确定返回值。
l 搜索case表达式,使用条件确定返回值。
使用简单表达式例子:
Select Product_Id,
Product_Type_Id,
Case Product_Type_Id
When 1Then 'Book'
When 2Then 'Video'
When 3Then 'Dvd'
Else 'CD'
End
From Products;
PRODUCT_ID PRODUCT_TYPE_ID CASEPRODUCT_TYPE_IDWHEN1THEN'B
--------- -------------------------------- ------------------------------
1 1 Book
2 1 Book
3 2 Video
4 2 Video
5 2 Video
6 2 Video
7 3 Dvd
8 3 Dvd
9 4 CD
使用搜索case表达式
Select Product_Id,
Product_Type_Id,
Case
When Product_Type_Id = 1Then 'Book'
When Product_Type_Id = 2 Then 'Video'
When Product_Type_Id = 3 Then 'Dvd'
Else 'CD'
End
From Products;
返回结果中上面是一样的
l MONTHS_BETWEEN两日期相差多少月
l ADD_MONTHS 加月份到日期
l NEXT_DAY 指定日期的下一天
l LAST_DAY 一个月中的最后一天
l ROUND Round日期
l TRUNC Truncate日期
l TO_CHAR(x[,format])函数用于将时间值转换为字符串,该函数还可以提供一个可选的参数format来说明x的格式。如:MONTH DDD,YYYY
l TO_DATE(x[,format])将字符串x转换成date类型。
下面是几个关于日期方面的SQL实例
SQL> select to_char(sysdate,'YYYYMMDD W HH24:MI:SS') from dual;
TO_CHAR(SYSDATE,'YYYYMMDDWHH24
------------------------------
20090202 1 18:00:43
SQL> select to_char(sysdate,'W') from dual;
TO_CHAR(SYSDATE,'W')
--------------------
1
SQL> select sysdate,to_char(sysdate,'D') from dual;
SYSDATE TO_CHAR(SYSDATE,'D')
----------- --------------------
2009-2-2 18 2
select to_char(sysdate,'yyyy') from dual; --年
select to_char(sysdate,'Q' from dual; --季
select to_char(sysdate,'mm') from dual; --月
select to_char(sysdate,'dd') from dual; --日
ddd年中的第几天
WW年中的第几个星期
W该月中第几个星期
D周中的星期几
hh小时(12)
hh24小时(24)
Mi分
ss秒
SQL> select to_char(sysdate,'day') from dual;
TO_CHAR(SYSDATE,'DAY')
----------------------
星期四
Ø 如果一个表在一个date类型的字段上面建立了索引,如何使用
alter session set NLS_DATE_FORMAT='YYYY-MM-DD HH24:MI:SS'
select sysdate from dual;
SYSDATE
-----------
2009-2-2 18
select trunc(sysdate) from dual;
TRUNC(SYSDATE)
--------------
2009-2-2
select trunc(sysdate) + 0.99999 from dual;
TRUNC(SYSDATE)+0.99999
----------------------
2009-2-2 23:59:59
select trunc(sysdate) + 1/24 from dual;
TRUNC(SYSDATE)+1/24
-------------------
2009-2-2 1:00:00
select trunc(sysdate) + 7/24 from dual;
TRUNC(SYSDATE)+7/24
-------------------
2009-2-2 7:00:00
select trunc(sysdate+1) from dual;
TRUNC(SYSDATE+1)
----------------
2009-2-3
select trunc(sysdate,'mm') from dual;
TRUNC(SYSDATE,'MM')
-------------------
2009-2-1
select trunc(add_months(sysdate,1),'mm') from dual;
TRUNC(ADD_MONTHS(SYSDATE,1),'M
------------------------------
2009-3-1
SQL> select last_day(sysdate) from dual;
LAST_DAY(SYSDATE)
-----------------
2009-2-28 18:11:3
SQL> select last_day(trunc(sysdate)) from dual;
LAST_DAY(TRUNC(SYSDATE))
------------------------
2009-2-28
SQL> select trunc(last_day(sysdate)) from dual;
TRUNC(LAST_DAY(SYSDATE))
------------------------
2009-2-28
SQL> select trunc(add_months(sysdate,1),'mm') - 1 from dual;
TRUNC(ADD_MONTHS(SYSDATE,1),'M
------------------------------
2009-2-28
Select Trunc(Sysdate, 'yyyy') + Rn - 1 Date0
From (Select Rownum Rn From All_Objects Where Rownum < 366);
DATE0
-----------
2009-1-1
2009-1-2
2009-1-3
……
今天是今年的第N天
SQL> SELECT TO_CHAR(SYSDATE,'DDD') FROM DUAL;
TO_CHAR(SYSDATE,'DDD')
----------------------
033
SQL> select add_months(sysdate,24) from dual;
ADD_MONTHS(SYSDATE,24)
----------------------
2011-2-2 18:15:56
SQL> select decode(to_char(last_day(trunc(sysdate,'y')+31),'dd'),'29','闰年','平年') from dual;
DECODE(TO_CHAR(LAST_DAY(TRUNC(
------------------------------
平年
SQL> select decode(to_char(last_day(trunc(add_months(sysdate,24),'y')+31),'dd'),'29','闰年','平年') from dual;
DECODE(TO_CHAR(LAST_DAY(TRUNC(
------------------------------
平年
SQL> select ceil(to_number(to_char(sysdate,'mm'))/3) from dual;
CEIL(TO_NUMBER(TO_CHAR(SYSDATE
------------------------------
1
SQL> select to_char(sysdate, 'Q') from dual;
TO_CHAR(SYSDATE,'Q')
--------------------
1
ORACLE 采用两种访问表中记录的方式:
l 全表扫描
全表扫描就是顺序地访问表中每条记录. ORACLE采用一次读入多个数据块(database block)的方式优化全表扫描.
l 通过ROWID访问表
你可以采用基于ROWID的访问方式情况,提高访问表的效率, , ROWID包含了表中记录的物理位置信息..ORACLE采用索引(INDEX)实现了数据和存放数据的物理位置(ROWID)之间的联系.通常索引提供了快速访问ROWID的方法,因此那些基于索引列的查询就可以得到性能上的提高.
索引是表的一个概念部分,用来提高检索数据的效率.通过索引查询数据比全表扫描要快.当ORACLE找出执行查询和Update语句的最佳路径时, ORACLE优化器将使用索引.同样在联结多个表时使用索引也可以提高效率. 另一个使用索引的好处是,它提供了主键(primary key)的唯一性验证.除了那些LONG或LONG RAW、LOB数据类型,你可以索引几乎所有的列. 通常,在大型表中使用索引特别有效. 当然,你也会发现,在扫描小表时,使用索引同样能提高效率.
虽然使用索引能得到查询效率的提高,但是我们也必须注意到它的代价.索引需要空间来
存储,也需要定期维护,每当有记录在表中增减或索引列被修改时, 索引本身也会被修改.这意味着每条记录的INSERT , DELETE , UPDATE将为此多付出4 , 5次的磁盘I/O . 因为索引需要额外的存储空间和处理,那些不必要的索引反而会使查询反应时间变慢。
大多数情况下,优化器通过WHERE子句访问INDEX.
定期的重构索引是有必要的.
ALTER INDEX <INDEXNAME> REBUILD <TABLESPACENAME>
CreateIndex infobase_title On infobase(title);
--创建唯一索引
Createunique Index infobase_key On infobase (Id);
--创建全文索引
CREATE INDEX infobase_content ON infobase(content)INDEXTYPE IS CTXSYS.CONTEXT;
--在全文索引进行检索
SELECT * FROM infobase WHERE CONTAINS (content,'first') > 0;
--创建同步全文索引过程
create or replace procedure sync_content
is
begin
execute immediate
'alter index infobase_content rebuild online' ||
' parameters ( ''sync'' )' ;
execute immediate
'alter index infobase_content rebuild online' ||
' parameters ( ''optimize full maxtime unlimited'' )' ;
end sync_content;
/
--创建作业执行同步过程
variable n number;
begin
dbms_job.submit(:n,'sync_content;',sysdate,
'sysdate+1/48');
commit;
end;
/
AlterTable infobase
Add Constraints infobase_key Primary Key(Id);
--表上创建主建相当于在列上的建了一个唯一的索引
在WHERE子句中,如果索引列所对应的值的第一个字符由通配符(WILDCARD)开始,索引将不被采用.如:like ‘%标题’。
这一点一定要注意。因为在我们开发的过程中经常遇到这样的问题,like ‘%标题%’会扫描全表,会给数据库的性能带来很大的压力。要尽可能避免这种写法,如果有必要可以用全文索引代替。如下面的例子:
selectcount(*) from infobase
where classid in ('(0001000300030001)','(0001000300030002)')
and (category like '%600755%' or category like'%600976%');
--可用全文索引代替
select count(*) from infobase
where classid in ('(0001000300030001)','(0001000300030002)')
and (CONTAINS (category, '600755') > 0or CONTAINS (category, '600976') > 0 );
但like ‘标题%’这种写法会使用索引
ORACLE的解析器按照从右到左的顺序处理FROM子句中的表名,因此FROM子句中写在最后的表(基础表 driving table)将被最先处理. 在FROM子句中包含多个表的情况下,你必须选择记录条数最少的表作为基础表.当ORACLE处理多个表时,会运用排序及合并的方式连接它们.首先,扫描第一个表(FROM子句中最后的那个表)并对记录进行派序,然后扫描第二个表(FROM子句中最后第二个表),最后将所有从第二个表中检索出的记录与第一个表中合适记录进行合并.
例如:
表 TAB1 16,384条记录
表 TAB2 1条记录
选择TAB2作为基础表 (最好的方法)
select count(*) from tab1,tab2
执行时间0.96秒
选择TAB2作为基础表 (不佳的方法)
select count(*) from tab2,tab1
执行时间26.09秒
如果有3个以上的表连接查询,那就需要选择交叉表(intersection table)作为基础表,交叉表是指那个被其他表所引用的表.
例如:
EMP表描述了LOCATION表和CATEGORY表的交集.
SELECT *
FROM LOCATION L ,
CATEGORY C,
EMP E
WHERE E.EMP_NO BETWEEN 1000 AND 2000
AND E.CAT_NO = C.CAT_NO
AND E.LOCN = L.LOCN
将比下列SQL更有效率
SELECT *
FROM EMP E ,
LOCATION L ,
CATEGORY C
WHERE E.CAT_NO = C.CAT_NO
AND E.LOCN = L.LOCN
AND E.EMP_NO BETWEEN 1000 AND 2000
ORACLE采用自下而上的顺序解析WHERE子句,根据这个原理,表之间的连接必须写在其他WHERE条件之前,那些可以过滤掉最大数量记录的条件必须写在WHERE子句的末尾.
例如:
第二个SQL要比第一个SQL查询效率高:
SELECT *
FROM EMP E
WHERE SAL > 50000
AND JOB = ‘MANAGER'
AND 25 < (SELECT COUNT(*) FROM EMP
WHERE MGR=E.EMPNO);
SELECT *
FROM EMP E
WHERE 25 < (SELECT COUNT(*) FROM EMP
WHERE MGR=E.EMPNO)
AND SAL > 50000
AND JOB = ‘MANAGER';
当你想在SELECT子句中列出所有的COLUMN时,使用动态SQL列引用‘*'是一个方便的方法.不幸的是,这是一个非常低效的方法.实际上,ORACLE在解析的过程中,会将'*' 依次转换成所有的列名,这个工作是通过查询数据字典完成的, 这意味着将耗费更多的时间.
当执行每条SQL语句时, ORACLE在内部执行了许多工作:解析SQL语句,估算索引的利用率, 绑定变量 ,读数据块等等. 由此可见,减少访问数据库的次数 , 就能实际上减少ORACLE的工作量.
例如,
以下有三种方法可以检索出雇员号等于0342或0291的职员.
方法1 (最低效)
SELECT EMP_NAME , SALARY , GRADE
FROM EMP
WHERE EMP_NO = 342;
SELECT EMP_NAME , SALARY , GRADE
FROM EMP
WHERE EMP_NO = 291;
方法2 (次低效)
DECLARE
CURSOR C1 (E_NO NUMBER) IS
SELECT EMP_NAME,SALARY,GRADE
FROM EMP
WHERE EMP_NO = E_NO;
BEGIN
OPEN C1(342);
FETCH C1 INTO …,..,.. ;
…..
OPEN C1(291);
FETCH C1 INTO …,..,.. ;
CLOSE C1;
END;
方法3 (高效)
SELECT A.EMP_NAME , A.SALARY , A.GRADE,
B.EMP_NAME , B.SALARY , B.GRADE
FROM EMP A,EMP B
WHERE A.EMP_NO = 342
AND B.EMP_NO = 291;
只要有可能,在程序中尽量多使用COMMIT,这样程序的性能得到提高,需求也会因为COMMIT所释放的资源而减少:
COMMIT所释放的资源:
a. 回滚段上用于恢复数据的信息.
b. 被程序语句获得的锁
c. redo log buffer 中的空间
d. ORACLE为管理上述3种资源中的内部花费
注意: 在使用COMMIT时必须要注意到事务的完整性,现实中效率和事务完整性往往是鱼和熊掌不可得兼
在含有子查询的SQL语句中,要特别注意减少对表的查询.
例如:
低效
Select Tab_Name
From Tables
Where Tab_Name = (Select Tab_Name From Tab_ColumnsWhere Version = 604)
And db_Ver = (Select Db_VerFrom Tab_Columns Where Version = 604);
高效
Select Tab_Name
From Tables
Where (Tab_Name, Db_Ver) = (Select Tab_Name, Db_Ver From Tab_Columns
Where Version = 604);
Update 多个Column例子:
低效:
Update Emp
Set Emp_Cat = (Select Max(Category)From Emp_Categories),
Sal_Range = (Select Max(Sal_Range) From Emp_Categories)
Where Emp_Dept = 0020;
高效:
Update Emp
Set (Emp_Cat, Sal_Range) = (Select Max(Category),Max(Sal_Range)
From Emp_Categories)
Where Emp_Dept = 0020;
下面是一个复杂的多表关联查询:
Select h.Empno, e.Ename, h.Hist_Type, t.Type_Desc,Count(*)
From History_Type t, Emp e, Emp_History h
Where h.Empno = e.Empno
And h.Hist_Type = t.Hist_Type
Group By h.Empno, e.Ename, h.Hist_Type, t.Type_Desc;
通过调用下面的函数可以提高效率:
Function Lookup_Hist_Type(TypIn Number) Return Varchar2 As
Tdesc Varchar2(30);
Cursor C1 Is
Select Type_Desc From History_Type Where Hist_Type = Typ;
Begin
Open C1;
Fetch C1
Into Tdesc;
Close C1;
Return(Nvl(Tdesc, '?'));
End;
Function Lookup_Emp(Emp In Number) Return Varchar2 As
Ename Varchar2(30);
Cursor C1 Is
Select Ename From Emp Where Empno = Emp;
Begin
Open C1;
Fetch C1
Into Ename;
Close C1;
Return(Nvl(Ename, '?'));
End;
--可用下在SQL来调用上面的两个函数
Select h.Empno,
Lookup_Emp(h.Empno),
h.Hist_Type,
Lookup_Hist_Type(h.Hist_Type),
Count(*)
From Emp_History h
Group By h.Empno, h.Hist_Type;
注:有时一个复杂的查询可以用一个复杂的SQL可以完成,殊不知复杂的SQL往往牺牲了执行效率. 能够掌握上面的运用函数解决问题的方法在实际工作中是非常有意义的。
当在SQL语句中连接多个表时,请使用表的别名并把别名前缀于每个Column上.这样一来,就可以减少解析的时间并减少那些由Column歧义引起的语法错误.
Ø 用EXISTS替代IN和用NOT EXISTS替代NOT IN
在前面的章节已经介绍过,不在描述。
通常来说 , 采用表连接的方式比EXISTS更有效率
Select Ename
From Emp e
Where Exists (Select 'X'
From Dept
Where Dept_No = e.Dept_No
And Dept_Cat = 'A');
更高效
Select Ename
From Dept d, Emp e
Where e.Dept_No = d.Dept_No
And Dept_Cat = 'A';
当提交一个包含一对多表信息(比如部门表和雇员表)的查询时,避免在SELECT子句中使用DISTINCT.一般可以考虑用EXIST替换
例如:
低效:
SelectDistinct Dept_No, Dept_Name
From Dept d, Emp e
Where d.Dept_No = e.Dept_No;
高效:
Select Dept_No, Dept_Name
From Dept d
Where Exists (Select 'X' From Emp e Where e.Dept_No = d.Dept_No);
EXISTS 使查询更为迅速,因为RDBMS核心模块将在子查询的条件一旦满足后,立刻返回结
果.
当WHERE子句中有索引列, ORACLE不能合并它们,ORACLE将用范围比较.
举例:
DEPTNO上有一个非唯一性索引,EMP_CAT也有一个非唯一性索引.
Select Ename
From Emp
Where Deptno > 20
And Emp_Cat = 'A';
这里只有EMP_CAT索引被用到,然后所有的记录将逐条与DEPTNO条件进行比较.执行路径如下:
TABLEACCESS BY ROWID ON EMP
INDEX RANGE SCAN ON CAT_IDX
当ORACLE无法判断索引的等级高低差别,优化器将只使用一个索引,它就是在WHERE子句中被列在最前面的.
举例:
DEPTNO上有一个非唯一性索引,EMP_CAT也有一个非唯一性索引.
Select Ename
From Emp
Where Deptno > 20
And Emp_Cat > 'A';
这里, ORACLE只用到了DEPT_NO索引.执行路径如下:
TABLEACCESS BY ROWID ON EMP
INDEX RANGE SCAN ON DEPT_IDX
我们来试一下以下这种情况:
SQL> select index_name, uniqueness from user_indexes where table_name = 'EMP';
INDEX_NAME UNIQUENES
------------------------------ ---------
EMPNO UNIQUE
EMPTYPE NONUNIQUE
SQL> select * from emp where empno >= 2 and emp_type = 'A' ;
no rows selected
Execution Plan
----------------------------------------------------------
0 SELECT STATEMENT Optimizer=CHOOSE
1 0 TABLE ACCESS (BY INDEX ROWID) OF 'EMP'
2 1 INDEX (RANGE SCAN) OF 'EMPTYPE' (NON-UNIQUE)
虽然EMPNO是唯一性索引,但是由于它所做的是范围比较,等级要比非唯一性索引的等式比较低!
如果两个或以上索引具有相同的等级,你可以强制命令ORACLE优化器使用其中的一个(通过它,检索出的记录数量少)
举例:
Select Ename
From Emp
Where Empno = 7935
And Deptno + 0 =10/*DEPTNO上的索引将失效*/
And Emp_Type || '' ='A'/*EMP_TYPE上的索引将失效*/
这是一种相当直接的提高查询效率的办法.但是你必须谨慎考虑这种策略,一般来说,只有在你希望单独优化几个SQL时才能采用它.
这里有一个例子关于何时采用这种策略:
假设在EMP表的EMP_TYPE列上有一个非唯一性的索引而EMP_CLASS上没有索引.
Select Ename
From Emp
Where Emp_Type = 'A'
And Emp_Class = 'X';
优化器会注意到EMP_TYPE上的索引并使用它.这是目前唯一的选择. 如果,一段时间以后,另一个非唯一性建立在EMP_CLASS上,优化器必须对两个索引进行选择,在通常情况下,优化器将使用两个索引并在他们的结果集合上执行排序及合并.然而,如果其中一个索引(EMP_TYPE)接近于唯一性而另一个索引(EMP_CLASS)上有几千个重复的值.排序及合并就会成为一种不必要的负担. 在这种情况下,你希望使优化器屏蔽掉EMP_CLASS索引.
用下面的方案就可以解决问题.
Select Ename
From Emp
Where Emp_Type = 'A'
And Emp_Class || ‘’ ='X';
WHERE子句中,如果索引列是函数的一部分.优化器将不使用索引而使用全表扫描.
举例:
低效:
Select *From Dept
Where Sal * 12 >25000;
高效:
Select *From Dept
Where Sal > 25000 / 12;
注意:这是一个非常实用的规则,请务必牢记
如果表中有两个以上(包括两个)索引,其中有一个唯一性索引,而其他是非唯一性.
在这种情况下,ORACLE将使用唯一性索引而完全忽略非唯一性索引.
举例:
Select Ename
From Emp
Where Empno = 2326
And Deptno = 20;
这里,只有EMPNO上的索引是唯一性的,所以EMPNO索引将用来检索记录.
TABLEACCESS BY ROWID ON EMP
INDEX UNIQUE SCAN ON EMP_NO_IDX
通常, 我们要避免在索引列上使用NOT, NOT会产生在和在索引列上使用函数相同的
影响. 当ORACLE”遇到”NOT,他就会停止使用索引转而执行全表扫描.
举例:
低效: (这里,不使用索引)
SELECT *FROM DEPT
WHERE DEPT_CODE NOT = 0;
高效: (这里,使用了索引)
SELECT *FROM DEPT
WHERE DEPT_CODE > 0;
需要注意的是,在某些时候, ORACLE优化器会自动将NOT转化成相对应的关系操作符.
NOT > to <=
NOT >= to <
NOT < to >=
NOT <= to >
注意:
在下面的这个测试例子中,故意犯了一些错误.例子中的低效率SQL是不能被执行的.
SQL> select * from emp where NOT empno > 1;
no rows selected
Execution Plan
----------------------------------------------------------
0 SELECT STATEMENT Optimizer=CHOOSE
1 0 TABLE ACCESS (BY INDEX ROWID) OF 'EMP'
2 1 INDEX (RANGE SCAN) OF 'EMPNO' (UNIQUE)
SQL> select * from emp where empno <= 1;
no rows selected
Execution Plan
----------------------------------------------------------
0 SELECT STATEMENT Optimizer=CHOOSE
1 0 TABLE ACCESS (BY INDEX ROWID) OF 'EMP'
2 1 INDEX (RANGE SCAN) OF 'EMPNO' (UNIQUE)
两者的效率完全一样,也许这符合关于”在某些时候, ORACLE优化器会自动将NOT转化成相对应的关系操作符”的观点.
Ø 避免在索引列上使用IS NULL和IS NOT NULL
避免在索引中使用任何可以为空的列,ORACLE将无法使用该索引.对于单列索引,如果列包含空值,索引中将不存在此记录.对于复合索引,如果每个列都为空,索引中同样不存在此记录. 如果至少有一个列不为空,则记录存在于索引中.
举例:
如果唯一性索引建立在表的A列和B列上,并且表中存在一条记录的A,B值为(123,null) , ORACLE将不接受下一条具有相同A,B值(123,null)的记录(插入).然而如果
所有的索引列都为空,ORACLE将认为整个键值为空而空不等于空.因此你可以插入1000
条具有相同键值的记录,当然它们都是空!
因为空值不存在于索引列中,所以WHERE子句中对索引列进行空值比较将使ORACLE停用该索引.
举例:
低效: (索引失效)
SELECT …
FROM DEPARTMENT
WHERE DEPT_CODE IS NOT NULL;
高效: (索引有效)
SELECT …
FROM DEPARTMENT
WHERE DEPT_CODE >=0;
如果索引是建立在多个列上,只有在它的第一个列(leading column)被where子句引用时,优化器才会选择使用该索引.
注意:这也是一条简单而重要的规则.
见以下实例.
SQL> create table multiindexusage ( inda number , indb number , descr varchar2(10));
Table created.
SQL> create index multindex on multiindexusage(inda,indb);
Index created.
SQL> set autotrace traceonly
SQL> select * from multiindexusage where inda = 1;
Execution Plan
----------------------------------------------------------
0 SELECT STATEMENT Optimizer=CHOOSE
1 0 TABLE ACCESS (BY INDEX ROWID) OF 'MULTIINDEXUSAGE'
2 1 INDEX (RANGE SCAN) OF 'MULTINDEX' (NON-UNIQUE)
SQL> select * from multiindexusage where indb = 1;
Execution Plan
----------------------------------------------------------
0 SELECT STATEMENT Optimizer=CHOOSE
1 0 TABLE ACCESS (FULL) OF 'MULTIINDEXUSAGE'
很明显, 当仅引用索引的第二个列时,优化器使用了全表扫描而忽略了索引
这一部门主要介绍Oracle数据库的管理和配置,内容主要是DBA日常的操作,作为开发人员只要了解就可以,会一些简单的操作就行。
数据库的备份分为热备份和冷备份,热备份是指数据库在线备份,即不关闭数据库的情况下;冷备份是指在关闭数据库,直接备份数据文件。
热备份:
Oracle支持三种方式类型的输出:
l 表方式(T方式),将指定表的数据导出。
l 用户方式(U方式),将指定用户的所有对象及数据导出。
l 全库方式(Full方式),瘵数据库中的所有对象导出。
数据导入(Import)的过程是数据导出(Export)的逆过程,分别将数据文件导入数据库和将数据库数据导出到数据文件。
1 table model
1) backup one user's table
exp icdmain/icd rows=y indexes=n compress=n buffer=65536 feedback=100000 volsize=0 file=exp_icdmain_table_yyyymmdd.dmp log=exp_icdmain_table_yyyymmdd.log tables=icdmain.commoninformation,icdmain.serviceinfo,icdmain.dealinfo
2) recover all table
imp icdmain/icd fromuser=icdmain touser=icdmain rows=y indexes=n commit=y buffer=65536 feedback=100000 ignore=y volsize=0 file=exp_icdmain_table_yyyymmdd.dmp log=imp_icdmain_table_yyyymmdd.log
3) recover some table of all table
imp icdmain/icd fromuser=icdmain touser=icdmain rows=y indexes=n commit=y buffer=65536 feedback=100000 ignore=y volsize=0 file=exp_icdmain_table_yyyymmdd.dmp log=imp_icdmain_table_yyyymmdd.log tables=commoninformation,serviceinfo
2 user model
1) backup all someone's object
exp icdmain/icd rows=y indexes=n compress=n buffer=65536 feedback=100000 volsize=0 owner=icdmain file=exp_icdmain_user_yyyymmdd.dmp log=exp_icdmain_user_yyyymmdd.log
2) recover all someone's object
imp icdmain/icd fromuser=icdmain touser=icdmain rows=y indexes=n commit=y buffer=65536 feedback=100000 ignore=y volsize=0 file=exp_icdmain_user_yyyymmdd.dmp log=imp_icdmain_user_yyyymmdd.log
3) recover some table of all someone's object
imp icdmain/icd fromuser=icdmain touser=icdmain rows=y indexes=n commit=y buffer=65536 feedback=100000 ignore=y volsize=0 file=exp_icdmain_user_yyyymmdd.dmp log=imp_icdmain_user_yyyymmdd.log tables=commoninformation,serviceinfo
3 full model
1)backup the full db for all
exp system/manager rows=y indexes=n compress=n buffer=65536 feedback=100000 volsize=0 full=y inctype=complete file=exp_fulldb_yyyymmdd.dmp log=exp_fulldb_yyyymmdd.log
2)backup the full db for zengliang
exp system/manager rows=y indexes=n compress=n buffer=65536 feedback=100000 volsize=0 full=y inctype=incremental file=exp_fulldb_zl_yyyymmdd.dmp log=exp_fulldb_zl_yyyymmdd.log
3)recover all date for full backup
imp system/manager rows=y indexes=n commit=y buffer=65536 feedback=100000 ignore=y volsize=0 full=y file=exp_fulldb_yyyymmdd.dmp log=imp_fulldb_yyyymmdd.log
4)recover all date for zengliang backup
imp system/manager rows=y indexes=n commit=y buffer=65536 feedback=100000 ignore=y volsize=0 full=y inctype=restore file=exp_fulldb_zl_yyyymmdd.dmp log=imp_fulldb_zl_yyyymmdd.log
shell脚本:
#backup_full.sh
rman target / cmdfile=/oracle/backup_full.rman
rman脚本全库及数据库归档日至进行全备份(backup_full.rman):
run{
allocate channel d1 device type disk;
backup database format '/oradata/backup/FULL_%T_%d_%U';
backup archivelog all delete input
format '/oradata/backup/full_%u_%p_%c.ac' filesperset = 3;
release channel d1;
}
exit;
#cron文件
0 12,18 * * * /bin/backup_full.sh
关于参数调整,是oracle的复杂性的一个具体体现。通常来讲,我们更倾向于让客户做
statspack 报告,然后告诉我们os监控的状况,在这些的信息的基础上,再向客户索取具体
的详细信息以诊断问题的所在。系统的调整,现在我们通常采用从等待事件入手的方法。因为一个系统感觉到慢,必然是在某个环节上出现等待,那么我们从等待最多的事件入手逐步诊断并解决问题。
ORACLE的最大连接数(sessions)与其参数文件中的进程数(process)有关,它们的关系如下:
sessions=(1.1*process+5)
查看当前连接数
SQL>select count(*) from v$ sessions;
查看oracle连接数设置
SQL>show parameter processes
修改最大连接数
SQL>alter system set processes=500 scope=spfile;
重启数据库生效
SQL> shutdown immediate;
SQL> startup;
对于内存的调整,相对来说简单一些,我们首先可以针对数据缓冲区的大小来看。首先
观察命中率。
数据缓冲区命中率
SQL> select value from v$sysstat where name ='physical reads';
VALUE
----------
14764
SQL> select value from v$sysstat where name ='physical reads direct';
VALUE
----------
50
SQL> select value from v$sysstat where name ='physical reads direct (lob)';
VALUE
----------
0
SQL> select value from v$sysstat where name ='consistent gets';
VALUE
----------
167763
假如 redo buffer allocation retries/ redo entries的比例超过1%我们就可以考虑增大log_buffer
通常来说,内存的调整的焦点就集中在这几个方面,更多更详细的内容,建议从statspack
入手来一步一步调整。最后关于内存的调整,再强调这一点,一定要结合操作系统来衡量,
任何理论都必须要实践来检验。在操作系统中观察 page in/out状况,发现问题严重,应
该考虑调小SGA。
查看SGA
SQL>select count(*) from v$ sessions;
--或
SQL> show parameter sga_max_size;
修改SAG
SQL> alter system set sga_max_size=500m scope=spfile;
重启生效
对于oracle来说,存在着32bit与64bit的问题。这个问题影响到的主要是SGA的大小。
在32bit的数据库下,通常oracle只能使用不超过1.7G的内存,即使我们拥有12G的内存,
但是我们却只能使用1.7G,这是一个莫大的遗憾。假如我们安装64bit的数据库,我们就可以使用很大的内存,几乎不可能达到上限。但是64bit的数据库必须安装在64bit 的操作系统上,可惜目前windows上只能安装32bit的数据库.对于linux操作系统下的数据库,由于在正常情况下Oracle对SGA的管理能力不超过1.7G。所以总的物理内存在4G以下。SGA的大小为物理内存的50%—75%。对于64位的小型系统,Oracle数据库对SGA的管理超过2G的限制,SGA设计在一个合适的范围内:物理内存的50%—70%,当SGA过大的时候会导致内存分页,影响系统性能。
我们通过下面的方式可以查看数据库是32bit还是64bit:
SQL> select * from v$version;
BANNER
----------------------------------------------------------------
Oracle8i Enterprise Edition Release 8.1.7.0.0 - Production
PL/SQL Release 8.1.7.0.0 - Production
CORE 8.1.7.0.0 Production
TNS for 32-bit Windows: Version 8.1.7.0.0 - Production
NLSRTL Version 3.4.1.0.0 – Production
假如是64bit oracle,在查询结果中一定会显示 64bit字样,没有出现,则一定是32bit oracle .当然,在os上通过file oracle也能看到
[oracle@ocn2 bin]$ cd $ORACLE_HOME/bin
[oracle@ocn2 bin]$ file oracle
oracle: setuid setgid ELF 32-bit LSB executable, Intel 80386, version 1, dynamically linked (uses shared libs), not stripped
但是在特定的操作系统下,可能提供了一定的手段,使得可以使用超过1.7G的内
存,达到2G 以上甚至更多。在这里我们针对不同的平台下的具体实现方式做一个总结。
hmmax内核参数定义单个共享内存段的最大值,如果该参数设置小于Oracle SGA设置,那么SGA就会被分配多个共享内存段。这在繁忙的系统中可能成为性能负担,带来系统问题。
Linux上该参数的缺省值通常为32M。
[root@neirong root]# more /proc/sys/kernel/shmmax
33554432
可以通过ipcs命令查看此设置下共享内存的分配,我们可以看到Oracle分配了多个共享内存段以满足SGA设置的需要:
[root@neirong root]# ipcs -sa
使用pmap我们可以看到每个共享内存段的地址空间
[root@neirong root]# ps -ef|grep 3102
为了避免多个共享内存段,我们可以修改shmmax内核参数,使SGA存在于一个共享内存段中。
通过修改/proc/sys/kernel/shmmax参数可以达到此目的。
[root@neirong root]# echo 1073741824 > /proc/sys/kernel/shmmax
[root@neirong root]# more /proc/sys/kernel/shmmax
1073741824
这里设为1G。
对于shmmax文件的修改,系统重新启动后会复位。可以通过修改 /etc/sysctl.conf使更改永久化。
在该文件内添加以下一行
这个更改在系统重新启动后生效
kernel.shmmax = 1073741824
重起数据库使更改生效:
SQL> shutdown immediate;
Database closed.
Database dismounted.
ORACLE instance shut down.
SQL> !
[oracle@neirong oracle]$ ipcs -sa
SQL> startup
此时进程的pmap映射显示为:
[oracle@neirong bdump]$ pmap 4178
实际上,如果没有修改shmmax参数,Oracle在启动过程中就会报出以下错误:
Starting ORACLE instance (normal)
Thu Nov 17 09:27:29 2005
WARNING: EINVAL creating segment of size 0x0000000033400000
fix shm parameters in /etc/system or equivalent
此类问题的产生原因一般都是因为系统中存在性能低下或者存在错误的SQL语句。
1、首先用通过Top命令来查看:
$ top
load averages: 1.61, 1.28, 1.25 HSWAPJSDB 10:50:44
172 processes: 160 sleeping, 1 running, 3 zombie, 6 stopped, 2 on cpu
CPU states: % idle, % user, % kernel, % iowait, % swap
Memory: 4.0G real, 1.4G free, 1.9G swap in use, 8.9G swap free
PID USERNAME THR PR NCE SIZE RES STATE TIME FLTS CPU COMMAND
20521 oracle 1 40 0 1.8G 1.7G run 6:37 0 47.77% oracle
20845 oracle 1 40 0 1.8G 1.7G cpu02 0:41 0 40.98% oracle
20847 oracle 1 58 0 1.8G 1.7G sleep 0:00 0 0.84% oracle
20780 oracle 1 48 0 1.8G 1.7G sleep 0:02 0 0.83% oracle
15828 oracle 1 58 0 1.8G 1.7G sleep 0:58 0 0.53% oracle
20867 root 1 58 0 4384K 2560K sleep 0:00 0 0.29% sshd2
20493 oracle 1 58 0 1.8G 1.7G sleep 0:03 0 0.29% oracle
20887 oracle 1 48 0 1.8G 1.7G sleep 0:00 0 0.13% oracle
20851 oracle 1 58 0 1.8G 1.7G sleep 0:00 0 0.10% oracle
20483 oracle 1 48 0 1.8G 1.7G sleep 0:00 0 0.09% oracle
20875 oracle 1 45 0 1064K 896K sleep 0:00 0 0.07% sh
20794 oracle 1 58 0 1.8G 1.7G sleep 0:00 0 0.06% oracle
20842 jiankong 1 52 2 1224K 896K sleep 0:00 0 0.05% sadc
20888 oracle 1 55 0 1712K 1272K cpu00 0:00 0 0.05% top
19954 oracle 1 58 0 1.8G 1.7G sleep 84:25 0 0.04% oracle
注释:现在可以发现在进程列表里,存在两个高CPU耗用的Oracle进程,他们分别消耗了47.77%和40.98%的CPU资源。
2、下一步找到存在问题的进程信息,以此确认它们是两个远程连接的用户进程。
$ ps -ef|grep 20521
oracle 20909 20875 0 10:50:53 pts/10 0:00 grep 20521
oracle 20521 1 47 10:43:59 ? 6:45 oraclejshs (LOCAL=NO)
$ ps -ef|grep 20845
oracle 20845 1 44 10:50:00 ? 0:55 oraclejshs (LOCAL=NO)
oracle 20918 20875 0 10:50:59 pts/10 0:00 grep 20845
3、下面我们再来看一下getsql.sql脚本
SELECT /*+ ORDERED */
sql_text
FROM v$sqltext a
WHERE (a.hash_value, a.address) IN (
SELECT DECODE (sql_hash_value,
0, prev_hash_value,
sql_hash_value
),
DECODE (sql_hash_value, 0, prev_sql_addr, sql_address)
FROM v$session b
WHERE b.paddr = (SELECT addr
FROM v$process c
WHERE c.spid = '&pid'))
ORDER BY piece ASC
/
注释:在此部分我们涉及了3个视图,并应用其关联进行数据获取。首先我们需要输入一个pid,这个pid就是process id,也就是我们在Top或ps中我们看到的PID.
注意,通过pid和v$process.spid相关联我们可以获得Process的相关信息,进而通过v$process.addr和v$session.paddr相关联,我们即可以获得和session相关的所有信息。
然后再结合v$sqltext,就可以获得当前session正在执行的SQL语句。
通过v$process视图,我们就以把操作系统和数据库关联起来了。
4、下面,我们来连接数据库,找到问题sql及进程
注释:通过Top中我们观察到的PID,进而应用我的getsql脚本,得到了以下结果输出。
此时我们就可以做出结论,这段代码就是当前正在肆意消耗CPU的元凶。
下面我们需要找出这段代码的问题,看一看是否可以通过优化来提高其效率,减少资源消耗。
5、下一步则可以通过dbms_system包来跟踪该进程
SQL> @getsid
Enter value for spid: 20521
old 3: select addr from v$process where spid = &spid)
new 3: select addr from v$process where spid = 20521)
SID SERIAL# USERNAME MACHINE
----------------------------------------------------------------
45 38991 HSUSER_V51 hswapjsptl1.hurray.com.cn
SQL> exec dbms_system.set_sql_trace_in_session(45,38991,true);
PL/SQL procedure successfully completed.
SQL> !
多个表空间的优势:
l 能够将数据字典与用户数据分离出来,避免由于字典对象和用户对象保存在同一个数据文件中而产生的I/O冲突
l 能够将回退数据与用户数据分离出来,避免由于硬盘损坏而导致永久性的数据丢失
l 能够将表空间的数据文件分散保存到不同的硬盘上,平均分布物理I/O操作
l 能够将某个表空间设置为脱机状态或联机状态,以便对数据库的一部分进行备份和恢复
l 能够将某个表空间设置为只读状态,从而将数据库的一部分设置为只读状态
l 能够为某种特殊用途专门设置一个表空间,比如临时表空间等,以优化表空间的使用效率
l 能够更佳灵活的为用户设置表空间限额
SYSTEM表空间内存储:
l 数据库的数据字典
l 所有PL/SQL程序的源代码和解析代码
l 数据库对象的定义
表空间数据字典
l v$tablespace 控制文件中获取的表空间的名称和编号信息
l v$datafile 控制文件中获取的数据文件的名称和编号信息
l v$tempfile 所有临时数据文件的基本信息
l v$sort_segment 实例所创建的排序区的信息
l v$sort_user 排序区的用户使用情况信息
l dba_tablespaces 数据库中表空间的名称和编号信息
l dba_segments 表空间中段的信息
l dba_extents 表空间中区的信息
l dba_free_space 表空间中空闲区的信息
l dba_data_files 数据文件亦即所属表空间的信息
l dba_temp_files 临时数据文件亦即所属表空间的信息
--单数据文件
createtablespace dmusertbs
datafile 'i:\oracle\oradata\dmusertbs.dbf'size 50M
autoextendon
next 5M
maxsize 500M;
(next参数指定每次自动增长的大小,maxsize为数据文件的最大大小);
--多数据数据文件
createtablespace dmusertbs
datafile 'i:\oracle\oradata\dmusertbs01.dbf'size 50M,
'i:\oracle\oradata\dmusertbs02.dbf'size 50M,
'i:\oracle\oradata\dmusertbs03.dbf'size 50M;
--恢复表空间为联机状态
alter tablespace user01 online;
--设置表空间为只读状态
alter tablespace user01 read only
--设置表空间为读写状态
alter tablespace user01 read write
--删除表空间(不包括对应的数据文件)
drop tablespace users including contents;
--删除表空间(包括对应的数据文件)
drop tablespace users including contents and datafiles;
数据文件数据字典
l DBA_DATA_FILES 数据库中所有数据文件的信息
l DBA_TEMP_FILES 数据库中所有临时数据文件的信息
l DBA_EXTENTS 表空间中已分配的区的描述信息,包括区所属的数据文件的编号
l DBA_FREE_SPACE 表空间中空闲区的信息
-- 在表空间lmusertbs上添加一个自动增长方式的数据文件
alter tablespace lmusertbs
add datafile 'i:\oracle\oradata\lmusertbs02.dbf'size 50M
autoextend on
next 5M
maxsize 500M;
-- 如果数据文件已经创建,将它设置成自动增长方式
alter database
datafile 'i:\oracle\oradata\dmusertbs01.dbf'
autoextend on
next 5M
maxsize 500M;
--取消已有数据文件的自动增长方式
alter database
datafile 'i:\oracle\oradata\dmusertbs01.dbf'
autoextend off;
--手工改变数据文件的大小:将数据文件dmusertbs01.dbf增大为500MB
alter database datafile 'i:\oracle\oradata\dmusertbs01.dbf'resize 500M;
Select Tablespace_Name,
Sum_m,
Max_m,
Count_Blocks Free_Blk_Cnt,
Sum_Free_m,
To_Char(100 * Sum_Free_m / Sum_m,'99.99') || '%' As Pct_Free
From (Select Tablespace_Name, Sum(Bytes) / 1024 /1024 As Sum_m
From Dba_Data_Files
Group By Tablespace_Name)
Left Join (Select Tablespace_Name As Fs_Ts_Name,
Max(Bytes) / 1024 /1024 As Max_m,
Count(Blocks) As Count_Blocks,
Sum(Bytes / 1024 /1024) As Sum_Free_m
From Dba_Free_Space
Group By Tablespace_Name) On Tablespace_Name = Fs_Ts_Name;
如果表空间已满,可以改变数据文件大小或增加数据文件。