Can this be converted to a set based query?

  • tfeuz (6/7/2011)


    Wayne,

    I had to make a fix to the code -- you did not constrain by the rule id when you create the temp table -- it caused duplicate values...

    Note the addition of the clause "and @RuleId = rc.rb_ruleid;"

    Good catch on this.

    I ended up stopping the query after 10 minutes --

    Wayne --- you have been more than genrous with your time and at 16 seconds per 1 million rows i think i am well within the range of "acceptable" -- do you still wish to continue? If so, I am happy to keep testing stuff....

    Tony

    I'd kinda like to see the query finish, and get the stats and execution plan. Plus, Kevin thinks it will do better without the CI on the table var... this ought to be a good test to check also.

    I'm kinda confused by the 10 minutes & 16 secs/1 million rows part... they don't seem to line up. At that rate, it would be pushing close to 40 million rows in 10 minutes... how many rows are we talking about here again? And wasn't it working in 16 seconds before?

    Wayne
    Microsoft Certified Master: SQL Server 2008
    Author - SQL Server T-SQL Recipes


    If you can't explain to another person how the code that you're copying from the internet works, then DON'T USE IT on a production system! After all, you will be the one supporting it!
    Links:
    For better assistance in answering your questions
    Performance Problems
    Common date/time routines
    Understanding and Using APPLY Part 1 & Part 2

  • TheSQLGuru (6/7/2011)


    I was giving my own presentation @ sqlsat77 while yours was in progress. 😀

    Ah, so you were. I heard it went pretty well.

    As long as they have fairly uniform distribution of values the table var should be ok I suppose. Still bet it will be more efficient without the PK though.

    My money's on the optimal solution having no spools, probably 1-2 intermediary temp tables, no CTEs and likely a hash join or two. Lotta work to get from here to there though.

    Right now, I have the TVF because there are two procedures that call it - one for just getting a single result set, and the other is this monster to get all of the unprocessed invoices. If I'm reading you correctly, you're suggesting to merge the TVF into the calling procedure - which would end up with duplicate code in multiple procedures. Am I reading you correctly?

    You've mentioned a couple of times about getting rid of the CTEs. I'm curious as to the rational for this... they are effectively just a sub-query, and they are only being called once. The only way I can see this improving things is in combination with the above part of merging the code into its own procedure for the massive run. Then I can see something like this. Am I missing something here?

    I admire you for taking this one on (and for sticking with it). Its unquestionably a paid gig IMHO.

    Thanks. And it true. But, earlier in the thread, I badgered Tony to get the DDL/sample data posted so that we could help. Once he did so, I sorta felt obligated to stick around and see it through.

    Wayne
    Microsoft Certified Master: SQL Server 2008
    Author - SQL Server T-SQL Recipes


    If you can't explain to another person how the code that you're copying from the internet works, then DON'T USE IT on a production system! After all, you will be the one supporting it!
    Links:
    For better assistance in answering your questions
    Performance Problems
    Common date/time routines
    Understanding and Using APPLY Part 1 & Part 2

  • Tony:

    Is RB_InvoicesToProcess 1:1 with Invoices?

    Is RB_InvoicesToProcess_Moved 1:1 with Invoices?

    Just to verify, you can have the same invoice in both of those tables?

    Wayne
    Microsoft Certified Master: SQL Server 2008
    Author - SQL Server T-SQL Recipes


    If you can't explain to another person how the code that you're copying from the internet works, then DON'T USE IT on a production system! After all, you will be the one supporting it!
    Links:
    For better assistance in answering your questions
    Performance Problems
    Common date/time routines
    Understanding and Using APPLY Part 1 & Part 2

  • WayneS (6/7/2011)


    tfeuz (6/7/2011)


    Wayne,

    I had to make a fix to the code -- you did not constrain by the rule id when you create the temp table -- it caused duplicate values...

    Note the addition of the clause "and @RuleId = rc.rb_ruleid;"

    Good catch on this.

    I ended up stopping the query after 10 minutes --

    Wayne --- you have been more than genrous with your time and at 16 seconds per 1 million rows i think i am well within the range of "acceptable" -- do you still wish to continue? If so, I am happy to keep testing stuff....

    Tony

    I'd kinda like to see the query finish, and get the stats and execution plan. Plus, Kevin thinks it will do better without the CI on the table var... this ought to be a good test to check also.

    I'm kinda confused by the 10 minutes & 16 secs/1 million rows part... they don't seem to line up. At that rate, it would be pushing close to 40 million rows in 10 minutes... how many rows are we talking about here again? And wasn't it working in 16 seconds before?

    Wayne -- we did have previous versions running in 16 seconds -- this is the first one that has taken longer.

    There are 80,000 Invoice rows and 1.2 million line item rows

  • WayneS (6/7/2011)


    Tony:

    Is RB_InvoicesToProcess 1:1 with Invoices?

    Is RB_InvoicesToProcess_Moved 1:1 with Invoices?

    Just to verify, you can have the same invoice in both of those tables?

    RB_InvoicesToProcess 1:1 with Invoices but you have to throw Rule Id in there as well. There will only be one entry for a given invoice for a given rule.

    Same applies to RB_InvoicesToProcess_Moved

    Now that beng said, my test data is only working on one rule so technically your statement

    "RB_InvoicesToProcess 1:1 with Invoices?" is correct at the present time.

    Tony

  • tfeuz (6/8/2011)


    WayneS (6/7/2011)


    tfeuz (6/7/2011)


    Wayne,

    I had to make a fix to the code -- you did not constrain by the rule id when you create the temp table -- it caused duplicate values...

    Note the addition of the clause "and @RuleId = rc.rb_ruleid;"

    Good catch on this.

    I ended up stopping the query after 10 minutes --

    Wayne --- you have been more than genrous with your time and at 16 seconds per 1 million rows i think i am well within the range of "acceptable" -- do you still wish to continue? If so, I am happy to keep testing stuff....

    Tony

    I'd kinda like to see the query finish, and get the stats and execution plan. Plus, Kevin thinks it will do better without the CI on the table var... this ought to be a good test to check also.

    I'm kinda confused by the 10 minutes & 16 secs/1 million rows part... they don't seem to line up. At that rate, it would be pushing close to 40 million rows in 10 minutes... how many rows are we talking about here again? And wasn't it working in 16 seconds before?

    Wayne -- we did have previous versions running in 16 seconds -- this is the first one that has taken longer.

    There are 80,000 Invoice rows and 1.2 million line item rows

    Gotcha! So, the mTVF didn't work out then. 🙁 :crying:

    Well, back to the drawing board then...

    Wayne
    Microsoft Certified Master: SQL Server 2008
    Author - SQL Server T-SQL Recipes


    If you can't explain to another person how the code that you're copying from the internet works, then DON'T USE IT on a production system! After all, you will be the one supporting it!
    Links:
    For better assistance in answering your questions
    Performance Problems
    Common date/time routines
    Understanding and Using APPLY Part 1 & Part 2

  • Tony,

    Starting over. The main (not single) procedure is totally rewritten to combine the TVF with it. If this works, we'll combine the TVF into the single procedure also.

    Start off by dropping those six indexes created on the Invoice table. We'll re-evaluate what is needed there based upon the execution plan that this creates.

    CREATE PROCEDURE spCalculateRuleInvoiceUnits @RuleUnitType numeric(9,2),@TermsRuleType numeric(9,2),@RuleId decimal(18,0)

    AS

    DECLARE @Col2Use sysname,

    @SQLCmd NVARCHAR(MAX);

    -- Determine which column to use

    SELECT @Col2Use = CASE (CalcDateType)

    WHEN 1 THEN 'ShipToDiscountDueDate'

    WHEN 2 THEN 'ShipToNetDueDate'

    WHEN 3 THEN 'TermsDiscountDueDate'

    WHEN 4 THEN 'TermsNetDueDate'

    ELSE 'InvoiceDate'

    END

    FROM dbo.RB_Rule

    WHERE RB_RuleId = @RuleId;

    -- Create a temp table to hold all needed data.

    -- Will probably need to modify the PK as we test this.

    CREATE TABLE #Invoice (

    InvoiceKey DECIMAL(18,0),

    ShipToKey DECIMAL(18,0),

    RulePeriodId DECIMAL(18,0),

    MyDateCol DATETIME,

    CategoryNumber NVARCHAR(50),

    TermsDiscountAmount DECIMAL(18,6),

    TermsPercent DECIMAL(18,6),

    IsAssociated AS CASE RulePeriodId

    WHEN -1 THEN 0

    WHEN -2 THEN 0

    WHEN -3 THEN -3

    ELSE 1

    END,

    PRIMARY KEY CLUSTERED (InvoiceKey, CategoryNumber)

    );

    -- Build a string to get the data from the appropriate column

    -- Are you sure that we can't do a UNION ALL here?

    -- Why would you want to process an InvoiceKey / RulePeriodId twice?

    SET @SQLCmd =

    ';WITH cte AS

    (

    SELECT itp.InvoiceKey, itp.RulePeriodId

    FROM dbo.RB_InvoicesToProcess itp

    WHERE itp.RB_RuleId = @RuleId

    UNION

    SELECT itpm.InvoiceKey, itpm.RulePeriodId

    FROM dbo.RB_InvoicesToProcess_Moved itpm

    WHERE itpm.RB_RuleId = @RuleId

    )

    INSERT INTO #Invoice

    (InvoiceKey,

    ShipToKey,

    RulePeriodId,

    MyDateCol,

    CategoryNumber,

    TermsDiscountAmount,

    TermsPercent)

    SELECT cte.InvoiceKey,

    i.ShipToKey,

    cte.RulePeriodId,

    i.' + @Col2Use + ',

    rc.CategoryNumber,

    i.TermsDiscountAmount,

    CASE WHEN i.TermsDiscountAmount = 0 OR @TermsRuleType = 0 THEN 0

    ELSE i.TermsDiscountAmount / CASE WHEN @TermsRuleType = 1 THEN it.TotalInvoiceAmount

    ELSE it.InvoiceSubTotal

    END

    END

    FROM cte

    JOIN dbo.Invoice i -- 1:1

    ON cte.InvoiceKey = i.InvoiceKey

    JOIN dbo.InvoiceTotal it -- 1:1 per invoice

    ON i.InvoiceKey = it.InvoiceKey

    JOIN dbo.RB_RuleCategory rc -- 1:N per supplier/category

    ON i.SupplierKey = rc.SupplierId;

    ';

    -- Execute the string. Pass in the necessary parameters.

    EXEC sp_executesql @SQLCmd, N'@RuleUnitType numeric(9,2),@TermsRuleType numeric(9,2),@RuleId decimal(18,0)', @RuleUnitType, @TermsRuleType, @RuleId;

    WITH cte AS

    (

    -- Get data from the temp table. Combine with aggregate info from other tables.

    SELECT i.InvoiceKey,

    i.ShipToKey,

    i.RulePeriodId,

    i.MyDateCol,

    i.CategoryNumber,

    i.TermsDiscountAmount,

    i.TermsPercent,

    i.IsAssociated,

    UnitsShipped = SUM(ili.UnitsShipped),

    GrossAmount = SUM(ili.GrossAmount),

    NetAmount = SUM(ili.NetAmount),

    TaxUnits = COUNT(itt.InvoiceKey),

    TaxAmount = SUM(itt.TaxAmount),

    AddUnits = COUNT(itac.InvoiceKey),

    AddAmount = SUM(CASE WHEN itac.[Type] = 'C' THEN ABS(itac.Amount) ELSE itac.amount END)

    FROM #Invoice i

    LEFT JOIN dbo.InvoiceLineItem ili

    ON i.InvoiceKey = ili.InvoiceKey

    AND ili.ProductRebateCategory = i.CategoryNumber

    LEFT JOIN dbo.InvoiceTotalTax itt

    ON i.InvoiceKey = itt.InvoiceKey

    AND itt.ProductRebateCategory = i.CategoryNumber

    LEFT JOIN dbo.InvoiceTotalAdditionalCosts itac

    ON i.InvoiceKey = itac.InvoiceKey

    AND itac.ProductRebateCategory = i.CategoryNumber

    GROUP BY i.InvoiceKey, i.ShipToKey, i.RulePeriodId, i.MyDateCol,

    i.CategoryNumber, i.TermsDiscountAmount, i.TermsPercent, i.IsAssociated

    ), cte2 AS

    (

    -- Add the RebateUnits and ChargeUnits columns

    SELECT *,

    RebateUnits = CASE (@RuleUnitType)

    WHEN 1 THEN ISNULL(GrossAmount,0) + ISNULL(TaxAmount,0) -- Gross

    WHEN 2 THEN ISNULL(NetAmount,0) + ISNULL(TaxAmount,0) -- Net

    WHEN 3 THEN ISNULL(UnitsShipped,0) + ISNULL(TaxUnits,0) -- Units

    END,

    ChargeUnits = CASE (@RuleUnitType)

    WHEN 1 THEN ISNULL(AddAmount,0) -- Gross

    WHEN 2 THEN ISNULL(AddAmount,0) -- Net

    WHEN 3 THEN ISNULL(AddUnits,0) -- Units

    END

    FROM cte

    )

    -- Insert everything into the RB_RuleInvoice table

    INSERT INTO dbo.[RB_RuleInvoice]

    ([RB_RuleId],

    [RB_RulePeriodId],

    [InvoiceKey],

    [IsAssociated],

    [ForcastData],

    [RebateUnits],

    [GrossAmount],

    [NetAmount],

    [Units],

    [DealerId],

    [RebateAmount],

    [RebateAmountPaid],

    [RebateAmountActual],

    [ReallocationAmount],

    [ReallocationAmountPaid],

    [ReallocationPaidFlag],

    [PostPeriodAdjustAmount],

    [TotalInvoiceNotPaid],

    [TotalReallocation],

    [PercentPayable])

    SELECT @RuleId,

    CASE WHEN t.RulePeriodId IS NULL OR t.RulePeriodId IN (-1,-3) THEN ISNULL(caAll.RB_RulePeriodId, caNext.RB_RulePeriodId)

    ELSE t.RulePeriodId

    END,

    t.InvoiceKey,

    t.IsAssociated,

    0,

    RebateUnits = ISNULL(CASE WHEN t.TermsDiscountAmount <> 0

    AND @TermsRuleType <> 0

    AND t.TermsPercent <> 0

    THEN t.RebateUnits - (t.ChargeUnits * t.TermsPercent)

    ELSE t.RebateUnits

    END,0),

    TotalGross = IsNull(t.GrossAmount,0) + ISNULL(t.TaxAmount,0) + ISNULL(t.AddAmount,0),

    TotalNet = IsNull(t.NetAmount,0) + ISNULL(t.TaxAmount,0) + ISNULL(t.AddAmount,0),

    TotalUnits = IsNull(t.UnitsShipped,0) + ISNULL(t.TaxUnits,0) + ISNULL(t.AddUnits,0),

    t.ShipToKey,

    0,

    0,

    0,

    0,

    0,

    0,

    0,

    0,

    0,

    0

    FROM cte2 t

    CROSS APPLY (SELECT RB_RulePeriodId

    FROM dbo.RB_RulePeriod

    WHERE RB_RuleId = @RuleId

    AND PeriodStarting <= t.MyDateCol

    AND PeriodEnding >= t.MyDateCol

    AND Reconciled = 0 ) caAll

    CROSS APPLY (SELECT TOP (1)

    RB_RulePeriodId

    FROM dbo.RB_RulePeriod

    WHERE RB_RuleId = @RuleId

    AND PeriodStarting >= t.MyDateCol

    AND Reconciled = 0

    ORDER BY RB_RulePeriodId ASC

    ) caNext;

    Wayne
    Microsoft Certified Master: SQL Server 2008
    Author - SQL Server T-SQL Recipes


    If you can't explain to another person how the code that you're copying from the internet works, then DON'T USE IT on a production system! After all, you will be the one supporting it!
    Links:
    For better assistance in answering your questions
    Performance Problems
    Common date/time routines
    Understanding and Using APPLY Part 1 & Part 2

  • WayneS (6/8/2011)


    Tony,

    Starting over. The main (not single) procedure is totally rewritten to combine the TVF with it. If this works, we'll combine the TVF into the single procedure also.

    Start off by dropping those six indexes created on the Invoice table. We'll re-evaluate what is needed there based upon the execution plan that this creates.

    CREATE PROCEDURE spCalculateRuleInvoiceUnits @RuleUnitType numeric(9,2),@TermsRuleType numeric(9,2),@RuleId decimal(18,0)

    AS

    DECLARE @Col2Use sysname,

    @SQLCmd NVARCHAR(MAX);

    -- Determine which column to use

    SELECT @Col2Use = CASE (CalcDateType)

    WHEN 1 THEN 'ShipToDiscountDueDate'

    WHEN 2 THEN 'ShipToNetDueDate'

    WHEN 3 THEN 'TermsDiscountDueDate'

    WHEN 4 THEN 'TermsNetDueDate'

    ELSE 'InvoiceDate'

    END

    FROM dbo.RB_Rule

    WHERE RB_RuleId = @RuleId;

    -- Create a temp table to hold all needed data.

    -- Will probably need to modify the PK as we test this.

    CREATE TABLE #Invoice (

    InvoiceKey DECIMAL(18,0),

    ShipToKey DECIMAL(18,0),

    RulePeriodId DECIMAL(18,0),

    MyDateCol DATETIME,

    CategoryNumber NVARCHAR(50),

    TermsDiscountAmount DECIMAL(18,6),

    TermsPercent DECIMAL(18,6),

    IsAssociated AS CASE RulePeriodId

    WHEN -1 THEN 0

    WHEN -2 THEN 0

    WHEN -3 THEN -3

    ELSE 1

    END,

    PRIMARY KEY CLUSTERED (InvoiceKey, CategoryNumber)

    );

    -- Build a string to get the data from the appropriate column

    -- Are you sure that we can't do a UNION ALL here?

    -- Why would you want to process an InvoiceKey / RulePeriodId twice?

    SET @SQLCmd =

    ';WITH cte AS

    (

    SELECT itp.InvoiceKey, itp.RulePeriodId

    FROM dbo.RB_InvoicesToProcess itp

    WHERE itp.RB_RuleId = @RuleId

    UNION

    SELECT itpm.InvoiceKey, itpm.RulePeriodId

    FROM dbo.RB_InvoicesToProcess_Moved itpm

    WHERE itpm.RB_RuleId = @RuleId

    )

    INSERT INTO #Invoice

    (InvoiceKey,

    ShipToKey,

    RulePeriodId,

    MyDateCol,

    CategoryNumber,

    TermsDiscountAmount,

    TermsPercent)

    SELECT cte.InvoiceKey,

    i.ShipToKey,

    cte.RulePeriodId,

    i.' + @Col2Use + ',

    rc.CategoryNumber,

    i.TermsDiscountAmount,

    CASE WHEN i.TermsDiscountAmount = 0 OR @TermsRuleType = 0 THEN 0

    ELSE i.TermsDiscountAmount / CASE WHEN @TermsRuleType = 1 THEN it.TotalInvoiceAmount

    ELSE it.InvoiceSubTotal

    END

    END

    FROM cte

    JOIN dbo.Invoice i -- 1:1

    ON cte.InvoiceKey = i.InvoiceKey

    JOIN dbo.InvoiceTotal it -- 1:1 per invoice

    ON i.InvoiceKey = it.InvoiceKey

    JOIN dbo.RB_RuleCategory rc -- 1:N per supplier/category

    ON i.SupplierKey = rc.SupplierId;

    ';

    -- Execute the string. Pass in the necessary parameters.

    EXEC sp_executesql @SQLCmd, N'@RuleUnitType numeric(9,2),@TermsRuleType numeric(9,2),@RuleId decimal(18,0)', @RuleUnitType, @TermsRuleType, @RuleId;

    WITH cte AS

    (

    -- Get data from the temp table. Combine with aggregate info from other tables.

    SELECT i.InvoiceKey,

    i.ShipToKey,

    i.RulePeriodId,

    i.MyDateCol,

    i.CategoryNumber,

    i.TermsDiscountAmount,

    i.TermsPercent,

    i.IsAssociated,

    UnitsShipped = SUM(ili.UnitsShipped),

    GrossAmount = SUM(ili.GrossAmount),

    NetAmount = SUM(ili.NetAmount),

    TaxUnits = COUNT(itt.InvoiceKey),

    TaxAmount = SUM(itt.TaxAmount),

    AddUnits = COUNT(itac.InvoiceKey),

    AddAmount = SUM(CASE WHEN itac.[Type] = 'C' THEN ABS(itac.Amount) ELSE itac.amount END)

    FROM #Invoice i

    LEFT JOIN dbo.InvoiceLineItem ili

    ON i.InvoiceKey = ili.InvoiceKey

    AND ili.ProductRebateCategory = i.CategoryNumber

    LEFT JOIN dbo.InvoiceTotalTax itt

    ON i.InvoiceKey = itt.InvoiceKey

    AND itt.ProductRebateCategory = i.CategoryNumber

    LEFT JOIN dbo.InvoiceTotalAdditionalCosts itac

    ON i.InvoiceKey = itac.InvoiceKey

    AND itac.ProductRebateCategory = i.CategoryNumber

    GROUP BY i.InvoiceKey, i.ShipToKey, i.RulePeriodId, i.MyDateCol,

    i.CategoryNumber, i.TermsDiscountAmount, i.TermsPercent, i.IsAssociated

    ), cte2 AS

    (

    -- Add the RebateUnits and ChargeUnits columns

    SELECT *,

    RebateUnits = CASE (@RuleUnitType)

    WHEN 1 THEN ISNULL(GrossAmount,0) + ISNULL(TaxAmount,0) -- Gross

    WHEN 2 THEN ISNULL(NetAmount,0) + ISNULL(TaxAmount,0) -- Net

    WHEN 3 THEN ISNULL(UnitsShipped,0) + ISNULL(TaxUnits,0) -- Units

    END,

    ChargeUnits = CASE (@RuleUnitType)

    WHEN 1 THEN ISNULL(AddAmount,0) -- Gross

    WHEN 2 THEN ISNULL(AddAmount,0) -- Net

    WHEN 3 THEN ISNULL(AddUnits,0) -- Units

    END

    FROM cte

    )

    -- Insert everything into the RB_RuleInvoice table

    INSERT INTO dbo.[RB_RuleInvoice]

    ([RB_RuleId],

    [RB_RulePeriodId],

    [InvoiceKey],

    [IsAssociated],

    [ForcastData],

    [RebateUnits],

    [GrossAmount],

    [NetAmount],

    [Units],

    [DealerId],

    [RebateAmount],

    [RebateAmountPaid],

    [RebateAmountActual],

    [ReallocationAmount],

    [ReallocationAmountPaid],

    [ReallocationPaidFlag],

    [PostPeriodAdjustAmount],

    [TotalInvoiceNotPaid],

    [TotalReallocation],

    [PercentPayable])

    SELECT @RuleId,

    CASE WHEN t.RulePeriodId IS NULL OR t.RulePeriodId IN (-1,-3) THEN ISNULL(caAll.RB_RulePeriodId, caNext.RB_RulePeriodId)

    ELSE t.RulePeriodId

    END,

    t.InvoiceKey,

    t.IsAssociated,

    0,

    RebateUnits = ISNULL(CASE WHEN t.TermsDiscountAmount <> 0

    AND @TermsRuleType <> 0

    AND t.TermsPercent <> 0

    THEN t.RebateUnits - (t.ChargeUnits * t.TermsPercent)

    ELSE t.RebateUnits

    END,0),

    TotalGross = IsNull(t.GrossAmount,0) + ISNULL(t.TaxAmount,0) + ISNULL(t.AddAmount,0),

    TotalNet = IsNull(t.NetAmount,0) + ISNULL(t.TaxAmount,0) + ISNULL(t.AddAmount,0),

    TotalUnits = IsNull(t.UnitsShipped,0) + ISNULL(t.TaxUnits,0) + ISNULL(t.AddUnits,0),

    t.ShipToKey,

    0,

    0,

    0,

    0,

    0,

    0,

    0,

    0,

    0,

    0

    FROM cte2 t

    CROSS APPLY (SELECT RB_RulePeriodId

    FROM dbo.RB_RulePeriod

    WHERE RB_RuleId = @RuleId

    AND PeriodStarting <= t.MyDateCol

    AND PeriodEnding >= t.MyDateCol

    AND Reconciled = 0 ) caAll

    CROSS APPLY (SELECT TOP (1)

    RB_RulePeriodId

    FROM dbo.RB_RulePeriod

    WHERE RB_RuleId = @RuleId

    AND PeriodStarting >= t.MyDateCol

    AND Reconciled = 0

    ORDER BY RB_RulePeriodId ASC

    ) caNext;

    LOL - you missed the constraint on Rb_Ruleid again -- I added it back in:

    CREATE PROCEDURE spCalculateRuleInvoiceUnits @RuleUnitType numeric(9,2),@TermsRuleType numeric(9,2),@RuleId decimal(18,0)

    AS

    DECLARE @Col2Use sysname,

    @SQLCmd NVARCHAR(MAX);

    -- Determine which column to use

    SELECT @Col2Use = CASE (CalcDateType)

    WHEN 1 THEN 'ShipToDiscountDueDate'

    WHEN 2 THEN 'ShipToNetDueDate'

    WHEN 3 THEN 'TermsDiscountDueDate'

    WHEN 4 THEN 'TermsNetDueDate'

    ELSE 'InvoiceDate'

    END

    FROM dbo.RB_Rule

    WHERE RB_RuleId = @RuleId;

    -- Create a temp table to hold all needed data.

    -- Will probably need to modify the PK as we test this.

    CREATE TABLE #Invoice (

    InvoiceKey DECIMAL(18,0),

    ShipToKey DECIMAL(18,0),

    RulePeriodId DECIMAL(18,0),

    MyDateCol DATETIME,

    CategoryNumber NVARCHAR(50),

    TermsDiscountAmount DECIMAL(18,6),

    TermsPercent DECIMAL(18,6),

    IsAssociated AS CASE RulePeriodId

    WHEN -1 THEN 0

    WHEN -2 THEN 0

    WHEN -3 THEN -3

    ELSE 1

    END,

    PRIMARY KEY CLUSTERED (InvoiceKey, CategoryNumber)

    );

    -- Build a string to get the data from the appropriate column

    -- Are you sure that we can't do a UNION ALL here?

    -- Why would you want to process an InvoiceKey / RulePeriodId twice?

    SET @SQLCmd =

    ';WITH cte AS

    (

    SELECT itp.InvoiceKey, itp.RulePeriodId

    FROM dbo.RB_InvoicesToProcess itp

    WHERE itp.RB_RuleId = @RuleId

    UNION

    SELECT itpm.InvoiceKey, itpm.RulePeriodId

    FROM dbo.RB_InvoicesToProcess_Moved itpm

    WHERE itpm.RB_RuleId = @RuleId

    )

    INSERT INTO #Invoice

    (InvoiceKey,

    ShipToKey,

    RulePeriodId,

    MyDateCol,

    CategoryNumber,

    TermsDiscountAmount,

    TermsPercent)

    SELECT cte.InvoiceKey,

    i.ShipToKey,

    cte.RulePeriodId,

    i.' + @Col2Use + ',

    rc.CategoryNumber,

    i.TermsDiscountAmount,

    CASE WHEN i.TermsDiscountAmount = 0 OR @TermsRuleType = 0 THEN 0

    ELSE i.TermsDiscountAmount / CASE WHEN @TermsRuleType = 1 THEN it.TotalInvoiceAmount

    ELSE it.InvoiceSubTotal

    END

    END

    FROM cte

    JOIN dbo.Invoice i -- 1:1

    ON cte.InvoiceKey = i.InvoiceKey

    JOIN dbo.InvoiceTotal it -- 1:1 per invoice

    ON i.InvoiceKey = it.InvoiceKey

    JOIN dbo.RB_RuleCategory rc -- 1:N per supplier/category

    ON i.SupplierKey = rc.SupplierId

    where rc.rb_ruleid = @RuleId;

    ';

    -- Execute the string. Pass in the necessary parameters.

    print @SQLCmd

    EXEC sp_executesql @SQLCmd, N'@RuleUnitType numeric(9,2),@TermsRuleType numeric(9,2),@RuleId decimal(18,0)', @RuleUnitType, @TermsRuleType, @RuleId;

    WITH cte AS

    (

    -- Get data from the temp table. Combine with aggregate info from other tables.

    SELECT i.InvoiceKey,

    i.ShipToKey,

    i.RulePeriodId,

    i.MyDateCol,

    i.CategoryNumber,

    i.TermsDiscountAmount,

    i.TermsPercent,

    i.IsAssociated,

    UnitsShipped = SUM(ili.UnitsShipped),

    GrossAmount = SUM(ili.GrossAmount),

    NetAmount = SUM(ili.NetAmount),

    TaxUnits = COUNT(itt.InvoiceKey),

    TaxAmount = SUM(itt.TaxAmount),

    AddUnits = COUNT(itac.InvoiceKey),

    AddAmount = SUM(CASE WHEN itac.[Type] = 'C' THEN ABS(itac.Amount) ELSE itac.amount END)

    FROM #Invoice i

    LEFT JOIN dbo.InvoiceLineItem ili

    ON i.InvoiceKey = ili.InvoiceKey

    AND ili.ProductRebateCategory = i.CategoryNumber

    LEFT JOIN dbo.InvoiceTotalTax itt

    ON i.InvoiceKey = itt.InvoiceKey

    AND itt.ProductRebateCategory = i.CategoryNumber

    LEFT JOIN dbo.InvoiceTotalAdditionalCosts itac

    ON i.InvoiceKey = itac.InvoiceKey

    AND itac.ProductRebateCategory = i.CategoryNumber

    GROUP BY i.InvoiceKey, i.ShipToKey, i.RulePeriodId, i.MyDateCol,

    i.CategoryNumber, i.TermsDiscountAmount, i.TermsPercent, i.IsAssociated

    ), cte2 AS

    (

    -- Add the RebateUnits and ChargeUnits columns

    SELECT *,

    RebateUnits = CASE (@RuleUnitType)

    WHEN 1 THEN ISNULL(GrossAmount,0) + ISNULL(TaxAmount,0) -- Gross

    WHEN 2 THEN ISNULL(NetAmount,0) + ISNULL(TaxAmount,0) -- Net

    WHEN 3 THEN ISNULL(UnitsShipped,0) + ISNULL(TaxUnits,0) -- Units

    END,

    ChargeUnits = CASE (@RuleUnitType)

    WHEN 1 THEN ISNULL(AddAmount,0) -- Gross

    WHEN 2 THEN ISNULL(AddAmount,0) -- Net

    WHEN 3 THEN ISNULL(AddUnits,0) -- Units

    END

    FROM cte

    )

    -- Insert everything into the RB_RuleInvoice table

    INSERT INTO dbo.[RB_RuleInvoice]

    ([RB_RuleId],

    [RB_RulePeriodId],

    [InvoiceKey],

    [IsAssociated],

    [ForcastData],

    [RebateUnits],

    [GrossAmount],

    [NetAmount],

    [Units],

    [DealerId],

    [RebateAmount],

    [RebateAmountPaid],

    [RebateAmountActual],

    [ReallocationAmount],

    [ReallocationAmountPaid],

    [ReallocationPaidFlag],

    [PostPeriodAdjustAmount],

    [TotalInvoiceNotPaid],

    [TotalReallocation],

    [PercentPayable])

    SELECT @RuleId,

    CASE WHEN t.RulePeriodId IS NULL OR t.RulePeriodId IN (-1,-3) THEN ISNULL(caAll.RB_RulePeriodId, caNext.RB_RulePeriodId)

    ELSE t.RulePeriodId

    END,

    t.InvoiceKey,

    t.IsAssociated,

    0,

    RebateUnits = ISNULL(CASE WHEN t.TermsDiscountAmount <> 0

    AND @TermsRuleType <> 0

    AND t.TermsPercent <> 0

    THEN t.RebateUnits - (t.ChargeUnits * t.TermsPercent)

    ELSE t.RebateUnits

    END,0),

    TotalGross = IsNull(t.GrossAmount,0) + ISNULL(t.TaxAmount,0) + ISNULL(t.AddAmount,0),

    TotalNet = IsNull(t.NetAmount,0) + ISNULL(t.TaxAmount,0) + ISNULL(t.AddAmount,0),

    TotalUnits = IsNull(t.UnitsShipped,0) + ISNULL(t.TaxUnits,0) + ISNULL(t.AddUnits,0),

    t.ShipToKey,

    0,

    0,

    0,

    0,

    0,

    0,

    0,

    0,

    0,

    0

    FROM cte2 t

    CROSS APPLY (SELECT RB_RulePeriodId

    FROM dbo.RB_RulePeriod

    WHERE RB_RuleId = @RuleId

    AND PeriodStarting <= t.MyDateCol

    AND PeriodEnding >= t.MyDateCol

    AND Reconciled = 0 ) caAll

    CROSS APPLY (SELECT TOP (1)

    RB_RulePeriodId

    FROM dbo.RB_RulePeriod

    WHERE RB_RuleId = @RuleId

    AND PeriodStarting >= t.MyDateCol

    AND Reconciled = 0

    ORDER BY RB_RulePeriodId ASC

    ) caNext;

    We are back to 14-16 seconds with the indexes removed...

    SQL Server parse and compile time:

    CPU time = 0 ms, elapsed time = 0 ms.

    SQL Server Execution Times:

    CPU time = 0 ms, elapsed time = 0 ms.

    SQL Server parse and compile time:

    CPU time = 0 ms, elapsed time = 0 ms.

    SQL Server Execution Times:

    CPU time = 0 ms, elapsed time = 0 ms.

    SQL Server parse and compile time:

    CPU time = 0 ms, elapsed time = 0 ms.

    SQL Server parse and compile time:

    CPU time = 0 ms, elapsed time = 0 ms.

    Table 'RB_Rule'. Scan count 1, logical reads 1, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.

    (1 row(s) affected)

    SQL Server Execution Times:

    CPU time = 0 ms, elapsed time = 0 ms.

    SQL Server Execution Times:

    CPU time = 0 ms, elapsed time = 0 ms.

    SQL Server Execution Times:

    CPU time = 0 ms, elapsed time = 0 ms.

    ;WITH cte AS

    (

    SELECT itp.InvoiceKey, itp.RulePeriodId

    FROM dbo.RB_InvoicesToProcess itp

    WHERE itp.RB_RuleId = @RuleId

    UNION

    SELECT itpm.InvoiceKey, itpm.RulePeriodId

    FROM dbo.RB_InvoicesToProcess_Moved itpm

    WHERE itpm.RB_RuleId = @RuleId

    )

    INSERT INTO #Invoice

    (InvoiceKey,

    ShipToKey,

    RulePeriodId,

    MyDateCol,

    CategoryNumber,

    TermsDiscountAmount,

    TermsPercent)

    SELECT cte.InvoiceKey,

    i.ShipToKey,

    cte.RulePeriodId,

    i.InvoiceDate,

    rc.CategoryNumber,

    i.TermsDiscountAmount,

    CASE WHEN i.TermsDiscountAmount = 0 OR @TermsRuleType = 0 THEN 0

    ELSE i.TermsDiscountAmount / CASE WHEN @TermsRuleType = 1 THEN it.TotalInvoiceAmount

    ELSE it.InvoiceSubTotal

    END

    END

    FROM cte

    JOIN dbo.Invoice i -- 1:1

    ON cte.InvoiceKey = i.InvoiceKey

    JOIN dbo.InvoiceTotal it -- 1:1 per invoice

    ON i.InvoiceKey = it.InvoiceKey

    JOIN dbo.RB_RuleCategory rc -- 1:N per supplier/category

    ON i.SupplierKey = rc.SupplierId

    where rc.rb_ruleid = @RuleId;

    SQL Server Execution Times:

    CPU time = 0 ms, elapsed time = 0 ms.

    SQL Server parse and compile time:

    CPU time = 0 ms, elapsed time = 9 ms.

    Table 'RB_RuleCategory'. Scan count 1, logical reads 2, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.

    Table 'RB_InvoicesToProcess'. Scan count 3, logical reads 1622, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.

    Table 'RB_InvoicesToProcess_Moved'. Scan count 1, logical reads 0, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.

    Table 'Worktable'. Scan count 0, logical reads 0, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.

    Table 'InvoiceTotal'. Scan count 3, logical reads 802, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.

    Table 'Invoice'. Scan count 3, logical reads 51162, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.

    Table 'Worktable'. Scan count 0, logical reads 0, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.

    (79432 row(s) affected)

    (1 row(s) affected)

    SQL Server Execution Times:

    CPU time = 3045 ms, elapsed time = 1683 ms.

    SQL Server Execution Times:

    CPU time = 3530 ms, elapsed time = 2174 ms.

    Table 'InvoiceTotalTax'. Scan count 1, logical reads 0, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.

    Table '#Invoice____________________________________________________________________________________________________________00000000065F'. Scan count 3, logical reads 785, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.

    Table 'InvoiceTotalAdditionalCosts'. Scan count 79432, logical reads 253242, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.

    Table 'InvoiceLineItem'. Scan count 3, logical reads 8698, physical reads 0, read-ahead reads 112, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.

    Table 'Worktable'. Scan count 5, logical reads 35, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.

    Table 'RB_RulePeriod'. Scan count 6, logical reads 6, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.

    Table 'Worktable'. Scan count 0, logical reads 0, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.

    Table 'RB_RuleInvoice'. Scan count 0, logical reads 81317, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.

    Warning: Null value is eliminated by an aggregate or other SET operation.

    (79432 row(s) affected)

    (1 row(s) affected)

    SQL Server Execution Times:

    CPU time = 20471 ms, elapsed time = 12635 ms.

    SQL Server Execution Times:

    CPU time = 24001 ms, elapsed time = 14814 ms.

    SQL Server parse and compile time:

    CPU time = 0 ms, elapsed time = 0 ms.

    SQL Server Execution Times:

    CPU time = 0 ms, elapsed time = 0 ms.

  • tfeuz (6/8/2011)


    LOL - you missed the constraint on Rb_Ruleid again -- I added it back in:

    Just making sure you're looking at the code as a sanity check! :w00t:

    Does this index improve anything?

    CREATE INDEX IX_1

    ON dbo.RB_RulePeriod (RB_RuleId, Reconciled, PeriodStarting, PeriodEnding);

    Wayne
    Microsoft Certified Master: SQL Server 2008
    Author - SQL Server T-SQL Recipes


    If you can't explain to another person how the code that you're copying from the internet works, then DON'T USE IT on a production system! After all, you will be the one supporting it!
    Links:
    For better assistance in answering your questions
    Performance Problems
    Common date/time routines
    Understanding and Using APPLY Part 1 & Part 2

  • WayneS (6/8/2011)


    tfeuz (6/8/2011)


    LOL - you missed the constraint on Rb_Ruleid again -- I added it back in:

    Just making sure you're looking at the code as a sanity check! :w00t:

    Does this index improve anything?

    CREATE INDEX IX_1

    ON dbo.RB_RulePeriod (RB_RuleId, Reconciled, PeriodStarting, PeriodEnding);

    15 seconds...

    SQL Server parse and compile time:

    CPU time = 0 ms, elapsed time = 0 ms.

    SQL Server parse and compile time:

    CPU time = 0 ms, elapsed time = 0 ms.

    Table 'RB_Rule'. Scan count 1, logical reads 1, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.

    (1 row(s) affected)

    SQL Server Execution Times:

    CPU time = 0 ms, elapsed time = 1 ms.

    SQL Server Execution Times:

    CPU time = 0 ms, elapsed time = 0 ms.

    SQL Server Execution Times:

    CPU time = 0 ms, elapsed time = 0 ms.

    ;WITH cte AS

    (

    SELECT itp.InvoiceKey, itp.RulePeriodId

    FROM dbo.RB_InvoicesToProcess itp

    WHERE itp.RB_RuleId = @RuleId

    UNION

    SELECT itpm.InvoiceKey, itpm.RulePeriodId

    FROM dbo.RB_InvoicesToProcess_Moved itpm

    WHERE itpm.RB_RuleId = @RuleId

    )

    INSERT INTO #Invoice

    (InvoiceKey,

    ShipToKey,

    RulePeriodId,

    MyDateCol,

    CategoryNumber,

    TermsDiscountAmount,

    TermsPercent)

    SELECT cte.InvoiceKey,

    i.ShipToKey,

    cte.RulePeriodId,

    i.InvoiceDate,

    rc.CategoryNumber,

    i.TermsDiscountAmount,

    CASE WHEN i.TermsDiscountAmount = 0 OR @TermsRuleType = 0 THEN 0

    ELSE i.TermsDiscountAmount / CASE WHEN @TermsRuleType = 1 THEN it.TotalInvoiceAmount

    ELSE it.InvoiceSubTotal

    END

    END

    FROM cte

    JOIN dbo.Invoice i -- 1:1

    ON cte.InvoiceKey = i.InvoiceKey

    JOIN dbo.InvoiceTotal it -- 1:1 per invoice

    ON i.InvoiceKey = it.InvoiceKey

    JOIN dbo.RB_RuleCategory rc -- 1:N per supplier/category

    ON i.SupplierKey = rc.SupplierId

    where rc.rb_ruleid = @RuleId;

    SQL Server Execution Times:

    CPU time = 0 ms, elapsed time = 0 ms.

    SQL Server parse and compile time:

    CPU time = 487 ms, elapsed time = 487 ms.

    Table 'RB_RuleCategory'. Scan count 1, logical reads 2, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.

    Table 'RB_InvoicesToProcess'. Scan count 3, logical reads 1622, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.

    Table 'RB_InvoicesToProcess_Moved'. Scan count 1, logical reads 0, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.

    Table 'Worktable'. Scan count 0, logical reads 0, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.

    Table 'InvoiceTotal'. Scan count 3, logical reads 802, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.

    Table 'Invoice'. Scan count 3, logical reads 51162, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.

    Table 'Worktable'. Scan count 0, logical reads 0, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.

    (79432 row(s) affected)

    (1 row(s) affected)

    SQL Server Execution Times:

    CPU time = 3171 ms, elapsed time = 1721 ms.

    SQL Server Execution Times:

    CPU time = 3671 ms, elapsed time = 2209 ms.

    Table 'InvoiceTotalTax'. Scan count 1, logical reads 0, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.

    Table '#Invoice____________________________________________________________________________________________________________00000000065F'. Scan count 3, logical reads 785, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.

    Table 'InvoiceTotalAdditionalCosts'. Scan count 79432, logical reads 253242, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.

    Table 'InvoiceLineItem'. Scan count 3, logical reads 8698, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.

    Table 'Worktable'. Scan count 5, logical reads 35, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.

    Table 'RB_RulePeriod'. Scan count 6, logical reads 6, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.

    Table 'Worktable'. Scan count 0, logical reads 0, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.

    Table 'RB_RuleInvoice'. Scan count 0, logical reads 81317, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.

    Warning: Null value is eliminated by an aggregate or other SET operation.

    (79432 row(s) affected)

    (1 row(s) affected)

    SQL Server Execution Times:

    CPU time = 20531 ms, elapsed time = 12129 ms.

    SQL Server Execution Times:

    CPU time = 25499 ms, elapsed time = 15351 ms.

    SQL Server parse and compile time:

    CPU time = 0 ms, elapsed time = 0 ms.

    SQL Server Execution Times:

    CPU time = 0 ms, elapsed time = 0 ms.

    Wayne -- just a quick note -- I am currently working on another query in an attempt to optimize. Following your examples you have laid out here is a tremendous help!

  • Tony,

    Well, I can't think of any other way to optimize this. I think that this leaves you with two choices:

    1. Use the iTVF. Pros: same code is used by two procs; just as fast. Cons: 6 new indexes, with ramifications they incur on insert/update/delete statements.

    2. Use this last method. Pros: just as fast, no new indexes. Cons: Duplicate functionality in two procedures.

    It comes down to: Indexes, and code reuse. Or, no indexes and duplicate code.

    I don't know your system or data. If the indexes are not an issue, I'd probably go with the iTVF method, just to keep that code in one place. (Plus, the two code pieces don't even look alike - they would each require consideration to implement changes.)

    Glad to hear that you've been able to take some of this and apply it to other code.... THAT is why I do this (to teach others, so that they can do it themselves!)

    Wayne
    Microsoft Certified Master: SQL Server 2008
    Author - SQL Server T-SQL Recipes


    If you can't explain to another person how the code that you're copying from the internet works, then DON'T USE IT on a production system! After all, you will be the one supporting it!
    Links:
    For better assistance in answering your questions
    Performance Problems
    Common date/time routines
    Understanding and Using APPLY Part 1 & Part 2

  • WayneS (6/7/2011)


    TheSQLGuru (6/7/2011)


    I was giving my own presentation @ sqlsat77 while yours was in progress. 😀

    Ah, so you were. I heard it went pretty well.

    As long as they have fairly uniform distribution of values the table var should be ok I suppose. Still bet it will be more efficient without the PK though.

    My money's on the optimal solution having no spools, probably 1-2 intermediary temp tables, no CTEs and likely a hash join or two. Lotta work to get from here to there though.

    Right now, I have the TVF because there are two procedures that call it - one for just getting a single result set, and the other is this monster to get all of the unprocessed invoices. If I'm reading you correctly, you're suggesting to merge the TVF into the calling procedure - which would end up with duplicate code in multiple procedures. Am I reading you correctly?

    You've mentioned a couple of times about getting rid of the CTEs. I'm curious as to the rational for this... they are effectively just a sub-query, and they are only being called once. The only way I can see this improving things is in combination with the above part of merging the code into its own procedure for the massive run. Then I can see something like this. Am I missing something here?

    I admire you for taking this one on (and for sticking with it). Its unquestionably a paid gig IMHO.

    Thanks. And it true. But, earlier in the thread, I badgered Tony to get the DDL/sample data posted so that we could help. Once he did so, I sorta felt obligated to stick around and see it through.

    Like I said - I believe the optimal solution will involve some intermediate temp tables to help the optimizer out. Too much going on for it to have a good chance at success, certainly for widely disparate inputs.

    CTEs can lead people to do that too - put too much into one query. It is my belief that people can wrap more up into a single CTE-based query than they could without CTEs and thus they get overly complex queries where the optimization proccess isn't successful. Without the CTE construct they would often be 'forced' into the best solution - which could well be intermediary temp tables. Like you mentioned CTEs if referenced more than once (which can be done unintentionally with multi-level queries) cause multiple instantiations of the result. It is my belief they are also often coded to lead to spooling, although I have no empiracle evidence to support that belief. 🙂

    Best,
    Kevin G. Boles
    SQL Server Consultant
    SQL MVP 2007-2012
    TheSQLGuru on googles mail service

  • 1) try queries with OPTION (MAXDOP 1), just for posterity's sake

    2) if this query can't possibly result in duplicates, use UNION ALL instead. that will avoid a SORT/DISTINCT operation:

    SELECT itp.InvoiceKey, itp.RulePeriodId

    FROM dbo.RB_InvoicesToProcess itp

    WHERE itp.RB_RuleId = @RuleId

    UNION

    SELECT itpm.InvoiceKey, itpm.RulePeriodId

    FROM dbo.RB_InvoicesToProcess_Moved itpm

    WHERE itpm.RB_RuleId = @RuleId

    3) Try removing the clustered PK from the temp table (unless you REALLY want to ensure uniqueness and throw an error if you don't have it)

    4) Did you guys ever find a useful index for the RB_RulePeriod table?

    Best,
    Kevin G. Boles
    SQL Server Consultant
    SQL MVP 2007-2012
    TheSQLGuru on googles mail service

Viewing 13 posts - 106 through 117 (of 117 total)

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