工作筆記

收集表的統計信息:

DECLARE

BEGIN

DBMS_STATS.GATHER_TABLE_STATS(USER,’F_IS_AGT_M’);

END;

Chown:用來更改某個目錄或文件的用戶名和用戶組的;

Chmod:用來修改某個目錄或文件的訪問權限。

啓監聽:lsnrctl start;;

MINUS比較兩個表時的字段時要把兩個表的主鍵列出來,這樣纔會根據主鍵找到兩個表對應的唯一一條數據,然後對兩個表要比對的字段進行比對;

更新多個字段update a set
   a = b.a
   ,b=b.b
   ,c=b.c
from t a,t b
where (a.
條件1) and (b.條件2)

`查看oracle某一用戶下所有對象:

Select count(*) from all_objects where owner=’EIBCBI’;

·使用to_date必須要字符串和’YYYYMMDD’嚴格對應,比如to_date(‘2012-12-12’,’’yyyy-mm-dd),字符串什麼格式,yyyymmdd就要什麼格式,否則匹配不上,to_date後再進行to_char時會報錯;

·查一個表影響到的腳本:

Select * from user_dependents t

Where t.dependents.name = ‘表名’;

·停止job

Select * from dba_jobs找到job號,然後在sql window執行

Begin

Dbms_job.remove(2684);

Commit;

End;

·向一個表插入多次值:select * from table_1 for updata然後點解鎖按鍵向表裏插入數據或者右擊表名,點擊edit data,向表裏插入數據,插入時選中那一列的列名,然後增加一行,再把數據粘貼到此表;

·壓縮命令:gzip 文件名  解壓命令:gzip -d 文件名

·找某一日期往後的時間序列:

  selectto_char(to_date('20091202','yyyymmdd')+level-1,'yyyymmdd')

  from dual

  connect by level<= 4000

·使用一個腳本通過輸入另一個腳本的參數和時間序列根據時間序列對另一個腳本進行調用執行:

create or replace procedure sss_20161222 is

begin

  for i in (selectto_char(to_date('20091202','yyyymmdd')+level-1,'yyyymmdd') d

  from dual

  connect by level<= 365

  ) loop

    sss_20161216(v_bbq=> i.d,

               v_agmt_num => '21502013014151');

                endloop;

end sss_20161222;

·Union:對兩個結果集進行並集操作,不包括重複行,同時進行默認規則的排序;

Union All:對兩個結果集進行並集操作,包括重複行,不進行排序;

Intersect:對兩個結果集進行交集操作,不包括重複行,同時進行默認規則的排序;

Minus:對兩個結果集進行差操作,不包括重複行,同時進行默認規則的排序;

可以在最後一個結果集中指定Order by子句改變排序方式。

 

·DML語言,比如updatedeleteinsert等修改表中數據的需要commit;

Insert into a select * from b,要求a表已經創建出和b表一樣的表結構;  create table a as select * from b;

使用imp導入之前要給用戶賦予MP_FULL_DATABASE權限的權限,否則會出現imp00013的報錯.

left join一個表時要根據left join 要使用的關聯字段來找leftjoin表放的位置,可以放在關聯字段所在left join的所在嵌套層裏。

會計科目是按照會計對象的經濟內容性質的不同而進行的分類標誌,是對會計要素的具體內容進行分類覈算的項目名稱。 

Aix、liux系統下查找jdk路徑使用whichjava命令,比如/usr/java5/jre/bin/java,其中java_homewei爲/usr/java5;

jdk跟eclipse、myeclipse、netbeans等軟件一樣是java語言的開發軟件,只不過jdk沒有像他們那樣的可視化開發界面,需要在cmd下面進行運行才行。
至於jre當然是java語言的運行環境,也就是虛擬機,jdk、eclipse、myeclipse、netbeans等軟件需要jre才能開發運行java語言;

部署wherephere下的war包需要在部署前進入war包裏修改兩個配置文件,第一個是修改web-inf裏的web.xml,加入工作目錄,第二個是修改web-inf/classes/meta-inf/esensso/ssoconfig-slave.xml,將第二行的sysid改成war包的文根名,將第三行第四行改成具體的ip以及端口名;

部署war包要修改的參數文件(配置時參照其它war包的參數文件):

在tomcat/conf/server.xml裏69行左右配tomcat的端口號;

在tomcat/webapps/“war包名稱”/WEB-INF/web.xml裏第三行<web-app>和<filter>之間添加工作目錄;

在配置好的工作目錄下新建conf文件夾,在文件夾下新建jdbc.conf,配置連接數據庫的相關信息;

 

跑腳本的時候select * from log_etl_sub 和select * fromlog_etl_main在後臺監控腳本的執行情況;

在命令執行語句開頭加上Nohup 在最後加上&可以在後臺執行這個命令,不怕正在使用的電腦關機。

164連接227執行ssh傳輸文件時顯示connection refused,下載AIX5.3系統ssh安裝包

並在227安裝及啓動服務後可用

斷開連接數據庫的用戶:

以sysdba登錄數據庫,select sid,serial# from v$session where username=’user_name’

找到後執行alter system kill session ‘sid,serial’;

Plsql developer 連接數據庫:打開tnsnames.ora配置。

使用TOOLS-SESSIONS工具來監測數據庫用戶對數據庫的操作情況,使用ACTIVE SESSIONS查看正在活動的進程。

對一個腳本連續執行多次後需清理緩存:

打開command window,執行 ALTER SYSTEM FLUSH SHARED POOL;

執行好幾個SQL語句用BEGIN END,在BEGIN END 之間插入要執行的多條SQL語句即可。

跑PACKAGE:打開PACKAGE BODIES,找到此PACKAGE,找到PACKAGE中的MAIN函數,右擊-TEST,給參數跑。

2016.3.30

當’’INSERT INTO TABLEA  SELECT  *  FROMTABLEB”不可執行時,採用以下方法:

定義一個變量tt使其等於以上SQL語句,再EXECUTE IMMEDIATE tt;

動態語句可以使用EXECUTE使用(DML中的TRUNCATE),靜態語句不可以(DDL中的DELETE);

注意轉義字符。比如在  ’’   裏面含有  ’’  等字符,裏邊的  ’’  要變成 ’’’’;

 

3.31

A表比B表多幾個字段,若從A表抽取B表的某幾個字段,抽取時需要把A表多出的幾個字段也加上,再在這幾個字段前加NULL;

DIP中的表有可能某些字段查不出來,因爲沒有開放權限;

P_DIP_ETL報錯的原因是其編寫定製的規定爲某一個日期只能執行一次,超過一次時需手工到LOG_ETL_END裏刪除日期:

DELETE FROMLOG_TEL_END WHERE WORK_DATE=’20160111’;

從數據庫導出對象:

用此工具

使用PL/SQL登錄,打開TOOL-Export User,全選所有對象,設置導出目錄與導出文件名

從DIP抽取數據過程:從DIP抽過來數據先給BI,BI給I後I對數據進行補錄,然後再傳給BI。

在PL/SQL Developer中執行腳本的時候對腳本中各部分運行時間進行監測:右擊腳本,選擇TEST,在打開的窗口點擊中上方的Create Profiler Report,然後輸入腳本參數,執行腳本,腳本執行完畢後點擊 Create Profiler Report鍵上面的Profiler鍵,查看腳本中各部分執行的時間。

分析函數

本文講述Oracle分析函數用法,首先建庫:

Sql代碼  

create table earnings -- 打工賺錢表  

(  

  earnmonth varchar2(6), -- 打工月份  

  area varchar2(20), -- 打工地區  

  sno varchar2(10), -- 打工者編號  

  sname varchar2(20), -- 打工者姓名  

  times int, -- 本月打工次數  

  singleincome number(10,2), -- 每次賺多少錢  

  personincome number(10,2) -- 當月總收入  

)  

然後插入實驗數據:

Sql代碼  

insert into earnings values('200912','北平','511601','大魁',11,30,11*30);  

insert into earnings values('200912','北平','511602','大凱',8,25,8*25);  

insert into earnings values('200912','北平','511603','小東',30,6.25,30*6.25);  

insert into earnings values('200912','北平','511604','大亮',16,8.25,16*8.25);  

insert into earnings values('200912','北平','511605','賤敬',30,11,30*11);  

  

insert into earnings values('200912','金陵','511301','小玉',15,12.25,15*12.25);  

insert into earnings values('200912','金陵','511302','小凡',27,16.67,27*16.67);  

insert into earnings values('200912','金陵','511303','小妮',7,33.33,7*33.33);  

insert into earnings values('200912','金陵','511304','小俐',0,18,0);  

insert into earnings values('200912','金陵','511305','雪兒',11,9.88,11*9.88);  

  

insert into earnings values('201001','北平','511601','大魁',0,30,0);  

insert into earnings values('201001','北平','511602','大凱',14,25,14*25);  

insert into earnings values('201001','北平','511603','小東',19,6.25,19*6.25);  

insert into earnings values('201001','北平','511604','大亮',7,8.25,7*8.25);  

insert into earnings values('201001','北平','511605','賤敬',21,11,21*11);  

  

insert into earnings values('201001','金陵','511301','小玉',6,12.25,6*12.25);  

insert into earnings values('201001','金陵','511302','小凡',17,16.67,17*16.67);  

insert into earnings values('201001','金陵','511303','小妮',27,33.33,27*33.33);  

insert into earnings values('201001','金陵','511304','小俐',16,18,16*18);  

insert into earnings values('201001','金陵','511305','雪兒',11,9.88,11*9.88);  

然後看看剛剛建好的庫:

Sql代碼  

select * from earnings;  

 

 

(1)sum函數,統計總合
按照月份,統計每個地區的總收入

Sql代碼  

select earnmonth, area, sum(personincome)  

from earnings  

group by earnmonth,area;  

 查看結果如下:

 

(2)rollup函數
按照月份,地區統計收入

Sql代碼  

select earnmonth, area, sum(personincome)  

from earnings  

group by rollup(earnmonth,area);  

 查看結果如下:

(3)cube函數
按照月份,地區進行收入總彙總

Sql代碼  

select earnmonth, area, sum(personincome)  

from earnings  

group by cube(earnmonth,area)  

order by earnmonth,area nulls last;  

 結果如下:

 

小結:sum是統計求和的函數。
group by 是分組函數,按照earnmonth和area先後次序分組。
以上三例都是先按照earnmonth分組,在earnmonth內部再按area分組,並在area組內統計personincome總合。
group by 後面什麼也不接就是直接分組。
group by 後面接 rollup 是在純粹的 group by 分組上再加上對earnmonth的彙總統計。
group by 後面接 cube 是對earnmonth彙總統計基礎上對area再統計。
另外那個 nulls last 是把空值放在最後。 

rollup和cube區別:
如果是ROLLUP(A, B, C)的話,GROUP BY順序
(A、B、C)
(A、B)
(A)
最後對全表進行GROUP BY操作。

如果是GROUP BY CUBE(A, B, C),GROUP BY順序
(A、B、C)
(A、B)
(A、C)
(A),
(B、C)
(B)
(C),
最後對全表進行GROUP BY操作。

 

(4)grouping函數
在以上例子中,是用rollup和cube函數都會對結果集產生null,這時候可用grouping函數來確認
該記錄是由哪個字段得出來的
grouping函數用法,帶一個參數,參數爲字段名,結果是根據該字段得出來的就返回1,反之返回0

Sql代碼  

select decode(grouping(earnmonth),1,'所有月份',earnmonth) 月份,  

       decode(grouping(area),1,'全部地區',area) 地區, sum(personincome) 總金額  

from earnings  

group by cube(earnmonth,area)  

order by earnmonth,area nulls last;  

 查看結果如下:

 

(5)rank() over開窗函數
按照月份、地區,求打工收入排序

Sql代碼  

select earnmonth 月份,area 地區,sname 打工者, personincome 收入,   

       rank() over (partition by earnmonth,area order by personincome desc) 排名  

from earnings;  

 查看結果:

 

(6)dense_rank() over開窗函數
按照月份、地區,求打工收入排序2

Sql代碼  

select earnmonth 月份,area 地區,sname 打工者, personincome 收入,   

       dense_rank() over (partition by earnmonth,area order by personincome desc) 排名  

from earnings;  

 結果如下:

 

(7)row_number() over開窗函數
按照月份、地區,求打工收入排序3

Sql代碼  

select earnmonth 月份,area 地區,sname 打工者, personincome 收入,   

       row_number() over (partition by earnmonth,area order by personincome desc) 排名  

from earnings;  

 結果如下:

 

通過(5)(6)(7)發現rank,dense_rank,row_number的區別:
結果集中如果出現兩個相同的數據,那麼rank會進行跳躍式的排名,
比如兩個第二,那麼沒有第三接下來就是第四;
但是dense_rank不會跳躍式的排名,兩個第二接下來還是第三;
row_number最牛,即使兩個數據相同,排名也不一樣。

 

(8)sum累計求和
根據月份求出各個打工者收入總和,按照收入由少到多排序

Sql代碼  

select earnmonth 月份,area 地區,sname 打工者,   

       sum(personincome) over (partition by earnmonth,area order by personincome) 總收入  

from earnings;  

 查看結果如下:

 

(9)max,min,avg和sum函數綜合運用
按照月份和地區求打工收入最高值,最低值,平均值和總額

Sql代碼  

select distinct earnmonth 月份, area 地區,  

       max(personincome) over(partition by earnmonth,area) 最高值,  

       min(personincome) over(partition by earnmonth,area) 最低值,  

       avg(personincome) over(partition by earnmonth,area) 平均值,  

       sum(personincome) over(partition by earnmonth,area) 總額  

from earnings;  

 結果如下:

 

(10)lag和lead函數
求出每個打工者上個月和下個月有沒有賺錢(personincome大於零即爲賺錢)

Sql代碼  

select earnmonth 本月,sname 打工者,  

       lag(decode(nvl(personincome,0),0,'沒賺','賺了'),1,0) over(partition by sname order by earnmonth) 上月,  

       lead(decode(nvl(personincome,0),0,'沒賺','賺了'),1,0) over(partition by sname order by earnmonth) 下月  

from earnings;  

 

 

說明:Lag和Lead函數可以在一次查詢中取出某個字段的前N行和後N行的數據(可以是其他字段的數據,比如根據字段甲查詢上一行或下兩行的字段乙),原來沒有分析函數的時候採用子查詢方法,但是比較麻煩,慚愧,我用子查詢有的還查不出來呢。

 

語法如下:

lag(value_expression [,offset] [,default])over ([query_partition_clase] order_by_clause);
lead(value_expression [,offset] [,default]) over ([query_partition_clase]order_by_clause);
其中:
value_expression:可以是一個字段或一個內建函數。
offset是正整數,默認爲1,指往前或往後幾點記錄.因組內第一個條記錄沒有之前的行,最後一行沒有之後的行,
default就是用於處理這樣的信息,默認爲空。你

 

再講講所謂的開窗函數,依本人遇見,開窗函數就是over([query_partition_clase] order_by_clause)。比如說,我採用sum求和,rank排序等等,但是我根據什麼來呢?over提供一個窗口,可以根據什麼什麼分組,就用partitionby,然後在組內根據什麼什麼進行內部排序,就用 order by。

 

這就是我理解的開窗函數。好了本文先寫到這,以後再有什麼心得體會再來補充。

 

拉鍊表

一、概念

       拉鍊表是針對數據倉庫設計中表存儲數據的方式而定義的,顧名思義,所謂拉鍊,就是記錄歷史。記錄一個事物從開始,一直到當前狀態的所有變化的信息。

在歷史表中對客戶的一生的記錄可能就這樣幾條記錄,避免了按每一天記錄客戶狀態造成的海量存儲的問題:

(NAME)人名  (START-DATE)開始日期  (END-DT)結束日期  (STAT)狀態

client           19000101                 19070901            H在家
client            19070901                19130901             A小學
client            19130901                19160901             B初中
client            19160901                19190901             C高中
client            19190901                19230901             D大學
client            19230901                19601231             E公司
client            19601231                29991231             H退休在家

       上面的每一條記錄都是不算末尾的,比如到19070901,client已經在A,而不是H了。所以除最後一條記錄因爲狀態到目前都未改變的,其餘的記錄實際上在END-DT那天,都不在是該條記錄END-DT那天的狀態。這種現象可以理解爲算頭不算尾。

 

二、算法

       1、採集當日全量數據到ND(NewDay)表;

       2、可從歷史表中取出昨日全量數據存儲到OD(OldDay)表;

       3、(ND-OD)就是當日新增和變化的數據,也就是當天的增量,用W_I表示;

       4、(OD-ND)爲狀態到此結束需要封鏈的數據,用W_U表示;

       5、將W_I表的內容全部插入到歷史表中,這些是新增記錄,start_date爲當天,而end_date爲max值;

       6、對歷史表進行W_U部份的更新操作,start_date保持不變,而end_date改爲當天,也就是關鏈操作;

 

       拉鍊表實際上是一個數據的有效更新處理方法。 在定義了對於該方法支持的幾個字段後可以對數據進行處理。

 

       講解一個加了幾個字段的的一種處理方法

       模擬場景

       1、定義兩個臨時表,一個爲當日全量數據,另一個爲需要新增或更新的數據;

       CREATE TABLEA_day_full;

       CREATE TABLE B;

       2、獲取當日全量數據

       INSERTINTO   A   SELECT (a,b,c,cur_date, max_date) FROMSOURCE_Table

       3、抽取新增或有變化的數據,從A臨時表到B臨時表;

       INSERTINTO   B SELECTfieldname  FROM  A WHERE   NOT IN (selectfieldname  from A_HIS where end_date='max_date'); 

       4、更新歷史表的失效記錄的end_date爲max值

       UPDATEA1  FROM a_his  A1,  B  A2 SETEnd_Date='current_date' WHERE A1.xx=A2.xx  AND A1.End_Date='max_date';

       5、將新增或者有變化的數據插入目標表*/

       INSERT INTOA_HIS SELECT * FROM B;

本系統拉鍊表:

1.從交易明細表把本月數據取到一個臨時表tmp;

2.從f_cr_loan_card把上月最後一天的數據取到tmp表,當做本月的期初數(如果本月爲一月,就取上期餘額非0的數據,如果本月爲非一月,就取上期的所有值)

3.將f_cr_loan_card把上月最後一天的數據的日期轉化爲本月第一天

4.將tmp表的數據的end_date改爲null,將tmp表的數據插入到tmp1

5.將tmp1的數據插入到f_cr_loan_card

動態SQL

 使用動態SQL是在編寫PL/SQL過程時經常使用的方法之一。很多情況下,比如根據業務的需要,如果輸入不同查詢條件,則生成不同的執行SQL查詢語句,對於這種情況需要使用動態SQL來完成。再比如,對於分頁的情況,對於不同的表,必定存在不同的字段,因此使用靜態SQL則只能針對某幾個特定的表來形成分頁。而使用動態的SQL,則可以對不同的表,不同的字段進行不同的分頁。這些情況的處理通常都是用動態SQL來完成。本文講述了動態SQL的日常用法。

 

一、動態SQL和靜態SQL

    1.靜態SQL

       靜態SQL通常用於完成可以確定的任務。比如傳遞部門號調用存儲過程,返回該部門的所有僱員及薪水信息,則該語句爲

           SELECT ename,sal INTO lv_ename,lv_sal FROM scott.emp WHERE deptno=&dno;

       對於上述類似的DML語句在第一次運行時進行編譯,而後續再次調用,則不再編譯該過程。即一次編譯,多次調用,使用的相同的執行

        計劃。此種方式被稱之爲使用的是靜態的SQL。

      

    2.動態SQL

       動態SQL通常是用來根據不同的需求完成不同的任務。比如分頁查詢,對於表emp分頁,需要使用字段僱員姓名,薪水,僱用日期,且按

       薪水降序生成報表,每頁顯示行數據。而對於表sales,需要使用字段僱員名稱,客戶名稱,銷售數量,銷售日期,且按銷售日期升序

       排列。以上兩種情況,可以創建存儲過程來對其進行分頁,通過定義變量,根據輸入不同的表名,字段名,排序方法來生成不同的SQL

       語句。對於輸入不同的參數,SQL在每次運行時需要事先對其編譯。即多次調用則需要多次編譯,此稱之爲動態SQL。

       動態SQL語句通常存放在字符串變量中,且SQL語句可以包含佔位符(使用冒號開頭)。

       也可以直接將動態SQL緊跟在EXECUTE IMMEDIATE語句之後,如EXECUTE IMMEDIATE 'alter table emp enable row movement'

      

    3.兩者的異同

       靜態SQL爲直接嵌入到PL/SQL中的代碼,而動態SQL在運行時,根據不同的情況產生不同的SQL語句。

       靜態SQL爲在執行前編譯,一次編譯,多次運行。動態SQL同樣在執行前編譯,但每次執行需要重新編譯。

       靜態SQL可以使用相同的執行計劃,對於確定的任務而言,靜態SQL更具有高效性。但缺乏靈活性

       動態SQL使用了不同的執行計劃,效率不如靜態SQL,但能夠解決複雜的問題。

       動態SQL容易產生SQL注入,爲數據庫安全帶來隱患。

      

    4.動態SQL語句的幾種方法

       a.使用EXECUTE IMMEDIATE語句

           包括DDL語句,DCL語句,DML語句以及單行的SELECT 語句。該方法不能用於處理多行查詢語句。

       b.使用OPEN-FOR,FETCH和CLOSE語句

           對於處理動態多行的查詢操作,可以使用OPEN-FOR語句打開遊標,使用FETCH語句循環提取數據,最終使用CLOSE語句關閉遊標。

       c.使用批量動態SQL

           即在動態SQL中使用BULK子句,或使用遊標變量時在fetch中使用BULK ,或在FORALL語句中使用BULK子句來實現。

       d.使用系統提供的PL/SQL包DBMS_SQL來實現動態SQL,關於該方式請參考後續博文。   

 

二、動態SQL的語法

    下面是動態SQL常用的語法之一

   

       EXECUTEIMMEDIATE dynamic_SQL_string

       [INTOdefined_variable1, defined_variable2, ...]

       [USING[IN | OUT | IN OUT] bind_argument1, bind_argument2,

       ...][{RETURNING| RETURN} field1, field2, ... INTO bind_argument1,

       bind_argument2,...]

 

    1.語法描述

       dynamic_SQL_string:存放指定的SQL語句或PL/SQL塊的字符串變量

       defined_variable1:用於存放單行查詢結果,使用時必須使用INTO關鍵字,類似於使用SELECT ename INTO v_name FROM scott.emp;

           只不過在動態SQL時,將INTOdefined_variable1移出到dynamic_SQL_string語句之外。

       bind_argument1:用於給動態SQL語句傳入或傳出參數,使用時必須使用USING關鍵字,IN表示傳入的參數,OUT表示傳出的參數,

           IN OUT則既可以傳入,也可傳出。

       RETURNING| RETURN 子句也是存放SQL動態返回值的變量。

 

    2.使用要點

        a.EXECUTEIMMEDIATE執行DML時,不會提交該DML事務,需要使用顯示提交(COMMIT)或作爲EXECUTE IMMEDIATE自身的一部分。

       b.EXECUTEIMMEDIATE執行DDL,DCL時會自動提交其執行的事務。

       c.對於多行結果集的查詢,需要使用遊標變量或批量動態SQL,或者使用臨時表來實現。

       d.當執行SQL時,其尾部不需要使用分號,當執行PL/SQL 代碼時,其尾部需要使用分號。

       f.動態SQL中的佔位符以冒號開頭,緊跟任意字母或數字表示。

      

三、動態SQL的使用(DDL,DCL,DML以及單行結果集)     

      

    1.使用EXECUTE IMMEDIATE處理DDL操作

       下面是一個簡單的DDL操作,將其封裝在存儲過程之中,通過傳入表名來進行調用。

   

       CREATE OR REPLACE PROCEDURE trunc_table(table_nameVARCHAR2)  --創建存儲過程trunc_table

       IS

         sql_statementVARCHAR2(100);

       BEGIN

         sql_statement := 'TRUNCATETABLE' || table_name;           --爲變量進行賦值,用於生成動態SQL語句

         EXECUTE IMMEDIATEsql_statement;                            --使用EXECUTE IMMEDIATE執行動態SQL語句

       END;

       /

 

       flasher@ORCL> create table tb2                                --從scott.emp生產表tb2

         2  as select empno,ename,sal,deptno from scott.emp;

 

       flasher@ORCL> select count(1) from tb2;

 

         COUNT(1)

       ----------

              14

             

       flasher@ORCL> exec trunc_table('tb2');                        --調用存儲過程來對錶tb2進行truncate

 

       flasher@ORCL> select count(1) from tb2;                       --表tb2被清空

 

         COUNT(1)

       ----------

               0    

 

       flasher@ORCL> insert into tb2                                 --重新爲表tb2生成記錄

         2  select empno,ename,sal,deptno from scott.emp;

 

       flasher@ORCL> commit;

 

    2.使用EXECUTE IMMEDIATE處理DCL操作  

       下面使用sys帳戶創建存儲過程grant_sys_priv用於給用戶授予權限

      

       sys@ORCL> connsys/redhat@orcl as sysdba

 

       CREATE OR REPLACE PROCEDURE grant_sys_priv(privVARCHAR2, username VARCHAR2)

       IS

           sql_statVARCHAR2(100);

       BEGIN

           sql_stat := 'GRANT' || priv || ' TO ' || username; 

           EXECUTE IMMEDIATEsql_stat;

       END;

       /  

 

       sys@ORCL> exec grant_sys_priv('connect','usr1');

 

    3.使用EXECUTE IMMEDIATE處理DML操作

       在使用EXECUTE IMMEDIATE處理DML操作時,分爲幾種情況,即不帶輸入參數,帶輸入參數,既有輸入也有輸出參數或返回參數等不同情

       況,下面分別對其描述。  

 

       a.沒有參數傳入傳出的DML語句

           下面的示例中,使用動態SQL刪除一條記錄,且未使用參數傳入。

          

           flasher@ORCL> select * from tb2 where empno=7900;          --刪除前

 

               EMPNOENAME             SAL     DEPTNO

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

                7900JAMES             950         30

 

           flasher@ORCL> DECLARE sql_statVARCHAR2(100);

             2  BEGIN

             3    sql_stat:='DELETEFROM flasher.tb2 WHERE empno=7900';   --使用動態SQL來刪除記錄

             4    EXECUTE IMMEDIATEsql_stat;

             5  END;

             6  /

 

           flasher@ORCL> SELECT * FROM tb2 where empno=7900;              --驗證刪除情況

 

           no rowsselected

 

       b.有參數傳入的DML語句(使用USING子句)

           對於使用了參數傳入的動態SQL,需要使用USING子句來指明傳入的參數。在下面的示例中,爲表tb2插入一條記錄,在DML語句中使

           用了四個佔位符(佔位符用以冒號開頭,緊跟任意字母或數字表示)。因此在使用EXECUTE IMMEDIATE使用USING子句爲其指定其參數。

          

           DECLARE                  --聲明變量

             sql_statVARCHAR2(100);

             lv_empnotb2.empno%TYPE := 7900;

             lv_enametb2.ename%TYPE := 'JAMES';

             lv_sal   tb2.sal%TYPE := 950;

 

           BEGIN

             sql_stat := 'INSERTINTO tb2VALUES(:1,:2,:3,:4)';                --DML語句中使用了佔位符

             EXECUTE IMMEDIATEsql_stat USING lv_empno, lv_ename,lv_sal,30;    --爲佔位符指定參數或值

             COMMIT;

           END;

           /

 

           flasher@ORCL> select * from tb2 where empno=7900;                   --驗證插入後的結果

 

               EMPNOENAME             SAL     DEPTNO

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

                7900JAMES             950         30

     

       c.處理包含returning子句的DML語句

           下面的示例中,對錶tb2進行更新,使用了兩個佔位符,一個是:percent,一個是:eno,因此在使用EXECUTE IMMEDIATE執行動態

           DML時,需要使用USING子句且帶兩個輸入參數。其次,動態DML中使用了RETURNING sal INTO :salary,因此EXECUTE IMMEDIATE後

           也必須使用RETURNING INTO varialbe_name。

          

           DECLARE

             salaryNUMBER(6, 2);

             sql_statVARCHAR2(100);

           BEGIN

             sql_stat := 'UPDATEtb2 SET sal = sal * (1 + :percent / 100)'   --更新sal列,使用佔位符:percent

                || 'WHERE empno = :eno RETURNING sal INTO :salary';      --使用了佔位符:eno,:salary,以及RETURNING子句

             EXECUTE IMMEDIATEsql_stat USING &1, &2RETURNING INTO salary;  --必須使用USING及RETURNING子句

             COMMIT;

             dbms_output.put_line('Newsalary: ' || salary);

           END;

           /

 

           Enter value for 1: 10

           Enter value for 2: 7900

           old   7:   EXECUTE IMMEDIATEsql_stat USING &1, &2 RETURNING INTO salary;

           new   7:   EXECUTE IMMEDIATEsql_stat USING 10, 7900 RETURNING INTO salary;

           Newsalary: 1045

   

       d.處理包含檢索值的單行查詢

           下面的示例中,使用SELECT 查詢獲得單行結果集,使用了佔位符:name,因此也需要使用USING子句爲其傳遞參數

          

           DECLARE

             sql_statVARCHAR2(100);

             emp_recordtb2%ROWTYPE;

           BEGIN

             sql_stat := 'SELECT* FROM tb2 WHERE ename = UPPER(:name)';     --動態SQL語句爲單行DQL語句

             EXECUTE IMMEDIATEsql_stat INTO emp_recordUSING '&name';       --使用USING子句爲其傳遞參數

             DBMS_OUTPUT.PUT_LINE('Thesalary is ' || emp_record.sal || ' for '||emp_record.ename);

           END;

           /

 

           Enter value for 1: james

           old   6:   EXECUTE IMMEDIATEsql_stat INTO emp_record USING '&1';

           new   6:   EXECUTE IMMEDIATEsql_stat INTO emp_record USING 'james';

           Thesalary is 1045 for JAMES

 

四、動態SQL的使用(處理多行結果集的查詢語句)  

    1.使用遊標變量來循環提取數據,其主要流程爲

       定義遊標變量

           TYPE cursortype IS REF CURSOR;

           cursor_variablecursortype;

       打開遊標變量

           OPEN cursor_variable FOR dynamic_string

           [USINGbind_argument[,bind_argument]...]

       循環提取數據

           FETCH cursor_variable INTO {var1[,var2]...| record_variable};

           EXIT WHEN cursor_variable%NOTFOUND

       關閉遊標變量

           CLOSE cursor_variable;

   

    2.使用遊標變量處理查詢多行結果集

       下面的示例中,首先定義了一個遊標類型,接下來定義遊標變量,以及存放結果集的變量,動態查詢語句將獲得多個結果集。

       OPEN cursorname FOR SELECT ... 時,其SELECT 語句使用了字符串變量(動態SQL),其後緊跟USING子句。

   

       DECLARE                             --遊標,變量的聲明

         TYPE emp_cur_type IS REF CURSOR;

           emp_cv       emp_cur_type;

           emp_record   tb2%ROWTYPE;

           sql_stat     VARCHAR2(100);                                                      

           v_dno        NUMBER := &dno;

        

       BEGIN

         sql_stat := 'SELECT* FROM tb2 WHERE deptno = :dno';   --動態多行結果集查詢語句

         OPEN emp_cv FOR sql_statUSINGv_dno;                   --OPEN 時使用動態查詢語句以及USING子句來傳遞參數

         LOOP

           FETCH emp_cv INTO emp_record;                        --從結果集中提取記錄

           EXIT WHEN emp_cv%NOTFOUND;

           dbms_output.put_line('Employeename:' || emp_record.ename || ',  Salary:' || emp_record.sal);

         END LOOP;

         CLOSE emp_cv;

       END;

       /

 

       Employee name:Henry,  Salary:

       Employee name:JONES,  Salary:

       Employee name:ADAMS,  Salary:

       Employee name:FORD,  Salary:

 

五、動態SQL的使用(FORALL及BULK子句的使用)        

    1.動態SQL中使用BULK子句的語法

   

       EXECUTEIMMEDIATEdynamic_string                          --dynamic_string用於存放動態SQL字符串

       [BULKCOLLECT INTO define_variable[,define_variable...]]  --存放查詢結果的集合變量

       [USINGbind_argument[,argument...]]                       --使用參數傳遞給動態SQL

       [{RETURNING|RETURN}                                     --返回子句

       BULKCOLLECT INTO return_variable[,return_variable...]];  --存放返回結果的集合變量

                 

       使用bulk collect into子句處理動態SQL中的多行查詢可以加快處理速度,從而提高應用程序的性能。當使用bulk子句時,集合類型可

       以是PL/SQL所支持的索引表、嵌套表和VARRY,但集合元素必須使用SQL數據類型。常用的三種語句支持BULK子句,分別爲EXECUTE

       IMMEDIATE,   FETCH 和FORALL。

      

    2.使用EXECUTE IMMEDIATE 結合BULK子句處理DML語句返回子句

       下面的例子,首先定義了兩個索引表類型以及其變量,接下來使用動態SQL語句來更新tb2的薪水,使用EXECUTE IMMEDIATE配合BULK

       COLLECTINTO 來處理結果集。

      

       DECLARE

         TYPE ename_table_type IS TABLE OF tb2.ename%TYPE INDEX BY BINARY_INTEGER;  --定義類型用於存放結果集

         TYPE sal_table_type IS TABLE OF tb2.sal%TYPE INDEX BY BINARY_INTEGER;

           ename_tableename_table_type;

           sal_tablesal_table_type;

           sql_statVARCHAR2(120);

           v_percentNUMBER :=&percent;

           v_dno     NUMBER :=&dno;

        

       BEGIN

         sql_stat := 'UPDATEtb2 SET sal = sal * (1 + :percent /100)'              --動態DML語句

             || 'WHERE deptno = :dno'

             || 'RETURNING ename, sal INTO :name,:salary';                        --使用了RETURNING子句,有返回值

         EXECUTE IMMEDIATEsql_stat USING v_percent, v_dno                          --執行動態SQL語句

           RETURNING BULK COLLECT INTO ename_table, sal_table;                      --使用BULK COLLECT INTO到集合變量

         FOR i IN 1..ename_table.COUNT                                              --使用FOR循環讀取集合變量的結果

         LOOP

           DBMS_OUTPUT.PUT_LINE('Employee' || ename_table(i) || ' Salary is:' || sal_table(i));

         END LOOP;

       END;

       /

 

       EmployeeHenry Salary is: 1694

       EmployeeJONES Salary is: 3841.75

       EmployeeADAMS Salary is: 1573

       EmployeeFORD Salary is: 3872

 

    3.使用EXECUTE IMMEDIATE 結合BULK子句處理多行查詢

       下面示例中,與前一個示例相同,只不過其動態SQL有查詢語句組成,且返回多個結果集,同樣使用了BULKCOLLECT INTO來傳遞結果。

      

       DECLARE

         TYPE ename_table_type IS TABLE OF tb2.ename%TYPE INDEX BY BINARY_INTEGER; --定義類型用於存放結果集

         TYPE sal_table_type IS TABLE OF tb2.sal%TYPE INDEX BY BINARY_INTEGER;

           ename_tableename_table_type;

           sal_tablesal_table_type;

           sql_statVARCHAR2(100);

       BEGIN

         sql_stat := 'SELECTename,sal FROM tb2 WHERE deptno =:dno';              --動態DQL語句,未使用RETURNING子句

         EXECUTE IMMEDIATEsql_stat BULK COLLECT INTO ename_table,sal_tableUSING &dno;  --使用BULK COLLECT INTO

         FOR i IN 1..ename_table.COUNT

         LOOP

           DBMS_OUTPUT.PUT_LINE('Employee' || ename_table(i) || ' Salary is:' || sal_table(i));

         END LOOP;

       END;

       /

 

       EmployeeHenry Salary is: 1694

       EmployeeJONES Salary is: 3841.75

       EmployeeADAMS Salary is: 1573

       EmployeeFORD Salary is: 4259.2

 

    4.使用FETCH子句結合BULK子句處理多行結果集

       下面的示例中首先定義了遊標類型,遊標變量以及複合類型,複合變量,接下來從動態SQL中OPEN遊標,然後使用FETCH將結果存放到復

       合變量中。即使用OPEN,FETCH代替了EXECUTE IMMEDIATE來完成動態SQL的執行。

      

       DECLARE

         TYPE empcurtype IS REF CURSOR;         --定義遊標類型及遊標變量

         emp_cvempcurtype;

         TYPE ename_table_type IS TABLE OF tb2.ename%TYPE INDEX BY BINARY_INTEGER;  --定義結果集類型及變量

         ename_tableename_table_type;

         sql_stat    VARCHAR2(120);

       BEGIN

         sql_stat := 'SELECTename FROM tb2 WHERE deptno = :dno';      --動態SQL字符串

         OPEN emp_cv FOR sql_stat                                      --從動態SQL中打開遊標

           USING &dno;

         FETCH emp_cv BULK COLLECT                                     --使用BULK COLLECT INTO提取結果集

           INTO ename_table;

         FOR i IN 1 .. ename_table.COUNT LOOP

           DBMS_OUTPUT.PUT_LINE('EmployeeName is ' || ename_table(i));

         END LOOP;

         CLOSE emp_cv;

       END;

       /

 

       Employee Name is Henry

       Employee Name is JONES

       Employee Name is ADAMS

       Employee Name is FORD

 

    5.使用FORALL語句中使用BULK子句

       下面是FORALL子句的語法

      

       FORALLindex IN lower bound..upperbound           --FORALL循環計數

           EXECUTEIMMEDIATE dynamic_string               --結合EXECUTE IMMEDIATE來執行動態SQL語句

           USINGbind_argument | bind_argument(index)     --綁定輸入參數

              [bind_argument| bind_argument(index)]...

           [{RETURNING| RETURN} BULK COLLECT INTO bind_argument[,bind_argument...]];  --綁定返回結果集

      

       FORALL子句允許爲動態SQL輸入變量,但FORALL子句僅支持DML(INSERT,DELETE,UPDATE)語句,不支持動態的SELECT語句。

       下面的示例中,首先聲明瞭兩個複合類型以及複合變量,接下來爲複合變量ename_table賦值,以形成動態SQL語句。緊接着使用FORALL

       子句結合EXECUTE IMMEDIATE 來提取結果集。

      

       DECLARE                                                 --定義複合類型及變量

         TYPE ename_table_type IS TABLE OF tb2.ename%TYPE;    

         TYPE sal_table_type IS TABLE OF tb2.sal%TYPE;

           ename_tableename_table_type;

           sal_tablesal_table_type;

           sql_statVARCHAR2(100);

       BEGIN

         ename_table := ename_table_type('BLAKE', 'FORD', 'MILLER');   --爲複合類型賦值

         sql_stat := 'UPDATEtb2 SET sal = sal * 1.1 WHERE ename = :1' --定義動態SQL語句

             || 'RETURNING sal INTO :2';

         FORALLi IN 1..ename_table.COUNT                              --爲FORALL 設定起始值

           EXECUTE IMMEDIATEsql_stat USING ename_table(i) --使用EXECUTE IMMEDIATE 結合RETURNING BULK COLLECT INTO獲取結果集

                  RETURNING BULK COLLECT INTO sal_table;

         FOR j IN 1..ename_table.COUNT

         LOOP

           DBMS_OUTPUT.PUT_LINE('Thenew salary is ' || sal_table(j) || ' for' ||ename_table(j)) ;

         END LOOP;

       END;

       /

          

       Thenew salary is 3135 for BLAKE

       Thenew salary is 4259.2 for FORD

       Thenew salary is 1760 for MILLER 

 

 

 

 

 

 

 

 

 

Merge into

Merge into table1

Using table2 on(table1.num = table2.num)

When matched then

Update

Set table1.bbq =table2.bbq

Where table1.g = 222

When not matched then

Insert

Values(110,1,g,b)

Where table2.g=11;

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

行列轉換函數

pivot 行轉列

測試數據 (id,類型名稱,銷售數量),案例:根據水果的類型查詢出一條數據顯示出每種類型的銷售數量。

 

?

1

2

3

4

5

6

7

8

9

create table demo(id int,name varchar(20),nums int);  ---- 創建表

insert into demo values(1, '蘋果', 1000);

insert into demo values(2, '蘋果', 2000);

insert into demo values(3, '蘋果', 4000);

insert into demo values(4, '橘子', 5000);

insert into demo values(5, '橘子', 3000);

insert into demo values(6, '葡萄', 3500);

insert into demo values(7, '芒果', 4200);

insert into demo values(8, '芒果', 5500);

Select * from (select name,nums from demo)pivot(sum(num)for name in())

 

分組查詢 (當然這是不符合查詢一條數據的要求的)

?

1

select name, sum(nums) nums from demo group by name

 

行轉列查詢

 

?

1

select * from (select name, nums from demo) pivot (sum(nums) for name in ('蘋果' 蘋果, '橘子', '葡萄', '芒果'));


 

注意: pivot(聚合函數 for 列名 in(類型)),其中 in(‘’) 中可以指定別名,in中還可以指定子查詢,比如 select distinct code from customers

當然也可以不使用pivot函數,等同於下列語句,只是代碼比較長,容易理解

 

unpivot 列轉行

顧名思義就是將多列轉換成1列中去
案例:現在有一個水果表,記錄了4個季度的銷售數量,現在要將每種水果的每個季度的銷售情況用多行數據展示。

 

創建表和數據

 

?

1

2

3

4

5

6

7

create table Fruit(id int,name varchar(20), Q1 int, Q2 int, Q3 int, Q4 int);

 

insert into Fruit values(1,'蘋果',1000,2000,3300,5000);

insert into Fruit values(2,'橘子',3000,3000,3200,1500);

insert into Fruit values(3,'香蕉',2500,3500,2200,2500);

insert into Fruit values(4,'葡萄',1500,2500,1200,3500);

select * from Fruit

 

列轉行查詢

 

?

1

select id , name, jidu, xiaoshou from Fruit unpivot (xiaoshou for jidu in (q1, q2, q3, q4) )

注意: unpivot沒有聚合函數,xiaoshou、jidu字段也是臨時的變量

 

列轉行:

使用over(partition byt.u_id)用法

SELECT NAME,wmsys.wm_concat(course||score)  FROM kecheng group BY NAME

列轉行(沒有現成的函數調用,直接用union把每條查出來的數據關聯起來即可):

1、集合查詢

實現的SQL語句:

select id,name,'語文' course,chinesescore from lie2hang

union

select id,name,'數學' course,math scorefrom lie2hang

union

select id,name,'英語' course,englishscore from lie2hang

union

select id,name,'歷史' course,historyscore from lie2hang

union

select id,name,'化學' course,chemistryscore from lie2hang;

 

Vm_concat函數:

oracle wm_concat(column)函數使我們經常會使用到的,使用oracle wm_concat(column)函數實現字段合併。

 

 

原數據顯示:

 

    U_ID   GOODS      NUM

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

       1   蘋果        2

       2   梨子        5

       1   西瓜        4

       3   葡萄        1

       3   香蕉        1

       1   橘子        3

 

 

create table shopping(u_id  int,goods varchar2(100),num int);

 

insert into shopping values(1,'蘋果',2);

insert into shopping values(2,'梨子',5);

insert into shopping values(1,'西瓜',4);

insert into shopping values(3,'葡萄',1);

insert into shopping values(3,'香蕉',1);

insert into shopping values(1,'橘子',3);

commit;

 

 

 

 

想要的結果1:

 

#########################################

 

        U_ID   GOODS_SUM

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

           1   蘋果,西瓜,橘子

           2   梨子

           3   葡萄,香蕉

           

#########################################

 

 

 colgoods_sum format a20;

 

select u_id, wm_concat(goods)goods_sum  from shopping  group by u_id;

 

 

 

 

 

想要的結果2:

 

#########################################

   U_ID    GOODS_SUM                                                                      

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

      1    蘋果(2斤),西瓜(4斤),橘子(3斤)                                                  

      2    梨子(5斤)                                                                      

      3    葡萄(1斤),香蕉(1斤)                                                             

 

#########################################

 

select u_id, wm_concat(goods || '(' || num|| '斤)' ) goods_sum  from shopping group by u_id ;

 

LISTAGG

2,測試數據

SQL>

SQL> select empno,ename,deptno fromscott.emp;

 

EMPNO ENAME DEPTNO

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

7369 SMITH 20

7499 ALLEN 30

7521 WARD 30

7566 JONES 20

7654 MARTIN 30

7698 BLAKE 30

7782 CLARK 10

7788 SCOTT 20

7839 KING 10

7844 TURNER 30

7876 ADAMS 20

7900 JAMES 30

7902 FORD 20

7934 MILLER 10

 

14 rows selected

 

3,作爲聚集函數

SQL> SELECT deptno,

2 LISTAGG(ename, ',') WITHIN GROUP(ORDER BYename) AS employees

3 FROM scott.emp

4 GROUP BY deptno;

 

DEPTNO EMPLOYEES

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

10 CLARK,KING,MILLER

20 ADAMS,FORD,JONES,SCOTT,SMITH

30 ALLEN,BLAKE,JAMES,MARTIN,TURNER,WARD

 

SQL>

--更換排序列

SQL> SELECT deptno,

2 LISTAGG(ename, ',') WITHIN GROUP(ORDER BYhiredate) AS employees

3 FROM scott.emp

4 GROUP BY deptno;

 

DEPTNO EMPLOYEES

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

10 CLARK,KING,MILLER

20 SMITH,JONES,FORD,SCOTT,ADAMS

30 ALLEN,WARD,BLAKE,TURNER,MARTIN,JAMES

--order by必須存在

SQL> SELECT deptno,

2 LISTAGG(ename, ',') WITHIN GROUP() ASemployees

3 FROM scott.emp

4 GROUP BY deptno;

 

SELECT deptno,

LISTAGG(ename, ',') WITHIN GROUP() ASemployees

FROM scott.emp

GROUP BY deptno

 

ORA-30491: ORDER BY 子句缺失

 

SQL> SELECT deptno,

2 LISTAGG(ename, ',') WITHIN GROUP(order bynull) AS employees

3 FROM scott.emp

4 GROUP BY deptno;

 

DEPTNO EMPLOYEES

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

10 CLARK,KING,MILLER

20 ADAMS,FORD,JONES,SCOTT,SMITH

30 ALLEN,BLAKE,JAMES,MARTIN,TURNER,WARD

==〉按字母順序排列

4,LISTAGG作爲分析函數使用

SQL> SELECT empno,

2 ename,

3 deptno,

4 LISTAGG(ename, ',') WITHIN GROUP(ORDER BYename) over(partition by deptno) AS employees

5 FROM scott.emp;

 

EMPNO ENAME DEPTNO EMPLOYEES

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

7782 CLARK 10 CLARK,KING,MILLER

7839 KING 10 CLARK,KING,MILLER

7934 MILLER 10 CLARK,KING,MILLER

7876 ADAMS 20 ADAMS,FORD,JONES,SCOTT,SMITH

7902 FORD 20 ADAMS,FORD,JONES,SCOTT,SMITH

7566 JONES 20 ADAMS,FORD,JONES,SCOTT,SMITH

7788 SCOTT 20 ADAMS,FORD,JONES,SCOTT,SMITH

7369 SMITH 20 ADAMS,FORD,JONES,SCOTT,SMITH

7499 ALLEN 30ALLEN,BLAKE,JAMES,MARTIN,TURNER,WARD

7698 BLAKE 30ALLEN,BLAKE,JAMES,MARTIN,TURNER,WARD

7900 JAMES 30ALLEN,BLAKE,JAMES,MARTIN,TURNER,WARD

7654 MARTIN 30ALLEN,BLAKE,JAMES,MARTIN,TURNER,WARD

7844 TURNER 30ALLEN,BLAKE,JAMES,MARTIN,TURNER,WARD

7521 WARD 30ALLEN,BLAKE,JAMES,MARTIN,TURNER,WARD

 

14 rows selected

5,其他實現方法參考

--model

SQL> SELECT deptno, vals

2 FROM (SELECT deptno, RTRIM(vals, ',') ASvals, rn

3 FROM scott.emp MODEL PARTITION BY(deptno)DIMENSION BY(ROW_NUMBER() OVER(PARTITION BY deptno ORDER BY ename) AS rn)MEASURES(CAST(ename AS VARCHAR2(4000)) AS vals) RULES(vals [ ANY ] ORDER BY rnDESC = vals [ CV() ] || ',' || vals [ CV() + 1 ]))

4 WHERE rn = 1

5 ORDER BY deptno;

 

DEPTNO VALS

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

10 CLARK,KING,MILLER

20 ADAMS,FORD,JONES,SCOTT,SMITH

30 ALLEN,BLAKE,JAMES,MARTIN,TURNER,WARD

 

--表函數:WMSYS.WM_CONCAT,10G已經提供該函數

SQL>

SQL> SELECT deptno, WMSYS.WM_CONCAT(ename)AS vals --<-- WM_CONCAT ~= STRAGG

2 FROM scott.emp

3 GROUP BY deptno;

 

DEPTNO VALS

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

10 CLARK,MILLER,KING

20 SMITH,FORD,ADAMS,SCOTT,JONES

30 ALLEN,JAMES,TURNER,BLAKE,MARTIN,WARD

 

索引

三、 唯一索引

1、 何時創建:當某列任意兩行的值都不相同

2、 當建立Primary Key(主鍵)或者Unique constraint(唯一約束)時,唯一索引將被自動建立

3、 語法:CREATE UNIQUE INDEX index ON table (column);

4、 演示

四、 組合索引

1、 何時創建:當兩個或多個列經常一起出現在where條件中時,則在這些列上同時創建組合索引

2、 組合索引中列的順序是任意的,也無需相鄰。但是建議將最頻繁訪問的列放在列表的最前面

3、 演示(組合列,單獨列)

五、 位圖索引

1、 何時創建:

列中有非常多的重複的值時候。例如某列保存了“性別”信息。

Where 條件中包含了很多OR操作符。

較少的update操作,因爲要相應的跟新所有的bitmap

2、 結構:位圖索引使用位圖作爲鍵值,對於表中的每一數據行位圖包含了TRUE(1)、FALSE(0)、或NULL值。

3、 優點:位圖以一種壓縮格式存放,因此佔用的磁盤空間比標準索引要小得多

4、 語法:CREATE BITMAP INDEX index ON table (column[, column]...);

5、 掩飾:

create table bitmaptable as select * fromindextable where owner in('SYS','PUBLIC');

分析,查找,建立索引,查找

六、 基於函數的索引

   1、何時創建:在WHERE條件語句中包含函數或者表達式時

2、 函數包括:算數表達式、PL/SQL函數、程序包函數、SQL函數、用戶自定義函數。

3、 語法:CREATE INDEX index ON table (FUNCTION(column));

4、 演示

必須要分析表,並且query_rewrite_enabled=TRUE

或者使用提示/*+INDEX(ic_index)*/

七、 反向鍵索引

目的:比如索引值是一個自動增長的列

多個用戶對集中在少數塊上的索引行進行修改,容易引起資源的爭用,比如對數據塊的等待。此時建立反向索引。

性能問題:

語法:

重建爲標準索引:反之不行

八、 鍵壓縮索引

比如表landscp的數據如下:

site feature job

Britten Park, Rose Bed 1, Prune

Britten Park, Rose Bed 1, Mulch

Britten Park, Rose Bed 1,Spray

Britten Park, Shrub Bed 1, Mulch

Britten Park, Shrub Bed 1, Weed

Britten Park, Shrub Bed 1, Hoe

……

查詢時,以上3列均在where條件中同時出現,所以建立基於以上3列的組合索引。但是發現重複值很多,所以考慮壓縮特性。

Create index zip_idx

on landscp(site, feature, job)

compress 2;

將索引項分成前綴(prefix)和後綴(postfix)兩部分。前兩項被放置到前綴部分。

Prefix 0: Britten Park, Rose Bed 1

Prefix 1: Britten Park, Shrub Bed 1

實際所以的結構爲:

0 Prune

0 Mulch

0 Spray

1 Mulch

1 Weed

1 Hoe

特點:組合索引的前綴部分具 有非選擇性時,考慮使用壓縮。減少I/O,增加性能。

九、 索引組織表(IOT)

將表中的數據按照索引的結構存儲在索引中,提高查詢速度。

犧牲插入更新的性能,換取查詢 性能。通常用於數據倉庫,提供大量的查詢,極少的插入修改工作。

必須指定主鍵。插入數據時,會根據主鍵列進行B樹索引排序,寫入磁盤。

十、 分區索引

簇:

A cluster is a group of tables that sharethe same data blocks because they share common columns and are often used together.

 

 

 

 

 

 

 

 

Decode、case when 、nvl:

DECODE:

DECODE(字段或字段的運算,值1,值2,值3)

       這個函數運行的結果是,當字段或字段的運算的值等於值1時,該函數返回值2,否則返回值3
當然值1,值2,值3也可以是表達式;

 

CASE WHEN:

--簡單Case函數 

CASE sex 

WHEN '1' THEN '男' 

WHEN '2' THEN '女' 

ELSE '其他' END 

--Case搜索函數 

CASE

WHEN sex = '1' THEN '男' 

WHEN sex = '2' THEN '女' 

ELSE '其他' END 

 

SELECT 

CASE WHEN salary <= 500 THEN '1' 

WHEN salary > 500 AND salary <=600  THEN '2' 

WHEN salary > 600 AND salary <=800  THEN '3' 

WHEN salary > 800 AND salary <= 1000THEN '4' 

ELSE NULL END salary_class

 

UPDATE Personnel 

SET salary =

CASE WHEN salary >= 5000   THEN salary * 0.9 

WHEN salary >= 2000 AND salary <4600  THEN salary * 1.15 

ELSE salary END

 

NVL:

NVL函數的格式如下:NVL(expr1,expr2)

含義是:如果oracle第一個參數爲空那麼顯示第二個參數的值,如果第一個參數的值不爲空,則顯示第一個參數本來的值。

例如:

SQL> select ename,NVL(comm, -1) fromemp;

數據導入導出數據庫的幾種方式與區別:

1.pl/sqldeveloper中tools - exporttables/import tables 和tools - export-user objects;

2.Exp/imp命令導出導入;

3.數據泵導出導入;

區別:

第一種方式界面化操作、操作簡便、適用於小數據量的數據導入導出,當導入導出大數據量時會導致developer的界面卡死;

第二種方式適用於比第一種方式更大數據量的導出導入,相對於數據泵的方式,命令更加簡單;

第三種方式效率更高,功能更全,但命令更加複雜,操作更加複雜;

數據泵是服務器端工具,exp是客戶端工具,所以數據泵操作更加安全可靠,不怕session中斷;

 

 

delete,truncate,drop的區別與特點

第一:相同點:
 truncate和不帶where子句的delete,以及drop 都會刪除表內的數據
第二:不同點:
1. truncate和delete只刪除數據不刪除表的結構(定義)
    drop 語句將刪除表的結構被依賴的約束(constrain)、觸發器(trigger)、索引(index);依賴於該表的存儲過程/函數將保留,但是變爲 invalid 狀態。
2. delete 語句是數據庫操作語言(dml),這個操作會放到 rollback segement 中,事務提交之後才生效;如果有相應的trigger執行的時候將被觸發。DELETE語句執行刪除的過程是每次從表中刪除一行,並且同時將該行的刪除操作作爲事務記錄在日誌中保存以便進行進行回滾操作。
truncate、drop 是數據庫定義語言(ddl),操作立即生效,原數據不放到rollbacksegment中,不能回滾,操作不觸發 trigger。
3.delete 語句不影響表所佔用的extent,高水線(high watermark)保持原位置不動;
   顯然drop 語句將表所佔用的空間全部釋放;   truncate 語句缺省情況下見空間釋放到 minextents個 extent,除非使用reuse storage;truncate 會將高水線復位(回到最開始)。
4.速度:一般來說: drop> truncate > delete
5.安全性:小心使用 drop 和 truncate,尤其沒有備份的時候.,否則哭都來不及。      
6.使用上:想刪除部分數據行用delete,注意帶上where子句. 回滾段要足夠大。
                 想刪除表,當然用drop。
                 想保留表而將所有數據刪除,如果和事務無關,用truncate即可;如果和事務有關,或者想觸發trigger,還是用delete。
                 如果是整理表內部的碎片,可以用truncate跟上reuse stroage,再重新導入/插入數據。

 

 

 

 

 

 

Union、union all、minus:

Union,對兩個結果集進行並集操作,不包括重複行,同時進行默認規則(按第一個字段進行排序)的排序;

Union All,對兩個結果集進行並集操作,包括重複行,不進行排序;

Intersect,對兩個結果集進行交集操作,不包括重複行,同時進行默認規則的排序;

Minus,對兩個結果集進行差操作,不包括重複行,同時進行默認規則的排序。

可以在最後一個結果集中指定Order by子句改變排序方式。

 

 

 

Connect by

CONNECT BY 爲查找的條件。

基本語法

select * from table [start with condition1]

connect by[prior] id=parentid

select t.*,level, CONNECT_BY_ROOT(id)

from tab_test t

 start with t.id = 0 connect by prior t.id =t.fid;

一般用來查找存在父子關係的數據,也就是樹形結構的數據;其返還的數據也能夠明確的區分出每一層的數據。

start with condition1 是用來限制第一層的數據,或者叫根節點數據;以這部分數據爲基礎來查找第二層數據,然後以第二層數據查找第三層數據以此類推。

connect by [prior] id=parentid 這部分是用來指明oracle在查找數據時以怎樣的一種關係去查找;比如說查找第二層的數據時用第一層數據的id去跟表裏面記錄的parentid字段進行匹配,如果這個條件成立那麼查找出來的數據就是第二層數據,同理查找第三層第四層…等等都是按這樣去匹配。

prior還有一種用法:

select * from table [start with condition1]

connect by id= [prior] parentid

這種用法就表示從下往上查找數據,可以理解爲從葉子節點往上查找父級幾點,用第一層數據的parentid去跟表記錄裏面的id進行匹配,匹配成功那麼查找出來的就是第二層數據;上面的那種就是從父級節點往下查找葉子節點。

其他特性

level關鍵字,代表樹形結構中的層級編號;第一層是數字1,第二層數字2,依次遞增。

CONNECT_BY_ROOT方法,能夠獲取第一層集結點結果集中的任意字段的值;例CONNECT_BY_ROOT(字段名)。

 

select ename,job,LEVEL from scott.empemp start with ename in ('KING') CONNECT BY PRIOR EMPNO =  MGR ANDLEVEL<=3;

 

 

 

with子查詢、rowid,rownum,dual

Rowid:ROWID 是一個類似於rownum的僞列,用於定位數據庫中一條記錄的一個相對唯一地址值。通常情況下,該值在該行數據插入到數據庫表時即被確定且唯一。而對於聚簇表,由於聚簇特性,不同表上的記錄由於存儲在相同的簇上,因此會擁有相同的ROWID。數據庫的大多數操作都是通過ROWID來完成的,而且使用ROWID來進行單記錄定位速度是最快的。

ROWID的特性組成及用途
  1、特性
      相對唯一性(聚簇表上不唯一)
      一旦確定,不可隨意更改
      使用10個字節存儲(擴展rowid),顯示爲18位的字符串
      特殊情況下,ROWID會發生變化(如下列情形)
         表的導入導出操作
         alter table tab_name move
         alter table tab_name shrinkspace
         flashback table tab_name
         拆分分區表
         分區表上更新一個值後記錄被移入到新分區
         合併兩個分區
  2、組成(擴展ROWID)
      數據庫對象的對象編號
      數據庫對象所在文件的文件編號
      數據庫對象上塊的編號
      塊上的行編號(起始值爲0)
  3、用途
      快速定位單行記錄
      展示行在表上如何存儲
      表上的一行的唯一標識符  
      用作數據類型column_name rowid

 

 

 

Rownum:

ROWNUM是對結果集加的一個僞列,即先查到結果集之後再加上去的一個列 (強調:先要有結果集)。簡單的說 rownum 是對符合條件結果的序列號。它總是從1開始排起的。所以你選出的結果不可能沒有1,而有其他大於1的值。

任何時候想把 rownum = 1 這條記錄拋棄是不對的,它在結果集中是不可或缺的,少了rownum=1 就像空中樓閣一般不能存在,所以你的 rownum 條件要包含到 1

但如果就是想要用 rownum > 10 這種條件的話話就要用嵌套語句,把 rownum 先生成,然後對他進行查詢。

select * 
from (selet rownum as rn
t1.* from a where ...)
where rn >10

 

Dual:

Oracle中的dual表是一個單行單列的虛擬表。
3. Dual
表是oracle與數據字典一起自動創建的一個表,這個表只有1列:DUMMY,數據類型爲VERCHAR2(1)dual表中只有一個數據'X', Oracle有內部邏輯保證dual表中永遠只有一條數據。

4. Dual表主要用來選擇系統變量或求一個表達式的值。

 

 

with子查詢:

with table as 相當於建個臨時表,將一個語句中某些中間結果放在臨時表空間的SQL語句,可以將查詢中的子查詢命名,放到SELECT語句的最前面,此語法從Oracle 9i開始新增。

(1)、基本語法格式:

with temptablename as (select ....)  

select ... from temptablename  

(2)、多個臨時表之間用『,』分開,格式如下:

with  

a as (select did,arg(salary) 平均工資 from work group by did),  

b as (select emp.*,w.salary from emp left join work w on emp.eid = w.eid)  

select * from a,b where wd.did =em.did and wd.平均工資>em.salary;  

(3)、建立臨時表的時候,默認使用檢索出來的字段名作爲臨時表字段名,也可以自己定義臨時表裏面的字段名,比如:

with t_a (column1) as (select count(*) a from t_1),  

t_b (column2) as (select count(*) b from t_2),  

t_c (column3) as (select count(*) c from t_3)  

select * from t_a, t_b, t_c where column1 = column2 and column2 = column3  

 

注意點:

(1) 此種語法創建的臨時表跟create temporarytable  as (select ... from ... where ...)  創建的臨時表不一樣,後者是會話結束就自動被消除,前者是檢索查詢完成以後就被消除。

(2)、建立多個臨時表的時候,後者是可以訪問前面已經建好的臨時表的。

(3)、從功能上講,跟子查詢一樣的效果。但是執行計劃不同,當有多個子查詢的時候,特別是相同子查詢,一般用with寫這部分,因爲子查詢結果存在內存臨時表中,執行效率當然也就會高很多。

 

 

 

 

 

 

 

 

 

 

 

 

 

 

統計信息

 1. 什麼是統計信息
統計信息主要是描述數據庫中表,索引的大小,規模,數據分佈狀況等的一類信息。比如,表的行數,塊數,平均每行的大小,索引的leaf blocks,索引字段的行數,不同值的大小等,都屬於統計信息。CBO正是根據這些統計信息數據,計算出不同訪問路徑下,不同join 方式下,各種計劃的成本,最後選擇出成本最小的計劃。
統計信息是存放在數據字段表中的,如tab$。一般我們從數據字段視圖中察看統計信息狀況,如DBA_TABLES,DBA_INDEXES,DBA_TAB_COL_STATISTICS,DBA_TAB_HISTOGRAMS 等。
列舉下DBA_TABLES,DBA_INDEXES視圖中表示統計信息的一些字段。這些字段只有蒐集過統計信息之後纔有值,否則是空的。這些字段中last_analyzed 字段表示上次統計信息蒐集的時間,大家可以根據這個字段,快速的瞭解最近一次統計信息蒐集的時間。

2. 如何蒐集統計信息
統計信息蒐集也是有多種方法,推薦大家使用DBMS_STATS表來進行統計信息蒐集及進行一般的統計信息維護工作
DBMS-STATS
包,主要提供了蒐集,刪除,導出,導入,修改統計信息的方法,分別對應於gather系列,delete系列,export 系列,import系列,set系列的子過程。一般可能主要是使用統計信息的蒐集,以及導出導入這樣的功能。具體來說,主要會使用到如下幾個子過程:
GATHER_INDEX_STATS Procedure
Gathers index statistics.


GATHER_TABLE_STATS Procedure
Gathers table and column (and index) statistics.

CREATE_STAT_TABLE Procedure
Creates a table with name stattab in ownname's schema which is capable ofholding statistics.

EXPORT_TABLE_STATS Procedure
Retrieves statistics for a particular table and stores them in the user stattable.

EXPORT_SCHEMA_STATS Procedure
Retrieves statistics for all objects in the schema identified by ownname andstores them in the user stat table identified by stattab.

IMPORT_INDEX_STATS Procedure
Retrieves statistics for a particular index from the user stat table identifiedby stattab and stores them in the dictionary.

IMPORT_TABLE_STATS Procedure
Retrieves statistics for a particular table from the user stat table identifiedby stattab and stores them in the dictionary.

IMPORT_SCHEMA_STATS Procedure
Retrieves statistics for all objects in the schema identified by ownname fromthe user stat table and stores them in the dictionary.

 

ORACLE優化器的優化方式有兩大類,即基於規則的優化方式(Rule-Based Optimization,簡稱爲RBO)和基於代價的優化方式(Cost-Based Optimization,簡稱爲CBO)

 

A RBO方式:優化器在分析SQL語句時,更據數據庫中表和索引等定義信息,遵循的是Oracle內部預定的一些規則。比如我們常見的:當一個where子句中的一列有索引時去走索引而不走全表掃描。

B CBO方式:依詞義可知,它是看語句的代價(Cost)了。基於代價的查詢,數據庫根據蒐集的表和索引的數據的統計信息(統計信息通過analyze 命令或者使用dbms_stats包來蒐集)綜合來決定選取一個數據庫認爲最優的執行計劃(實際上不一定最優)。統計信息給出表的大小、有多少行、每行的長度等信息。

注意:這些統計信息起初在庫內是沒有的,是根據 analyze 命令或者dbms_stats包來定期蒐集後纔出現的,所以很多的時侯過期統計信息會令優化器做出一個錯誤的執行計劃,因些我們應及時更新這些信息。爲了使用基於成本的優化器(CBO) , 你必須經常運行analyzedbms_stats命令,以增加數據庫中的對象統計信息(object statistics)的準確性。

Oracle8及以後的版本,Oracle強列推薦用CBO的方式。

 

1. 如何查看對象統計信息(objectstatistics)

 

CBO模式,對象統計信息至關重要。如何查看對象統計信息(object statistics)

Oracle中關於表的統計信息是在數據字典中的,可以下SQL查詢到,eg

SELECTtable_name,num_rows, blocks, empty_blocks AS empty, avg_space, chain_cnt,avg_row_len

FROMdba_tables

WHERE owner = ONT

AND table_name= OE_ORDER_LINES_ALL;

TABLE_NAMENUM_ROWS BLOCKS EMPTY AVG_SPACE CHAIN_CNT AVG_ROW_LEN

OE_ORDER_LINES_ALL5344 505 5 0 0 441

可以看到數據字典中統計到的該表有5344筆記錄,我們下SQL驗證一下:

selectcount(*) from apps.OE_ORDER_LINES_ALL;

發現返回是16518筆記錄,可見這個表的統計信息是比較陳舊的,真實數據與統計到的數據有較大的差別。在這種情況下,如果某個View用到此Table,且系統使用CBO的方式,則可能導致Oracleoptimizer給出效率低下的執行計劃。

 

此時可以用ANALYZE去重新統計OE_ORDER_LINES_ALL這個表,可以下SQL

ANALYZE TABLEONT.OE_ORDER_LINES_ALL COMPUTE STATISTICS;

再次Query數據字典:

TABLE_NAMENUM_ROWS BLOCKS EMPTY AVG_SPACE CHAIN_CNT AVG_ROW_LEN

OE_ORDER_LINES_ALL16518 1530 1035 865 257 643

 

發現此時的信息已是最新的了。有了比較正確的統計信息,optimizer才能給出高效的執行計劃。

 

2. 併發請求:統計數據收集模式(FNDGSCST)/ Gather Schema Statistics

 

Oracle ERP中有幾個與Gather有關的標準Request

Gather AllColumn Statistics FND_STATS.GATHER_ALL_COLUMN_STATS()

Gather ColumnStatistics FND_STATS.GATHER_COLUMN_STATS()

Gather SchemaStatistics FND_STATS.GATHER_SCHEMA_STATS()

Gather TableStatistics FND_STATS.GATHER_TABLE_STATS()

查看FND_STATS 這個Package的寫法,其實它就是在調用Oracle DBStandardPackage dbms_stats 中的某些Function

Oracle DB中常用的Gather有以下一些,DBA也可以直接在Database級別上定期Run這些Function,以便能讓Oracle統計到最新的數據庫狀況:

dbms_stats.gather_database_stats();

dbms_stats.gather_schema_stats();

dbms_stats.gather_table_stats();

dbms_stats.gather_index_stats();

 

Oracle CBO需要系統定期分析統計表/索引。只有這樣CBO才能使用正確的SQL訪問路徑,提高查詢效率。因此在Instance Leveloptimizer_mode= choose ,定期運行ANALYZE dbms_stats是非常重要的,尤其是當上次統計後,數據量已發生較大變化之後。

注意:統計操作是很耗資源的動作,要在系統Loading小的時候進行。

 

一、什麼是統計信息

 

統計信息主要是描述數據庫中表,索引的大小,規模,數據分佈狀況等的一類信息。例如,表的行數,塊數,平均每行的大小,索引的leaf blocks,索引字段的行數,不同值的大小等,都屬於統計信息。CBO正是根據這些統計信息數據,計算出不同訪問路徑下,不同join 方式下,各種計劃的成本,最後選擇出成本最小的計劃。

統計信息是存放在數據字典表中的,如tab$,一般可通過察看某些視圖來獲取統計信息狀況,如DBA_TABLES,DBA_INDEXES,DBA_TAB_COL_STATISTICS,DBA_TAB_HISTOGRAMS等。在這些視圖中包含表示統計信息的一些字段,這些字段只有蒐集過統計信息之後纔有值,否則是空的。例如,last_analyzed 字段表示上次統計信息蒐集的時間,可以根據這個字段,快速的瞭解最近一次統計信息蒐集的時間。

 

二、收集統計信息的方法

 

使用gather_stats_job自動收集是在創建數據庫時自動創建的,並由調度程序進行管理。他會收集數據庫中優化程序統計信息缺失或已過時的所有對象的統計信息。

使用dbms_stats 程序包手動收集收集的是系統統計信息。

通過設置數據庫初始化參數進行收集。

通過從另一個數據庫導入統計信息進行收集。

三、Oracle自動收集統計信息的原理

 

統計信息對於Oracle數據庫來說至關重要,尤其是在使用CBO(基於成本的優化器)模式的時候,統計信息包括表的使用塊數、空閒塊數、平均行長度、統計信息收集時間等。在Oracle9i數據庫中,兩種優化器模式RBOCBO並存,在默認情況下,optimizer_mode參數的值是choosechoose不是優化器模式,它表示在分析數據庫中的語句時,如果在對象上有統計信息,就是用CBO方式生成執行計劃,如果對象上沒有統計信息,是使用RBO模式。

 

從總體上來說,CB的準確度高於RBO,但是它要求要有統計信息和統計信息必須準確,否則Oracle可能會做出錯誤的判斷。所以在Oracle9i數據庫中,我們會自己來規劃在什麼樣的時間採用什麼樣的策略來收集統計信息。也就是說,Oracle9i的統計信息收集工作必須通過手工方式來實現。

 

到了Oracle10g,默認情況下,optimizer_mode=all_rows,也就是採用了CBO的方式,爲了保證執行計劃的準確,在週一到週五(晚22:00-次日6:00),通過一個jobgather_stat_job)自動收集對象的統計信息。這種自動收集統計信息的方式並不是收集所有對象的統計信息,而是收集沒有統計信息的對象和統計信息過舊的對象。

 

AutomaticStatistics Gathering是由Scheduler調度GATHER_STATS_JOB作業來完成的,在GATHER_STATS_JOB作業中則調用DBMS_STATS.GATHER_DATABASE_STATS_JOB_PROC存儲過程。GATHER_DATABASE_STATS_JOB_PROC是一個內部的存儲過程,基本上跟DBMS_STATS.GATHER_DATABASE_STATS的功能一樣,但在其內部有優先順序的考慮,更新量(變化量)越多的表將會越優先收集統計信息。爲對象收集統計信息的條件是,之前從來沒有收集過的或者是更新的(包括insert,update,delete,truncate)記錄數超過當前總記錄數10%的表(在Oracle11g中則提供了SET_TABLE_PREFS函數修改10%這個閾值)。記錄數的更改量由Oracle數據庫自動監控,在初始化參數statistics_level設置爲TYPICAL或者ALL時,自動監控即會生效。

 

3.1 調整當更新量達(變化量)達到多少時開始統計信息收集任務

 

1

2

3

4

BEGIN

  DBMS_STATS.SET_TABLE_PREFS ( ownname=>'XXXXX', tabname =>'T1', pname =>'STALE_PERCENT', pvalue =>'5');

END;

/

3.2 調整自動收集統計信息的執行時間

 

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

selectt1.window_name,t1.repeat_interval,t1.duration from dba_scheduler_windowst1,dba_scheduler_wingroup_members t2

wheret1.window_name=t2.window_name and t2.window_group_name in('MAINTENANCE_WINDOW_GROUP','BSLN_MAINTAIN_STATS_SCHED');

 

WINDOW_NAME                    REPEAT_INTERVAL                                             DURATION

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

MONDAY_WINDOW                 freq=daily;byday=MON;byhour=22;byminute=0; bysecond=0        +000 04:00:00

TUESDAY_WINDOW                freq=daily;byday=TUE;byhour=22;byminute=0; bysecond=0        +000 04:00:00

WEDNESDAY_WINDOW               freq=daily;byday=WED;byhour=22;byminute=0;bysecond=0        +000 04:00:00

THURSDAY_WINDOW               freq=daily;byday=THU;byhour=22;byminute=0; bysecond=0        +000 04:00:00

FRIDAY_WINDOW                 freq=daily;byday=FRI;byhour=22;byminute=0; bysecond=0        +000 04:00:00

SATURDAY_WINDOW               freq=daily;byday=SAT;byhour=6;byminute=0; bysecond=0         +000 20:00:00

SUNDAY_WINDOW                 freq=daily;byday=SUN;byhour=6;byminute=0; bysecond=0         +000 20:00:00

 

# WINDOW_NAME:任務名

#REPEAT_INTERVAL:任務重複間隔時間

# DURATION:持續時間

 

# 1.停止任務

BEGIN

DBMS_SCHEDULER.DISABLE(

name=>'"SYS"."FRIDAY_WINDOW"',

force=>TRUE);

END;

 

# 2.修改任務的持續時間,單位是分鐘

BEGIN

DBMS_SCHEDULER.SET_ATTRIBUTE(

name=>'"SYS"."FRIDAY_WINDOW"',

attribute=>'DURATION',

value=>numtodsinterval(180,'minute'));

END;

 

# 3.開始執行時間,BYHOUR=2,表示2點開始執行

BEGIN

DBMS_SCHEDULER.SET_ATTRIBUTE(

name=>'"SYS"."FRIDAY_WINDOW"',

attribute=>'REPEAT_INTERVAL',

value=>'FREQ=WEEKLY;BYDAY=MON;BYHOUR=2;BYMINUTE=0;BYSECOND=0');

END;

 

# 4.開啓任務

BEGIN

DBMS_SCHEDULER.ENABLE(

name=>'"SYS"."FRIDAY_WINDOW"');

END;

3.3 禁用統計信息自動收集

 

1

2

3

4

BEGIN

  DBMS_SCHEDULER.DISABLE('GATHER_STATS_JOB');

END;

/

四、DBMS_STATS

 

DBMS_STATS包,主要提供了蒐集(gather),刪除(delete),導出(export),導入(import),修改(set)統計信息的方法。

 

dbms_statsanalyze的區別:

 

dbms_statsOracle9i及後續版本中用於收集統計信息的包,雖然analyze命令也一直可以使用,但是現在已經不推薦使用analyze命令來收集統計信息,而是使用dbms_stats。兩者之間有很大的不同,dbms_stats能正確收集分區表的統計信息,也就是說能夠收集global statistic,而analyze只能收集最低層次對象的統計信息,然後推導和彙總出高一級對象的統計信息,如果分區表只會收集分區統計信息,然後再彙總出所有分區的統計信息,得到表一級的統計信息。

 

4.1 什麼是golbalstatistic

 

golbalstatistic是指直接從對象本身收集到的統計信息,而不是從下一級對象“推導”和“彙總”出來的統計信息,golbal statistic對於優化器來說非常重要,一個SQL,除非其查詢條件限制了數據只在分區上,否則大多數情況下需要golbal statistic才能得到正確的執行計劃。有的統計值可以從下一級對象進行彙總後得到,如表的總行數,可以通過各分區的行數相加得到。但有的統計值不能通過下一級對象得到,比如列上的唯一值數量(distinct value)以及密度值(density)。

 

4.2 使用DBMS_STATS.GATHER_DATABASE_STATS收集整個數據庫的統計信息

 

1

2

3

4

5

6

7

BEGIN

 dbms_stats.gather_database_stats(estimate_percent =>dbms_stats.AUTO_SAMPLE_SIZE,

                                  method_opt       => 'for allindexed columns',

                                   options          => 'GATHER AUTO',

                                   cascade          => TRUE);

END;

/

參數說明:

 

1.estimate_percent:採樣的百分比,使用dbms_stats.auto_sample_size選項允許Oracle自動估算要採樣的一個segment的最佳百分比。

 

2.method_opt選項適合在表和索引數據發生變化時刷新統計數據:

 

for table:只統計表

for allindexed columns:只統計有索引的表列

for allindexes:只分析統計相關索引

for allcolumns:分析所有的列

dbms_statsmethod_opt參數尤其適合在表和索引數據發生變化時刷新統計數據。method_opt參數也適合用於判斷哪些列需要直方圖(histograms)。某些情況下,索引內的各個值的分佈會影響CBO是使用一個索引還是執行一次全表掃描的決策。例如,假如在where子句中指定的值的數量不對稱,全表掃描就顯得比索引訪問更經濟。

 

如果有一個高度傾斜的索引(某些值的行數不對稱),就可創建Oracle直方圖統計。但在現實世界中,出現這種情況的機率相當小。使用CBO時,最常見的錯誤之一就是在CBO統計中不必要地引入直方圖。爲了智能地生成直方圖,Oracledbms_stats準備了method_opt參數。在method_opt子句中,還有一些重要的選項,包括skewonlyrepeatauto

 

method_opt=>'forall columns size skewonly'

method_opt=>'forall columns size repeat'

method_opt=>'forall columns size auto'

(1).skewonly選項會耗費大量處理時間,因爲它要檢查每個索引中的每個列的值的分佈情況。如果dbms_stat發現一個索引的各個列分佈得不均勻,那麼就會爲該索引創建直方圖,幫助基於成本的SQL優化器決定是進行索引訪問,還是進行全表掃描訪問。

(2).repeat選項在重新分析任務所消耗的資源就會少一些。使用repeat選項時,只會爲現有的直方圖重新分析索引,不再搜索其他直方圖機會。定期重新分析統計數據時,應該採取這種方式。

(3).auto選項根據數據分佈以及應用程序訪問列的方式來創建直方圖。

 

3.options控制Oracle統計信息的刷新方式:

 

gather:重新分析整個架構

gather empty:只分析目前還沒有統計的表

gather stale:只重新分析修改量超過10%的表(包括插入、更新和刪除)

gather auto:重新分析當前沒有統計的對象,以及統計數據過期(變髒)的對象。使用gather auto類似於組合使用gather stalegather empty

4.3 使用DBMS_STATS.GATHER_SCHEMA_STATS收集整個用戶下對象的統計信息

 

1

2

3

4

5

6

7

execdbms_stats.gather_schema_stats(

ownname =>'SCOTT',

options =>'GATHER AUTO',

estimate_percent=> dbms_stats.auto_sample_size,

method_opt=> 'for all columns size repeat',

degree =>15

)

4.4 使用DBMS_STATS.GATHER_TABLE_STATS收集表、列、索引的統計信息

 

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

dbms_stats.gather_table_stats(

ownerVARCHAR2,

tablenameVARCHAR2,

partnameVARCHAR2,

estimate_percentNUMBER,

block_sampleBOOLEAN,

method_optVARCHAR2,

degree NUMBER,

granularityVARCHAR2,

cascadeBOOLEAN,

stattabVARCHAR2,

statidVARCHAR2,

statownVARCHAR2,

no_invalidateBOOLEAN,

force BOOLEAN

)

參數說明:

 

 

 

1.owner:要分析表的所有者

 

2.tablename:要分析的表的表名

 

3.partname:分區名

 

4.estimate_percent:採樣行的百分比,從0.000001-100null爲全部分析,不採樣。常量DBMS_STATS.AUTO_SAMPLE_SIZE是默認值,由Oracle決定最佳採樣率。

 

5.block_sample:是否用塊採樣代替行採樣。

 

6.method_opt:決定histograms信息是怎樣被統計的,method_opt的取值如下:

 

for allcolumns:統計所有的histograms

for allindexed columns:統計所有index列的histograms

for all hiddencoloumns:統計hidden列的histograms

for columns<list> SIZE <N> | REPEAT | AUTO | SKEWONLY 統計指定列的histogramsN的取值範圍是0-254

7.degree:設置統計信息收集的並行度,默認值爲null

 

8.cascade:收集索引的統計信息,默認爲false

 

9.stattab:指定存儲統計信息的表。

 

10.statid:如果多個表的統計信息存儲在一個stattab中時,statid用作分區條件。

 

11.statown:存儲統計信息表的所有着。

 

如果不指定上述三個參數,則統計信息會被更新到數據字典。

 

12.force:即使表鎖住了也收集統計信息。

 

4.5 統計信息的導出導入刪除操作

 

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

# 1.創建統計信息歷史保留表

execdbms_stats.create_stat_table(

ownname =>'',

stattab =>''

)

 

# 2.導出整個scheme的統計信息

execdbms_stats.export_schema_stats(

ownname =>'',

stattab =>''

)

 

# 3.分析scheme

Execdbms_stats.gather_schema_stats(

ownname =>'',

options =>'GATHER AUTO',

estimate_percent=> dbms_stats.auto_sample_size,

method_opt=> 'for all indexed columns ',

degree => 6

 

# 4.分析表

execdbms_stats.gather_table_stats(

ownname =>'',

tabname=>'',

estimate_percent=> 10,

method_opt=>'for all indexed columns'

)

 

# 5.分析索引

execdbms_stats.gather_index_stats(

ownname =>'',

indname =>'',

estimate_percent=> 10,

degree => 6

)

 

# 6.如果發現執行計劃走錯,刪除表的統計信息

execdbms_stats.delete_table_stats(

ownname =>'',

tabname =>''

)

 

# 7.導入錶的歷史統計信息

execdbms_stats.import_table_stats(

ownname =>'',

tabname =>'',

stattab =>'')

4.6 鎖住統計信息

 

將一個表的統計信息鎖住,以防止錯誤的統計信息將此正確的信息覆蓋掉時需要用到LOCK_TABLE_STATS包:

 

1

2

3

4

DBMS_STATS.LOCK_TABLE_STATS(

ownname    VARCHAR2,

tabname    VARCHAR2

);

分區

分區提供以下優點:

(1)由於將數據分散到各個分區中,減少了數據損壞的可能性;

(2)可以對單獨的分區進行備份和恢復;

(3)可以將分區映射到不同的物理磁盤上,來分散IO;

(4)提高可管理性、可用性和性能。

Oracle 10g提供了以下幾種分區類型:

(1)範圍分區(range);

(2)哈希分區(hash);

(3)列表分區(list);

(4)範圍-哈希複合分區(range-hash);

(5)範圍-列表複合分區(range-list)。

Range分區:

  Range分區是應用範圍比較廣的表分區方式,它是以列的值的範圍來做爲分區的劃分條件,將記錄存放到列值所在的range分區中。

如按照時間劃分,2010年1月的數據放到a分區,2月的數據放到b分區,在創建的時候,需要指定基於的列,以及分區的範圍值。

在按時間分區時,如果某些記錄暫無法預測範圍,可以創建maxvalue分區,所有不在指定範圍內的記錄都會被存儲到maxvalue所在分區中。

如:

create table pdba (id number, time date)partition by range (time)

(

partition p1 values less than(to_date('2010-10-1', 'yyyy-mm-dd')),

partition p2 values less than (to_date('2010-11-1','yyyy-mm-dd')),

partition p3 values less than(to_date('2010-12-1', 'yyyy-mm-dd')),

partition p4 values less than (maxvalue)

)

Hash分區:

  對於那些無法有效劃分範圍的表,可以使用hash分區,這樣對於提高性能還是會有一定的幫助。hash分區會將表中的數據平均分配到你指定的幾個分區中,列所在分區是依據分區列的hash值自動分配,因此你並不能控制也不知道哪條記錄會被放到哪個分區中,hash分區也可以支持多個依賴列。

如:

create table test

(

transaction_id number primary key,

item_id number(8) not null

)

partition by hash(transaction_id)

(

partition part_01 tablespace tablespace01,

partition part_02 tablespace tablespace02,

partition part_03 tablespace tablespace03

);

在這裏,我們指定了每個分區的表空間。

List分區:

  List分區也需要指定列的值,其分區值必須明確指定,該分區列只能有一個,不能像range或者hash分區那樣同時指定多個列做爲分區依賴列,但它的單個分區對應值可以是多個。

  在分區時必須確定分區列可能存在的值,一旦插入的列值不在分區範圍內,則插入/更新就會失敗,因此通常建議使用list分區時,要創建一個default分區存儲那些不在指定範圍內的記錄,類似range分區中的maxvalue分區。

在根據某字段,如城市代碼分區時,可以指定default,把非分區規則的數據,全部放到這個default分區。

如:

create table custaddr
(

id varchar2(15 byte) not null,

areacode varchar2(4 byte)
)

partition by list (areacode)
( partition t_list025 values ('025'), 
partition t_list372 values ('372') , 
partition t_list510 values ('510'),

partition p_other values (default)

)

 

 

 

Sql解析過程

解析有兩種:硬解析和軟解析
OracleSQL語句的解析步驟如下:
1
語法檢測。判斷一條SQL語句的語法是否符合SQL的規範;
2
語義檢查。語法正確的SQL語句在解析的第二個步驟就是判斷該SQL語句所訪問的表及列是否準確;

3、 檢查共享池中是否有相同的語句存在。假如執行的SQL語句已經在共享池中存在同樣的副本,那麼該 
SQL
語句將會被軟解析,也就是可以重用已解析過的語句的執行計劃和優化方案,可以忽略語句解析過程 
中最耗費資源的步驟,這也是我們爲什麼一直強調避免硬解析的原因。這個步驟又可以分爲兩個步驟:

(1、)驗證SQL語句是否完全一致。在這個步驟中,Oracle將會對傳遞進來的SQL語句使用HASH函數運算 
得出HASH值,再與共享池中現有語句的HASH值進行比較看是否一一對應。

(2、)驗證SQL語句執行環境是否相同。

通過如上三個步驟檢查以後,如果SQL語句是一致的,那麼就會重用原有SQL語句的執行計劃和優化方案, 也就是我們通常所說的軟解析。如果SQL語句沒有找到同樣的副本,那麼就需要進行硬解析了。

4、 Oracle根據提交的SQL語句再查詢相應的數據對象是否有統計信息。如果有統計信息的話,那麼CBO 
會使用這些統計信息產生所有可能的執行計劃(可能多達成千上萬個)和相應的Cost,最終選擇Cost最低 
的那個執行計劃。如果查詢的數據對象無統計信息,則按RBO的默認規則選擇相應的執行計劃。這個步驟 
也是解析中最耗費資源的,因此我們應該極力避免硬解析的產生。至此,解析的步驟已經全部完成, 
Oracle
將會根據解析產生的執行計劃執行SQL語句和提取相應的數據。

 

 

 

 

sql語句的執行步驟

1)語法分析,分析語句的語法是否符合規範,衡量語句中各表達式的意義。

2)語義分析,檢查語句中涉及的所有數據庫對象是否存在,且用戶有相應的權限。

3)視圖轉換,將涉及視圖的查詢語句轉換爲相應的對基表查詢語句。

4)表達式轉換,將複雜的 SQL 表達式轉換爲較簡單的等效連接表達式。

5)選擇優化器,不同的優化器一般產生不同的“執行計劃”

6)選擇連接方式, ORACLE 有三種連接方式,對多表連接 ORACLE 可選擇適當的連接方式。

7)選擇連接順序,對多表連接 ORACLE選擇哪一對錶先連接,選擇這兩表中哪個表做爲源數據表。

8)選擇數據的搜索路徑,根據以上條件選擇合適的數據搜索路徑,如是選用全表搜索還是利用索引或是其他的方式。

9)運行“執行計劃”

 

執行計劃:

一:什麼是Oracle執行計劃?

 

執行計劃是一條查詢語句在Oracle中的執行過程或訪問路徑的描述

 

 

 

 

 

二:怎樣查看Oracle執行計劃?

 

因爲我一直用的PLSQL遠程連接的公司數據庫,所以這裏以PLSQL爲例:

 

①:配置執行計劃需要顯示的項:

 

工具  首選項>   窗口類型  計劃窗口  根據需要配置要顯示在執行計劃中的列

 

執行計劃配置

 

執行計劃的常用列字段解釋:

 

基數(Rows):Oracle估計的當前操作的返回結果集行數

 

字節(Bytes):執行該步驟後返回的字節數

 

耗費(COST)、CPU耗費:Oracle估計的該步驟的執行成本,用於說明SQL執行的代價,理論上越小越好(該值可能與實際有出入)

 

時間(Time):Oracle估計的當前操作所需的時間

 

②:打開執行計劃:

 

SQL窗口執行完一條select語句後按 F5 即可查看剛剛執行的這條查詢語句的執行計劃

 

執行計劃查看

 

 

 

注:在PLSQL中使用SQL命令查看執行計劃的話,某些SQL*PLUS命令PLSQL無法支持,比如SET AUTOTRACE ON

 

執行計劃sql查看

 

 

 

 

 

 

 

三:看懂Oracle執行計劃

 

看懂執行計劃

 

①:執行順序:

 

根據Operation縮進來判斷,縮進最多的最先執行;(縮進相同時,最上面的最先執行)

 

例:上圖中 INDEX RANGE SCAN INDEX UNIQUE SCAN 兩個動作縮進最多,最上面的 INDEX RANGE SCAN 先執行;

 

同一級如果某個動作沒有子ID就最先執行

 

同一級的動作執行時遵循最上最右先執行的原則

 

例:上圖中 TABLE ACCESS BY GLOBAL INDEX ROWID TABLE ACCESS BY INDEX ROWID兩個動作縮進都在同一級,則位於上面的 TABLEACCESS BY GLOBAL INDEX ROWID 這個動作先執行;這個動作又包含一個子動作 INDEX RANGE SCAN,則位於右邊的子動作 INDEX RANGE SCAN 先執行;

 

圖示中的SQL執行順序即爲:

 

INDEX RANGESCAN  >  TABLEACCESS BY GLOBAL INDEX ROWID  >  INDEX UNIQUE SCAN  >  TABLEACCESS BY INDEX ROWID  >  NESTED LOOPS OUTER  >  SORTGROUP BY  > SELECT STATEMENT, GOAL = ALL_ROWS

 

注:PLSQL提供了查看執行順序的功能按鈕(上圖中的紅框部分)

 

 

 

②:對圖中動作的一些說明:

 

1. 上圖中 TABLE ACCESS BY   即描述的是該動作執行時表訪問(或者說Oracle訪問數據)的方式;

 

表訪問的幾種方式:(非全部)

 

TABLE ACCESSFULL(全表掃描)

TABLE ACCESS BYROWID(通過ROWID的表存取)

TABLE ACCESS BYINDEX SCAN(索引掃描)

1 TABLE ACCESS FULL(全表掃描):

 

Oracle會讀取表中所有的行,並檢查每一行是否滿足SQL語句中的 Where 限制條件;

 

全表掃描時可以使用多塊讀(即一次I/O讀取多塊數據塊)操作,提升吞吐量;

 

使用建議:數據量太大的表不建議使用全表掃描,除非本身需要取出的數據較多,佔到表數據總量的 5% ~ 10% 或以上

 

2 TABLE ACCESS BY ROWID(通過ROWID的表存取) :

 

先說一下什麼是ROWID

 

rowid

 

ROWID是由Oracle自動加在表中每行最後的一列僞列,既然是僞列,就說明表中並不會物理存儲ROWID的值;

 

你可以像使用其它列一樣使用它,只是不能對該列的值進行增、刪、改操作;

 

一旦一行數據插入後,則其對應的ROWID在該行的生命週期內是唯一的,即使發生行遷移,該行的ROWID值也不變。

 

讓我們再回到 TABLE ACCESS BY ROWID 來:

 

行的ROWID指出了該行所在的數據文件、數據塊以及行在該塊中的位置,所以通過ROWID可以快速定位到目標數據上,這也是Oracle中存取單行數據最快的方法;

 

3 TABLE ACCESS BY INDEX SCAN(索引掃描):

 

在索引塊中,既存儲每個索引的鍵值,也存儲具有該鍵值的行的ROWID

 

一個數字列上建索引後該索引可能的概念結構如下圖:

 

index

 

所以索引掃描其實分爲兩步:

 

Ⅰ:掃描索引得到對應的ROWID

 

Ⅱ:通過ROWID定位到具體的行讀取數據

 

----------------索引掃描延伸-------------------

 

索引掃描又分五種:

 

INDEX UNIQUESCAN(索引唯一掃描)

INDEX RANGE SCAN(索引範圍掃描)

INDEX FULL SCAN(索引全掃描)

INDEX FAST FULLSCAN(索引快速掃描)

INDEX SKIP SCAN(索引跳躍掃描)

a) INDEX UNIQUESCAN(索引唯一掃描):

 

針對唯一性索引(UNIQUE INDEX)的掃描,每次至多隻返回一條記錄;

 

表中某字段存在 UNIQUEPRIMARY KEY 約束時,Oracle常實現唯一性掃描;

 

b) INDEX RANGESCAN(索引範圍掃描):

 

使用一個索引存取多行數據;

 

發生索引範圍掃描的三種情況:

 

在唯一索引列上使用了範圍操作符(如:>   <   <>  >=   <=   between

在組合索引上,只使用部分列進行查詢(查詢時必須包含前導列,否則會走全表掃描)

對非唯一索引列上進行的任何查詢

c) INDEX FULLSCAN(索引全掃描):

 

進行全索引掃描時,查詢出的數據都必須從索引中可以直接得到(注意全索引掃描只有在CBO模式下才有效)

 

-----------------------延伸閱讀:Oracle優化器簡述-----------------------

 

Oracle中的優化器是SQL分析和執行的優化工具,它負責生成、制定SQL的執行計劃。

 

Oracle的優化器有兩種:

 

RBORule-Based Optimization基於規則的優化器

CBOCost-Based Optimization基於代價的優化器

RBO

 

RBO有嚴格的使用規則,只要按照這套規則去寫SQL語句,無論數據表中的內容怎樣,也不會影響到你的執行計劃;

 

換句話說,RBO對數據“不敏感”,它要求SQL編寫人員必須要了解各項細則;

 

RBO一直沿用至ORACLE 9i,從ORACLE 10g開始,RBO已經徹底被拋棄。

 

CBO

 

CBO是一種比RBO更加合理、可靠的優化器,在ORACLE 10g中完全取代RBO

 

CBO通過計算各種可能的執行計劃的“代價”,即COST,從中選用COST最低的執行方案作爲實際運行方案;

 

它依賴數據庫對象的統計信息,統計信息的準確與否會影響CBO做出最優的選擇,也就是對數據“敏感”。

 

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

 

d) INDEX FASTFULL SCAN(索引快速掃描):

 

掃描索引中的所有的數據塊,與 INDEX FULL SCAN 類似,但是一個顯著的區別是它不對查詢出的數據進行排序(即數據不是以排序順序被返回)

 

e) INDEX SKIPSCAN(索引跳躍掃描):

 

Oracle 9i後提供,有時候複合索引的前導列(索引包含的第一列)沒有在查詢語句中出現,oralce也會使用該複合索引,這時候就使用的INDEX SKIP SCAN;

 

什麼時候會觸發 INDEX SKIP SCAN 呢?

 

前提條件:表有一個複合索引,且在查詢時有除了前導列(索引中第一列)外的其他列作爲條件,並且優化器模式爲CBO

 

Oracle發現前導列的唯一值個數很少時,會將每個唯一值都作爲常規掃描的入口,在此基礎上做一次查找,最後合併這些查詢;

 

例如:

 

假設表empename(僱員名稱)、job(職位名)、sex(性別)三個字段,並且建立了如 create indexidx_emp on emp (sex, ename, job) 的複合索引;

 

因爲性別只有 '' '' 兩個值,所以爲了提高索引的利用率,Oracle可將這個複合索引拆成 ('', ename, job)('', ename, job) 這兩個複合索引;

 

當查詢 select * from emp where job = 'Programmer' 時,該查詢發出後:

 

Oracle先進入sex''的入口,這時候使用到了 ('', ename, job) 這條複合索引,查找 job ='Programmer' 的條目;

 

再進入sex''的入口,這時候使用到了 ('', ename, job) 這條複合索引,查找 job ='Programmer' 的條目;

 

最後合併查詢到的來自兩個入口的結果集。

 

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

 

 

 

2. 上圖中的 NESTED LOOPS 描述的是表連接方式;

 

JOIN 關鍵字用於將兩張表作連接,一次只能連接兩張表,JOIN 操作的各步驟一般是串行的(在讀取做連接的兩張表的數據時可以並行讀取);

 

表(rowsource)之間的連接順序對於查詢效率有很大的影響,對首先存取的表(驅動表)先應用某些限制條件(Where過濾條件)以得到一個較小的row source,可以使得連接效率提高。

 

-------------------------延伸閱讀:驅動表(Driving Table)與匹配表(Probed Table-------------------------

 

驅動表(Driving Table):

 

表連接時首先存取的表,又稱外層表(Outer Table),這個概念用於 NESTED LOOPS(嵌套循環) HASHJOIN(哈希連接)中;

 

如果驅動表返回較多的行數據,則對所有的後續操作有負面影響,故一般選擇小表(應用Where限制條件後返回較少行數的表)作爲驅動表。

 

匹配表(Probed Table):

 

又稱爲內層表(Inner Table),從驅動表獲取一行具體數據後,會到該表中尋找符合連接條件的行。故該表一般爲大表(應用Where限制條件後返回較多行數的表)。

 

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

 

表連接的幾種方式:

 

SORT MERGE JOIN(排序-合併連接)

NESTED LOOPS(嵌套循環)

HASH JOIN(哈希連接)

CARTESIANPRODUCT(笛卡爾積)

注:這裏將首先存取的表稱作 row source 1,將之後參與連接的表稱作 row source 2

 

1 SORT MERGE JOIN(排序-合併連接):

 

假設有查詢:select a.name, b.name from table_A a join table_B b on (a.id= b.id)

 

內部連接過程:

 

a) 生成 row source 1 需要的數據,按照連接操作關聯列(如示例中的a.id)對這些數據進行排序

 

b) 生成 row source 2 需要的數據,按照與 a) 中對應的連接操作關聯列(b.id)對數據進行排序

 

c) 兩邊已排序的行放在一起執行合併操作(對兩邊的數據集進行掃描並判斷是否連接)

 

延伸:

 

如果示例中的連接操作關聯列 a.idb.id 之前就已經被排過序了的話,連接速度便可大大提高,因爲排序是很費時間和資源的操作,尤其對於有大量數據的表。

 

故可以考慮在 a.idb.id 上建立索引讓其能預先排好序。不過遺憾的是,由於返回的結果集中包括所有字段,所以通常的執行計劃中,即使連接列存在索引,也不會進入到執行計劃中,除非進行一些特定列處理(如僅僅只查詢有索引的列等)。

 

排序-合併連接的表無驅動順序,誰在前面都可以;

 

排序-合併連接適用的連接條件有: <   <=  =   >   >= ,不適用的連接條件有: <>   like

 

2 NESTED LOOPS(嵌套循環):

 

內部連接過程:

 

a) 取出 row source 1 row 1(第一行數據),遍歷 row source 2 的所有行並檢查是否有匹配的,取出匹配的行放入結果集中

 

b) 取出 row source 1 row 2(第二行數據),遍歷 row source 2 的所有行並檢查是否有匹配的,取出匹配的行放入結果集中

 

c) ……

 

rowsource 1 (即驅動表)中返回了 N 行數據,則 row source 2 也相應的會被全表遍歷 N 次。

 

因爲 rowsource 1 的每一行都會去匹配 row source 2 的所有行,所以當 row source 1 返回的行數儘可能少並且能高效訪問 row source 2(如建立適當的索引)時,效率較高。

 

延伸:

 

嵌套循環的表有驅動順序,注意選擇合適的驅動表。

 

嵌套循環連接有一個其他連接方式沒有的好處是:可以先返回已經連接的行,而不必等所有的連接操作處理完才返回數據,這樣可以實現快速相應。

 

應儘可能使用限制條件(Where過濾條件)使驅動表(row source 1)返回的行數儘可能少,同時在匹配表(row source 2)的連接操作關聯列上建立唯一索引(UNIQUE INDEX)或是選擇性較好的非唯一索引,此時嵌套循環連接的執行效率會變得很高。若驅動表返回的行數較多,即使匹配表連接操作關聯列上存在索引,連接效率也不會很高。

 

3HASH JOIN(哈希連接) :

 

哈希連接只適用於等值連接(即連接條件爲  = 

 

HASH JOIN對兩個表做連接時並不一定是都進行全表掃描,其並不限制表訪問方式;

 

內部連接過程簡述:

 

a) 取出 row source 1(驅動表,在HASH JOIN中又稱爲Build Table的數據集,然後將其構建成內存中的一個 Hash TableHash函數的Hash KEY就是連接操作關聯列),創建Hash位圖(bitmap

 

b) 取出 row source 2(匹配表)的數據集,對其中的每一條數據的連接操作關聯列使用相同的Hash函數並找到對應的 a) 裏的數據在 Hash Table 中的位置,在該位置上檢查能否找到匹配的數據

 

----------------延伸閱讀:Hash Table相關----------------

 

來自Wiki的解釋:

 

In computing, ahash table (hash map) is a data structure used to implement an associativearray, a structure that can map keys to values. A hash table uses a hashfunction to compute an index into an array of buckets or slots, from which thedesired value can be found.

 

散列(hash)技術:在記錄的存儲位置和記錄具有的關鍵字key之間建立一個對應關係 f ,使得輸入key後,可以得到對應的存儲位置 f(key),這個對應關係 f 就是散列(哈希)函數;

 

採用散列技術將記錄存儲在一塊連續的存儲空間中,這塊連續的存儲空間就是散列表(哈希表);

 

 不同的key經同一散列函數散列後得到的散列值理論上應該不同,但是實際中有可能相同,相同時即是發生了散列(哈希)衝突,解決散列衝突的辦法有很多,比如HashMap中就是用鏈地址法來解決哈希衝突;

 

哈希表是一種面向查找的數據結構,在輸入給定值後查找給定值對應的記錄在表中的位置以獲取特定記錄這個過程的速度很快。

 

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

 

HASH JOIN的三種模式:

 

OPTIMAL HASHJOIN

ONEPASS HASHJOIN

MULTIPASS HASHJOIN

1) OPTIMAL HASHJOIN

 

OPTIMAL 模式是從驅動表(也稱Build Table)上獲取的結果集比較小,可以把根據結果集構建的整個HashTable都建立在用戶可以使用的內存區域裏。

 

optimal_hash_join

 

連接過程簡述:

 

Ⅰ:首先對Build Table內各行數據的連接操作關聯列使用Hash函數,把Build Table的結果集構建成內存中的Hash Table。如圖所示,可以把Hash Table看作內存中的一塊大的方形區域,裏面有很多的小格子,Build Table裏的數據就分散分佈在這些小格子中,而這些小格子就是Hash Bucket(見上面Wiki的定義)。

 

Ⅱ:開始讀取匹配表(Probed Table)的數據,對其中每行數據的連接操作關聯列都使用同上的Hash函數,定位Build Table裏使用Hash函數後具有相同值數據所在的Hash Bucket

 

Ⅲ:定位到具體的Hash Bucket後,先檢查Bucket裏是否有數據,沒有的話就馬上丟掉匹配表(Probed Table)的這一行。如果裏面有數據,則繼續檢查裏面的數據(驅動表的數據)是否和匹配表的數據相匹配。

 

2): ONEPASS HASHJOIN :

 

從驅動表(也稱Build Table)上獲取的結果集較大,無法將根據結果集構建的Hash Table全部放入內存中時,會使用 ONEPASS 模式。

 

one_pass_hash_join

 

連接過程簡述:

 

Ⅰ:對Build Table內各行數據的連接操作關聯列使用Hash函數,根據Build Table的結果集構建Hash Table後,由於內存無法放下所有的Hash Table內容,將導致有的Hash Bucket放在內存裏,有的Hash Bucket放在磁盤上,無論放在內存裏還是磁盤裏,Oracle都使用一個Bitmap結構來反映這些Hash Bucket的狀態(包括其位置和是否有數據)。

 

Ⅱ:讀取匹配表數據並對每行的連接操作關聯列使用同上的Hash函數,定位BitmapBuild Table裏使用Hash函數後具有相同值數據所在的Bucket。如果該Bucket爲空,則丟棄匹配表的這條數據。如果不爲空,則需要看該Bucket是在內存裏還是在磁盤上。

 

如果在內存中,就直接訪問這個Bucket並檢查其中的數據是否匹配,有匹配的話就返回這條查詢結果。

 

如果在磁盤上,就先把這條待匹配數據放到一邊,將其先暫存在內存裏,等以後積累了一定量的這樣的待匹配數據後,再批量的把這些數據寫入到磁盤上(上圖中的 Dump probe partitions to disk)。

 

Ⅲ:當把匹配表完整的掃描了一遍後,可能已經返回了一部分匹配的數據了。接下來還有Hash Table中一部分在磁盤上的Hash Bucket數據以及匹配表中部分被寫入到磁盤上的待匹配數據未處理,現在Oracle會把磁盤上的這兩部分數據重新匹配一次,然後返回最終的查詢結果。

 

3): MULTIPASSHASH JOIN

 

當內存特別小或者相對而言Hash Table的數據特別大時,會使用 MULTIPASS 模式。MULTIPASS會多次讀取磁盤數據,應儘量避免使用該模式。

 

 

 

3. 上圖中的 OUTER 描述的是表連接類型;

 

表連接的兩種類型:

 

INNER JOIN(內連接)

OUTER JOIN(外連接)

示例數據說明:

 

現有AB兩表,A表信息如下:

 

table_A

 

B表信息如下:

 

table_B

 

下面的例子都用AB兩表來演示。

 

1 INNER JOIN(內連接):

 

只返回兩表中相匹配的記錄。

 

INNER JOIN 又分爲兩種:

 

等值連接(連接條件爲  =

非等值連接(連接條件爲,如  >  >= <  <=  等)

等值連接用的最多,下面以等值連接舉例:

 

內連接的兩種寫法:

 

Ⅰ: selecta.id A_ID, a.name A_NAME, b.id B_ID, b.name B_NAME from A a inner join B b on(a.id = b.id)

 

Ⅱ: selecta.id A_ID, a.name A_NAME, b.id B_ID, b.name B_NAME from A a join B b on (a.id =b.id)

 

連接時只返回滿足連接條件(a.id = b.id)的記錄:

 

inner_join

 

2 OUTER JOIN(外連接):

 

OUTER JOIN 分爲三種:

 

LEFT OUTER JOIN(可簡寫爲 LEFT JOIN,左外連接)

RIGHT OUTER JOIN RIGHT JOIN,右外連接)

FULL OUTER JOIN FULL JOIN,全外連接)

a) LEFT JOIN(左連接):

 

返回的結果不僅包含符合連接條件的記錄,還包含左邊表中的全部記錄。(若返回的左表中某行記錄在右表中沒有匹配項,則右表中的返回列均爲空值)

 

兩種寫法:

 

Ⅰ:selecta.id A_ID, a.name A_NAME, b.id B_ID, b.name B_NAME from A a left outer join B bon (a.id = b.id)

 

Ⅱ:selecta.id A_ID, a.name A_NAME, b.id B_ID, b.name B_NAME from A a left join B b on(a.id = b.id)

 

返回結果:

 

left_join

 

b) RIGHT JOIN(右連接):

 

返回的結果不僅包含符合連接條件的記錄,還包含右邊表中的全部記錄。(若返回的右表中某行記錄在左表中沒有匹配項,則左表中的返回列均爲空值)

 

兩種寫法:

 

Ⅰ:selecta.id A_ID, a.name A_NAME, b.id B_ID, b.name B_NAME from A a right outer join Bb on (a.id = b.id)

 

Ⅱ:selecta.id A_ID, a.name A_NAME, b.id B_ID, b.name B_NAME from A a right join B b on(a.id = b.id)

 

返回結果:

 

right_join

 

c) FULL JOIN(全連接):

 

返回左右兩表的全部記錄。(左右兩邊不匹配的項都以空值代替)

 

兩種寫法:

 

Ⅰ:selecta.id A_ID, a.name A_NAME, b.id B_ID, b.name B_NAME from A a full outer join B bon (a.id = b.id)

 

Ⅱ:selecta.id A_ID, a.name A_NAME, b.id B_ID, b.name B_NAME from A a full join B b on(a.id = b.id)

 

返回結果:

 

full_join

 

---------------------延伸閱讀: (+) 操作符-------------------

 

(+) 操作符是Oracle特有的表示法,用來表示外連接(只能表示左外、右外連接),需要配合Where語句使用。

 

特別注意:(+) 操作符在左表的連接條件上表示右連接,在右表的連接條件上表示左連接。

 

如:

 

Ⅰ:selecta.id A_ID, a.name A_NAME, b.id B_ID, b.name B_NAME from A a, B b where a.id =b.id(+)

 

查詢結果:

 

右邊( )

 

實際與左連接 select a.id A_ID, a.name A_NAME, b.id B_ID, b.name B_NAMEfrom A a left join B b on (a.id = b.id) 效果等價

 

Ⅱ:selecta.id A_ID, a.name A_NAME, b.id B_ID, b.name B_NAME from A a, B b where a.id(+)= b.id

 

查詢結果:

 

左邊( )

 

實際與右連接 select a.id A_ID, a.name A_NAME, b.id B_ID, b.name B_NAMEfrom A a right join B b on (a.id = b.id) 效果等價

 

 

 

 

 

 

HINT

ORACLEHINT詳解

 

  hintsoracle提供的一種機制,用來告訴優化器按照我們的告訴它的方式生成執行計劃。我們可以用hints來實現: 

 

  1) 使用的優化器的類型 

 

  2) 基於代價的優化器的優化目標,是all_rows還是first_rows 

 

  3) 表的訪問路徑,是全表掃描,還是索引掃描,還是直接利用rowid 

 

  4) 表之間的連接類型 

 

  5) 表之間的連接順序 

 

  6) 語句的並行程度 

 

  2HINT可以基於以下規則產生作用 

 

  表連接的順序、表連接的方法、訪問路徑、並行度 

 

  3HINT應用範圍 

 

  dml語句 

 

  查詢語句 

 

  4、語法 

 

  {DELETE|INSERT|SELECT|UPDATE} /*+ hint [text][hint[text]]... */ 

 

  or 

 

  {DELETE|INSERT|SELECT|UPDATE} --+ hint [text][hint[text]]... 

 

  如果語(句)法不對,則ORACLE會自動忽略所寫的HINT,不報錯 

 

  1. /*+ALL_ROWS*/ 

 

  表明對語句塊選擇基於開銷的優化方法,並獲得最佳吞吐量,使資源消耗最小化

 

  例如

 

  SELECT /*+ALL_ROWS*/ EMP_NO,EMP_NAM,DAT_INFROM BSEMPMS WHERE EMP_NO='SCOTT'; 

 

  2. /*+FIRST_ROWS*/ 

 

  表明對語句塊選擇基於開銷的優化方法,並獲得最佳響應時間,使資源消耗最小化

 

  例如

 

  SELECT /*+FIRST_ROWS*/ EMP_NO,EMP_NAM,DAT_IN FROMBSEMPMS WHERE EMP_NO='SCOTT'; 

 

  3. /*+CHOOSE*/ 

 

  表明如果數據字典中有訪問表的統計信息,將基於開銷的優化方法,並獲得最佳的吞吐量

 

  表明如果數據字典中沒有訪問表的統計信息,將基於規則開銷的優化方法

 

  例如

 

  SELECT /*+CHOOSE*/ EMP_NO,EMP_NAM,DAT_IN FROMBSEMPMS WHERE EMP_NO='SCOTT'; 

 

  4. /*+RULE*/ 

 

  表明對語句塊選擇基於規則的優化方法

 

  例如

 

  SELECT /*+ RULE */ EMP_NO,EMP_NAM,DAT_IN FROMBSEMPMS WHERE EMP_NO='SCOTT'; 

 

  5. /*+FULL(TABLE)*/ 

 

  表明對錶選擇全局掃描的方法

 

  例如

 

  SELECT /*+FULL(A)*/ EMP_NO,EMP_NAM FROMBSEMPMS A WHERE EMP_NO='SCOTT'; 

 

  6. /*+ROWID(TABLE)*/ 

 

  提示明確表明對指定表根據ROWID進行訪問

 

  例如

 

  SELECT /*+ROWID(BSEMPMS)*/ * FROM BSEMPMSWHERE ROWID>='AAAAAAAAAAAAAA' 

 

  AND EMP_NO='SCOTT'; 

 

  7. /*+CLUSTER(TABLE)*/ 

 

  提示明確表明對指定表選擇簇掃描的訪問方法,它只對簇對象有效

 

  例如

 

  SELECT /*+CLUSTER */ BSEMPMS.EMP_NO,DPT_NOFROM BSEMPMS,BSDPTMS 

 

  WHERE DPT_NO='TEC304' ANDBSEMPMS.DPT_NO=BSDPTMS.DPT_NO; 

 

  8. /*+INDEX(TABLE INDEX_NAME)*/ 

 

  表明對錶選擇索引的掃描方法

 

  例如

 

  SELECT /*+INDEX(BSEMPMS SEX_INDEX) USESEX_INDEX BECAUSE THERE ARE FEWMALE BSEMPMS */ FROM BSEMPMS WHERE SEX='M'; 

 

  9. /*+INDEX_ASC(TABLE INDEX_NAME)*/ 

 

  表明對錶選擇索引升序的掃描方法

 

  例如

 

  SELECT /*+INDEX_ASC(BSEMPMS PK_BSEMPMS) */FROM BSEMPMS WHERE DPT_NO='SCOTT'; 

 

  10. /*+INDEX_COMBINE*/ 

 

  爲指定表選擇位圖訪問路經,如果INDEX_COMBINE中沒有提供作爲參數的索引,將選擇出位圖索引的布爾組合方式

 

  例如

 

  SELECT /*+INDEX_COMBINE(BSEMPMS SAL_BMIHIREDATE_BMI)*/ * FROM BSEMPMS 

 

  WHERE SAL<5000000 AND HIREDATE 

 

  11. /*+INDEX_JOIN(TABLE INDEX_NAME)*/ 

 

  提示明確命令優化器使用索引作爲訪問路徑

 

  例如

 

  SELECT /*+INDEX_JOIN(BSEMPMS SAL_HMIHIREDATE_BMI)*/ SAL,HIREDATE 

 

  FROM BSEMPMS WHERE SAL<60000; 

 

  12. /*+INDEX_DESC(TABLE INDEX_NAME)*/ 

 

  表明對錶選擇索引降序的掃描方法

 

  例如

 

  SELECT /*+INDEX_DESC(BSEMPMS PK_BSEMPMS) */FROM BSEMPMS WHERE DPT_NO='SCOTT'; 

 

  13. /*+INDEX_FFS(TABLE INDEX_NAME)*/ 

 

  對指定的表執行快速全索引掃描,而不是全表掃描的辦法

 

  例如

 

  SELECT /*+INDEX_FFS(BSEMPMS IN_EMPNAM)*/ *FROM BSEMPMS WHERE DPT_NO='TEC305'; 

 

  14. /*+ADD_EQUAL TABLEINDEX_NAM1,INDEX_NAM2,...*/ 

 

  提示明確進行執行規劃的選擇,將幾個單列索引的掃描合起來

 

  例如

 

  SELECT /*+INDEX_FFS(BSEMPMSIN_DPTNO,IN_EMPNO,IN_SEX)*/ * FROM BSEMPMS WHERE EMP_NO='SCOTT' ANDDPT_NO='TDC306'; 

 

  15. /*+USE_CONCAT*/ 

 

  對查詢中的WHERE後面的OR條件進行轉換爲UNION ALL的組合查詢

 

  例如

 

  SELECT /*+USE_CONCAT*/ * FROM BSEMPMS WHEREDPT_NO='TDC506' AND SEX='M'; 

 

  16. /*+NO_EXPAND*/ 

 

  對於WHERE後面的OR 或者IN-LIST的查詢語句,NO_EXPAND將阻止其基於優化器對其進行擴展

 

  例如

 

  SELECT /*+NO_EXPAND*/ * FROM BSEMPMS WHEREDPT_NO='TDC506' AND SEX='M'; 

 

  17. /*+NOWRITE*/ 

 

  禁止對查詢塊的查詢重寫操作

 

  18. /*+REWRITE*/ 

 

  可以將視圖作爲參數

 

  19. /*+MERGE(TABLE)*/ 

 

  能夠對視圖的各個查詢進行相應的合併

 

  例如

 

  SELECT /*+MERGE(V) */A.EMP_NO,A.EMP_NAM,B.DPT_NO FROM BSEMPMS A (SELET DPT_NO 

 

  ,AVG(SAL) AS AVG_SAL FROM BSEMPMS B GROUP BYDPT_NO) V WHERE A.DPT_NO=V.DPT_NO 

 

  AND A.SAL>V.AVG_SAL; 

 

  20. /*+NO_MERGE(TABLE)*/ 

 

  對於有可合併的視圖不再合併

 

  例如

 

  SELECT /*+NO_MERGE(V) */A.EMP_NO,A.EMP_NAM,B.DPT_NO FROM BSEMPMS A (SELECT DPT_NO,AVG(SAL) AS AVG_SALFROM BSEMPMS B GROUP BY DPT_NO) V WHERE A.DPT_NO=V.DPT_NO ANDA.SAL>V.AVG_SAL; 

 

  21. /*+ORDERED*/ 

 

  根據表出現在FROM中的順序,ORDERED使ORACLE依此順序對其連接

 

  例如

 

  SELECT /*+ORDERED*/ A.COL1,B.COL2,C.COL3 FROMTABLE1 A,TABLE2 B,TABLE3 C WHERE A.COL1=B.COL1 AND B.COL1=C.COL1; 

 

  22. /*+USE_NL(TABLE)*/ 

 

  將指定表與嵌套的連接的行源進行連接,並把指定表作爲內部表

 

  例如

 

  SELECT /*+ORDERED USE_NL(BSEMPMS)*/BSDPTMS.DPT_NO,BSEMPMS.EMP_NO,BSEMPMS.EMP_NAM FROM BSEMPMS,BSDPTMS WHEREBSEMPMS.DPT_NO=BSDPTMS.DPT_NO; 

 

  23. /*+USE_MERGE(TABLE)*/ 

 

  將指定的表與其他行源通過合併排序連接方式連接起來

 

  例如

 

  SELECT /*+USE_MERGE(BSEMPMS,BSDPTMS)*/ * FROMBSEMPMS,BSDPTMS WHERE BSEMPMS.DPT_NO=BSDPTMS.DPT_NO; 

 

  24. /*+USE_HASH(TABLE)*/ 

 

  將指定的表與其他行源通過哈希連接方式連接起來

 

  例如

 

  SELECT /*+USE_HASH(BSEMPMS,BSDPTMS)*/ * FROMBSEMPMS,BSDPTMS WHERE BSEMPMS.DPT_NO=BSDPTMS.DPT_NO; 

 

  25. /*+DRIVING_SITE(TABLE)*/ 

 

  強制與ORACLE所選擇的位置不同的表進行查詢執行

 

  例如

 

  SELECT /*+DRIVING_SITE(DEPT)*/ * FROMBSEMPMS,DEPT@BSDPTMS WHERE BSEMPMS.DPT_NO=DEPT.DPT_NO; 

 

  26. /*+LEADING(TABLE)*/ 

 

  將指定的表作爲連接次序中的首表

 

  27. /*+CACHE(TABLE)*/ 

 

  當進行全表掃描時,CACHE提示能夠將表的檢索塊放置在緩衝區緩存中最近最少列表LRU的最近使用端 

 

  例如

 

  SELECT /*+FULL(BSEMPMS) CAHE(BSEMPMS) */EMP_NAM FROM BSEMPMS; 

 

  28. /*+NOCACHE(TABLE)*/ 

 

  當進行全表掃描時,CACHE提示能夠將表的檢索塊放置在緩衝區緩存中最近最少列表LRU的最近使用端 

 

  例如

 

  SELECT /*+FULL(BSEMPMS) NOCAHE(BSEMPMS) */EMP_NAM FROM BSEMPMS; 

 

  29. /*+APPEND*/ 

 

  直接插入到表的最後,可以提高速度

 

  insert /*+append*/ into test1 select * fromtest4 ; 

 

  30. /*+NOAPPEND*/ 

 

  通過在插入語句生存期內停止並行模式來啓動常規插入

 

  insert /*+noappend*/ into test1 select * fromtest4 ; 

 

  31. NO_INDEX: 指定不使用哪些索引 

 

  /*+ NO_INDEX ( table [index [index]...] )*/ 

 

  select /*+ no_index(emp ind_emp_salind_emp_deptno)*/ * from emp where deptno=200 and sal>300; 

 

  32. parallel 

 

  select /*+ parallel(emp,4)*/ * from emp wheredeptno=200 and sal>300; 

 

  另:每個SELECT/INSERT/UPDATE/DELETE命令後只能有一個/*+ */,但提示內容可以有多個,可以用逗號分開,空格也可以。 

 

  如:/*+ ordered index() use_nl() */ 

 

--------- 

類似如下的一條語句:insert into xxxx select /*+parallel(a) */ * from xxx a;數據量大約在75G左右,這位兄弟從上午跑到下午還沒跑完,過來問我咋回事,說平常2hrs能跑完的東西跑了好幾個小時還撒動靜。查看系統性能也比較正常,cpuio都不繁忙,平均READ速度在80M/s左右(勉強湊合),但平均寫速度只有10M不到。等待事件裏面大量的‘PX Deq Credit: send blkd’,這裏能看出並行出了問題,從而最後得知是並行用法有問題,修改之後20分鐘完成了該操作。正確的做法應該是: 

alter sessionenable dml parallel 

 

insert/*+parallel(xxxx,4) */ into xxxx select /*+parallel(a) */ * from xxx a; 

 

因爲oracle默認並不會打開PDML,對DML語句必須手工啓用。另外不得不說的是,並行不是一個可擴展的特性,只有在數據倉庫或作爲DBA等少數人的工具在批量數據操作時利於充分利用資源,而在OLTP環境下使用並行需要非常謹慎。事實上PDML還是有比較多的限制的,例如不支持觸發器,引用約束,高級複製和分佈式事務等特性,同時也會帶來額外的空間佔用,PDDL樣是如此。

--------- 

selectcount(*) 

  From wid_serv_prod_mon_1100 a 

 where a.acct_month = 201010 

   and a.partition_id = 10 

   and serv_state not in ('2HB', '2HL', '2HJ','2HP', '2HF') 

   and online_flag in (0) 

   and incr_product_id in (2000020) 

   and product_id in (2020966, 2020972,2100297, 2021116) 

   and billing_mode_id = 1 

   and exp_date > to_date('201010','yyyymm') 

   and not exists (select /*+no_index (bIDX_W_CDR_MON_SERV_ID_1100)*/ 

         1 

          from wid_cdr_mon_1100 b 

         where b.acct_month = 201010 

           and b.ANA_EVENT_TYPE_4 in 

               ('10201010201', '10202010201','10203010201', '10203010202', '10203030201', '10203030202', '10204010201','10204010202', '10204030201') 

           and a.serv_id = b.serv_id) 

 

 

Group by

group by 一般和聚合函數一起使用纔有意義,比如 count sum avg等,使用groupby的兩個要素:
   (1) 出現在select後面的字段 要麼是是聚合函數中的,要麼就是group by 中的.
   (2) 要篩選結果 可以先使用where 再用group by 或者先用group by 再用having


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章