1

我有一個事務表的存儲過程的問題,用戶有一個Web表單來查找幾個值的事務。SQL Server 2005正確的索引來過濾30,000,000個寄存器

該過程耗時過長,我不知道如何設置正確的索引。

這裏是我的存儲過程:

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 

回答

1

你只需要創建的WHERE子句中使用的所有字段的單獨的索引,即transactionDatetransactionStatusID等,如果你有一個id_ent作爲額外的過濾器,把它作爲一個領先的列:

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

注意,對於每個查詢只有一個索引將被使用,並SQL Server會盡量選擇最合適的一個(第m最後選擇一個)。

另請注意,在生產表上放置NOLOCK提示是一個非常糟糕的主意。您可以在單個查詢內有髒讀數

如果id_ent是所有表格中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的單個值,則不會產生任何影響,但如果您將添加另一個值,將自行支付。

更新:

如果你有定期的查詢過濾器上的多個條件和速度很慢,你可以考慮幾個條件創造更多的複合索引。

看到這篇文章在我的博客上如何做到這一點的一些建議:

+0

在此先感謝。問題是,我可以搜索字段的任何組合,可以是日期和交易類型或交易類型和用戶ID等等等等等等......而其他的事情是,創建此表的人使得字段Id_Ent成爲主鍵(id_ent + transactionID)但id_ent在整個表中是相同的值,我請他改變它,因爲它會影響我的索引,不是嗎? – jmpena 2009-12-11 15:18:24

+0

只要你總是過濾PK的第一部分(我看到你這樣做)就可以擁有複合PK。 – Quassnoi 2009-12-11 15:19:39

+0

yeah id_Ent在所有的表格上,我實際上在做「JOINT」,就像你說的「ttc.id_ent = t.id_ent」,在創建所有的索引來測試你的建議,我讓你知道這種方式...謝謝 – jmpena 2009-12-11 15:49:16

0

您可以通過使用Profiler來記錄負載,然後用得到一些經驗信息索引調整嚮導以及該工作負載來確定能夠最好地處理工作負載的索引。

您創建的索引越多,插入的工作就越多,因此在搜索的所有內容上創建索引可能不是一個好主意。

0

我發現在這種情況下可以更快地創建臨時表而不是通用表表達式。這也可以讓你返回分頁的總數。

+0

一個超過30,000,000的tmp表,我認爲它會傷害,順便說一句,我用計數(*)的記錄計數類似的查詢,但在這種情況下,我不需要它,用戶將pagind,直到他得到一個「否在此頁面上找到的記錄「消息。 – jmpena 2009-12-11 17:36:15

+0

你仍然會像你一樣構建where子句,但是這將用於填充臨時表,然後可以給出該過濾器中的項目總數並返回請求的任何頁面。 – cjk 2009-12-12 08:48:20

0

問題,我之前我問自己創建一個索引:

  1. 是怎麼回事這個表(或表)爲只讀或讀寫?

ReadWrite - 每次更新/刪除表時,索引都會被更新。它對於「select」可能是快速的,但對於插入,更新和刪除來說可能很慢。 To quote MS「如果表中有大量索引,則會增加優化程序爲查詢計劃選擇次優索引的機率。」

  • 您在fly.What我會做的就是盡力(在特定的時間或生產DB)上運行QA跟蹤,看看哪些用戶正在嘗試創建查詢跑。您可以將數據庫從生產轉儲到您的沙盒/嘗試工具,如索引調整嚮導(它可以告訴您需要哪些索引),SQL DMVs等,以找到瓶頸所在。這個問題不一定只有這個SP,但可能會有死鎖,臨時表/臨時DB使用不當等。

  • 如果您有理由相信這個表是罪魁禍首,您還應該嘗試對錶進行分區水平

  • 當你執行任何查詢,看執行計劃,並查找表scans-這通常意味着一些索引丟失

  • ReadReadRead

  • +0

    感謝您的信息,這是一個交易表,僅用於插入和選擇(不更新或刪除)。 我想使用一個鏡像表,所以如果用戶想要實時報告可以使用該表索引並使事務表可操作,我認爲是一個更好的主意。 – jmpena 2009-12-11 17:39:41

    +0

    執行計劃顯示Index Seek和Cluster Index Seek – jmpena 2009-12-11 17:40:18