SQL語言小結和優化(整理)

           上面幾篇討論的是數據庫方面的事務和鎖方面的一些內容,實際上我們在實際中最常用的就是基本的sql語句了,實際上我個人感覺sql很難記憶(哈哈,語法和編程語言差太多了),所以往往有時候寫些複雜的sql語句就必須google一下語法了,雖然現在有不錯的ORM的工具,但還是覺得簡單得數據庫操作使用ORM比較方便,但太複雜的應用就不太爽了,當然我們可以利用面向對象的思路把複雜的sql語句給拆分成簡單的。      

1.數據定義語言(DDL)
 常用的DDL語句
 Create Table 創建數據庫對象
 Create Index 創建數據庫表索引
 Drop Table   刪除數據庫表
 Drop Index   刪除數據庫表索引
 Truncate     刪除表中所有行業
 
 Alert Table  增加表列,重定義表列,更改存儲分配
 Alert Table  ADD CONSTRAINT 在已有的表上增加約束

2.數據操縱語言(DML)
 Insert 增加數據行到表
 Delete 從表中刪除數據行
 Update 更改表中數據
 Select 從表或視圖中檢索數據行

3.數據控制語言(DCL)
 DCL用於規定數據庫用戶的各種權限
 
 GRANT   將權限或角色授予用戶或其他角色
 REVOKE  從用戶或數據庫角色回收權限
 Set ROLE 禁止或允許一個角色

4.數據庫事務控制
 
 常用的事務語句包括
 
 COMMIT WORK 把當前事務更改永久化(寫入磁盤)
 ROLLBACK 作廢上次提交以來的所有更改

SQL語句基本語法
 
 每條SQL語句必須以分號結束.

 每條SQL語句可以單獨寫成一行,但爲了清楚,也可以分成若干行.

 SQL語句對大小寫不敏感,對於SQL語句的關鍵字(例如,Insert,Select 等),

表名、列名等,可以大小寫混寫;但是對列的內容是大小寫敏感的。

別名

數據表名稱 AS 數據表別名
或者是:
數據表名稱 數據表別名

Eg:
 Use SAMPLE
 Select e.員工編號 ,e.員工姓名
 From 員工數據表 AS e

Select 語句
 Select 語句基本結構如下:
 Select  select_list
 [INTO new_table_name]
 From table_list
 [Where search_conditions]
 [GROUP BY group_by_list]
 [Having search_conditions]
 [ODER BY order_list[ASC|DESC]]
 
 1.DISTINCT關鍵字
  eg:
  Use SAMPLE
  Select DISTINCT 所屬部門
  from 員工數據表
 2.TOP 關鍵字
 
From子句
 聯接條件---on子句
 派生表---sp
 
 Select 員工數據表.員工編號,項目數據表.項目名稱
 From 員工數據表 join 項目數據表 ON
 (員工數據表.員工編號 = 項目數據表.負責人)
 
 Select EMP.員工編號,EMP.員工姓名,SP.部門名稱
 From 員工數據表 AS EMP ,
 (Select 部門數據表.部門編號,部門數據表.部門名稱
 From 部門數據表
 Where 部門數據表.部門編號>2
 ) AS SP
 Where EMP.部門編號=SP.部門編號

Where 子句
 Where子句中查詢和限定的條件可以是
 比較運算符(如常=、<>、<和>).
 範圍說明(BETWEEN 和 NOT BETWEEN)。
 可選列表(IN、NOT IN)
 模式匹配(Like 和NOT Like).
 是否爲空值(IS NULL 和 IS NOT NULL)。
 上述條件的邏輯組合(AND 、OR和NOT)。

GROUP BY 子句
  GROUP BY 子句的主要作用是可以將數據記錄依據設置的條件分成多個組,
而且只有使用了GROUP BY

子句,SELECT中所使用的彙總函數(例如SUM、COUNT、MIN、MAX等)
纔會起作用。
eg:
 Select 所屬部門,AVG(工資) AS 平均工資 From 員工數據表 GROUP BY
所屬部門
  HAVING 關鍵字
 HAVING子句將對GROUP BY 子句選擇出來的結果進行再次篩選,最後輸出符

合HAVING 子句中條件的記錄。
eg:
 Select 所屬部門,AVG(工資) AS 平均工資 From 員工數據表 GROUP BY
所屬部門 HAVING 平均工資>2000
 
  Where 子句用來篩選From子句中指定的操作所產生的記錄
  GROUP BY 子句將Where子句中結果驚醒分組
  HAVING 子句將從經過分組後的中間結果集中篩選記錄
 
  ALL關鍵字
  CUBE關鍵字
  WITH CUBE 關鍵字的主要作用是自動對GROUP BY 子句中列出的字段進行
分組彙總運算。
 EG:
  Select 所屬部門 ,性別,AVG(工資) From 員工數據表 GROUP BY
所屬部門,性別 WITH CUBE
 
 ROLLUP 關鍵字
 
ORDER BY 子句
 
 ASC ---升序
 DESC --降序

COMPUTE 和COMPUTE BY 子句
 
使用UNION 子句
 UNION運算符可以用來將2個或多個查詢結果集組合起來,成爲一個結果集

。使用UNION運算符的結果集都必須滿足下列條件:
 具有相同的結構
 字段數目相同
 結果集中相對應的數據類型必須兼容
 UNION運算符指定的格式如下:
  Select 語句
  UNION[ALL]
  Select 語句

彙總函數
 SUM函數
 AVG函數
 COUNT函數
 COUNT(*)函數
 MAX函數
 MIN函數

聯接查詢
  通過使用聯接產尋可以根據各個數據表之間的邏輯關係從2個或多個
數據表中檢索數據。
 
  如何提高Select語句的效率
  (1).使用EXISTS關鍵字檢查結果集
  (2).使用標準的連接代替嵌套查詢
  (3).有效避免整表掃描
 
  聯接的類型
 
  內聯接
  內聯結的格式爲:
  數據表1 INNER JOIN 數據表2 ON 聯接表達式
  內聯接將通過使用比較運算符根據需要聯接的數據表的公共的字段
值來匹配二表中的記錄。
  eg:
   Select * From 員工數據表 INNER JOIN 項目數據表
   ON 員工數據表.員工編號 = 項目數據表.負責人
 
 外聯接
 外聯接包括3種 左向外聯接、右向外聯接、完整外部聯接

 左向外聯接的格式爲:
 數據表1 LEFT JOIN 數據表2 ON 連接表達式
 或者是:
 數據表1 LEFT OUTER JOIN 數據表2 ON 連接表達式
 使用左向聯接進行查詢的結果集將包括數據表1中所有的記錄,而僅僅是連

接字段所匹配的記錄,那麼結果集想對應的有關數據表2的所有字段將爲空值

.例如下面將檢索員工數據表中所有記錄,並將項目表中顯得負責人字段匹配

的記錄輸出到結果集:
Select *
      From 員工數據表 LEFT JION 項目數據表
      ON  員工數據表.員工編號 =項目數據表.負責人

 右向外聯接
 右向外聯接的格式:
  數據表1 RIGHT JOIN 數據表2 ON 表達式
  或者是:
  數據表1 RIGHT OUTER JOIN 數據表2 ON 表達式
  和左向外聯接相反.

 完整外聯接
 完整外聯接的格式
  數據表1 FULL JOIN 數據表2 ON 表達式 
  或者是:
  數據表1 FULL OUTER JOIN 數據表2 ON 表達式
 
 交叉聯接
 交叉連接的格式爲:
 數據表1 CROSS JOIN 數據表2
 如果在Select語句中沒有使用Where子句,那麼交叉聯接將返回數據表1和數

據表2的卡笛爾積,即交叉聯接返回數據表1中的所有記錄,以及數據表1中所

有的記錄與數據表2中所有句路的組合.結果集的記錄數等於數據表1的記錄

數目乘以數據表2的記錄數目.

在From和Where分句中指定聯接

嵌套查詢
  嵌套查詢指的是一個外層查詢中包含一個內層查詢.其中外層查詢稱爲主

查詢,內層查詢稱爲子查詢.
  使用IN 和 NOT IN 關鍵字
  Use SAMPLE
  Select 員工姓名
  From 員工數據表
  Where 員工編號 IN
  (Select DISTINCT 負責人
  From 項目數據表
  Where 結束日期<'January 1,1,2006')
 使用比較運算符
 主要是返回單個值
  Use SAMPLE
  Select 員工姓名
  From 員工數據表
  Where 員工編號 =
  (Select DISTINCT 負責人
  From 項目數據表
  Where 項目名稱='Demo1')
 使用EXISTS 和 NOT EXISTS 關鍵字

數據更改
  Insert
  Update
  Delete

下面是sql語句優化的一些提示要點,也是google來的,感謝提供者。

操作符優化

IN 操作符

用IN寫出來的SQL的優點是比較容易寫及清晰易懂,這比較適合現代軟件開發的風格。
但是用IN的SQL性能總是比較低的,從ORACLE執行的步驟來分析用IN的SQL與不用IN的SQL有以下區別:
       ORACLE試圖將其轉換成多個表的連接,如果轉換不成功則先執行IN裏面的子查詢,再查詢外層的表記錄,如果轉換成功則直接採用多個表的連接方式查詢。由此可見用IN的SQL至少多了一個轉換的過程。一般的SQL都可以轉換成功,但對於含有分組統計等方面的SQL就不能轉換了。
       推薦方案:在業務密集的SQL當中儘量不採用IN操作符。

NOT IN操作符

       此操作是強列推薦不使用的,因爲它不能應用表的索引。
       推薦方案:用NOT EXISTS 或(外連接+判斷爲空)方案代替

<> 操作符(不等於)

       不等於操作符是永遠不會用到索引的,因此對它的處理只會產生全表掃描。
推薦方案:用其它相同功能的操作運算代替,如
       a<>0 改爲 a>0 or a<0
       a<>’’ 改爲 a>’’

IS NULL 或IS NOT NULL操作(判斷字段是否爲空)

       判斷字段是否爲空一般是不會應用索引的,因爲B樹索引是不索引空值的。
       推薦方案:
用其它相同功能的操作運算代替,如
       a is not null 改爲 a>0 或a>’’等。
       不允許字段爲空,而用一個缺省值代替空值,如業擴申請中狀態字段不允許爲空,缺省爲申請。
       建立位圖索引(有分區的表不能建,位圖索引比較難控制,如字段值太多索引會使性能下降,多人更新操作會增加數據塊鎖的現象) 

> 及 < 操作符(大於或小於操作符)

       大於或小於操作符一般情況下是不用調整的,因爲它有索引就會採用索引查找,但有的情況下可以對它進行優化,如一個表有100萬記錄,一個數值型字段A,30萬記錄的A=0,30萬記錄的A=1,39萬記錄的A=2,1萬記錄的A=3。那麼執行A>2與A>=3的效果就有很大的區別了,因爲A>2時ORACLE會先找出爲2的記錄索引再進行比較,而A>=3時ORACLE則直接找到=3的記錄索引。
LIKE操作符
LIKE操作符可以應用通配符查詢,裏面的通配符組合可能達到幾乎是任意的查詢,但是如果用得不好則會產生性能上的問題,如LIKE ‘%5400%’ 這種查詢不會引用索引,而LIKE ‘X5400%’則會引用範圍索引。一個實際例子:用YW_YHJBQK表中營業編號後面的戶標識號可來查詢營業編號 YY_BH LIKE ‘%5400%’ 這個條件會產生全表掃描,如果改成YY_BH LIKE ’X5400%’ OR YY_BH LIKE ’B5400%’ 則會利用YY_BH的索引進行兩個範圍的查詢,性能肯定大大提高。
UNION操作符
UNION在進行錶鏈接後會篩選掉重複的記錄,所以在錶鏈接後會對所產生的結果集進行排序運算,刪除重複的記錄再返回結果。實際大部分應用中是不會產生重複的記錄,最常見的是過程表與歷史表UNION。如:
select * from gc_dfys
union
select * from ls_jg_dfys
這個SQL在運行時先取出兩個表的結果,再用排序空間進行排序刪除重複的記錄,最後返回結果集,如果表數據量大的話可能會導致用磁盤進行排序。
推薦方案:採用UNION ALL操作符替代UNION,因爲UNION ALL操作只是簡單的將兩個結果合併後就返回。
select * from gc_dfys
union all
select * from ls_jg_dfys
SQL書寫的影響

同一功能同一性能不同寫法SQL的影響

如一個SQL在A程序員寫的爲
       Select * from zl_yhjbqk
B程序員寫的爲
       Select * from dlyx.zl_yhjbqk(帶表所有者的前綴)
C程序員寫的爲
       Select * from DLYX.ZLYHJBQK(大寫表名)
D程序員寫的爲
       Select * from DLYX.ZLYHJBQK(中間多了空格)

 

 

 

以上四個SQL在ORACLE分析整理之後產生的結果及執行的時間是一樣的,但是從ORACLE共享內存SGA的原理,可以得出ORACLE對每個SQL 都會對其進行一次分析,並且佔用共享內存,如果將SQL的字符串及格式寫得完全相同則ORACLE只會分析一次,共享內存也只會留下一次的分析結果,這不僅可以減少分析SQL的時間,而且可以減少共享內存重複的信息,ORACLE也可以準確統計SQL的執行頻率。

 

 

 

WHERE後面的條件順序影響

 

 

 

WHERE子句後面的條件順序對大數據量表的查詢會產生直接的影響,如
Select * from zl_yhjbqk where dy_dj = '1KV以下' and xh_bz=1
Select * from zl_yhjbqk where xh_bz=1 and dy_dj = '1KV以下'
以上兩個SQL中dy_dj(電壓等級)及xh_bz(銷戶標誌)兩個字段都沒進行索引,所以執行的時候都是全表掃描,第一條SQL的dy_dj = '1KV以下'條件在記錄集內比率爲99%,而xh_bz=1的比率只爲0.5%,在進行第一條SQL的時候99%條記錄都進行dy_djxh_bz的比較,而在進行第二條SQL的時候0.5%條記錄都進行dy_djxh_bz的比較,以此可以得出第二條SQL的CPU佔用率明顯比第一條低。

 

 

 

查詢表順序的影響

在FROM後面的表中的列表順序會對SQL執行性能影響,在沒有索引及ORACLE沒有對錶進行統計分析的情況下ORACLE會按表出現的順序進行鏈接,由此因爲表的順序不對會產生十分耗服務器資源的數據交叉。(注:如果對錶進行了統計分析,ORACLE會自動先進小表的鏈接,再進行大表的鏈接)

 

 

 

SQL語句索引的利用

對操作符的優化(見上節)

對條件字段的一些優化

採用函數處理的字段不能利用索引,如:

substr(hbs_bh,1,4)=’5400’,優化處理:hbs_bh like ‘5400%’

trunc(sk_rq)=trunc(sysdate), 優化處理:

sk_rq>=trunc(sysdate) and sk_rq<trunc(sysdate+1)

進行了顯式或隱式的運算的字段不能進行索引,如:

ss_df+20>50,優化處理:ss_df>30
‘X’||hbs_bh>’X5400021452’,優化處理:hbs_bh>’5400021542’
sk_rq+5=sysdate,優化處理:sk_rq=sysdate-5
hbs_bh=5401002554,優化處理:hbs_bh=’ 5401002554’,注:此條件對hbs_bh 進行隱式的to_number轉換,因爲hbs_bh字段是字符型。

條件內包括了多個本表的字段運算時不能進行索引,如:

ys_df>cx_df,無法進行優化
qc_bh||kh_bh=’5400250000’,優化處理:qc_bh=’5400’ and kh_bh=’250000’

 

 

 

應用ORACLE的HINT(提示)處理

 

 

 

提示處理是在ORACLE產生的SQL分析執行路徑不滿意的情況下要用到的。它可以對SQL進行以下方面的提示

目標方面的提示:

COST(按成本優化)
RULE(按規則優化)
CHOOSE(缺省)(ORACLE自動選擇成本或規則進行優化)
ALL_ROWS(所有的行儘快返回)
FIRST_ROWS(第一行數據儘快返回)

執行方法的提示:

USE_NL(使用NESTED LOOPS方式聯合)
USE_MERGE(使用MERGE JOIN方式聯合)
USE_HASH(使用HASH JOIN方式聯合)

索引提示:

INDEX(TABLE INDEX)(使用提示的表索引進行查詢)

其它高級提示(如並行處理等等)    

ORACLE的提示功能是比較強的功能,也是比較複雜的應用,並且提示只是給ORACLE執行的一個建議,有時如果出於成本方面的考慮ORACLE也可能不會按提示進行。根據實踐應用,一般不建議開發人員應用ORACLE提示,因爲各個數據庫及服務器性能情況不一樣,很可能一個地方性能提升了,但另一個地方卻下降了,ORACLE在SQL執行分析方面已經比較成熟,如果分析執行的路徑不對首先應在數據庫結構(主要是索引)、服務器當前性能(共享內存、磁盤文件碎片)、數據庫對象(表、索引)統計信息是否正確這幾方面分析。
上面就sql語句說了一通,下面我就在儲存過程中的一點小技巧來說明。一般情況下我們在存儲過程中都會定義變量,但如何要操作的表名是變量的話就要使用sp_executesql函數來執行拼裝的sql語句的字符串,拼裝中可以帶輸出參數。例如:
CREATE procedure test1  as
declare
    @nAccountPer      bigint,
    @nAllAccount      bigint,   
    @nflag                int,
    @ntableName           varchar(2),
    @selectSentense         nvarchar(4000)
   
begin
    set @nflag=1
    set @nAccountPerZone=0
    set @nAllAccount=0
    while(@nflag<3)
       begin
        if(@nflag<10)
        set @ntableName='0'+convert(varchar(2),@nflag)
        else
        set @ntableName=convert(varchar(2),@nflag)
        set @selectSentense='select @nAccountPer=count(distinct uid) from zone'+@ntableName+' where end_date<'+"'"+'1905-1-1'+"'"                  
        print @selectSentense             
        exec sp_executesql @selectSentense, N'@nAccountPer bigint out', @nAccountPer out 
        set @nAllAccount=@nAllAccount+@nAccountPer
        set @nflag=@nflag+1
        end
    print @nAllAccount
end
GO
如何需要兩個輸出參數的話,要這樣設定
 exec sp_executesql @selectSentense, N ' @nAccountPer bigint out,@nAccountPer2 bigint out', @nAccountPer out  ,@nAccountPer2 out

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章