SQL 語句優化--中間表的使用優化


上周五,公司的同事,從客戶那邊回來,說一個人員選擇頁面很慢,頁面打開需要15s左右,后來自己也試了一下,也的確需要比較長的時間,客戶反應比較強烈。

      通過DMV查出緩慢的兩個語句如下:

語句一:


select * from (select id,objname,objno,station,orgid,seclevel, ROW_NUMBER() OVER 
order by seclevel desc,id descas pos from humres tbalias  where isdelete=0  
and id in (select h.id from humres h,orgunitlink o where o.col1 like '%4028b14f17578814011757e2bebf002a%' 
and h.orgids like '%'+o.oid+'%')) as T where T.pos>0 and T.pos<=20

  語句二:


select count( id) as totalnum from humres tbalias  where isdelete=0  
and id in (select h.id from humres h,orgunitlink o where o.col1 
like '%4028b14f17578814011757e2bebf002a%' and h.orgids like '%'+o.oid+'%')

 

    查詢的兩個表的數據量: 表humres:  1920 行 ,  191次 IO  

                                   表orgunitlink: 256 行, 11 次IO 

    最大表才不到2000行數據,查詢為何如此慢。看看執行計划:

   查詢開銷:

(20 行受影響)
表 
'Worktable'。掃描計數 1,邏輯讀取 8270 次,物理讀取 0 次,預讀 0 次,lob 邏輯讀取 0 次,lob 物理讀取 0 次,lob 預讀 0 次。
表 
'humres'。掃描計數 2,邏輯讀取 382 次,物理讀取 0 次,預讀 0 次,lob 邏輯讀取 0 次,lob 物理讀取 0 次,lob 預讀 0 次。
表 
'orgunitlink'。掃描計數 1,邏輯讀取 11 次,物理讀取 0 次,預讀 0 次,lob 邏輯讀取 0 次,lob 物理讀取 0 次,lob 預讀 0 次。

      這時發現:由於采用“%”來模糊查詢,系統無法使用hash聯結算法,采用嵌套循環算法。使得IO次數,多了8000個IO。

      這里的col1,orgids :多個32位id,中間用","號隔開串, 如:'4028b14f175788140117581e030d00c7,4028b14f17578814011757f6131d0065'

      一開始,我的思路是通過函數,將列col1,orgids中通過‘,’分開的函數隔離開來,將一行變多行,使其使用hash聯結,但是后來發現雖然使用了hash聯結

,但在系統在col1,orgids轉換成多列時也很耗時間,最后想了一個更好的辦法:

     通過觸發器建立中間表,將orgids,col1存放到另一個表中,由於這里都是一些基本數據,人員和機構,一般的變化的頻率不高,也就一兩天修改一次就很了不起了。建立觸發器對數據庫其他性能影響很小。全部代碼和改寫的SQL如下:  


--優化 by zping :2008年11月17日15:14:50
--
建立humres中間表
create table  humres_r
( id 
varchar(32),
  orgid 
varchar(32)
)
--建立orgunitlink中間表
create table orgunitlink_r
( id 
varchar(32),
  oid 
varchar(32),
  orgid 
varchar(32)
)

--建立數據一列轉多行數據
--
@ids數據類型為:為32位id,中間用","號隔開, 如:'4028b14f175788140117581e030d00c7,4028b14f17578814011757f6131d0065'
--
@id為數據表的唯一id
create function getCols(@id varchar(32),@ids varchar(8000))
returns  @tb table (
  id 
varchar(32),
  orgid 
varchar(32)
)
as
begin
  
declare @num as int,@result varchar(8000);
  
set @result=replace(@ids,',','');
  
set @num=len(@result)/32 -- 因為數據格式為32固定字符加“,”
while @num<>0
begin
  
insert into @tb values(@id,substring(@result,(@num-1)*32+1,32));
  
set @num=@num-1;
end
  
return;
end
go

--建立表humres的觸發器,用於同步中間表的數據
create TRIGGER trigger_humres
ON humres 
for INSERT,update,delete
as
 
if @@rowcount = 0 --如果影響的行數為 0,則結束觸發器運行,避免占用資源
  return
 
begin tran
 
truncate table humres_r
 
insert into humres_r
 
select a.id,b.orgid from humres a cross apply dbo.getCols(id,orgids) b
 
commit tran
go

--建立表orgunitlink的觸發器,用於同步中間表的數據
create TRIGGER trigger_orgunitlink
ON orgunitlink 
for  INSERT,update,delete
as
 
if @@rowcount = 0 --如果影響的行數為 0,則結束觸發器運行,避免占用資源
  return
 
begin tran
  
truncate table orgunitlink_r
  
insert into orgunitlink_r
  
select a.id,a.oid,b.orgid from orgunitlink a cross apply dbo.getCols(id,col1) b
 
commit tran
go

--修改后的SQL語句,計算總數
select count(id) as totalnum from humres tbalias  where isdelete=0  
and id in (
select h.id from 
humres_r h,orgunitlink_r o 
where o.orgid='4028b14f17578814011757e2bebf002a' and
h.orgid
=o.oid
)
--取語句的20條數據
select * from (select id,objname,objno,station,orgid,seclevel,
ROW_NUMBER() 
OVER ( order by seclevel desc,id descas pos from humres
tbalias  
where isdelete=0  and id in (select h.id from 
humres_r h, orgunitlink_r o 
where o.orgid='4028b14f17578814011757e2bebf002a' and
h.orgid
=o.oid))
as T where T.pos>0 and T.pos<=20

    通過以上的修改優化,點開頁面速度很快

    總結:     

    這里,由於前期表設計的不合理,造成速度很慢,而且在業務邏輯不變,程序不變的情況下,是一個比較擇中的優化方法。同時在后面設計表時要,注意讓SQL優化器能使用到Hash聯結,同時要注意 符合第一設計范式啊,否則即使數據很小,速度也很慢。

注意!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系我们删除。



 
粤ICP备14056181号  © 2014-2021 ITdaan.com