Proc is giving poor performance

  • Skull Killer (12/3/2008)


    Hi there

    U can also create a var @ActionTable TABLE(id INT IDENTITY(1,1),Action VARCHAR(20))

    insert the possible actions into this table

    INSERT INTO @ActionTable (id,Action)

    SELECT 'NEW'

    UNION ALL

    SELECT 'DELETE'

    UNION ALL

    ...

    or better u could create a physical not temporary table

    Join this table with CONTACTHISTORY and do the CASE with the id column of this new table (it reduces the VARCHAR comparisons to 1 per row). Its not necessary to create a physical temporary table since it's a small one that works better in memory.

    Hope this makes the procedure better

    Hi SK, I've been hanging on to this as a next step for the OP...

    INTO #Trades

    FROM CONTRACTHISTORY B WITH (NOLOCK)

    ...

    ...

    INNER JOIN (SELECT 1 AS tradeaction, 'NEW' AS [action] UNION ALL

    SELECT 2, 'DELETE' UNION ALL

    SELECT 3, 'AMEND' UNION ALL

    SELECT 4, 'BREAK' UNION ALL

    SELECT 0, NULL) x ON x.[action] = B.[action]

    INNER JOIN PAAREPORTHISTORY A WITH(NOLOCK) ON A.ContractNo = B.ContractNo AND A.Branch = B.Branch AND A.EODDate = B.EODDate AND A.EODDATE = O.EODDATE

    AND A.tradeaction = x.tradeaction

    ...because it can perform significantly better than the CASE construct which it replaces. Not sure how the ELSE 0 END fits in with this, however.

    Cheers

    ChrisM

    โ€œWrite the query the simplest way. If through testing it becomes clear that the performance is inadequate, consider alternative query forms.โ€ - Gail Shaw

    For fast, accurate and documented assistance in answering your questions, please read this article.
    Understanding and using APPLY, (I) and (II) Paul White
    Hidden RBAR: Triangular Joins / The "Numbers" or "Tally" Table: What it is and how it replaces a loop Jeff Moden

  • Hi Gila Monster

    That's true. But creating a temp table, even with no data, costs more than a table variable. That's why I justified the choice that way.

  • Hi

    I remembered, u can also replace the table variable I suggested before or the way u did it in the middle of the SELECT statement with a Common Table Expression (cte) like this

    ;WITH actionCTE as (SELECT 1 as ID,'NEW' as Action

    UNION ALL

    SELECT 2, 'DELETE'

    UNION ALL

    ...)

    --place your query (SELECT, INSERT, UPDATE or DELETE) here and use the actionCTE as a table

    I don't know if it performs faster than the table variable, but since it removes a INSERT statement, it should be

  • Skull Killer (12/4/2008)


    That's true. But creating a temp table, even with no data, costs more than a table variable.

    Why?

    Gail Shaw
    Microsoft Certified Master: SQL Server, MVP, M.Sc (Comp Sci)
    SQL In The Wild: Discussions on DB performance with occasional diversions into recoverability

    We walk in the dark places no others will enter
    We stand on the bridge and no one may pass
  • Because it has to "reserve" and then destroy the structure in the temp database and it does the COMMITs.

    Temp variables only get to disk if they are big, and since we can't do indexing in table variables they only better work better for small or shortuse rowsets. By shortuse I mean that they are not that small but doesn't need indexes nor a large rowsize. You can see this post Table types. I think there is also some articles here in sqlservercentral.

  • Skull Killer (12/4/2008)


    Because it has to "reserve" and then destroy the structure in the temp database and it does the COMMITs.

    Same with a table variable. Main difference is that table variables don't participate in transactions and temp tables do.

    Temp variables only get to disk if they are big

    Same with a temp table, though they both are recorded in TempDB's system tables and both get 'reserved' space in tempDB. SQL will try to keep them in memory as much as possible, because it knows that the data is very likely to be reused very soon. In case they do have to go to disk, they need entries in TempDB.

    http://support.microsoft.com/default.aspx?scid=kb;en-us;305977

    Also this one, though it is my blog and hence can't be used to prove my position

    http://sqlinthewild.co.za/index.php/2007/12/19/temp-tables-and-table-variables/

    Gail Shaw
    Microsoft Certified Master: SQL Server, MVP, M.Sc (Comp Sci)
    SQL In The Wild: Discussions on DB performance with occasional diversions into recoverability

    We walk in the dark places no others will enter
    We stand on the bridge and no one may pass
  • since the transactions and locks does not occur in table variables, my point still stands for small tables like the one in this procedure. anyway, The cte solution works for this procedure since it is needed only in one statement. And tanks for the clarification in the difference between variable and temp tables.

    In the first post about temp tables I also said it would be good to make a permanent table with the same structure. It would be good for 2 reasons: it would permanently register you actions (u could put a foreign key in the contacthistory table) and it would allow for a change in the contacthistory table replacing the type of the action field to integer, reducing the row size of the table. Since it's a history table you could, if that is an issue, create the new table as actionHistory and do the necessary things to maintain it, or adding a field to the table marking the deleted actions instead of deleting it.

    Since I have been at work I was just shouting out some solutions/ideias I remembered, I will now take a better look at the procedure and reformulate it as I analyse it.

    please give some feedback on the suggestion I made here about the permanent table.

  • Hi

    after "fast analyzing" the original procedure I achieved the statement. I didn't look into the other posts, so u should apply the same stuff u applied before, at least the things that worked about the functions. All the posts I read about it seems logic. I didn't alter all the things I think u should do since U can check it better if U do it yourself. Since I am not used with the tables and their relations I would probably do it wrong. So use the code i will post in the end and try the things I talk about in the comments. Let me know if it helped.

    ALTER PROCEDURE [dbo].[prc_SummaryRpt] (

    @FromDate SMALLDATETIME,

    @ToDate SMALLDATETIME,

    @ProfCode SMALLINT,

    @Branch SMALLINT = 0,

    @WHCode VARCHAR(5) = NULL,

    @CCYCode VARCHAR(5) = NULL,

    @ContractFlag BIT = 0

    )

    AS

    DECLARE @ERROR INTEGER

    /* I don't see u using this in the procedure so

    DECLARE @Profile TABLE (

    ProfCode INTEGER,

    ProfName VARCHAR(50) COLLATE DATABASE_DEFAULT,

    Level0 VARCHAR(20) COLLATE DATABASE_DEFAULT,

    Level1 VARCHAR(20) COLLATE DATABASE_DEFAULT,

    Level2 VARCHAR(20) COLLATE DATABASE_DEFAULT,

    Level3 VARCHAR(20) COLLATE DATABASE_DEFAULT,

    Level4 VARCHAR(20) COLLATE DATABASE_DEFAULT,

    Level5 VARCHAR(20) COLLATE DATABASE_DEFAULT,

    HCount INTEGER,

    soeid VARCHAR(10) COLLATE DATABASE_DEFAULT,

    AdhocProfile BIT,

    AllLV BIT,

    AllWH BIT,

    AllCcy BIT,

    ProfileTypeCode VARCHAR(20) COLLATE DATABASE_DEFAULT

    )*/

    BEGIN

    SET NOCOUNT ON

    CREATE TABLE #Trades(

    EODDate SMALLDATETIME,

    LVDesc VARCHAR(50) COLLATE DATABASE_DEFAULT,

    WHCode VARCHAR(10) COLLATE DATABASE_DEFAULT,

    CcyCode VARCHAR(10) COLLATE DATABASE_DEFAULT,

    Branch TINYINT,

    ContractNo VARCHAR(200) COLLATE DATABASE_DEFAULT,

    Daily_Accrual NUMERIC(28, 16),

    Trader VARCHAR(200) COLLATE DATABASE_DEFAULT,

    Time_Decay_MTM NUMERIC(28, 16) ,

    Reval_Rates_MTM NUMERIC(28, 16) ,

    FX_Rates_MTM NUMERIC(28, 16) ,

    New_Trades_MTM NUMERIC(28, 16) ,

    Cancelled_Trades_MTM NUMERIC(28, 16) ,

    Amended_Trades_MTM NUMERIC(28, 16) ,

    Economic_Revenue NUMERIC(28, 16) ,

    Economic_Revenue_FromHandoff NUMERIC(28, 16) ,

    Difference_with_Intellect NUMERIC(28, 16),

    tradeaction SMALLINT

    )

    BEGIN TRANSACTION

    IF @Branch = 0 SET @Branch=NULL

    IF (@ContractFlag = 0)

    BEGIN

    WITH cteActions AS (

    SELECT ID = 1, [Action] = 'NEW'

    UNION ALL

    SELECT ID = 2, [Action] = 'DELETE'

    UNION ALL

    SELECT ID = 3, [Action] = 'AMEND'

    UNION ALL

    SELECT ID = 4, [Action] = 'BREAK'

    )

    /* About CTEs : u can only use the cte in the next insert, update, delete, select statement

    u can use a previous declared cte in the next cte like this

    WITH Cte1 as (...),

    Cte1 as (select ... from cte1, ...

    SELECT*/

    INSERT INTO #Trades (

    EODDate,

    LVDesc,

    WHCode,

    CcyCode,

    Daily_Accrual,

    Trader,

    Branch,

    ContractNo,

    Time_Decay_MTM,

    Reval_Rates_MTM,

    FX_Rates_MTM,

    New_Trades_MTM,

    Cancelled_Trades_MTM,

    Amended_Trades_MTM,

    Economic_Revenue,

    Difference_with_Intellect,

    Economic_Revenue_FromHandoff,

    tradeaction

    )

    SELECT DISTINCT

    EODDate = A.EODDate,

    LV = D.LVDesc,

    WH = RTRIM(C.WHCode),

    Ccy = B.CcyCode,

    Daily_Accrual = O.DailyAccrual,

    Trader = H.TraderName,

    Branch = A.Branch,

    ContactNo = A.ContractNo,

    Time_Decay_MTM = A.TDMTMUSD-A.PREIDMTMUSD,

    Reval_Rates_MTM = A.PARALLELTIMTMUSD-A.TDMTMUSD,

    FX_Rates_MTM = A.FIMTMUSD-A.PARALLELTIMTMUSD,

    New_Trades_MTM = CASE

    WHEN cteA.ID = 1 THEN A.IDMTMUSD

    ELSE 0

    END,

    Cancelled_Trades_MTM = CASE

    WHEN cteA.ID = 2 and P.Contractno is null THEN A.IDMTMUSD - A.FIMTMUSD

    ELSE 0

    END,

    Amended_Trades_MTM = CASE

    WHEN cteA.ID between 3 AND 4 THEN A.IDMTMUSD-0

    WHEN cteA.ID = 2 and P.ContractNo is not null THEN 0-A.FIMTMUSD

    ELSE 0

    END,

    --Since the select set the values sequentially in the order it appears, this sould work

    Economic_Revenue = Reval_Rates_MTM +

    Time_Decay_MTM +

    FX_Rates_MTM +

    New_Trades_MTM +

    Cancelled_Trades_MTM +

    Amended_Trades_MTM,

    Difference_with_Intellect = 0,

    Economic_Revenue_FromHandoff = ISNULL(O.ECONVAL,0),

    TradeAction = A.TradeAction

    FROM CONTRACTHISTORY B (NOLOCK) LEFT JOIN cteActions cteA on b.Action=cteA.Action

    /*If the previous statement doesn't work u can put a "," after the cte and put another CTE as

    select b.*,ActionID=cteA.id from CONTRACTHISTORY B (NOLOCK) LEFT JOIN cteActions cteA on b.Action=cteA.Action*/,

    TRIPL_ForOffShore.dbo.TBL_WAREHOUSE C (NOLOCK), /*u should link every tables by inner join,

    put the left join on a cte (it ensures that it work every time)*/

    TRIPL_ForOffShore.dbo.TBL_LEGALVEHICLE D (NOLOCK),

    TRIPL_ForOffShore.dbo.TBL_TRADER H (NOLOCK),

    CTCDETAILHISTORY O (NOLOCK),

    dbo.fn_ProfileLegalVechile(@ProfCode,@Branch) L,/*If this is just to return a partial table/value U can put it in a CTE too

    unless u use it for other stuff 2 since it permits a centralization of the code*/

    dbo.fn_ProfileWareHouse(@ProfCode,@WHCode) M,

    dbo.fn_ProfileCurrency(@ProfCode,@CCYCode) N,

    PAAREPORTHISTORY A (NOLOCK) LEFT JOIN

    dbo.fn_getPreversionContracts(@FromDate,@ToDate) P

    ON A.ContractNo = P.Contractno AND A.EodDate = P.EodDate

    WHERE A.ContractNo = B.ContractNo AND

    A.Branch = B.Branch AND

    A.EODDate = B.EODDate AND

    B.WHCode = C.WHCode AND

    B.Branch = C.LVCode AND

    C.LVCode = D.LVCode AND

    C.TraderCode = H.TraderCode AND

    A.EODDATE = O.EODDATE AND

    B.CCYCODE = O.CCYCODE AND

    B.WHCODE = O.WHCODE AND

    B.Branch = L.LVCode AND

    B.WHCode = M.WHCode AND

    B.CcyCode = N.CcyCode AND

    A.EODDate BETWEEN @FromDate AND @ToDate AND

    B.EODDate BETWEEN @FromDate AND @ToDate AND

    O.EODDate BETWEEN @FromDate AND @ToDate AND

    A.tradeaction = ISNULL(cteA.ID,0)

    /*if u want u can put the previous statement in a CTE, removing temp tables and table variable from all the procedure*/

    SELECT EODDate = CONVERT(varchar(15), EODDate,106),

    LV = LVDesc,

    WH = WHCode,

    Ccy = CcyCode,

    Trader = Trader,

    Daily_Accrual = ISNULL(AVG(Daily_Accrual),0),

    Time_Decay_MTM = ISNULL(CONVERT(NUMERIC,SUM(Time_Decay_MTM)),0),

    Reval_Rates_MTM = ISNULL(CONVERT(NUMERIC,SUM(Reval_Rates_MTM)),0),

    FX_Rates_MTM = ISNULL(CONVERT(NUMERIC,SUM(FX_Rates_MTM)),0),

    New_Trades_MTM = ISNULL(CONVERT(NUMERIC,SUM(New_Trades_MTM)),0),

    Cancelled_Trades_MTM = ISNULL(CONVERT(NUMERIC,SUM(Cancelled_Trades_MTM)),0),

    Amended_Trades_MTM = ISNULL(CONVERT(NUMERIC,SUM(Amended_Trades_MTM)),0),

    Economic_Revenue = ISNULL(SUM(Economic_Revenue),0),

    Economic_Revenue_FromHandoff = Economic_Revenue_FromHandoff,

    Difference_with_Intellect = Difference_with_Intellect

    FROM #Trades

    GROUP BY EODDate,

    LVDesc,

    WHCode,

    CcyCode,

    Trader,

    Difference_with_Intellect,

    Economic_Revenue_FromHandoff

    ORDER BY EODDate,

    LVDesc,

    WHCode,

    CcyCode,

    Trader

    SELECT ProfCode = ProfCode,

    ProfName = ProfName,

    Level0 = 'EODDate',

    Level1 = Level1,

    Level2 = Level2,

    Level3 = Level3,

    Level4 = Level4,

    Level5 = Level5,

    HCount = HCount + 1,

    soeid = soeid,

    AdhocProfile = AdhocProfile,

    AllLV = AllLV,

    AllWH = AllWH,

    AllCcy = AllCcy,

    ProfileTypeCode = ProfileTypeCode

    FROM TRIPL_ForOffShore.dbo.TBL_PROFILES

    WHERE ProfCode = @ProfCode

    END

    SET @Error = @@Error

    IF @Error <> 0 GOTO ERROR_HANDLER

    COMMIT TRANSACTION

    SET NOCOUNT OFF

    RETURN 0

    ERROR_HANDLER:

    EXECUTE prc_LogErrors @ERROR, NULL, @@PROCID

    ROLLBACK TRANSACTION

    SET NOCOUNT OFF

    RETURN 1 -- FAILURE

    END

    GO

    SET QUOTED_IDENTIFIER OFF

    GO

    SET ANSI_NULLS ON

    GO

    well it seems i discovered another way to do the ๐Ÿ˜‰ . It means ) and not ; )

    I did a little reformat, u copy what u think best for yourself but always format the code. I exaggerated a little in the [Field Name] = [Field Name].

Viewing 8 posts - 31 through 37 (of 37 total)

You must be logged in to reply to this topic. Login to reply