0

私は2つのテーブルを持っています(それらを変更することはできません)

Parent (id, date, amount)
Child (parent_id, key, value)

索引

Parent.pk (id)
Parent.idx1 (id, date) include (amount)
Child.pk (parent_id, key)
Child.idx1 (parent_id, key, value)

とクエリ

select sum(amount)
from Parent as p
left outer join Child as c1 on c1.parent_id = p.id and c1.key = 'X'
left outer join Child as c2 on c2.parent_id = p.id and c2.key = 'Y'
where p.date between '20120101' and '20120131'
and c1.value = 'x1'
and c2.value = 'y1'

問題はパフォーマンスです。
親には ~1,500,000 レコード、子には ~6,000,000 レコードがあります

テイク 1

このクエリには ~3 秒かかりますが、これは私のシナリオでは長すぎます。数ミリ秒未満である必要があります。

実行計画は、SQL Server がインデックス スキャンを実行しており、クラスター化されたインデックス シークをParent.idx1使用してマージ結合を 実行していることを示しています。Child.idx1

テイク 2

私がに変更Parent.idx1するとき

Parent.idx1 (date, id) include (amount)

Sqlサーバーは、クラスター化されたインデックススキャンを選択しParent.pk、再び結合を結合しChild.idx1ます。実行時間は約 6 秒です。

テイク 3

強制的に使用Parent.idx1 (date, id) include (amount)すると、マージ結合の前に結果がソートされ、実行時間はさらに悪化します。

テイク 4

インデックス付きビューを作成しようとしましたが、LEFT OUTER JOIN のために使用できません。

そのようなクエリを作成する方法はありますか - 両方のフィルターを使用した親子結合 - より高速ですか?
非正規化なし。

更新 2013-07-04:
回答者には INNER JOIN を使用してください - はい、はるかに高速ですが、使用できません。
ここで示したのは、私が本当に必要としているものの単純化されたバージョンです。
MS Dynamics NAV の "G/L Entry" (親) テーブルと "Ledger Entry Dimension" (子) テーブルの SQL ビューを作成して、そのアプリケーションから読み取ることができるようにする必要があります。現在、完全なビューは次のようになっています。

create view analysis
as
select 
    v.id as view_id
    , p.date
    , p.Amount
    , c1.value as value1
    , c2.value as value2
    , c3.value as value3
    , c4.value as value4
from Parent as p
    cross join analysis_view as v
    left outer join Child as c1 on c1.parent_id = p.id and c1.key = v.key1
    left outer join Child as c2 on c2.parent_id = p.id and c2.key = v.key2
    left outer join Child as c3 on c3.parent_id = p.id and c3.key = v.key3
    left outer join Child as c4 on c4.parent_id = p.id and c4.key = v.key4

ここで、analysis_view には現在 8 つのレコードが含まれており、次のようanalysis_view (id, key1, key2, key3, key4)
になっています。

select sum(amount)
from analysis
where view_id = 1 and date between '20120101' and '20120131'
and value1 = 'x1'
and value2 = 'x2'

また

select sum(amount)
from analysis
where view_id = 1 and date between '20120101' and '20120131'
and value1 = 'x1'
and value3 = 'z1'

MS Dynamics NAV には既に正規化されていないテーブルがあり、そこからのクエリは高速ですが、私たちの場合は非常に大きく (~10GB)、誰かが新しい分析ビューを作成すると、システム全体が約 1 時間ロックされます。また、NAV は結合を生成する方法を認識していないため、SQL Server 側で定義する必要があります。

4

3 に答える 3

0

パフォーマンスに影響を与えていることがいくつかあります (私は専門家ではありませんが)。それらの 1 つはChild、そのテーブルのすべての列をインデックスのプライマリ列として持つインデックスを作成することですが、これはあまり意味がありません。もう 1 つのことは、テーブルc1との値に従ってクエリをフィルタリングし、クエリをc2に変換していることINNER JOINです。EXISTS代わりに使用するには、次のように変更してみてください。

select sum(amount)
from Parent as p
where p.date between '20120101' and '20120131'
and exists(select 1 from Child 
           where parent_id = p.id and key = 'X'
           and value = 'x1')
and exists(select 1 from Child 
           where parent_id = p.id and key = 'Y'
           and value = 'y1')
于 2013-07-03T20:36:00.350 に答える