轉發自: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;
如果表空間已滿,可以改變數據文件大小或增加數據文件。