8

私はこのクエリを持っています...これは非常にゆっくりと実行されます(ほぼ1分):

select distinct main.PrimeId 
from PRIME main 
join   
( 
select distinct p.PrimeId   from PRIME p   
left  outer join ATTRGROUP a 
on p.PrimeId = a.PrimeId   or p.PrimeId = a.RelatedPrimeId    
where a.PrimeId is not null and a.RelatedPrimeId is not null  
) mem  
on main.PrimeId = mem.PrimeId

PRIME テーブルには 18,000 行あり、PrimeId に PK があります。

ATTRGROUP テーブルには 24,000 行あり、PrimeId、col2、RelatedPrimeId、cols 4 ~ 7 に複合 PK があります。RelatedPrimeId には別のインデックスもあります。

クエリは最終的に 8.5k 行を返します。これは、ATTRGROUP テーブルの PrimeId または RelatedPrimeId と一致する PRIME テーブルの PrimeId の個別の値です。

ATTRGROUP の代わりに ATTRADDRESS を使用して、同じクエリを実行しました。ATTRADDRESS は、ATTRGROUP と同じキーとインデックスの構造を持っています。11,000 行しかありませんが、これは確かに小さいですが、その場合、クエリは約 1 秒で実行され、11,000 行が返されます。

だから私の質問はこれです:

構造が同一であるにもかかわらず、あるテーブルで別のテーブルよりもクエリが非常に遅くなるのはどうしてでしょうか。

これまでのところ、SQL 2005 と (同じデータベースを使用してアップグレードされた) SQL 2008 R2 でこれを試しました。私たち 2 人が別々に同じ結果を得て、同じバックアップを 2 台の異なるコンピューターに復元しました。

その他の詳細:

  • 括弧内のビットは、遅いクエリでも 1 秒未満で実行されます
  • 実行計画に手がかりがある可能性がありますが、私にはわかりません。これはその一部で、疑わしい 3 億 2000 万行の操作があります。

ここに画像の説明を入力 ここに画像の説明を入力

ただし、そのテーブルの実際の行数は 320M ではなく、24k を少し超えています。

OR ではなく UNION を使用するように、クエリの括弧内の部分をリファクタリングすると、次のようになります。

select distinct main.PrimeId 
from PRIME main 
join   
( 
select distinct p.PrimeId   from PRIME p   
left  outer join ATTRGROUP a 
on p.PrimeId = a.PrimeId
where a.PrimeId is not null and a.RelatedPrimeId is not null  
UNION
select distinct p.PrimeId   from PRIME p   
left  outer join ATTRGROUP a 
on p.PrimeId = a.RelatedPrimeId    
where a.PrimeId is not null and a.RelatedPrimeId is not null  
) mem  
on main.PrimeId = mem.PrimeId

...その後、遅いクエリは 1 秒未満かかります。

これに関する洞察をいただければ幸いです。さらに情報が必要な場合はお知らせください。質問を更新します。ありがとう!

ところで、この例では冗長な結合があることに気付きました。本番環境ではすべてが動的に生成され、括弧内のビットはさまざまな形式になるため、これを簡単に削除することはできません。


編集

ATTRGROUP のインデックスを再構築しましたが、大きな違いはありません。

編集2

したがって、一時テーブルを使用する場合:

select distinct p.PrimeId into #temp
from PRIME p   
left  outer join ATTRGROUP a 
on p.PrimeId = a.PrimeId   or p.PrimeId = a.RelatedPrimeId    
where a.PrimeId is not null and a.RelatedPrimeId is not null  

select distinct main.PrimeId 
from Prime main join   
#temp mem  
on main.PrimeId = mem.PrimeId

...再び、元の OUTER JOIN に OR があっても、1 秒もかからずに実行されます。私はこのような一時テーブルが嫌いで、常に敗北を認めているように感じるので、使用するリファクタリングではありませんが、これだけの違いがあるのは興味深いと思いました。

編集3

統計を更新しても違いはありません。

これまでのすべての提案に感謝します。

4

4 に答える 4

6

私の経験では、JOIN 句で OR よりも 2 つの左結合を使用する方が適切です。したがって、代わりに:

    left  outer join ATTRGROUP a 
    on p.PrimeId = a.PrimeId   or p.PrimeId = a.RelatedPrimeId

私は提案します:

    left  outer join ATTRGROUP a 
    on p.PrimeId = a.PrimeId
    left  outer join ATTRGROUP a2
    on p.PrimeId = a2.RelatedPrimeId    
于 2011-08-12T08:51:33.773 に答える
3

メインクエリがサブクエリと相関していないことに気付きました:

select distinct main.PrimeId 
from PRIME main 
join   
( 
select distinct p.PrimeId   from PRIME p   
left  outer join ATTRGROUP a 
on p.PrimeId = a.PrimeId
where *main.PrimeId = a.PrimeId*  
UNION
select distinct p.PrimeId   from PRIME p   
left  outer join ATTRGROUP a 
on p.PrimeId = a.RelatedPrimeId    
where *main.PrimeId = a.PrimeId*  
) mem  
on main.PrimeId = mem.PrimeId

この構成では、'is not null' 句も使用する必要はありません (主キーは決して null 値を保持しないため、それが必要になることはありますか?)。

私は、OR 構造を避けるように教えられましたが (既に他の人がアドバイスしているように)、「is not null」または「in valuelist」構造も避けるように教えられました。これらはほとんど (NOT) EXISTS 句で置き換えることができます。

于 2011-11-25T09:26:04.367 に答える
1

これは直接的な答えではありませんが、ATTRGROUP.PrimeIdおよびATTRGROUP.RelatedPrimeIdからmainを参照するFK制約がある場合、クエリはこれよりもはるかに単純なものと同等です。

select PrimeId   from ATTRGROUP a 
union
select RelatedPrimeId from ATTRGROUP a 
于 2011-08-12T14:54:55.133 に答える
0

あるテーブルで 1 つのクエリが他のテーブルよりもはるかに遅くなる理由の 1 つは、そのテーブルの統計が古く、間違ったクエリ プランを選択していることです。

ただし、とにかく他の人が提案した or 句を取り除くリファクタリングをサポートします。

于 2011-08-12T14:59:52.117 に答える