大大毛 的笔记

  DDM's Note

哪怕没有办法一定有说法,
就算没有鸽子一定有乌鸦,
固执无罪 梦想有价,
让他们惊讶.

posts - 14, comments - 23, trackbacks - 0, articles - 58
   :: 首页 ::  :: 联系 ::  :: 管理

大資料量SQL性能優化之轉換思路

Posted on 2008-11-11 18:55 大大毛 阅读(505) 评论(0)  编辑  收藏 所属分类: SQL

問題 

      這一次有同事在做一個小CASE時遇到問題無法解決. 整個的需求比較簡單, 是需要從資料庫中撈資料出來進行列印, 需求如下:
      1. 從tbMain中找符合條件的客戶.限定條件:只有一筆資料的客戶
      2. 查詢tbForeclose, 剔除掉某些客戶.剔除條件:姓名存在於tbForeclose中的客戶即要從名單中剔除
      3. 從tbDetail中撈客戶的明細資料出來進行列印.關聯條件:依tbMain中客戶ID直接帶出即可

-- 主檔(同一客戶會有多筆資料)
Create   Table  tbMain (
    aID 
int   identity ( 1 , 1 ),
    pID 
varchar ( 10 not   null ,     -- 客戶ID
    pName  nvarchar ( 20 not   null ,     -- 客戶姓名
    pDoing  varchar ( 20 not   null
);
-- 剔除客戶檔(僅存放客戶姓名)
Create   Table  tbForeclose (
    pName 
nvarchar ( 20 not   null      -- 剔除客戶姓名
);
-- 客戶資料明細檔
Create   Table  tbDetail (
    pID 
varchar ( 10 primary   key ,     -- 客戶ID(KEY)
    pSex  char ( 1 not   null          -- 客戶其他詳細資料
);

 

示例數據

嘗試及解決
      就此需求表面來看, 實現起來很是簡單, 只要一條SQL就好:

Select
        
*
    
From
        tbDetail
    
Inner   Join  (
        
Select
                pID,pName
            
From
                tbMain
            
Where
                
Not   Exists  ( Select   1   From  tbForeclose  Where  tbForeclose.pName = tbMain.pName)
            
Group   By
                pID,pName
            
Having
                
Count ( * ) = 1
    ) tbMain1 
On  tbMain1.pID = tbDetail.pID

      但此需求在開發時卻有遇到一些限制條件, 不能這樣去實現.
      限制條件:
         1. tbMain,tbForeclose,tbDetail三表分別存在於不同的DB, 因DB所屬系統各不相同, 線上環境則會有可能布署在不同DBServer上.
         2. tbMain記錄筆數極大,索引效率低下.
         3. 三張TABLE是屬於其他的系統, 本次需求不允許進行UPDATE的操作.
      因此, 需要對應解決.

      對於限制條件1, 資料處理必須依3個步驟進行(見圖上A,B,C), 如圖1:
                        o_1.jpg

      對於限制條件2, 主要卡在步驟A的超時上(其後都僅處理到少量資料), 實測時在本機的查詢分析器RUN步驟A的SQL就要10分鍾,符合的資料筆數為百萬左右.除非是將ADO連線設為不超時, 否則一定會查詢失敗.

      我首先的想法就是想要提高查詢效率. 
         . 經檢查tbMain發現pID列上有索引而pName上卻無, 所以將步驟A進行分解, 僅查詢符合的pID, 實測步驟A1可以在10分鍾以內完成, 見圖2.
                           o_2.jpg
         . 嘗試減少步驟A1的處理筆數, 比如先依pID的前兩碼進行分組, 再進行多次查詢, 實測資料量最大的一組pID約在2-3分鍾, 單次資料量減少到十萬.
      通過測試的結果可以看到該問題僅通過改善/分解查詢已經不能實現解決. 

      那麽是否能夠將步驟A的筆數減少到一筆呢?
         . 因三檔都不允許進行UPDATE的操作, 在處理完TOP 1的一筆後因無法對處理過的pID進行標識, 無法獲取下一筆的資料.

      既然多種方法都不可行, 那麽就只有完全改變處理的流程, 通過多添加一張表(專為此開發而新增的TABLE)並將消耗最大的步驟A交由DB內部來完成, 最終解決方法步驟如下:
      1. 先手工新增臨時TABLE
      
--新增臨時檔
Create Table tbTemp (
    aID 
int identity(1,1),
    pID 
varchar(10)
);

      2. 如果是需要多次RUN的話, 則要先Truncate Table tbTemp, 然後通過Insert Into tbTemp Select....在DB內部完成資料的篩選, 由於在客戶端不需要撈一筆資料, 所以耗時非常的短, 達到秒級.
      3. 由於新增檔案是有KEY值(Identity), 所以只要拿到批次添加的最大/最小ID值即可通過循環來逐筆處理了.
      最終解決方案如圖3
                              
                           o_3.jpg


只有注册用户登录后才能发表评论。


网站导航:
 

i am ddm