优化复杂的查询

时间:2019-04-09 12:36:31

标签: sql-server tsql stored-procedures

下面的过程需要9秒钟才能执行,并且主表只有30000条记录。

   ALTER PROC [dbo].[TransactionReport_NewWork] @StartDate datetime = NULL, @EndDate datetime = NULL
, @Mid varchar(50) = NULL -- varchar(5000)=null                                            
, @BatchNo varchar(50) = NULL,
@AuthId varchar(50) = NULL, @RRN varchar(50) = NULL   --varchar(500)=null                                                                      
, @CardNo varchar(50) = NULL, @PageNo int = NULL, @PageSize int = 10, @ReportType int = NULL, @ReportSubType int = NULL,
@IsOrder varchar(10) = NULL,
@CurrencyCode varchar(10) = NULL
, @InvoiceNo varchar(50) = NULL
, @AppCode varchar(5) = NULL
, @ProductCodes StringValues READONLY,   --varchar(10)=null,                            
@TIDS StringValues READONLY
, @SettlementStatus IntegerValues READONLY --  varchar(50)=null                                            
, @TransactionType IntegerValues READONLY
, @ReportValues StringValues READONLY
AS


  SET @PageNo = (CASE
    WHEN @PageNo = 0 THEN 1
    WHEN @PageNo IS NULL THEN 1
    ELSE @PageNo
  END)


  SET @PageSize = (CASE
    WHEN @PageSize = 0 THEN 2147483647
    ELSE @PageSize
  END)

  SELECT
    x.*  --into #TransLogDetail

  FROM (SELECT TOP 100 PERCENT
    ROW_NUMBER() OVER (
    --order by a.TransactionDateTime desc          
    ORDER BY
    CASE
      WHEN j.ProductName IS NOT NULL THEN j.ProductCode
    END ASC,
    CASE
      WHEN j.ProductName IS NULL THEN a.TransactionDateTime
    END DESC,
    a.TransactionDateTime DESC
    ) SNo,
    b.MerchantName,
    a.var64_42 MID,
    a.var64_41 TID,
    (CASE
      WHEN c.TransactionType = 'SALE' THEN d.TransactionStatus
      WHEN c.TransactionType = 'MOTO' THEN 'MOTO'
      --when c.TransactionType ='LOYALTY_POINTS_REDEMPTION' then c.TransactionType          
      ELSE d.TransactionStatus
    END)
    AS TransactionType,
    a.var64_60 BatchNo,
    (CASE
      WHEN
        a.var64_54 IS NOT NULL THEN CAST(CONVERT(decimal, a.var64_04) / 100 AS numeric(18, 2)) - CAST(ISNULL(CONVERT(decimal, a.var64_54) / 100, 0) AS numeric(18, 2))
      ELSE CAST(CONVERT(decimal, a.var64_04) / 100 AS numeric(18, 2))
    END) Amount,
    (CASE
      WHEN c.TransactionType = 'MOTO' THEN SUBSTRING(a.var64_02, 1, 6) + '******' + SUBSTRING(a.var64_02, 13, 4)
      ELSE
        --substring(a.var64_35,1,6) +'******'+  substring(a.var64_35,13,4)                                   
        SUBSTRING(a.var64_02, 1, 6) + '******' + SUBSTRING(a.var64_02, 13, 4)

    END)
    CardNumber,
    a.var64_38 AuthID,
    a.var64_37 RRN,
    a.var64_62 InvoiceNo,
    CONVERT(datetime, STUFF(STUFF((CONVERT(varchar, DATEPART(YEAR, GETDATE())) + a.var64_13 + ' ' + a.var64_12), 12, 0, ':'), 15, 0, ':'))
    TransactionDateTime,
    e.SettlementStatus,
    a.var64_35 Track2,
    a.var64_11 Stan,
    h.Description AS POSEntryMode,
    g.Description AS POSConditionCode,
    (CASE
      WHEN a.var64_54 IS NULL THEN NULL
      ELSE CAST(ISNULL(CONVERT(decimal, a.var64_54) / 100, 0) AS numeric(18, 2))
    END) Tip,
    a.var64_55 BatchData,
    a.OrderNo,
    a.var64_48 KSN
    --, convert(varchar,a.TransactionDateTime,120) TransactionDateTime_Web                                  
    ,
    STUFF(STUFF((CONVERT(varchar, DATEPART(YEAR, GETDATE())) + a.var64_13 + ' ' + a.var64_12), 12, 0, ':'), 15, 0, ':') TransactionDateTime_Web
    --, isnull( i.Code +'('+isnull(i.Symbol,'')+')', N'PKR(?)') as CurrencyCode                            
    ,
    ISNULL(i.Code, 'PKR') AS CurrencyCode,
    a.var64_02 Pan,
    j.ProductName,
    (CASE
      WHEN a.var64_28 = '' THEN 0
      ELSE CAST(ISNULL(CONVERT(decimal, a.var64_28) / 100, 0) AS numeric(18, 2))
    END) AS ProductPrice,
    (CASE
      WHEN a.var64_28 = '' THEN 0
      ELSE CAST(ISNULL(CONVERT(decimal, RIGHT(a.var64_61, 12)) / 100, 0) AS numeric(18, 2))
    END) ProductQuantity,
    --a.var64_28 as  ProductPrice                      
    --,a.var64_61 as ProductQuantity,               
    (CASE
      WHEN LEN(a.var64_63) >= 57 THEN CAST(ISNULL(CONVERT(decimal, SUBSTRING(a.var64_63, 1, 12)) / 100, 0) AS numeric(18, 2)) +
        CAST(ISNULL(CONVERT(decimal, SUBSTRING(a.var64_63, 43, 12)) / 100, 0) AS numeric(18, 2))
      ELSE 0
    END) TotalDiscount,
    TotalRecords = COUNT(*) OVER (),
    TotalPages = CAST(CEILING(COUNT(*) OVER () / (@PageSize * 1.0)) AS int)
  --substring(a.var64_63,1,12)+'Part2'+substring(a.var64_63,43,12) TotalDiscount              

  FROM TransactionResponseLog a
  LEFT JOIN Merchant b
    ON a.var64_42 = b.mid
    AND b.isactive = 1
  LEFT JOIN GatewayTransactionType c
    ON a.TransactionTypeID = c.TransactionTypeID
  LEFT JOIN TransactionStatus d
    ON a.TransactionStatusID = d.TransactionStatusId
  LEFT JOIN SettlementStatus e
    ON a.SettlementStatusID = e.SettlementStatusId
  LEFT JOIN Association f
    ON
    --substring(a.var64_35,1,1)                               
    SUBSTRING(a.var64_02, 1, 1)
    = f.PaymentAssocationCode
  LEFT JOIN POSConditionCode g
    ON a.var64_25 = g.Code
  LEFT JOIN POSEntryMode h
    ON a.var64_22 = h.Code
  LEFT JOIN CurrencyCode i
    ON i.IsoCode = ISNULL(a.var64_49, '0586')
  LEFT JOIN ProuctWithRequestId j
    ON a.TransRequestID = j.TransRequestID

  WHERE a.var64_42 = ISNULL(@MID, a.var64_42)

  --and   a.var64_49 =isnull(@CurrencyCode,a.var64_49)                            

  AND
  (
  -------------For  Currency Check                           

  ------- For Currency  Check Is Not Null                                  
  ((
  --a.var64_49 =@CurrencyCode                           
  i.IsoCode = @CurrencyCode
  )
  AND (@CurrencyCode IS NOT NULL))

  OR
  ----For All Transactions                                              
  ((@CurrencyCode IS NULL)
  AND (1 = 1))
  )

  AND (
  -------------For  InvoiceNo Check                           
  --select * from TerminalSequence             
  ------- For InvoiceNo  Check Is Not Null                                  
  ((a.var64_62 = @InvoiceNo)
  AND (@InvoiceNo IS NOT NULL)
  AND a.var64_60 = (SELECT
    dbo.fn_LPAD(a.BatchNo, 6, '0')
  FROM TerminalSequence a
  WHERE a.TID IN (SELECT
    *
  FROM @ReportValues)
  AND a.AppCode = @AppCode)


  )

  OR
  ----For All Transactions                                              
  ((@InvoiceNo IS NULL)
  )
  )

  AND (
  ((j.ProductCode IN (SELECT
    *
  FROM @ProductCodes)
  )
  AND (@ProductCodesCount <> 0))
  OR ((@ProductCodesCount = 0))
  )


  AND (
  ((a.var64_41 IN (SELECT
    *
  FROM @TIDS)
  )
  AND (@TIdCount <> 0))
  OR ((a.var64_41 = a.var64_41)
  AND (@TIdCount = 0))
  )

  AND (
  ------- For Gateway  Transactions Other Than Moto                                        
  ((@TranTypeExMotoCount <> 0)
  AND (a.TransactionStatusId IN (SELECT
    *
  FROM @TransactionTypeExMoto)
  )
  AND (a.TransactionTypeId <> @MotoId-- (  select * from @TransactionTypeMoto   )                                         
  )
  )

  OR
  ------- For Gateway Moto Transaction                                        
  ((@TranTypeMotoCount <> 0)
  AND (a.TransactionTypeId = @MotoId))
  --- For All Transactions                                          
  OR
  ((a.TransactionStatusId = a.TransactionStatusId)
  AND (@TranTypeCount = 0))

  )

  AND CONVERT(date, a.TransactionDateTime) BETWEEN ISNULL(CONVERT(date, @StartDate), a.TransactionDateTime)
  AND ISNULL(CONVERT(date, @EndDate), a.TransactionDateTime)
  AND (
  ((a.SettlementStatusId IN (SELECT
    *
  FROM @SettlementStatus)
  )
  AND (@SettlementStatusCount <> 0))
  OR ((a.SettlementStatusId = a.SettlementStatusId)
  AND (@SettlementStatusCount = 0))
  )

  AND a.var64_38 = ISNULL(@AuthID, a.var64_38)
  AND a.var64_37 = ISNULL(@RRN, a.var64_37)
  --and substring(a.var64_35,1,16) =isnull(@CardNo,substring(a.var64_35,1,16))                                             
  AND
  (
  (
  (c.TransactionType IN ('SALE', 'REFUND', 'SETTLEMENT', 'LOYALTY_POINTS_REDEMPTION'))
  AND
  --( substring(a.var64_35,1,16) =isnull(@CardNo,substring(a.var64_35,1,16))  )                              
  (a.var64_02 = ISNULL(@CardNo, a.var64_02)
  )
  )
  OR ((c.TransactionType = 'MOTO')
  AND (a.var64_02 = ISNULL(@CardNo, a.var64_02)))

  )

  --and a. = isnull(@BatchNo,a.var64_60)                           

  AND
  (
  -------------For  BatchNo Check                           
  ------- For BatchNo  Check Is Not Null                                  
  ((a.var64_60 = @BatchNo)
  AND (@BatchNo IS NOT NULL))
  OR
  ----For BatchNo  Check Is  Null                                             
  ((@BatchNo IS NULL)
  AND (1 = 1)))


  AND a.var64_39 = '00'

  AND c.TransactionType IN ('SALE', 'MOTO', 'PUSH_QR_SALE', 'REFUND', 'SETTLEMENT', 'LOYALTY_POINTS_REDEMPTION')

  AND (
  -------------For Transactions Based On Order                              

  ------- For Is Order  1 Get Only Order Transactions                                     
  ((@IsOrder = 1)
  AND (a.OrderNo IS NOT NULL))
  ------- For Is Order 0 Get Other Transactions Than Order Transactions                                           
  OR
  ((@IsOrder = 0)
  AND (a.OrderNo IS NULL))
  OR
  ----For All Transactions                                              
  ((@IsOrder IS NULL)
  AND (1 = 1))
  )


  AND
  ----Start Of First And                                                
  (

  (

  @ReportTypeVar = 'TO_DATE'
  AND (
  ((CONVERT(varchar, a.TransactionDateTime, 106) IN (SELECT
    *
  FROM @ReportValues)
  )
  AND (@ReportValCount <> 0))
  OR ((CONVERT(varchar, a.TransactionDateTime, 106) = CONVERT(varchar, a.TransactionDateTime, 106))
  AND (@ReportValCount = 0))
  )

  )
  OR (

  @ReportTypeVar = 'WEEKDAY'
  AND (
  ((DATENAME(WEEKDAY, a.TransactionDateTime) IN (SELECT
    *
  FROM @ReportValues)
  )
  AND (@ReportValCount <> 0))
  OR ((DATENAME(WEEKDAY, a.TransactionDateTime) = DATENAME(WEEKDAY, a.TransactionDateTime))
  AND (@ReportValCount = 0))
  )
  )

  OR (
  @ReportTypeVar = 'MONTH'
  AND (
  ((DATENAME(MONTH, a.TransactionDateTime) IN (SELECT
    *
  FROM @ReportValues)
  )
  AND (@ReportValCount <> 0))
  OR ((DATENAME(MONTH, a.TransactionDateTime) = DATENAME(MONTH, a.TransactionDateTime))
  AND (@ReportValCount = 0))
  )
  )
  OR (
  @ReportTypeVar = 'QUARTER'
  AND (
  ((DATENAME(QUARTER, a.TransactionDateTime) IN (SELECT
    *
  FROM @ReportValues)
  )
  AND (@ReportValCount <> 0))
  OR ((DATENAME(QUARTER, a.TransactionDateTime) = DATENAME(QUARTER, a.TransactionDateTime))
  AND (@ReportValCount = 0))
  )
  )

  OR (
  @ReportTypeVar = 'DayOfMonth'
  AND (
  ((DATENAME(DAY, a.TransactionDateTime) IN (SELECT
    *
  FROM @ReportValues)
  )
  AND (@ReportValCount <> 0))
  OR ((DATENAME(DAY, a.TransactionDateTime) = DATENAME(DAY, a.TransactionDateTime))
  AND (@ReportValCount = 0))
  )
  )


  OR (

  @ReportTypeVar = 'TID_WISE'
  AND (
  ((a.var64_41 IN (SELECT
    *
  FROM @ReportValues)
  )
  AND (@ReportValCount <> 0))
  OR ((a.var64_41 = a.var64_41)
  AND (@ReportValCount = 0))
  )


  )

  OR (

  @ReportTypeVar = 'BATCHNO'
  AND (
  ((a.var64_60 IN (SELECT
    *
  FROM @ReportValues)
  )
  AND (@ReportValCount <> 0))
  OR ((1 = 1)
  AND (@ReportValCount = 0))
  )
  )
  OR (

  @ReportTypeVar = 'ASSOCIATION'
  AND (
  ((f.AssociationName IN (SELECT
    *
  FROM @ReportValues)
  )
  AND (@ReportValCount <> 0))
  OR ((f.AssociationName = f.AssociationName)
  AND (@ReportValCount = 0))
  )
  )
  )

  ----End Of First And                                                
  ORDER BY CASE
    WHEN j.ProductName IS NOT NULL THEN j.ProductCode
  END ASC,
  CASE
    WHEN j.ProductName IS NULL THEN a.TransactionDateTime
  END DESC, a.TransactionDateTime DESC) x
  ORDER BY x.sno ASC
  OFFSET ((@PageNo - 1) * @PageSize) ROWS
  FETCH NEXT @PageSize ROWS ONLY;

**The result of execution plan is below.**

表“工作表”。扫描计数3,逻辑读55959,物理读0,预读1158,lob逻辑读0,lob物理读0,lob预读0。 表“工作文件”。扫描计数0,逻辑读0,物理读0,预读0,lob逻辑读0,lob物理读0,lob预读0。

“ TransactionRequestLog” 。扫描计数1,逻辑读2979,物理读0,预读0,lob逻辑读0,lob物理读0,lob预读0。

“产品层次结构” 。扫描计数1,逻辑读1,物理读0,预读0,lob逻辑读0,lob物理读0,lob预读0。

“ POSConditionCode” 。扫描计数1,逻辑读19452,物理读0,预读0,lob逻辑读0,lob物理读0,lob预读0。 表'#B1653DF7'。扫描计数1,逻辑读19452,物理读0,预读0,lob逻辑读0,lob物理读0,lob预读0。

“关联” 。扫描计数1,逻辑读19452,物理读0,预读0,lob逻辑读0,lob物理读0,lob预读0。

“结算状态” 。扫描计数1,逻辑读38905,物理读0,预读0,lob逻辑读0,lob物理读0,lob预读0。

“交易状态” 。扫描计数1,逻辑读38905,物理读0,预读0,lob逻辑读0,lob物理读0,lob预读0。

“商人” 。扫描计数1,逻辑读1750680,物理读0,预读0,lob逻辑读0,lob物理读0,lob预读0。 表'#B2596230'。扫描计数1,逻辑读19453,物理读0,预读0,lob逻辑读0,lob物理读0,lob预读0。 表'#A586BBB9'。扫描计数1,逻辑读19454,物理读0,预读0,lob逻辑读0,lob物理读0,lob预读0。 表“工作表”。扫描计数0,逻辑读0,物理读0,预读0,lob逻辑读0,lob物理读0,lob预读0。

“ transactionresponselog” 。扫描计数1,逻辑读2110,物理读0,预读0,lob逻辑读0,lob物理读0,lob预读0。

“ GatewayTransactionType” 。扫描计数1,逻辑读1,物理读0,预读0,lob逻辑读0,lob物理读0,lob预读0。

'POSEntryMode'。扫描计数1,逻辑读1,物理读0,预读0,lob逻辑读0,lob物理读0,lob预读0。

“货币代码” 。扫描计数1,逻辑读5,物理读0,预读0,lob逻辑读0,lob物理读0,lob预读0。

除transactionresponselog和transactionrequestlog外的所有表都是主表。

1 个答案:

答案 0 :(得分:3)

这是一个无所不包的查询,有很多关于它的文章,盖尔·肖(Gail Shaw)像她的文章How to Confuse the SQL Server Query Optimizer一样写了好几次。没有简单的方法可以优化此查询。我的建议是使用动态SQL简化条件。您似乎也只共享了整个过程的一部分。之所以认为动态SQL是一种解决方法,而并非每次都重新编译,是因为查询非常复杂,并且可能在完全优化之前超时。 您的某些列检查是通过以下方式编写的:

AND (
            -------------For  BatchNo Check                           
            ------- For BatchNo  Check Is Not Null                                  
            (
                (a.var64_60 = @BatchNo)
                AND (@BatchNo IS NOT NULL)
                )
            OR
            ----For BatchNo  Check Is  Null                                             
            (
                (@BatchNo IS NULL)
                AND (1 = 1)
                )
            )

可以简化为以下形式:

AND (a.var64_60 = @BatchNo OR @BatchNo IS NULL)

这类似于与表值参数中的多个值进行比较。

AND (j.ProductCode IN ( SELECT * FROM @ProductCodes) OR @ProductCodesCount = 0)

使用动态代码还将简化引擎的@ReportTypeVar条件。

请注意,即使使用动态SQL,您也需要能够对查询进行参数设置,以防止进行任何形式的SQL注入。