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子句中使用的所有字段上创建单独的索引,即transactionDatetransactionStatusID。如果您有 aid_ent作为附加过滤器,请将其作为前导列:

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

请注意,对于每个查询,只会使用一个索引,并且SQL Server会尝试选择最合适的索引(最具选择性的索引)。

另请注意,NOLOCK在生产表上放置提示是一个非常糟糕的主意。您可以在单个查询中进行脏读。

如果在所有表中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

,因为可以进行早期过滤。

如果您只有一个 的值,这没有什么区别id_ent,但如果您再添加另一个值,则将自行付费。

更新:

如果您的重复查询过滤多个条件并且速度很慢,则可以考虑在多个条件上创建额外的复合索引。

有关如何执行此操作的一些建议,请参阅我的博客中的这篇文章:

于 2009-12-11T15:15:05.423 回答
0

您可以通过使用分析器记录工作负载来获取一些经验信息,然后使用索引调整向导来确定最能处理工作负载的索引。

您创建的索引越多,插入工作要做的工作就越多,因此为正在搜索的所有内容创建索引可能不是一个好主意。

于 2009-12-11T15:35:59.620 回答
0

我发现在这种情况下,创建临时表而不是公用表表达式会更快。这也允许您返回分页的总数。

于 2009-12-11T15:46:46.977 回答
0

在创建索引之前我会问自己的问题:

  1. 这个表(或多个表)是只读的还是读写的?

读写 - 每次对表进行更新/删除时,都会更新索引。“选择”可能会很快,但插入、更新和删除会很慢。引用 MS “如果表上有大量索引,则会增加优化器为查询计划选择次优索引的机会。”

  1. 您正在动态创建查询。我要做的是尝试在 QA(或特定时间的生产数据库)上运行跟踪并查看用户正在尝试运行什么。您可以将数据库从生产环境转储到沙箱/尝试使用索引调整向导(它可以告诉您需要哪些索引)、SQL DMV等工具来找出瓶颈所在。问题不仅在于此 SP,还可能存在死锁、临时表/临时 DB 使用不当等。

  2. 如果你有理由相信这张表是罪魁祸首,你还应该尝试水平分区表

  3. 当您执行任何查询时,请查看执行计划并查找表扫描 - 这通常意味着缺少某些索引

  4. 阅读阅读阅读

于 2009-12-11T15:48:46.070 回答