ORACLE日常操作手冊


轉發自:http://blog.csdn.net/lichangzai/article/details/7955766

 

以前為開發人員編寫的oracle基礎操作手冊,都基本的oracle操作和SQL語句寫法,適合初學者。

因是很久之前寫的,文章中可能會存在不准確的地方,希望指正。

 

ORACLE日常操作手冊

目錄

一、......數據庫的啟動和關閉...4

1.   數據庫的正常啟動步驟...4

2.   數據庫的正常關閉步驟...4

3.   幾種關閉數據庫方法對比...4

4.   數據庫的啟動關閉過程...4

二、......創建數據庫用戶...5

1、    以DBA用戶登錄數據庫(如system,sys)...5

2、    用create user語法創建用戶...5

3、    賦表空間使用權限...5

4、    給用戶賦權限...5

5、    刪除用戶...5

三、......ORACL常用的數據類型...5

四、......基本的SQL語句的寫法...6

1、    rowid和rownum的區別...6

Ø   刪除表中重復記錄...6

Ø   使表處於可編輯狀態...6

Ø   批量刪除記錄...7

Ø   分頁查詢...7

2、    delete和truncate 、drop的區別...7

3、    多表關聯查詢...7

不等連接實例...7

Ø   查詢員工的工資等級...7

內連接實例...8

Ø   查詢詳細信息記錄...8

Ø   關聯更新和刪除...8

外連接實例...8

Ø   查詢沒有附件信息記錄...8

自連接實例...8

Ø   查詢員工和主管之間的關系...8

Ø   遞歸查詢...8

4、    子查詢...8

單行子查詢實例...9

Ø   查詢內容大小大於平均大小的記錄...9

Ø   在having子句中使用子查詢...9

Ø   在from子句中使用子查詢(內聯視圖)...9

Ø   可能碰到的兩個錯誤...9

多行子查詢實例...9

Ø   在多行查詢中使用in操作符...9

Ø   在多行子查詢中使用any操作符...10

Ø   在多行子查詢中使用all操作符...10

多例子查詢實例...10

Ø   檢索每種產品類型中價格最低的產品...10

關聯子查詢實例...10

Ø   在關聯子查詢中exists10

Ø   在關聯子查詢中not exists10

Ø   Exists和not exists與in和not in的比較...10

嵌套子查詢實例...11

Ø   多層嵌套子查詢...11

5、    使用集合操作符...12

Ø   Union all使用實例...12

Ø   Union使用實例...12

Ø   Intersect使用實例...13

Ø   Minus使用實例...13

6、    Decode函數和Case表達式的比較...13

Ø   Decode函數使用實例...13

Ø   Case表達式使用實例...14

7、    其它...15

五、......日期和時間的存儲與處理...15

1、    常用的幾個日期函數說明...15

2、    常用的日期計算實例...15

Ø   取得當前日期是本月的第幾周...15

Ø   取得當前日期是一個星期中的第幾天,注意星期日是第一天...15

Ø   取當前日期是星期幾中文顯示:16

Ø   如果一個表在一個date類型的字段上面建立了索引,如何使用...16

Ø   得到當前的日期...16

Ø   得到當天凌晨0點0分0秒的日期...16

Ø   得到這天的最后一秒...16

Ø   得到小時的具體數值...16

Ø   得到明天凌晨0點0分0秒的日期...17

Ø   本月一日的日期...17

Ø   得到下月一日的日期...17

Ø   返回當前月的最后一天...17

Ø   得到一年的每一天...17

Ø   如何在給現有的日期加上2年...18

Ø   判斷某一日子所在年分是否為潤年...18

Ø   判斷兩年后是否為潤年...18

Ø   得到日期的季度...18

六、......SQL語句的優化寫法...18

1、    oracle訪問Table的方式...18

2、    創建索引...19

Ø   創建普通索引實例...19

Ø   創建全文索引實例...19

Ø   創建主建...20

3、    SQL優化實例及問題...20

Ø   使用like操作符的問題...20

Ø   選擇最有效率的表名順序(只在基於規則的優化器中有效)20

Ø   WHERE子句中的連接順序...21

Ø   SELECT子句中避免使用’*’21

Ø   減少訪問數據庫的次數...21

Ø   盡量多使用COMMIT.22

Ø   減少對表的查詢...22

Ø   通過內部函數提高SQL效率.23

Ø   使用表的別名(Alias)24

Ø   用EXISTS替代IN和用NOT EXISTS替代NOT IN..24

Ø   用表連接替換EXISTS.24

Ø   用EXISTS替換DISTINCT.24

Ø   等式比較和范圍比較...25

Ø   不明確的索引等級...25

Ø   強制索引失效...26

Ø   避免在索引列上使用計算....26

Ø   自動選擇索引...27

Ø   避免在索引列上使用NOT.27

Ø   避免在索引列上使用IS NULL和IS NOT NULL.28

Ø   總是使用索引的第一個列...28

七、......常見的數據庫管理和優化配置...29

1、    數據庫的備份...29

Ø   導出/導入(Export/Import)29

Ø   rman備份實例...30

2、    數據庫的參數配置及性能調整...31

Ø   如何增加ORACLE連接數...31

Ø   關於內存參數的調整...31

Ø   32bit 和 64bit 的問題...32

Ø   Linux上shmmax參數的設置及含義...32

Ø   解決CPU高度消耗(100%)的數據庫問...33

3、    存儲管理...35

Ø   創建表空間...36

Ø   管理表空間...36

Ø   管理數據文件...36

Ø   查看表空間的使用情況...37

 

本文檔約定:

1、  文中的數據庫主要用到了公司cms、pms庫結構

2、  所有SQL都實際的測試通過,放心使用。

3、  如果有再需要了解的部分以后可以再做補充。

 

 

 

 

 

一、          數據庫的啟動和關閉

1.        數據庫的正常啟動步驟

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.

l        啟動和關閉監聽

[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.

3.        幾種關閉數據庫方法對比

SHUTDOWN有四個參數:NORMAL、TRANSACTIONAL、IMMEDIATE、ABORT。缺省不帶任何參數時表示是NORMAL。

SHUTDOWN NORMAL:不允許新的連接、等待會話結束、等待事務結束、做一個檢查點並關閉數據文件。啟動時不需要實例恢復,這種方法往往不能關閉數據庫或等待很長時間

SHUTDOWN TRANSACTIONAL:不允許新的連接、不等待會話結束、等待事務結束、做一個檢查點並關閉數據文件。啟動時不需要實例恢復。

SHUTDOWN IMMEDIATE:不允許新的連接、不等待會話結束、不等待事務結束、做一個檢查點並關閉數據文件。沒有結束的事務是自動rollback的。啟動時不需要實例恢復。最常用的方法。

SHUTDOWN ABORT:不允許新的連接、不等待會話結束、不等待事務結束、不做檢查點且沒有關閉數據文件。啟動時自動進行實例恢復。一般不推薦采用,只有在數據庫無法關閉時使用,可能造成數據庫的不一致。

4.        數據庫的啟動關閉過程

二、          創建數據庫用戶

1、 DBA用戶登錄數據庫(如system,sys)

SQL>conn system/oracle@orcl

注:如果在本地服務器登錄@orcl可以去掉

2、 create user語法創建用戶

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;

3、 賦表空間使用權限

alter user user_name quota unlimited on user_tablespace quota unlimited on user_tablespace;

4、 給用戶賦權限

GRANT connect, resource TO cmsuser;

l         Connect用戶能登錄數據庫的權限

l         Resource用戶能創建一些數據庫對像的權限,表、視圖,存儲過程,一般是授予開發人員的

5、 刪除用戶

DropUser cmsuser Cascade;

l         使用cascade參數可以刪除該用戶的全部objects

 

三、          ORACL常用的數據類型

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         LOBoracle8以前叫long)變量主要是用來存儲大量數據的數據庫字段,最大可以存儲4G字節的內容,CLOB:存儲單字節字符數據(如英文)NCLOB:用來存儲定寬多字節字符數據(如漢字),BLOB:用來存儲無結構的二進制數據(word、pdf文檔)。

 

四、          基本的SQL語句的寫法

1、 rowiddelete和rownum的區別

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;

2、 deletetruncate、drop 的區別

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;

3、 多表關聯查詢

根據連接中使用操作符的不同,連接條件可以分為兩類:

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指定樹的根從哪個節點開始

4、 子查詢

子查詢有兩種基本類型:

l         單行子查詢:不向外部的SQL返回結果,或者只返回一行。

l         多行子查詢:向外部的SQL返回一行或多行。

另外子查詢還有三種子類型:

l         多列子查詢:向外部的SQL語句返回多列。

l         關聯子查詢:引用外的SQL語句中的一錢或多列。

l         嵌套子查詢:位於另外一個子查詢中。子查詢最多可以嵌套255層。

單行子查詢實例

Ø        查詢內容大小大於平均大小的記錄

Select *
  From Infobase
 Where Contentsize > (Select Avg(Contentsize) From Infobase);

 

Ø        having子句中使用子查詢

檢索那些平均價格低於同類產品平均價格最大值的產品的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子句中使用子查詢(內聯視圖)

就外部查詢的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子句,必須在外查詢中進行任何排序

多行子查詢實例

Ø        在多行查詢中使用in操作符

檢索信息表里符合classid條件的記錄:

Select *
  From Infobase
 Where Classid In
       (Select '(' ||Id || ')'From Class Where Name Like '營業部%')

Ø        在多行子查詢中使用any操作符

檢查是否有任何員工的工資低於salary_grades表中任何一級的最低工資:

Select e.Employee_Id, e.Last_Name
  From Employees e
 Where e.Salary < Any (Select Sg.Low_SalaryFrom Salary_Grades Sg);

Ø        在多行子查詢中使用all操作符

檢查是否有任何員工的工資高於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);                              
--注意:這個例子是日常的開發很典型的例子,會經常用到,一定要學會應用

關聯子查詢實例

Ø        在關聯子查詢中exists

檢索那些負責管理其它員工的員工記錄:

Select Employee_Id, Last_Name
  From Employees
 Outer Where Exists
 (Select Employee_Id
                From Employees Inner
               Inner Where Inner.Manager_Id = Outer.Employee_Id);

Ø        在關聯子查詢中not exists

檢索從未購買過的產品

Select Product_Id,Name
  From Products a
 Where Not Exists (Select 1 From Purchases Where Product_Id = a.Product_Id)
-- 子句的1是個虛擬列,沒有意義,改成其它值也可以

Ø        Existsnot 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個查詢:一個嵌套子查詢、一個子查詢和一個外部查詢。可以由里到外自己逐步分析,得到運行結果。

5、 使用集合操作符

集合操作符可以將兩個或多個查詢返回的行組合起來,當使用集合操作符的時候,必須牢記下列的限制條件:所有查詢所返回的列數以及列的類型必須匹配,列名可以不同

集合操作符主要有:

l         Union all 返回各個查詢檢索出的所有行,包括重復的行。

l         Union 返回各個查詢檢索出的所有行,不包括重復的行。

l         Intersect 返回兩個查詢共有行。

l         Minus 返回第二個查詢檢索出的行從第一個查詢檢索出的行中減去之后剩余的記錄。

 

Ø        Union all使用實例

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查詢時要有排重操作,所以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使用實例

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使用實例

Minus 返回第二個查詢檢索出的行從第一個查詢檢索出的行中減去之后剩余的記錄.

下例是從infobase返回的行中減去從infobase_temp中返回的行,然后返回剩余的行:

SelectId,classid From infobase Where Rownum <5
Minus
Select Id
,classid From infobase_temp;

        ID CLASSID

---------- --------------------------------------------------------------------------------

   1000002 (000100010003)

   1000005 (00010001000400030007)

6、 Decode函數和Case表達式的比較

Ø        Decode函數使用實例

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表達式使用實例

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;

返回結果中上面是一樣的

7、 其它

五、          日期和時間的存儲與處理

1、 常用的幾個日期函數說明

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類型。

2、 常用的日期計算實例

下面是幾個關於日期方面的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

Ø        得到當天凌晨0點0分0秒的日期

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

Ø        得到明天凌晨0點0分0秒的日期

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

Ø        如何在給現有的日期加上2年

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

六、          SQL語句的優化寫法

1、 oracle訪問Table的方式

ORACLE 采用兩種訪問表中記錄的方式:

l         全表掃描

全表掃描就是順序地訪問表中每條記錄. ORACLE采用一次讀入多個數據塊(database block)的方式優化全表掃描.

l         通過ROWID訪問表

你可以采用基於ROWID的訪問方式情況,提高訪問表的效率, , ROWID包含了表中記錄的物理位置信息..ORACLE采用索引(INDEX)實現了數據和存放數據的物理位置(ROWID)之間的聯系.通常索引提供了快速訪問ROWID的方法,因此那些基於索引列的查詢就可以得到性能上的提高.

2、 創建索引

索引是表的一個概念部分,用來提高檢索數據的效率.通過索引查詢數據比全表掃描要快.當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);

--表上創建主建相當於在列上的建了一個唯一的索引

3、 SQL優化實例及問題

Ø        使用like操作符的問題

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

Ø        WHERE子句中的連接順序

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子句中避免使用’*’

當你想在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所釋放的資源而減少:

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;

 

Ø        通過內部函數提高SQL效率.

下面是一個復雜的多表關聯查詢:

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往往犧牲了執行效率. 能夠掌握上面的運用函數解決問題的方法在實際工作中是非常有意義的。

Ø        使用表的別名(Alias)

當在SQL語句中連接多個表時,請使用表的別名並把別名前綴於每個Column上.這樣一來,就可以減少解析的時間並減少那些由Column歧義引起的語法錯誤.

 

Ø        EXISTS替代IN和用NOT EXISTS替代NOT IN

在前面的章節已經介紹過,不在描述。

Ø        用表連接替換EXISTS

通常來說 , 采用表連接的方式比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';

Ø        EXISTS替換DISTINCT

當提交一個包含一對多表信息(比如部門表和雇員表)的查詢時,避免在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, 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日常的操作,作為開發人員只要了解就可以,會一些簡單的操作就行。

1、 數據庫的備份

數據庫的備份分為熱備份和冷備份,熱備份是指數據庫在線備份,即不關閉數據庫的情況下;冷備份是指在關閉數據庫,直接備份數據文件。

熱備份:

Ø        導出/導入(Export/Import)

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

 

Ø        rman備份實例

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

 

2、 數據庫的參數配置及性能調整

關於參數調整,是oracle的復雜性的一個具體體現。通常來講,我們更傾向於讓客戶做

statspack 報告,然后告訴我們os監控的狀況,在這些的信息的基礎上,再向客戶索取具體

的詳細信息以診斷問題的所在。系統的調整,現在我們通常采用從等待事件入手的方法。因為一個系統感覺到慢,必然是在某個環節上出現等待,那么我們從等待最多的事件入手逐步診斷並解決問題。

 

Ø         如何增加ORACLE連接數

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;

重啟生效

Ø         32bit 64bit的問題

對於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 以上甚至更多。在這里我們針對不同的平台下的具體實現方式做一個總結。

Ø         Linuxshmmax參數的設置及含義

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

Ø         解決CPU高度消耗(100%)的數據庫問

此類問題的產生原因一般都是因為系統中存在性能低下或者存在錯誤的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> !

 

3、 存儲管理

多個表空間的優勢:

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;

如果表空間已滿,可以改變數據文件大小或增加數據文件。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM