成人午夜激情影院,小视频免费在线观看,国产精品夜夜嗨,欧美日韩精品一区二区在线播放

當前位置:首頁>>軟件教程>>新聞內容  
一句T-SQL語句引發的思考
作者:leimin 發布時間:2004-1-16 15:26:56 | 【字體:

有一網友問:關于MS SQLSERVER索引優化問題:
 有表Stress_test(id  int, key char(2))
        id 上有普通索引;
        key 上有簇索引;
        id 有有限量的重復;
        key 有無限量的重復;

現在我需要按邏輯與查詢表中key='Az' AND key='Bw' AND key='Cv' 的id

求教高手最有效的查詢語句

測試環境:
     Hardware:P4 2.6+512M+80G
     Software:windows server 2003(Enterprise Edition)+Sqlserver 2000 +sp3a

  首先我們建立一個測試的數據,為使數據盡量的分布和隨即,我們通過RAND()來隨機產生2個隨機數再組合成一個字符串,首先插入的數據是1,000,000條記錄,然后在循環插入到58,000,000條記錄。
   因為是隨機產生的數據,所以如果你自己測試的數據集和我測試的會不一樣,但對索引的優化和運行的效率是一樣的。
   下面的“--//測試腳本”是產生測試數據的腳本,你可以根據需要修改 @maxgroup, @maxLoop的值,比如測試1百萬的記錄可以:

     Select @maxgroup=1000
     Select @maxLoop=1000

如果要測試5千萬:

     Select @maxgroup=5000
     Select @maxLoop=10000

所以如果你的SERVER或PC比較慢,請耐心等待.....,
 (在我的PC上運行的速度是插入1百萬條的時間是1.14m,插入5千八百萬條的時間是19.41m,重新建立INDEX的時間是34.36m)

 

作為一般的開發人員很容易就想到的語句:

   --語句1

    select a.[id] from 
    (select distinct [id] from stress_test where [key] = 'Az') a,
    (select distinct [id] from stress_test where [key] = 'Bw') b ,
    (select distinct [id] from stress_test where [key] = 'Cv') c
    where a.id = b.id and a.id = c.id

   --語句2

     select [id] 
     from stress_test 
     where [key]='Az' or [key]='Bw' or [key]='Cv'
     group by id having(count(distinct [key])=3)
  

   --語句5

    SELECT distinct a.[id] FROM stress_test AS a,stress_test AS b,stress_test AS c
    WHERE a.[key]='Az' AND b.[key]='Bw' AND c.[key]='Cv'
      AND a.[id]=b.[id] AND a.[id]=c.[id]

但作為T-SQL的所謂“高手”可能會認為這種寫法很“土”,也顯得沒有水平,所以會選擇一些子查詢和外連接的寫法,按常理子查詢的效率是比較高的:

   --語句3

    select distinct [id] from stress_test A where
    not exists (
    select 1 from
     (select 'Az' as k union all select 'Bw' union all select 'Cv') B
    left join stress_test C on  C.id=A.id and B.[k]=C.[key]
    where C.id is null)

   --語句4

     select distinct a.id from stress_test a
     where not exists
     ( select * from keytb c
      where not exists
      ( select * from stress_test b
       where
             b.id = a.id
             and
             c.kf1 = b.[key]
       )
     )

我們先分析這幾條語句(針對5千8百萬條數據進行分析):

請大家要特別留心Estimated row count的值。

語句1:從執行規劃中我們可以看出,MSSQLSERVER選擇的索引優化非常有規律,先通過CLUSTERED INDEX篩選出符合[KEY]='Az'條件的ID,然后進行HASH MATCH,在找出ID相等的;依次類推最終檢索到符合所有條件的記錄。中間的Estimated row count的值都不大。

語句2:從執行規劃中我們可以看出,是先通過CLUSTERED INDEX篩選出符合 [key]='Az' or [key]='Bw' or [key]='Cv' 符合所有條件的ID,然后分組進行2次HASH MATCH 所有的ID。我們可以看出Estimated row count的值是越來越少,從最初的369,262到最后排序的只有402。

語句3:從執行規劃中我們可以看是非常復雜的,是先通過3組 通過CONSTANT SCAN和NON-CLUSTERED INDEX檢索出符合 A.ID=C.ID AND [key]='**' 的記錄3組,然后分組進行外鍵匹配,再將3組的數據合并,排序,然后再和一個NON-CLUSTERED INDEX檢索出的記錄集進行外鍵匹配,我們可以看出MSSQLSERVER會對所有的記錄(5千萬條)記錄進行分組,Estimated row count的值是:58,720,000,所以這句T-SQL的瓶頸是對5千萬條記錄進行分組。

語句4:從執行規劃中我們可以看和語句3有相似之處,都要對所有的記錄(5千萬條)記錄進行分組,所以這是檢索的瓶頸,而且使用的索引都是NON-CLUSTERED INDEX。

語句5:從執行規劃中我們可以看出,先通過CLUSTERED INDEX檢索出符合[Key]='Az'的記錄集,然后進行HASH MATCH和SORTS,因為數量少所以是非常會的,在和通過NON-CLUSTERED INDEX檢索[KEY]='Bw'的記錄進行INNER JOIN,在和通過CLUSTERED INDEX檢索[KEY]='Cv'的記錄進行合并,最后是對4百萬條數據進行分組檢索,如果是6列,我們可以看出Estimated row count的值是遞增,越來越大,最后的分組檢索的Estimated row count的值是3.46E+15,這已經形成巨大的瓶頸。

我們可以先測試一下小的數據量(50000條);

大家可以下面測試腳本的:

   Select @maxgroup=500
   Select @maxLoop=100

----------------------------------------------------------------------
 |------------------語句 1----語句 2----語句 3----語句 4----語句 5----|
 | 5萬(3列)        5ms       19ms     37ms     59ms      0ms
 | 5萬(6列)        1ms       26ms     36ms     36ms     1ms
 

從測試的的數據來看,語句5的效率是最高的,幾乎沒有花費時間,而語句2的效率只能說是一般。如果測試到這里就結束了,我們可以毫不猶豫的選擇語句 5 :-(,繼續進行下面的測試.....

我們測試百萬條以上的記錄:
 1.先對1百萬條記錄進行測試(選取3列)
 2.先對1百萬條記錄進行測試(選取6列)
 3.對5千萬條數據測試(選取3列)
 4.對5千萬條數據測試(選取6列)

統計表1:
 ----------------------------------------------------------------------
 |------------------語句 1----語句 2----語句 3----語句 4----語句 5----|
 | 1百萬(3列)    0.77%     0.41%    49.30%     48.99%     0.52%
 | 1百萬(6列)     1.61%     0.81%    48.99%     47.44%     1.14%
 | 5千萬(3列)     0.14%     0.18%    48.88%     48.86%     1.93%
 | 5千萬(6列)     0.00%     0.00%     0.00%      0.00%   100.00%
統計表2:
 ----------------------------------------------------------------------
 |------------------語句 1----語句 2----語句 3----語句 4----語句 5----|
 | 1百萬(3列)     9ms       22ms     723ms     753ms      4ms
 | 1百萬(6列)      15ms      38ms     764ms     773ms     11ms
 | 5千萬(3列)     575ms     262ms  110117ms  110601ms  12533ms
 | 5千萬(6列)    1070ms     576ms  107988ms  109704ms     10m以上


測試總結:(我們可以比較關注:語句 2和語句 5)
1.在1百萬條記錄的情況下,語句 5是最快的,但在5千萬條記錄下是最慢的。這說明INDEX的優化一定的情況下,數據量不同,檢索的效率也是不同的。我們平時在寫T-SQL時一般關注的時INDEX的使用,只要我們寫的T-SQL是利用CLUSTERED INDEX,我們就認為是最優化了,其實這是一個誤區,我們還要關注Estimated row count的值,大量的I/O操作是我們應該關注的,所以我們應該根據數據量的不同選擇相應的T-SQL語句,不要認為在小數據量下是最高的在大數據量的狀態下也許是最慢的:-(。

2.在執行規劃中最快的,并不是運行最快的,我們可以看在1百萬(6列)在這行中,語句 2和語句 5的比例是0.81%:1.14%,但實際的運行效率是,38ms:11ms。所以,我們在選擇T-SQL是要考慮本地I/O的速度,所以在優化語句時不僅要看執行規劃還要計算一下具體的效率。

在測試的語句上加入:

    SET STATISTICS TIME  ON/OFF
    SET STATISTICS IO  ON/OFF
是一個很好的調試方法。


3.綜合評價,語句 2的效率是最高的,執行效率沒有隨數據量變化而有很大的差別。

4.執行規劃越簡單的語句(語句1),綜合效率越高,反之則越低(語句3,語句4)。

5.在平時寫T-SQL語句時,一定要根據不同的數據量進行測試,雖然都是用CLUSTERED INDEX,但檢索的效率卻大相徑庭。

--//測試腳本
USE Northwind
GO
if exists(select * from sysobjects where name=N'stress_test' and type='U')
Drop table stress_test
GO
--//定義測試的表stress_test,存放所有的測試數據
Create table stress_test([id] int,[key] char(2))

GO
--//插入測試的數據
Set nocount on
--//變量定義
Declare @id int   --//Stress_test ID 值
Declare @key char(2)  --//Stress_test [key] 值
Declare @maxgroup int  --//組最大的循環數
Declare @maxLoop int  --//ID最大的循環數
Declare @tempGroup int  --//臨時變量
Declare @tempLoop int  --//臨時變量
Declare @tempint1 int  --//臨時變量
Declare @tempint2 int  --//臨時變量
Declare @rowcount int  --//記錄事務提交的行數

--//初始化變量
Select @id=1
Select @maxgroup=1000
Select @maxLoop=1000
Select @tempGroup=1
Select @tempLoop=1
Select @key=''
Select @rowcount=0

while @tempLoop<=@maxLoop
begin
 while @tempGroup<=@maxGroup
 begin
  select @tempint1=65+convert(int,rand()*50)
  select @tempint2=65+convert(int,rand()*100)
  if (@tempint1>=122 or @tempint2>=122)
    begin
    select @tempint1=@tempint1-100
    select @tempint2=@tempint2-100
   
    if (@tempint1<=65 or @tempint2<=65)
     begin
     select @tempint1=@tempint1+57
     select @tempint2=@tempint2+57
    end
   end
  select @key=char(@tempint1)+char(@tempint2)
  if @rowcount=0
  begin tran ins
     insert into stress_test([id],[key])values(@id,@key)
       select @rowcount=@rowcount+1
  
   if @rowcount>3000 --//判斷當行數達到3000條時,開始提交事務
   begin
       commit tran ins
      select @rowcount=0
   end
  
  select @tempGroup=@tempgroup+1
 end
 if @rowcount>0
 begin
  commit tran ins
  select @rowcount=0
 end

 select @tempGroup=1
 select @id=@id+1
 select @tempLoop=@tempLoop+1
end
GO
--//刪除KEY值為NULL的記錄
delete stress_test where [key]is null
GO
--//建立簇索引PK_STRESS
Create Clustered index pk_stress on stress_test([Key])
--//建立非簇索引NI_STRESS_ID
Create NonClustered index NI_stress_id on stress_test([id])
GO
--//定義測試的表keytb
if exists(select * from sysobjects where name=N'keytb' and type='U')
Drop table keytb
GO
create table keytb   -----//存放你需要匹配的值的表
(
  kf1  varchar(20)
)

--//存放你需要匹配的值,暫定為三個
insert into keytb(kf1) values('Az');
insert into keytb(kf1) values('Bw');
insert into keytb(kf1) values('Cv');

--insert into keytb(kf1) values('Du');
--insert into keytb(kf1) values('Ex');
--insert into keytb(kf1) values('Fy');
GO


下面我們就開始測試幾種T-SQL的INDEX優化問題:

--先對1百萬條/1億條記錄進行測試(選取3列)的T-SQL:

PRINT '第一種語句:'
SET STATISTICS TIME  ON
SET STATISTICS IO  ON
select a.[id] from
(select distinct [id] from stress_test where [key] = 'Az') a,
(select distinct [id] from stress_test where [key] = 'Bw') b ,
(select distinct [id] from stress_test where [key] = 'Cv') c
where a.id = b.id and a.id = c.id
GO
PRINT '第二種語句:'
select [id]
from stress_test 
where [key]='Az' or [key]='Bw' or [key]='Cv'
group by id having(count(distinct [key])=3)
GO
PRINT '第三種語句:'
select distinct [id] from stress_test A where
not exists (
select 1 from
(select 'Az' as k union all select 'Bw' union all select 'Cv') B
left join stress_test C on  C.id=A.id and B.[k]=C.[key]
where C.id is null)
GO
PRINT '第四種語句:'
select distinct a.id from stress_test a
 where not exists
 ( select * from keytb c
   where not exists
   ( select * from stress_test b
     where
       b.id = a.id
       and
       c.kf1 = b.[key]
   )
 )
GO
PRINT '第五種語句:'
SELECT distinct a.[id] FROM stress_test AS a,stress_test AS b,stress_test AS c
WHERE a.[key]='Ac' AND b.[key]='Bb' AND c.[key]='Ca'
      AND a.[id]=b.[id] AND a.[id]=c.[id]

GO
SET STATISTICS TIME  OFF
SET STATISTICS IO  OFF

--先對1百萬條/1億條記錄進行測試(選取6列)的T-SQL:
PRINT '第一種語句:'
SET STATISTICS TIME  ON
SET STATISTICS IO  ON
select a.[id] from
(select distinct [id] from stress_test where [key] = 'Az') a,
(select distinct [id] from stress_test where [key] = 'Bw') b ,
(select distinct [id] from stress_test where [key] = 'Cv') c,
(select distinct [id] from stress_test where [key] = 'Du') d,
(select distinct [id] from stress_test where [key] = 'Ex') e,
(select distinct [id] from stress_test where [key] = 'Fy') f
where a.[id] = b.[id] and a.[id] = c.[id] and a.[id] = d.[id] and a.[id] = e.[id] and a.[id] = f.[id]
GO
PRINT '第二種語句:'
select [id]
from stress_test 
where [key]='Az' or [key]='Bw' or [key]='Cv' or [Key]='Du'or [Key]='Ex'or [Key]='Fy'
group by id having(count(distinct [key])=6)
GO
PRINT '第三種語句:'
select distinct [id] from stress_test A where
not exists (
select 1 from
(select 'Az' as k union all select 'Bw' union all select 'Cv'union all select 'Du'union all select 'Ex'union all select 'Fy') B
left join stress_test C on  C.id=A.id and B.[k]=C.[key]
where C.id is null)
GO
PRINT '第四種語句:'
select distinct a.id from stress_test a
 where not exists
 ( select * from keytb c
   where not exists
   ( select * from stress_test b
     where
       b.id = a.id
       and
       c.kf1 = b.[key]
   )
 )
GO
PRINT '第五種語句:'
SELECT distinct a.[id] FROM stress_test AS a,stress_test AS b,stress_test AS c,stress_test AS d,stress_test AS e,stress_test AS f
WHERE a.[key]='Az' AND b.[key]='Bw' AND c.[key]='Cv' AND d.[key]='Du' AND e.[key]='Ex' AND f.[key]='Fy'
     and a.[id] = b.[id] and a.[id] = c.[id] and a.[id] = d.[id] and a.[id] = e.[id] and a.[id] = f.[id]

GO
SET STATISTICS TIME  OFF
SET STATISTICS IO  OFF

請參考:

http://expert.csdn.net/Expert/topic/2630/2630484.xml?temp=.9921686


文章來源:csdn
 放生
 愚愛
 夠愛
 觸電
 白狐
 葬愛
 光榮
 畫心
 火花
 稻香
 小酒窩
 下雨天
 右手邊
 安靜了
 魔杰座
 你不像她
 邊做邊愛
 擦肩而過
 我的答鈴
 懷念過去
 等一分鐘
 放手去愛
 冰河時代
 你的承諾
 自由飛翔
 原諒我一次
 吻的太逼真
 左眼皮跳跳
 做你的愛人
 一定要愛你
 飛向別人的床
 愛上別人的人
 感動天感動地
 心在跳情在燒
 玫瑰花的葬禮
 有沒有人告訴你
 即使知道要見面
 愛上你是一個錯
 最后一次的溫柔
 愛上你是我的錯
 怎么會狠心傷害我
 不是因為寂寞才想
 親愛的那不是愛情
 難道愛一個人有錯
 寂寞的時候說愛我
主站蜘蛛池模板: 教育| 泗水县| 杨浦区| 渭源县| 桑植县| 靖远县| 翼城县| 永胜县| 建水县| 江陵县| 扎鲁特旗| 嵊泗县| 临夏市| 怀来县| 庆元县| 谢通门县| 宝丰县| 理塘县| 尤溪县| 安乡县| 英德市| 柳州市| 宜黄县| 西华县| 进贤县| 兰考县| 山西省| 崇州市| 竹溪县| 屏边| 兰州市| 多伦县| 高雄市| 乐陵市| 凤翔县| 和静县| 静宁县| 永昌县| 攀枝花市| 峨眉山市| 库车县|