2

制約

クエリはアプリケーションによって動的に構築されているため、現時点では変更できません。また、今日、今週、または今月でさえ、コードを修正して PROD にプッシュすることはできません。これは、データベースで解決する必要があります。それが私がインデックスを評価している理由です。

データベースにはCaseHistory、約 1,000 万行のテーブルがあります。ひどくはありませんが、成長痛です。次のような検索に起因するクエリでは、読み取り時間が長くなり始めています。

select CaseNumber
    ,isnull(
        (
            select convert(varchar,min(CreationTimeGMT),101)
            from CaseHistory
            where CaseNumber = c.CaseNumber
                and ActionTypeID = 1
        ), 'N/A'
    ) as CreationTimeGMT
    ...
from [Case] c
where CaseNumber in (
    select CaseNumber from CaseHistory
    where ActionTypeID <> 1 and
        CreationTimeGMT >= '10/25/2013'
    ) AND
    CaseNumber in (
        select CaseNumber from CaseHistory
        where ActionTypeID <> 1 and
            CreationTimeGMT <= '10/25/2013'
    )

さて、一見、取得するCreateionTimeGMTサブクエリが問題かと思われるかもしれませんが、実行計画を分析したのでそうは思いません。このクエリの実行計画では、SEEKに対して処理の 99% が使用されましたIX_CaseHistory_1(下の [現在のインデックス] に表示)。CaseNumberサブクエリであるとは思わない理由をさらに具体的にするために、次のように に対して直接検索します。

select CaseNumber
    ,isnull(
        (
            select convert(varchar,min(CreationTimeGMT),101)
            from CaseHistory
            where CaseNumber = c.CaseNumber
                and ActionTypeID = 1
        ), 'N/A'
    ) as CreationTimeGMT
    ...
from [Case] c
where CaseNumber = '123456'

sub 1sですが、前述のクエリは ~ の間13sで実行されます15s

現在のインデックス

IX_CaseHistory (CaseNumber (ASC))
IX_CaseHistory_1 (ActionTypeID (ASC))
IX_CaseHistory_2 (CreationTimeGMT (ASC))

だから、私がしたいのは、クラスター化されたカバーされたインデックスを構築することCaseNumber, ActionTypeID, CreationTimeGMTです。現在、クラスター化インデックスは にありIDENTITY PKます。

なぜクラスター化したのですか?

このクエリも高速に実行したいので (1 日に 1,000 回実行されます):

select  CaseHistoryID
    ,CaseNumber
    ,ActionTypeID
    ,CreationTimeGMT
    ,UserID
    ,Notes
from    CaseHistory
where   CaseNumber = @CaseNumber
order by CreationTimeGMT

ただし、基本的な懸念事項が 1 つあります。これが書き込み時間にどのような影響を与えるかをどのように予測できますか?

4

2 に答える 2

1

最初にSQLを少し作り直したほうがいいでしょう。

SELECT
        c.[CaseNumber],
        isnull(convert(varchar, min(h.[CreationTimeGMT]), 101), 'N/A'),
        ...
FROM [Case] c
LEFT JOIN [CaseHistory] h ON h.[CaseNumber] = c.[CaseNumber]
GROUP BY
        c.[CaseNumber]
WHERE
        h.[ActionTypeID] = 1
    AND
        EXISTS(
            SELECT
                    h.[CaseNumber]
            FROM [CaseHistory] h
            WHERE
                    h.[CaseNumber] = c.[CaseNumber]
                AND
                    h.[ActionTypeID] <> 1
                AND
                    h.[CreationTimeGMT] BETWEEN '10/25/2013' AND '10/25/2013');

これを行うと、where 句のサブクエリ (ies/y) がより複雑な命題であることがわかります。

CaseHistoryクラスター化されたインデックスは一意であるため、 のままにしておく必要があると思いますCaseHistoryID。私はカバーインデックスを作成したくなるでしょう

`CaseNumber`, `ActionType`, `CreationTimeGMT`

しかし、<> 1サブクエリの " " のために、条件を反転させてみることもできます。

                    h.[CreationTimeGMT] BETWEEN '10/25/2013' AND '10/25/2013'
                AND
                    h.[ActionTypeID] <> 1);

このカバリングインデックスも追加します

`CaseNumber`, `CreationTimeGMT`, `ActionType`

これまでと同様に、パフォーマンスの鍵は、最も選択的な条件を最初に取得することです。

データ、統計、環境などを持っていないため、データベースの実際のコストを予測することはできません...

于 2013-10-28T15:10:01.447 に答える
1

これが書き込み時間にどのような種類のヒットをもたらすかをどのように予測できますか?

挿入の場合 (「書き込み」の意味だと思います)、クラスター化インデックスを操作する際の主な懸念は、新しいデータがどこに挿入されるかです。通常、クラスター化されたインデックス (自動インクリメント キーなど)の末尾に値を追加する場合、書き込みは非常に高速である必要があります。新しいレコードが最後に追加されるだけです。

あなたの場合、挿入はシーケンシャルではなく、既存のデータ内にランダムに配置されていると想定しています。その場合、フィル ファクターを考慮する必要があります。これにより、挿入を受け入れるために既存のレコード間にどのくらいのスペースを空けておくかが決まります。

FILL FACTOR が低いと多くの挿入が可能になるというトレードオフは、非インデックス列の読み取り時間が長くなることです。これは、結果のデータが複数のページに分散し、より多くの I/O が必要になる可能性があるためです。また、テーブルは新しい挿入のために空のスペースを割り当てる必要があるため、より多くのディスクスペースが必要です (単に自動的に拡大するのではなく)。

フィル ファクターを 80 に減らし (新しい挿入用に 20% のスペースを残すことを意味します)、定期的にテーブルを再編成して、新しいデータ用にレコード間にスペースを確保します。

于 2013-10-28T15:06:21.603 に答える