1

トランザクション テーブルのストアド プロシージャに問題があります。ユーザーは、複数の値でトランザクションを検索するための Web フォームを持っています。

プロセスに時間がかかりすぎて、適切なインデックスを設定する方法がわかりません。

ここに私のストアドプロシージャがあります:

CREATE PROCEDURE dbo.cg_searchTransactions
(
    @id_Ent tinyint,
    @transactionTypeID int = NULL,
    @transactionID numeric(18,0) = NULL,
    @channelID int = NULL,
    @transactionDateFrom datetime = NULL,
    @transactionDateTo datetime = NULL,
    @transactionStatusID INT = NULL,
    @documentType INT = NULL,
    @documentNumber varchar(50) = NULL,
    @userName varchar(50) = NULL,
    @accountFromNumber varchar(20) = NULL,
    @accountToNumber varchar(20) = NULL,
    @amountFrom money = NULL,
    @amountTo money = NULL,
    @correlationID varchar(30) = NULL,
    @externalReference varchar(20) = NULL,
    @externalReference2 varchar(20) = NULL,
    @PageIndex INT = 1, 
    @PageSize INT = 20
)
AS
BEGIN
    SET NOCOUNT ON


        DECLARE @QUERY VARCHAR(MAX)
        SET @QUERY = '
            WITH Trans AS (
            SELECT
                ROW_NUMBER() OVER (ORDER BY transactionID DESC) AS Row,
                T.id_Ent,
                T.transactionID,
                T.trnTypeCurrencyID,
                T.transactionDate,
                T.transactionStatusID,
                T.documentType,
                T.documentNumber,
                T.childDocumentType,
                T.childDocumentNumber,
                T.userName,
                T.accountFromNumber,
                T.accountFromType,
                T.accountFromCurrency,
                T.accountDescriptionFrom,
                T.costCenterFrom,
                T.subtotalFrom,
                T.taxamountFrom,
                T.taxamountFrom2,
                T.amountFrom,
                T.accountToNumber,
                T.accountToType,
                T.accountToCurrency,
                T.accountDescriptionTo,
                T.costCenterTo,
                T.subtotalTo,
                T.taxamountTo,
                T.taxamountTo2,
                T.amountTo,
                T.exchangeCurrency,
                T.traderAuthNumber,
                T.benefContractNumber,
                T.contractNumber,
                T.merchantID,
                T.creditCardAuthorizationNumber,
                T.comment,
                T.companyServiceCommision,
                T.usercommission,
                T.companyServiceAuthorizationNumber,
                T.customerBranchId,
                T.correlationID,
                T.transactionStartTime,
                T.transactionEndTime,
                T.enlapsedTime,
                T.serverName,
                T.externalReference,
                T.externalReference2,
                T.externalTrxType,
                T.beneficiaryName,

                C.shortName AS ChannelsShortName,
                TT.shortName AS TransactionTypesShortName,
                TS.shortName AS TransactionStatusDefShortName,
                DT.shortName AS DocumentTypesShortName,
                CDT.shortName AS ChildDocumentTypesShortName,
                AFT.shortName AS AccountTypesShortNameFrom,
                ATT.shortName AS AccountTypesShortNameTo,
                CURF.shortName AS CurrenciesShortNameFrom,
                CURT.shortName AS CurrenciesShortNameTo
            FROM
                Transactions T (NOLOCK) 

                    INNER JOIN TransactionTypesCurrencies TTC
                        ON  T.id_Ent = TTC.id_Ent
                            AND T.trnTypeCurrencyID = TTC.trnTypeCurrencyID

                        INNER JOIN Channels C
                            ON  TTC.id_Ent = C.id_Ent
                                AND TTC.channelID = C.ID

                        INNER JOIN TransactionTypes TT
                            ON  TTC.id_Ent = TT.id_Ent
                                AND TTC.transactionTypeID = TT.transactionTypeID

                    INNER JOIN TransactionStatusDef TS
                        ON  T.id_Ent = TS.ent_Ent
                            AND T.transactionStatusID = TS.ID

                    INNER JOIN DocumentTypes DT
                        ON  T.id_Ent = DT.id_Ent
                            AND T.documentType = DT.ID

                    INNER JOIN DocumentTypes CDT
                        ON  T.id_Ent = CDT.id_Ent
                            AND T.childDocumentType = CDT.ID

                    INNER JOIN AccountTypes AFT
                        ON  T.id_Ent = AFT.id_Ent
                            AND T.accountFromType = AFT.ID

                    INNER JOIN AccountTypes ATT
                        ON  T.id_Ent = ATT.id_Ent
                            AND T.accountToType = ATT.ID

                    INNER JOIN Currencies CURF
                        ON  T.id_Ent = CURF.id_Ent
                            AND T.accountFromCurrency = CURF.ID

                    INNER JOIN Currencies CURT
                        ON  T.id_Ent = CURT.id_Ent
                            AND T.accountToCurrency = CURT.ID
            WHERE 
                T.id_Ent = ' + CONVERT(VARCHAR,@id_Ent)
                IF NOT @transactionDateFrom IS NULL
                    SET @QUERY = @QUERY + ' AND T.transactionDate >= ''' + CONVERT(VARCHAR,@transactionDateFrom,121) + ''''

                IF NOT @transactionDateTo IS NULL
                    SET @QUERY = @QUERY + ' AND T.transactionDate <= ''' + CONVERT(VARCHAR,@transactionDateTo,121) + ''''

                IF NOT @transactionStatusID IS NULL
                    SET @QUERY = @QUERY + ' AND T.transactionStatusID = ' + CONVERT(VARCHAR,@transactionStatusID)

                IF NOT @documentType IS NULL
                    SET @QUERY = @QUERY + ' AND T.documentType = ' + CONVERT(VARCHAR,@documentType)

                IF NOT @userName IS NULL
                    SET @QUERY = @QUERY + ' AND T.userName = ''' + @userName + ''''

                IF NOT @documentNumber IS NULL
                    SET @QUERY = @QUERY + ' AND T.documentNumber = ''' + @documentNumber + ''''

                IF NOT @accountFromNumber IS NULL
                    SET @QUERY = @QUERY + ' AND T.accountFromNumber = ''' + @accountFromNumber + ''''

                IF NOT @accountToNumber IS NULL
                    SET @QUERY = @QUERY + ' AND T.accountToNumber = ''' + @accountToNumber + ''''

                IF NOT @amountFrom IS NULL
                    SET @QUERY = @QUERY + ' AND T.amountTo >= ' + CONVERT(VARCHAR,@amountFrom)

                IF NOT @amountTo IS NULL
                    SET @QUERY = @QUERY + ' AND T.amountTo <= ' + CONVERT(VARCHAR,@amountTo)

                IF NOT @correlationID IS NULL
                    SET @QUERY = @QUERY + ' AND T.correlationID = ''' + @correlationID + ''''

                IF NOT @externalReference IS NULL
                    SET @QUERY = @QUERY + ' AND T.externalReference = ''' + @externalReference + ''''

                IF NOT @externalReference2 IS NULL
                    SET @QUERY = @QUERY + ' AND T.externalReference2 = ''' + @externalReference2 + ''''

                IF NOT @channelID IS NULL
                    SET @QUERY = @QUERY + ' AND C.ID = ' + CONVERT(VARCHAR,@channelID)

                IF NOT @transactionTypeID IS NULL
                    SET @QUERY = @QUERY + ' AND TT.transactionTypeID = ' + CONVERT(VARCHAR,@transactionTypeID)

            SET @QUERY = @QUERY + ')'
            SET @QUERY = @QUERY + 'SELECT * FROM Trans WHERE Row BETWEEN (' + CONVERT(VARCHAR,@PageIndex) + ' - 1) * ' + CONVERT(VARCHAR,@PageSize) + ' + 1 AND ' + CONVERT(VARCHAR,@PageIndex) + '*' + CONVERT(VARCHAR,@PageSize)

            SET @QUERY = @QUERY + 'OPTION (FAST 1)'

            EXEC(@QUERY)

END
4

4 に答える 4

1

WHERE句で使用されるすべてのフィールド、つまりtransactionDateなどに個別のインデックスを作成するだけで済みます。追加のフィルターとしてtransactionStatusIDがある場合は、それを先頭の列として含めます。id_ent

CREATE INDEX ix_transaction_transactionDate ON transaction (id_ent, transactionDate)
CREATE INDEX ix_transaction_transactionStatusID ON transaction (id_ent, transactionStatusID)
-- etc.

クエリごとに 1 つのインデックスのみが使用され、SQL Server最も適切なもの (最も選択的なもの) を選択しようとすることに注意してください。

NOLOCKまた、本番テーブルにヒントを配置することは非常に悪い考えであることに注意してください。1 つのクエリ内でダーティ リードを実行できます。

id_entがすべてのテーブルの a の一部である場合はPRIMARY KEY、それを定数に置き換えることをお勧めします。このクエリ:

SELECT  *
FROM    Transactions t
JOIN    TransactionTypesCurrencies ttc
ON      ttc.trnTypeCurrencyID = t.trnTypeCurrencyID 
WHERE   t.id_ent = @id_ent
        AND ttc.id_ent = @id_ent

一般に、これよりも優れています。

SELECT  *
FROM    Transactions t
JOIN    TransactionTypesCurrencies ttc
ON      ttc.id_ent = t.id_ent
        AND ttc.trnTypeCurrencyID = t.trnTypeCurrencyID 
WHERE   t.id_ent = @id_ent

、早期フィルタリングを行うことができるため。

これは、 の値が 1 つしかない場合は違いはありませんが、別の値をid_ent追加する場合は元が取れます。

アップデート:

複数の条件でフィルター処理する繰り返しクエリがあり、速度が遅い場合は、いくつかの条件で追加の複合インデックスを作成することを検討できます。

これを行う方法に関するいくつかの提案については、私のブログのこの記事を参照してください。

于 2009-12-11T15:15:05.423 に答える
0

このシナリオでは、共通テーブル式よりも一時テーブルを作成する方が迅速であることがわかりました。これにより、ページングの合計数を返すこともできます。

于 2009-12-11T15:46:46.977 に答える
0

プロファイラーを使用してワークロードを記録し、そのワークロードでインデックス チューニング ウィザードを使用して、ワークロードを処理するのに最適なインデックスを決定することで、経験的な情報を得ることができます。

作成するインデックスが多いほど、挿入に必要な作業が増えるため、検索対象のすべてにインデックスを作成することはお勧めできません。

于 2009-12-11T15:35:59.620 に答える
0

索引を作成する前に自問する質問:

  1. このテーブル (または複数のテーブル) は読み取り専用になりますか、それとも読み書き可能になりますか?

ReadWrite - テーブルへの更新/削除があるたびに、インデックスが更新されます。「選択」では高速ですが、挿入、更新、および削除では遅い場合があります。MS の言葉を引用すると、「テーブルに多数のインデックスがある場合、オプティマイザーがクエリ プランに最適ではないインデックスを選択する可能性が高くなります。」

  1. その場でクエリを作成しています。私が行うことは、QA (または特定の時間の実稼働 DB) でトレースを実行して、ユーザーが実行しようとしているものを確認することです。本番環境からサンドボックスに DB のダンプを取得したり、インデックス チューニング ウィザード (必要なインデックスを教えてくれる) やSQL DMVなどのツールを試して、ボトルネックの場所を見つけることができます。問題はこの SP だけである必要はありませんが、デッドロック、一時テーブル/一時 DB の不適切な使用などがある可能性があります。

  2. このテーブルが原因であると確信できる場合は、テーブルを水平方向に分割することも試してください。

  3. クエリを実行するときは、実行計画を見て、テーブル スキャンを探します。これは通常、一部のインデックスが欠落していることを意味します。

  4. 読んで、読んで、読んで。

于 2009-12-11T15:48:46.070 に答える