Another Crappy Cursor

  • This cursor has given me headaches since day 1. It was tolerable in sql2000 because it was a batch process and ran overnight. However in 2005 the performance has gone so far south of tolerable that you can't see it (~3 minutes to ~8 hours!!!). And this isn't the largest recordset (233000 rows) it processes. It joins 2 tables then runs cumulative updates on 1 table based on results from both. If the item number changes, it resets the cum total. Reading on here, it looks like maybe a candidate for using a tally table, but I can't quite get it.

    Here's the cursor:

    set nocount on

    Declare @Record_Noas int

    Declare @CumPlnQtyas float

    Declare @mRqmntsas float

    Declare @mCIDas float

    Declare @CumCumInDemandas float

    Declare @IOHandNetas float

    Declare @IOHoldNetas float

    Declare @Countas integer

    Declare @TTypeas varchar(30)

    Declare @ChkItemas varchar(47)

    Declare @CumItemas varchar(47)

    Declare @Update1_Ctras integer

    Declare @Update2_Ctras integer

    Declare CumInDemand_cursorCURSOR FORWARD_ONLY FOR

    SELECT b.Record_No

    , a.InvOnHoldNet

    , a.InvOnHandNet

    , b.Item

    , b.PlnQty

    , b.CumInDemand

    From ETPTables903.dbo.tInvMove b

    , ETPTables903.dbo.tItemMaster a

    Where b.item = a.item

    and (b.TType <> "+ (Planned Receipt)" or b.ttype is null)

    Order by b.item

    , b.TDt

    , b.TType

    , b.fOrder

    FOR UPDATE OF CumInDemand

    Set @ChkItem = "0000000000000000"

    Set @Count = 0

    Set @Update1_Ctr = 0

    Set @Update2_Ctr = 0

    OPEN CumInDemand_cursor

    FETCH NEXT FROM CumInDemand_cursor INTO @Record_No, @IOHoldNet, @IOHandNet, @CumItem, @CumPlnQty, @CumCumInDemand

    SELECT @@fetch_status

    WHILE @@fetch_status = 0

    BEGIN

    IF @ChkItem <> @CumItem

    BEGIN

    Set @ChkItem = @CumItem

    Set @mCid = @IOHandNet - @IOHoldNet

    END

    Set @mCID = @mCID - @CumPlnQty

    If @mCID >= 0

    BEGIN

    Update ETPTables903.dbo.tInvMove

    Set CumInDemand = 0 Where current of CumInDemand_cursor

    Set @Update1_Ctr = @Update1_Ctr + 1

    END

    Else

    BEGIN

    Update ETPTables903.dbo.tInvMove

    Set CumInDemand = @mCID * (-1) Where current of CumInDemand_cursor

    Set @Update2_Ctr = @Update2_Ctr + 1

    END

    FETCH NEXT FROM CumInDemand_cursor INTO @Record_No, @IOHoldNet, @IOHandNet, @CumItem, @CumPlnQty, @CumCumInDemand

    SELECT @@fetch_status

    SET @Count = @Count + 1

    CONTINUE

    END

    CLOSE CumInDemand_cursor

    DEALLOCATE CumInDemand_cursor

    Here's some data (actually the results from running it):

    Record_NoInvOnHoldNetInvOnHandNetItemPlnQtyCumInDemand

    20040154JV10

    21040154JV10

    22040154JV10

    23040154JV10

    24040154JV11

    25040154JV12

    26040154JV13

    27040154JV14

    48020411604-1400-901500

    49020411604-1400-901500

    50020411604-1400-901500

    51020411604-1400-901500

    52020411604-1400-9015046

    53020411604-1400-9015096

    54020411604-1400-90150146

    55020411604-1400-90150196

    56020411604-1400-90150246

    57020411604-1400-90150296

    58020411604-1400-90150346

    69020411604-1400-9014350

    Anybody got a clue?

  • Please put your data into a form that we can use for tests (like INSERT statements). Here is a link that explains this:http://www.sqlservercentral.com/articles/Best+Practices/61537/

    [font="Times New Roman"]-- RBarryYoung[/font], [font="Times New Roman"] (302)375-0451[/font] blog: MovingSQL.com, Twitter: @RBarryYoung[font="Arial Black"]
    Proactive Performance Solutions, Inc.
    [/font]
    [font="Verdana"] "Performance is our middle name."[/font]

  • I'll second that. In fact, it would be good if you'd include sample data from before and after your cursor runs. This seems like a pretty straight forward re-write, but it'll help if you get the prep work ready.

    John Rowan

    ======================================================
    ======================================================
    Forum Etiquette: How to post data/code on a forum to get the best help[/url] - by Jeff Moden

  • Also, this bit here:

    ...

    SELECT @@fetch_status

    WHILE @@fetch_status = 0

    BEGIN

    IF @ChkItem <> @CumItem

    BEGIN

    Set @ChkItem = @CumItem

    Set @mCid = @IOHandNet - @IOHoldNet

    END

    ...

    Is this just to avoid redundantly recalculating the same @ChkItem and @mCid items for every @CumItem group or do you really need to use only the first values in the group?

    [font="Times New Roman"]-- RBarryYoung[/font], [font="Times New Roman"] (302)375-0451[/font] blog: MovingSQL.com, Twitter: @RBarryYoung[font="Arial Black"]
    Proactive Performance Solutions, Inc.
    [/font]
    [font="Verdana"] "Performance is our middle name."[/font]

  • OK, no answers yet, but this is what I have come up with:

    --====== Load up the working table

    SELECT b.Record_No

    , b.Item

    , b.PlnQty

    , Rank() Over(Order by b.Item) as ChkItem

    , Row_Number() Over(Partition by b.Item Order by b.TDt, b.TType, b.fOrder) as CumItemNo

    , (a.InvOnHandNet - a.InvOnHoldNet) as Diff1

    , Cast(0 as Float) as mCID

    INTO #tmpInvItem

    From ETPTables903.dbo.tInvMove b

    , ETPTables903.dbo.tItemMaster a

    Where b.item = a.item

    and (b.TType <> "+ (Planned Receipt)" or b.ttype is null)

    Order by b.item, b.TDt, b.TType, b.fOrder

    --======Add a Primary Key/Clustered Index for speed & order

    Alter Table #tmpInvItem

    ADD CONSTRAINT PK_tmpInvItem PRIMARY KEY CLUSTERED (ChkItem, CumItemNo)

    --======Calculate the mCID (CumInDemand) values

    Update t1

    Set mCID = Diff1 - (Select SUM(t2.PlnQty) from #tmpInvItem t2

    Where t2.ChkItem = t1.ChkItem

    And t2.CumItemNo >= 1

    And t2.CumItemNo < t1.CumItemNo)

    From #tmpInvItem t1

    --======Push it back into the source table

    Update ETPTables903.dbo.tInvMove

    Set CumInDemand = (Case When mCID >=0 Then 0 Else -1 * mCID End)

    , @Update1_Ctr = @Update1_Ctr + (Case When mCID >=0 Then 1 Else 0 End)

    , @Update2_Ctr = @Update2_Ctr + (Case When mCID >=0 Then 0 Else 1 End)

    , @Count = @Count + 1

    From ETPTables903.dbo.tInvMove IM

    Join #tmpInvItem TI on

    TI.Record_No = IM.Record_No

    And TI.item = IM.item

    Where IM.TType <> "+ (Planned Receipt)" or IM.ttype is null)

    It definitely could be consolidated some, but it should be much faster than what you currently have.

    [font="Times New Roman"]-- RBarryYoung[/font], [font="Times New Roman"] (302)375-0451[/font] blog: MovingSQL.com, Twitter: @RBarryYoung[font="Arial Black"]
    Proactive Performance Solutions, Inc.
    [/font]
    [font="Verdana"] "Performance is our middle name."[/font]

  • If you really want some speed out of this, consider the following article (Grouped Running Total and Running Total sections), please... should be able to do 223,000 rows in about 2 seconds...

    http://www.sqlservercentral.com/articles/Advanced+Querying/61716/

    --Jeff Moden


    RBAR is pronounced "ree-bar" and is a "Modenism" for Row-By-Agonizing-Row.
    First step towards the paradigm shift of writing Set Based code:
    ________Stop thinking about what you want to do to a ROW... think, instead, of what you want to do to a COLUMN.

    Change is inevitable... Change for the better is not.


    Helpful Links:
    How to post code problems
    How to Post Performance Problems
    Create a Tally Function (fnTally)

  • Actually, Jeff, it's not that different from what I am already doing, except that it is dependent on an Index that may not exist.

    [font="Times New Roman"]-- RBarryYoung[/font], [font="Times New Roman"] (302)375-0451[/font] blog: MovingSQL.com, Twitter: @RBarryYoung[font="Arial Black"]
    Proactive Performance Solutions, Inc.
    [/font]
    [font="Verdana"] "Performance is our middle name."[/font]

  • Oops, wait a minute, I am getting my threads mixed up... Never mind.

    [font="Times New Roman"]-- RBarryYoung[/font], [font="Times New Roman"] (302)375-0451[/font] blog: MovingSQL.com, Twitter: @RBarryYoung[font="Arial Black"]
    Proactive Performance Solutions, Inc.
    [/font]
    [font="Verdana"] "Performance is our middle name."[/font]

  • Ok, my bad. I was in a hurry last night, and didn't get everything in the shape it should have been. Here's a full script end to end that will build the tables, load the data, process it, and display the final results, including a before vs. after view of the CumInDemand field. Thanks for taking a look and making suggestions.

    set nocount on

    --Set up tables

    IF OBJECT_ID('tInvMove','U') IS NOT NULL

    DROP TABLE [tInvMove]

    IF OBJECT_ID('tItemMaster','U') IS NOT NULL

    DROP TABLE [tItemMaster]

    IF OBJECT_ID('BEFORE','U') IS NOT NULL

    DROP TABLE [BEFORE]

    IF OBJECT_ID('AFTER','U') IS NOT NULL

    DROP TABLE [AFTER]

    /****** Object: Table [dbo].[tInvMove] Script Date: 05/01/2008 09:38:56 ******/

    SET ANSI_NULLS ON

    GO

    SET QUOTED_IDENTIFIER ON

    GO

    SET ANSI_PADDING ON

    GO

    CREATE TABLE [dbo].[tInvMove](

    [Record_No] [int] IDENTITY(1,1) NOT NULL,

    [Item] [varchar](47) NULL,

    [TDt] [datetime] NULL,

    [TType] [varchar](30) NULL,

    [fOrder] [varchar](9) NULL,

    [PlnQty] [float] NULL CONSTRAINT [DF_tInvMove_PlnQty] DEFAULT (0),

    [CumInDemand] [float] NULL CONSTRAINT [DF_tInvMove_CumInDemand] DEFAULT (0),

    CONSTRAINT [PK_tInvMove] PRIMARY KEY NONCLUSTERED

    (

    [Record_No] ASC

    )WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]

    ) ON [PRIMARY]

    /****** Object: Table [dbo].[tItemMaster] Script Date: 05/01/2008 09:43:57 ******/

    SET ANSI_NULLS ON

    GO

    SET QUOTED_IDENTIFIER ON

    GO

    SET ANSI_PADDING ON

    GO

    CREATE TABLE [dbo].[tItemMaster](

    [Item] [varchar](47) NOT NULL,

    [InvOnHoldNet] [float] NULL,

    [InvOnHandNet] [float] NULL,

    CONSTRAINT [PK_tItemMaster] PRIMARY KEY NONCLUSTERED

    (

    [Item] ASC

    )WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]

    ) ON [PRIMARY]

    GO

    SET ANSI_PADDING OFF

    --Load Sample Data

    TRUNCATE TABLE [tItemMaster]

    INSERT INTO [tItemMaster]

    ([Item]

    ,[InvOnHoldNet]

    ,[InvOnHandNet])

    SELECT '0154JV',0,4 UNION ALL

    SELECT '11604-1400-901',0,221

    TRUNCATE TABLE [tInvMove]

    SET IDENTITY_INSERT [tInvMove] ON

    INSERT INTO [tInvMove]

    ([Record_No]

    ,[Item]

    ,[TDt]

    ,[TType]

    ,[fOrder]

    ,[PlnQty]

    ,[CumInDemand])

    SELECT 20,'0154JV','Oct 24 2008 5:00AM','- (Mat.Requirement)','B00087533',1,0 UNION ALL

    SELECT 21,'0154JV','Oct 28 2008 5:00AM','- (Mat.Requirement)','B00087541',1,0 UNION ALL

    SELECT 22,'0154JV','Dec 18 2008 6:00AM','- (Mat.Requirement)','B00091515',1,0 UNION ALL

    SELECT 23,'0154JV','Dec 22 2008 6:00AM','- (Mat.Requirement)','B00091523',1,0 UNION ALL

    SELECT 24,'0154JV','Jan 23 2009 8:00AM','Assembly Part Demand','0',1,1 UNION ALL

    SELECT 25,'0154JV','Jan 30 2009 8:00AM','Assembly Part Demand','0',1,2 UNION ALL

    SELECT 26,'0154JV','Feb 13 2009 8:00AM','Assembly Part Demand','0',1,3 UNION ALL

    SELECT 27,'0154JV','Feb 20 2009 8:00AM','Assembly Part Demand','0',1,4 UNION ALL

    SELECT 48,'11604-1400-901','Apr 14 2008 12:00AM','- (Mat.Requirement)','100359378',50,0 UNION ALL

    SELECT 49,'11604-1400-901','Apr 19 2008 12:00AM','- (Mat.Requirement)','100360031',50,0 UNION ALL

    SELECT 50,'11604-1400-901','Apr 19 2008 12:00AM','- (Mat.Requirement)','100360032',50,0 UNION ALL

    SELECT 51,'11604-1400-901','Apr 19 2008 12:00AM','- (Mat.Requirement)','100360033',50,0 UNION ALL

    SELECT 52,'11604-1400-901','Apr 19 2008 12:00AM','- (Mat.Requirement)','100360034',50,46 UNION ALL

    SELECT 53,'11604-1400-901','Apr 19 2008 12:00AM','- (Mat.Requirement)','100360035',50,96 UNION ALL

    SELECT 54,'11604-1400-901','Apr 26 2008 12:00AM','- (Mat.Requirement)','100361123',50,146 UNION ALL

    SELECT 55,'11604-1400-901','Apr 26 2008 12:00AM','- (Mat.Requirement)','100361124',50,196 UNION ALL

    SELECT 56,'11604-1400-901','Apr 26 2008 12:00AM','- (Mat.Requirement)','100361125',50,246 UNION ALL

    SELECT 57,'11604-1400-901','Apr 26 2008 12:00AM','- (Mat.Requirement)','100361126',50,296 UNION ALL

    SELECT 58,'11604-1400-901','Apr 26 2008 12:00AM','- (Mat.Requirement)','100361127',50,346 UNION ALL

    SELECT 59,'11604-1400-901','Jun 4 2008 12:00AM','- (Mat.Requirement)','100362743',50,1143 UNION ALL

    SELECT 60,'11604-1400-901','Jun 5 2008 12:00AM','- (Mat.Requirement)','100362744',50,1257 UNION ALL

    SELECT 61,'11604-1400-901','Jun 6 2008 12:00AM','- (Mat.Requirement)','100362745',50,1363 UNION ALL

    SELECT 62,'11604-1400-901','Jun 9 2008 12:00AM','- (Mat.Requirement)','100362746',50,1465 UNION ALL

    SELECT 63,'11604-1400-901','Jun 10 2008 12:00AM','- (Mat.Requirement)','100362747',50,1576 UNION ALL

    SELECT 64,'11604-1400-901','Jun 11 2008 12:00AM','- (Mat.Requirement)','100363798',50,1703 UNION ALL

    SELECT 65,'11604-1400-901','Jun 12 2008 12:00AM','- (Mat.Requirement)','100363799',50,1805 UNION ALL

    SELECT 66,'11604-1400-901','Jun 13 2008 12:00AM','- (Mat.Requirement)','100363800',50,1911 UNION ALL

    SELECT 67,'11604-1400-901','Jun 16 2008 12:00AM','- (Mat.Requirement)','100363801',50,2013 UNION ALL

    SELECT 68,'11604-1400-901','Jun 17 2008 12:00AM','- (Mat.Requirement)','100363802',50,2119 UNION ALL

    SELECT 69,'11604-1400-901','May 13 2008 6:15PM','- (Mat.Requirement)','100360063',4,350

    SET IDENTITY_INSERT [tInvMove] OFF

    --Get "BEFORE" Snapshot

    SELECT b.Record_No

    , a.InvOnHoldNet

    , a.InvOnHandNet

    , b.Item

    , b.PlnQty

    , b.CumInDemand

    INTO BEFORE

    From tInvMove b

    , tItemMaster a

    Where b.item = a.item

    and (b.TType <> '+ (Planned Receipt)' or b.ttype is null)

    Order by b.item

    , b.TDt

    , b.TType

    , b.fOrder

    /* Begin problem code */

    --Set up Variables

    Declare @Record_Noas int

    Declare @CumPlnQtyas float

    Declare @mRqmntsas float

    Declare @mCIDas float

    Declare @CumCumInDemandas float

    Declare @IOHandNetas float

    Declare @IOHoldNetas float

    Declare @Countas integer

    Declare @TTypeas varchar(30)

    Declare @ChkItemas varchar(47)

    Declare @CumItemas varchar(47)

    Declare @Update1_Ctras integer

    Declare @Update2_Ctras integer

    --Create Cursor

    Declare CumInDemand_cursorCURSOR FORWARD_ONLY FOR

    SELECT b.Record_No

    , a.InvOnHoldNet

    , a.InvOnHandNet

    , b.Item

    , b.PlnQty

    , b.CumInDemand

    From tInvMove b

    , tItemMaster a

    Where b.item = a.item

    and (b.TType <> '+ (Planned Receipt)' or b.ttype is null)

    Order by b.item

    , b.TDt

    , b.TType

    , b.fOrder

    FOR UPDATE OF CumInDemand

    --Initialize Variables

    Set @ChkItem = '0000000000000000'

    Set @Count = 0

    Set @Update1_Ctr = 0

    Set @Update2_Ctr = 0

    OPEN CumInDemand_cursor

    FETCH NEXT FROM CumInDemand_cursor INTO @Record_No, @IOHoldNet, @IOHandNet, @CumItem, @CumPlnQty, @CumCumInDemand

    WHILE @@fetch_status = 0

    BEGIN

    --Check Current Item against previous item, if different, CumInDemand resets for this item

    IF @ChkItem <> @CumItem

    BEGIN

    Set @ChkItem = @CumItem

    Set @mCid = @IOHandNet - @IOHoldNet

    END

    Set @mCID = @mCID - @CumPlnQty

    If @mCID >= 0

    BEGIN

    Update tInvMove

    Set CumInDemand = 0 Where current of CumInDemand_cursor

    Set @Update1_Ctr = @Update1_Ctr + 1

    END

    Else

    BEGIN

    Update tInvMove

    Set CumInDemand = @mCID * (-1) Where current of CumInDemand_cursor

    Set @Update2_Ctr = @Update2_Ctr + 1

    END

    --Get next record

    FETCH NEXT FROM CumInDemand_cursor INTO @Record_No, @IOHoldNet, @IOHandNet, @CumItem, @CumPlnQty, @CumCumInDemand

    SET @Count = @Count + 1

    CONTINUE

    END

    CLOSE CumInDemand_cursor

    DEALLOCATE CumInDemand_cursor

    /* End problem code */

    --Get "AFTER" Snapshot

    SELECT b.Record_No

    , a.InvOnHoldNet

    , a.InvOnHandNet

    , b.Item

    , b.PlnQty

    , b.CumInDemand

    INTO AFTER

    From tInvMove b

    , tItemMaster a

    Where b.item = a.item

    and (b.TType <> '+ (Planned Receipt)' or b.ttype is null)

    Order by b.item

    , b.TDt

    , b.TType

    , b.fOrder

    select b.record_no, b.item, b.cumindemand, a.cumindemand

    from before b

    inner join [after] a

    on b.record_no = a.record_no

  • rbarryyoung (4/30/2008)


    Also, this bit here:

    ...

    SELECT @@fetch_status

    WHILE @@fetch_status = 0

    BEGIN

    IF @ChkItem <> @CumItem

    BEGIN

    Set @ChkItem = @CumItem

    Set @mCid = @IOHandNet - @IOHoldNet

    END

    ...

    Is this just to avoid redundantly recalculating the same @ChkItem and @mCid items for every @CumItem group or do you really need to use only the first values in the group?

    That piece of code checks the current record against the previous record. If the item number is the same, it keeps track of it and cumulatively updates the CumInDemand field. If the item has changed, it resets so the CumInDemand only pertains to this item.

    And (duh! me), I figured out that the SELECT @@fetch_status did not need to be there. Don't ask me why I didn't catch that before.

  • So, you're happy with the speed of a While loop? Might as well use a "fire hose" cursor if you're gonna do that.

    --Jeff Moden


    RBAR is pronounced "ree-bar" and is a "Modenism" for Row-By-Agonizing-Row.
    First step towards the paradigm shift of writing Set Based code:
    ________Stop thinking about what you want to do to a ROW... think, instead, of what you want to do to a COLUMN.

    Change is inevitable... Change for the better is not.


    Helpful Links:
    How to post code problems
    How to Post Performance Problems
    Create a Tally Function (fnTally)

  • OK, looking at your data now, it is clear that my routines do not respect the relationship between your "Order By" fields and the sequencing of CumInDemand. I do not have time to fix it now, but hopefully, this can get you started.

    [font="Times New Roman"]-- RBarryYoung[/font], [font="Times New Roman"] (302)375-0451[/font] blog: MovingSQL.com, Twitter: @RBarryYoung[font="Arial Black"]
    Proactive Performance Solutions, Inc.
    [/font]
    [font="Verdana"] "Performance is our middle name."[/font]

  • HOLY CRAP! :w00t::w00t::w00t::w00t: I rewrote the query based on Jeff's Running Total post, and it went from 8 hours to 6 seconds! Jeff, I bow down before you! THANKS!!!!!!!

    --===== Build Temp table

    SELECT b.Record_No

    , a.InvOnHoldNet

    , a.InvOnHandNet

    , b.Item

    , b.PlnQty

    , b.CumInDemand

    , cast(0 as float) CumDemand

    INTO #tInvMove

    From tInvMove b

    , tItemMaster a

    Where b.item = a.item

    and (b.TType <> '+ (Planned Receipt)' or b.ttype is null)

    Order by b.item

    , b.TDt

    , b.TType

    , b.fOrder

    --===== Add Primary Key

    ALTER TABLE #tInvMove ADD PRIMARY KEY CLUSTERED (Record_No)

    --===== Declare the variables

    DECLARE @CumDemand float

    SET @CumDemand = 0

    DECLARE @PrevItem varchar(47)

    SET @PrevItem = ''

    --===== Changed the formula to "reset" when the item changes

    UPDATE #tInvMove

    SET @CumDemand = CumDemand = CASE

    WHEN Item = @PrevItem

    THEN @CumDemand + PlnQty

    ELSE PlnQty -- Restarts total at "0 + current amount"

    END,

    @PrevItem = Item

    FROM #tInvMove WITH (TABLOCKX)

    --===== Push the changes back to the main table

    UPDATE tInvMove

    SET CumInDemand = CASE

    WHEN ((t.InvOnHandNet - t.InvOnHoldNet) - t.CumDemand) > 0

    THEN 0

    ELSE ((t.InvOnHandNet - t.InvOnHoldNet) - t.CumDemand) * (-1)

    END

    FROM tInvMove m

    INNER JOIN #tInvMove t

    ON m.Record_No = t.Record_No

    DROP TABLE #tInvMove

  • Less code too.

    [font="Times New Roman"]-- RBarryYoung[/font], [font="Times New Roman"] (302)375-0451[/font] blog: MovingSQL.com, Twitter: @RBarryYoung[font="Arial Black"]
    Proactive Performance Solutions, Inc.
    [/font]
    [font="Verdana"] "Performance is our middle name."[/font]

  • mdevore (5/2/2008)


    HOLY CRAP! :w00t::w00t::w00t::w00t: I rewrote the query based on Jeff's Running Total post, and it went from 8 hours to 6 seconds! Jeff, I bow down before you! THANKS!!!!!!!

    Heh... ran so fast I'll bet you didn't think it ran at all, huh? Lemme know before you try to use a While loop for anything again... 😀

    And, thanks for the awesome compliment! 🙂

    --Jeff Moden


    RBAR is pronounced "ree-bar" and is a "Modenism" for Row-By-Agonizing-Row.
    First step towards the paradigm shift of writing Set Based code:
    ________Stop thinking about what you want to do to a ROW... think, instead, of what you want to do to a COLUMN.

    Change is inevitable... Change for the better is not.


    Helpful Links:
    How to post code problems
    How to Post Performance Problems
    Create a Tally Function (fnTally)

Viewing 15 posts - 1 through 15 (of 15 total)

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