1. 程式人生 > 其它 >SQL優化一(SQL使用技巧)

SQL優化一(SQL使用技巧)

1、行列轉換:

  decode(條件,值1,返回值1,值2,返回值2,...值n,返回值n,預設值);

  select decode(sign(變數1-變數2),-1,變數1,變數2) from dual; --取較小值

  sign()函式根據某個值是0、正數還是負數,分別返回0、1、-1

  例如:

  變數1=10,變數2=20

  則sign(變數1-變數2)返回-1,decode解碼結果為“變數1”,達到了取較小值的目的。

舉例:查詢emp表中的每個部門的人數?

SELECT sum(decode(deptno,10,1,0)) as 部門10,
            sum(decode(deptno,20,1,0)) as 部門20,
            sum(decode(deptno,30,1,0)) as 部門30 from emp

      做個對比:select deptno,count(deptno) from emp group by deptno

2、遞迴查詢的優化

   lpad/rpad( string, padded_length, [ pad_string ] )

   解釋:字元不夠的時候向左或者向右填充。

   例如:

SQL> select lpad('abcde',10,'x') from dual;
      LPAD('ABCDE',10,'X')
      xxxxxabcde
SELECT ID, FATHER_ID, NAME, CONNECT_BY_ISLEAF LEAF   
FROM T_TREE   
START WITH FATHER_ID = 0   
CONNECT BY PRIOR ID = FATHER_ID;  

解釋:CONNECT_BY_ISLEAF 判斷該行記錄是否為葉子節點,如果是返回1,否則返回0

          START WITH FATHER_ID = 0 規定哪一條記錄為根節點

          CONNECT BY PRIOR ID = FATHER_ID 判斷此節點的父節點是哪一條記錄

例子:

 SELECT LPAD(MENU_NAME,
            LENGTHB(MENU_NAME) + LEVEL,
            DECODE(CONNECT_BY_ISLEAF, 1, ' | ', '+'))
  FROM MENU
  START WITH PID = '0'
  CONNECT BY PRIOR ID = PID;

3、利用分析函式排序和去重

http://blog.csdn.net/haiross/article/details/15336313#comments

分析函式是什麼? 分析函式是Oracle專門用於解決複雜報表統計需求的功能強大的函式,它可以在資料中進行分組然後計算基於組的某種統計值,並且每一組的每一行都可以返回一個統計值。

分析函式和聚合函式的不同之處是什麼? 普通的聚合函式用group by分組,每個分組返回一個統計值,返回的欄位名只能是分組名。而分析函式採用partition by分組,並且每組每行都可以返回一個統計值,返回的欄位名可以是每個欄位,因為是對應到記錄的,所以沒有關係。

分析函式的形式 分析函式帶有一個開窗函式over(),包含三個分析子句:分組(partition by), 排序(order by), 視窗(rows) ,他們的使用形式如下:over(partition by xxx order by yyy rows between zzz)

開窗函式over()包含三個分析子句:分組子句(partition by), 排序子句(order by), 視窗子句(rows) 視窗就是分析函式分析時要處理的資料範圍,就拿sum來說,它是sum視窗中的記錄而不是整個分組中的記錄,因此我們在想得到某個欄位的累計值時,我們需要把視窗指定到該分組中的第一行資料到當前行, 如果你指定該視窗從該分組中的第一行到最後一行,那麼該組中的每一個sum值都會一樣,即整個組的總和。

OVER(PARTITION BY DEPTNO ORDER BY ENAME ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW):當前組第一行到當前行的彙總
OVER(PARTITION BY DEPTNO ORDER BY ENAME ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING):當前行到最後一行的彙總

OVER(PARTITION BY DEPTNO ORDER BY ENAME ROWS BETWEEN 1 PRECEDING AND CURRENT ROW):當前行的上一行(rownum-1)到當前行的彙總

OVER(PARTITION BY DEPTNO ORDER BY ENAME ROWS BETWEEN 1 PRECEDING AND 2 FOLLOWING):當前行的上一行(rownum-1)到下兩行(rownum+2)的彙總

而無論是否省略分組子句,如下結論都是成立的:

 1、視窗子句不能單獨出現,必須有order by子句時才能出現。

 2、當省略視窗子句時:     a) 如果存在order by則預設的視窗是unbounded preceding and current row  --當前組的第一行到當前行,即在當前組中,第一行到當前行,這裡強調一下,如果partition by欄位和order by 欄位一樣的話,這個order by不生效,相當於省略了order by

    b) 如果同時省略order by則預設的視窗是unbounded preceding and unbounded following  --整個組

兩個order by的執行時機

分析函式(以及與其配合的開窗函式over())是在整個sql查詢結束後(sql語句中的order by的執行比較特殊)再進行的操作, 也就是說sql語句中的order by也會影響分析函式的執行結果:

    a) 兩者一致:如果sql語句中的order by滿足與分析函式配合的開窗函式over()分析時要求的排序,即sql語句中的order by子句裡的內容和開窗函式over()中的order by子句裡的內容一樣,那麼sql語句中的排序將先執行,分析函式在分析時就不必再排序;     b) 兩者不一致:如果sql語句中的order by不滿足與分析函式配合的開窗函式over()分析時要求的排序,即sql語句中的order by子句裡的內容和開窗函式over()中的order by子句裡的內容不一樣,那麼sql語句中的排序將最後在分析函式分析結束後執行排序。

常用的分析函式:

  1、row_number() over(partition by ... order by ...)  為每一條記錄返回一個唯一的值。當碰到相同資料時,排名按照記錄集中記錄的順序依次遞增,現實情景為:個人在分組內的排名   2、rank() over(partition by ... order by ...)  得到每條記錄在資料中的排名,排名不跳躍   3、dense_rank() over(partition by ... order by ...) 得到每條記錄在資料中的排名,排名跳躍   4、count() over(partition by ... order by ...) 每個分組中,某個欄位的統計   5、max() over(partition by ... order by ...)   6、min() over(partition by ... order by ...)   7、sum() over(partition by ... order by ...)   8、avg() over(partition by ... order by ...)   9、first_value() over(partition by ... order by ...) 得到第一個記錄值   10、last_value() over(partition by ... order by ...) 得到最後一個記錄值   11、lag() over(partition by ... order by ...) lag函式可以在一次查詢中取出同一欄位的前n行的資料

  12、lead() over(partition by ... order by ...) lead函式可以在一次查詢中取出同一欄位的後n行的值

lag(arg1,arg2,arg3) 第一個引數是列名, 第二個引數是偏移的offset, 第三個引數是超出記錄視窗時的預設值。

select id,name,lag(name,1,0) over(order by id) from kkk; 

 例子:select ename,deptno,sal,sum(sal) over(partition by deptno order by ename) as A from emp

去重:

  1、利用rowid的唯一性查詢或刪除重複資料

   select ROWNUM,ROWID,d1.* from dept2 d1 where d1.rowid=(select min(d2.rowid) from dept2 d2 where d2.deptno=d1.deptno);

2、給定重複行序號並去重

SELECT FWZL
    FROM (SELECT FWZL,
                 ROW_NUMBER() OVER(PARTITION BY FWZL ORDER BY ID DESC) RN
            FROM T_FWXX) F

                       WHERE F.RN = 1

 4、求佔比、小計和總計

      分析函式RATIO_TO_REPORT 用來計算當前記錄的指標expr佔開窗函式over中包含記錄的所有同一指標的百分比. 這裡如果開窗函式的統計結果為null或者為0,就是說佔用比率的被除數為0或者為null, 則得到的結果也為0. 開窗條件query_partition_clause決定被除數的值, 如果使用者忽略了這個條件, 則計算查詢結果中所有記錄的彙總值. 使用者不能使用其他分析函式或者ratio_to_report作為分析函式ratio_to_report的引數expr, 也就是說這個函式

 百分比(求這個欄位值佔整組的百分比):select deptno,ename,empno,round(RATIO_TO_REPORT(sal) OVER(PARTITION BY deptno)*100,1) 百分比 from emp

rollup()與cube():排列組合分組

1)、group by rollup(a, b, c): 首先會對(a、b、c)進行group by, 然後再對(a、b)進行group by, 其後再對(a)進行group by, 最後對全表進行彙總操作。

2)、group by cube(a, b, c): 則首先會對(a、b、c)進行group by, 然後依次是(a、b),(a、c),(a),(b、c),(b),(c), 最後對全表進行彙總操作。

ROLLUP,是GROUP BY子句的一種擴充套件,可以為每個分組返回小計記錄以及為所有分組返回總計記錄。

CUBE,也是GROUP BY子句的一種擴充套件,可以返回每一個列組合的小計記錄,同時在末尾加上總計記錄。

求每個分組的小計和總計:select deptno,sal,sum(sal) from emp group by rollup(deptno,sal)

求每一個列的組合的小計記錄:select deptno,sum(sal) from emp group by cube(deptno,sal)

聚合函式(within group語法):

select rank(1500) within group (order by sal desc) "rank of 1500" from emp;

    解釋:如果存在一條記錄,這條記錄的salary欄位值為1500。那麼將該條記錄插入emp表中後,按照sal欄位降序排列後,該條記錄的序號為多少?

可以使用within group關鍵字的函式有rank,dense_rank,PERCENT_RANK,PERCENTILE_CONT,PERCENTILE_DISC等

 5、單條記錄插入多表

      原理:利用一個insert all 語法:insert all when .. then

INSERT ALL WHEN LOCALE = 1 THEN INTO EMPLOYEE1(ID, LOCALE, NAME, AGE, GENDER, CODE) VALUES (ID, LOCALE, NAME, AGE, GENDER, CODE)

                  WHEN LOCALE = 32 THEN INTO EMPLOYEE2(ID, LOCALE, NAME, AGE, GENDER, CODE) VALUES(ID, LOCALE, NAME, AGE, GENDER, CODE)

insert all into table values()

              into  table values()

 6、Merge的使用

 解釋:DML語句,適用於批量處理

   MERGE INTO table_name alias1    USING (table|view|sub_query) alias2    ON (join condition)    WHEN MATCHED THEN    UPDATE table_name   SET col1 = col_val1,    col2 = col2_val where 條件   WHEN NOT MATCHED THEN    INSERT (column_list) VALUES (column_values) where 條件;

MERGE INTO EMPLOYEE E

USING (SELECT * FROM EMPLOYEE1) E1

ON (E.NAME = E1.NAME)

WHEN MATCHED THEN

    UPDATE SET E.CODE = E1.CODE, E.AGE = E1.AGE

WHEN NOT MATCHED THEN

   INSERT  (E.ID, E.LOCALE, E.NAME, E.AGE, E.GENDER, E.CODE) VALUES(E1.ID, E1.LOCALE, E1.NAME, E1.AGE, E1.GENDER, E1.CODE);

備註:對兩張表的兩個欄位相匹配,如果匹配上了就做更新操作,否則就做插入操作。

 7、KEEP的使用

     keep是Oracle下的另一個分析函式,他的用法不同於通過over關鍵字指定的分析函式,可以用於這樣一種場合下:取同一個分組下以某個欄位排序後,對指定欄位取最小或最大的那個值。

  一般寫法是 MIN [ MAX ] (A) KEEP(DENSE_RANK FIRST [ LAST ] ORDER BY B),這裡引用別人說的明的解釋一下:

DENSE_RANK

功能描述:根據ORDER BY子句中表達式的值,從查詢返回的每一行,計算它們與其它行的相對位置。組內的資料按ORDER BY子句排序,然後給每一行賦一個號,從而形成一個序列,該序列從1開始,往後累加。每次ORDER BY表示式的值發生變化時,該序列也隨之增加。有同樣值的行得到同樣的數字序號(認為null時相等的)。密集的序列返回的時沒有間隔的數。

FIRST

功能描述:從DENSE_RANK返回的集合中取出排在最前面的一個值的行(可能多行,因為值可能相等),因此完整的語法需要在開始處加上一個集合函式以從中取出記錄。

LAST

功能描述:從DENSE_RANK返回的集合中取出排在最後面的一個值的行(可能多行,因為值可能相等),因此完整的語法需要在開始處加上一個集合函式以從中取出記錄。

所以預設排序下,FIRST可以理解是取小值,LAST取大值。而前面的MIN或者MAX則是在KEEP的結果集中取某一欄位的最大值或最小值。

  keep和普通分析函式的區別:普通的分析函式只是列出分組後的記錄,而對每一個組的記錄進行統計分析。

                            keep對分組內的函式通過order by和max(),min()選取某個欄位的值。可以理解成這裡的keep就是sum() groud by deptno 前的sum()那樣

  例項:取出各個部門薪資最高的員工編號

  1、select deptno,empno,sal,max(empno) keep(dense_rank first order by sal desc) over(partition by deptno) from emp2

  2、select deptno,max(empno) keep(dense_rank first order by sal desc) from emp2 group by deptno 

        解釋:按deptno分組,再對分組中的sal降序,取出第一個sal的員工號

8、SQL查詢正則表示式的使用

  ORACLE中的支援正則表示式的函式主要有下面四個: 1,REGEXP_LIKE :與LIKE的功能相似

    select * from emp where regexp_like(empno,'7[0-9]{2}9') 2,REGEXP_INSTR :與INSTR的功能相似

REGEXP_INSTR

6個引數

第一個是輸入的字串

第二個是正則表示式

第三個是標識從第幾個字元開始正則表示式匹配。(預設為1)

第四個是標識第幾個匹配組。(預設為1)

第五個是指定返回值的型別,如果該引數為0,則返回值為匹配位置的第一個字元,如果該值為非0則返回匹配值的最後一個位置。

第六個是是取值範圍:

i:大小寫不敏感;

c:大小寫敏感;

n:點號 . 不匹配換行符號;

m:多行模式;

x:擴充套件模式,忽略正則表示式中的空白字元。

    SELECT REGEXP_INSTR(a,'[0-9]+') AS A FROM test_reg_substr;

3,REGEXP_SUBSTR :與SUBSTR的功能相似

REGEXP_SUBSTR函式格式如下:

   function REGEXP_SUBSTR(String, pattern, position, occurrence, modifier) __srcstr     :需要進行正則處理的字串 __pattern    :進行匹配的正則表示式 __position   :起始位置,從第幾個字元開始正則表示式匹配(預設為1) __occurrence :標識第幾個匹配組,預設為1 __modifier   :模式('i'不區分大小寫進行檢索;'c'區分大小寫進行檢索。預設為'c'。)

  1. --1、查詢使用正則分割後的第一個值,也就是34  
  2. SELECT REGEXP_SUBSTR('34,56,-23','[^,]+',1,1,'i') AS STR FROM DUAL;  
  3. --結果是:34   
  4. --2、查詢使用正則分割後的最後一個值,也就是-23  
  5. SELECT REGEXP_SUBSTR('34,56,-23','[^,]+',1,3,'i') AS STR FROM DUAL;  
  6. --結果是:-23  

4,REGEXP_REPLACE :與REPLACE的功能相似

9、常見函式

TRUNC:擷取函式

EXTRACT:用於從一個date或者interval型別中擷取到特定的部分

NVL

DECODE

length:字元長度

lengthb:位元組長度

ASCII

INITCAP:首字母大寫

SOUNDEX:返回由四個字元組成的程式碼 (SOUNDEX) 以評估兩個字串的相似性

MONTHS_BETWEEN

ADD_MONTHS

NEXT_DAY

LAST_DAY

ROUND:函式用於把數值欄位舍入為指定的小數位數

10、分頁函式

--普通寫法

SELECT AA.FWZL, AA.FWTYBH
  FROM (SELECT A.FWZL, A.FWTYBH, ROWNUM RN
          FROM (SELECT F.FWZL, F.FWTYBH FROM FW F ORDER BY F.FWTYBH DESC) A
         WHERE ROWNUM <= 120020) AA
 WHERE AA.RN > 120000;

--rowid寫法

SELECT /*+ ROWID(FW) */ FW.FWZL, FW.FWTYBH
   FROM FW FW,
        (SELECT AA.RID, AA.RN
           FROM (SELECT A.RID, ROWNUM RN
                  FROM (SELECT /*+ index(F IDX_FW_FWTYBH) */
                         ROWID RID
                          FROM FW F
                          ORDER BY F.FWTYBH DESC) A
                 WHERE ROWNUM <= 120020) AA
          WHERE AA.RN > 120000) B
  WHERE FW.ROWID = B.RID;