How to update a Table using Loop?

  • Hi All,

    I'm trying to update the following table (SQL Server 2008 R2):

    MerchantNoTerminalNoCounterNo

    7000001370000014NULL

    7000001370000015NULL

    7000001370000016NULL

    7000001370000013NULL

    7000003270000032NULL

    7000003270000031NULL

    I need the CounterNo to be updated as following, against TerminalNo:

    MerchantNoTerminalNoCounterNo

    7000001370000014Counter 4

    7000001370000015Counter 3

    7000001370000016Counter 2

    7000001370000013Counter 1

    7000003270000032Counter 2

    7000003270000031Counter 1

    I'm trying to do this by the following method, and stuck how to assign the value to the Counter?

    DECLARE @CounterNo INT

    SET @CounterNo = 1

    WHILE (@CounterNo <= 5)

    BEGIN

    UPDATE tblRPT_TXNDetails SET RFMTerminalDesc = 'Counter ' + @CounterNo

    END

    Go

    Thanx in Advance

  • -- always check an update as the equivalent SELECT before running it.

    -- you may wish to replace the ORDER BY to, say, TerminalNo

    SELECT

    MerchantNo,

    TerminalNo,

    CounterNo = ROW_NUMBER() OVER(PARTITION BY MerchantNo ORDER BY (SELECT NULL))

    FROM tblRPT_TXNDetails;

    -- when you're satisfied that CounterNo looks correct, convert the query into an UPDATE like this:

    WITH Updater AS (

    SELECT RFMTerminalDesc, CounterNo = ROW_NUMBER() OVER(PARTITION BY MerchantNo ORDER BY (SELECT NULL))

    FROM tblRPT_TXNDetails)

    UPDATE Updater SET RFMTerminalDesc = CounterNo


    [font="Arial"]Low-hanging fruit picker and defender of the moggies[/font]

    For better assistance in answering your questions, please read this[/url].


    Understanding and using APPLY, (I)[/url] and (II)[/url] Paul White[/url]

    Hidden RBAR: Triangular Joins[/url] / The "Numbers" or "Tally" Table: What it is and how it replaces a loop[/url] Jeff Moden[/url]

  • eligiable (3/2/2014)


    Hi All,

    I'm trying to update the following table (SQL Server 2008 R2):

    MerchantNoTerminalNoCounterNo

    7000001370000014NULL

    7000001370000015NULL

    7000001370000016NULL

    7000001370000013NULL

    7000003270000032NULL

    7000003270000031NULL

    I need the CounterNo to be updated as following, against TerminalNo:

    MerchantNoTerminalNoCounterNo

    7000001370000014Counter 4

    7000001370000015Counter 3

    7000001370000016Counter 2

    7000001370000013Counter 1

    7000003270000032Counter 2

    7000003270000031Counter 1

    I'm trying to do this by the following method, and stuck how to assign the value to the Counter?

    DECLARE @CounterNo INT

    SET @CounterNo = 1

    WHILE (@CounterNo <= 5)

    BEGIN

    UPDATE tblRPT_TXNDetails SET RFMTerminalDesc = 'Counter ' + @CounterNo

    END

    Go

    Thanx in Advance

    It looks to me like Merchant No and Terminal No might be the unique identifier. Assuming the table to be updated as Table1 and the source as Table2 use the following

    UPDATE a SET a.counterno = b.counterno

    FROM Table1 a INNER JOIN Table2 b ON

    a.MerchantNo = b.MerchantNo

    and a.TerminalNo = b.TerminalNo

    -----------------------------------------------------------------------------------------------------------

    "Ya can't make an omelette without breaking just a few eggs" 😉

  • @OP - What you can take from this is everyone's answer is showing you a better way of doing to UPDATE so you can AVOID the loop 🙂

    ______________________________________________________________________________Never argue with an idiot; Theyll drag you down to their level and beat you with experience

  • Thanx ChrisM@home :-), that really helped, check out the following statement:

    SELECT DISTINCT TerminalNo, MerchantNo

    INTO #tblGen_TermCounter

    FROM tblRPT_TXNDetails;

    ALTER TABLE #tblGen_TermCounter ADD CounterDesc BIGINT;

    WITH Updater AS (

    SELECT CounterDesc, CounterNo = ROW_NUMBER() OVER(PARTITION BY MerchantNo ORDER BY (SELECT NULL))

    FROM #tblGen_TermCounter

    )

    UPDATE Updater SET CounterDesc = CounterNo;

    ALTER TABLE #tblGen_TermCounter ALTER COLUMN CounterDesc NVARCHAR(50);

    UPDATE #tblGen_TermCounter SET CounterDesc = 'Counter ' + CounterDesc;

    UPDATE tblRPT_TXNDetails SET RFMTerminalDesc = #tblGen_TermCounter.CounterDesc

    FROM #tblGen_TermCounter

    WHERE tblRPT_TXNDetails.MerchantNo = #tblGen_TermCounter.MerchantNo AND tblRPT_TXNDetails.TerminalNo = #tblGen_TermCounter.TerminalNo

    I cannot update the tblRPT_TXNDetails table directly, coz it contains more than 1 million records, and when I try to execute the statement on tblRPT_TXNDetails, the ROW_NUMBER() returns the Row Number on which the record is available in the table.

    I needed to apply the same Counter No, to each record having the same TerminalNo, therefore I need to 1st pull out the Distinct Records based on TerminalNo and then to update the tblRPT_TXNDetails table.

    Thanx again :-), please tell me, if require improvement.

  • Perry Whittle Thanx for the help :-).

  • eligiable (3/2/2014)


    Thanx ChrisM@home :-), that really helped, check out the following statement:

    SELECT DISTINCT TerminalNo, MerchantNo

    INTO #tblGen_TermCounter

    FROM tblRPT_TXNDetails;

    ALTER TABLE #tblGen_TermCounter ADD CounterDesc BIGINT;

    WITH Updater AS (

    SELECT CounterDesc, CounterNo = ROW_NUMBER() OVER(PARTITION BY MerchantNo ORDER BY (SELECT NULL))

    FROM #tblGen_TermCounter

    )

    UPDATE Updater SET CounterDesc = CounterNo;

    ALTER TABLE #tblGen_TermCounter ALTER COLUMN CounterDesc NVARCHAR(50);

    UPDATE #tblGen_TermCounter SET CounterDesc = 'Counter ' + CounterDesc;

    UPDATE tblRPT_TXNDetails SET RFMTerminalDesc = #tblGen_TermCounter.CounterDesc

    FROM #tblGen_TermCounter

    WHERE tblRPT_TXNDetails.MerchantNo = #tblGen_TermCounter.MerchantNo AND tblRPT_TXNDetails.TerminalNo = #tblGen_TermCounter.TerminalNo

    I cannot update the tblRPT_TXNDetails table directly, coz it contains more than 1 million records, and when I try to execute the statement on tblRPT_TXNDetails, the ROW_NUMBER() returns the Row Number on which the record is available in the table.

    I needed to apply the same Counter No, to each record having the same TerminalNo, therefore I need to 1st pull out the Distinct Records based on TerminalNo and then to update the tblRPT_TXNDetails table.

    Thanx again :-), please tell me, if require improvement.

    I guess I don't understand the need for the 2 column alterations.

    --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)

  • Hi Jeff Moden

    1st alteration will allow to store the Counter values in CounterDesc as an INT or BigINT depends on the records (I got 1 Million and increasing), so I declared BIGINT.

    The 2nd Time I need to put a Phrase 'Counter' with the generated value in CounterDesc so I need to alter it to NVARCHAR.

    The Updater is not allowing to store the value in NVARCHAR or VARCHAR, that's why I need to alter it again.

  • eligiable (3/2/2014)


    Hi Jeff Moden

    1st alteration will allow to store the Counter values in CounterDesc as an INT or BigINT depends on the records (I got 1 Million and increasing), so I declared BIGINT.

    The 2nd Time I need to put a Phrase 'Counter' with the generated value in CounterDesc so I need to alter it to NVARCHAR.

    The Updater is not allowing to store the value in NVARCHAR or VARCHAR, that's why I need to alter it again.

    You're storing the word "Counter", a space, and the character equivalent of a number in the column. It doesn't need to be NVARCHAR... it a waste of bytes when read into memory and a wast of bytes on the disk.

    I also don't know why you feel compelled to store the word "Counter" in every row of the table. It's a denormalization that costs one byte for each letter (7 bytes), a byte for the space, 1 to 10 bytes for the character representation of a number, and 2 bytes to store the length indicator behind the scenes not to mention losing the advantages of a fixed datatype to a variable length datatype and mixing data in a column. That's a total of 11 to 19 bytes of storage where 4 bytes to store an INT would have been just fine. If you really need to have the word "Counter" present at display time, then add it at display time instead of storing a million copies of the word in a million row table.

    If you elect to continue what I believe to be an unnecessary consumption of memory and disk space, you still don't need to change datatypes in the column to do this. Such changes to a Temp Table are fully logged in the logfile of TempDB and that takes a lot of time, memory, and disk thrashing if the table is large enough. I strongly recommend that you learn how to use CAST and CONVERT instead.

    In fact, as Chris did in his code, there is no need for any of what you have posted as code. There's not even the need for a temp table. All you have to do with his code is to figure out if you want to continue the mistake of storing the word "Counter" in every row and learn how to use CAST or CONVERT on his code to help you continue making that mistake.

    For your convenience, here's Chris' code again with the addition of the word "Counter".

    WITH Updater AS (

    SELECT RFMTerminalDesc, CounterNo = ROW_NUMBER() OVER(PARTITION BY MerchantNo ORDER BY (SELECT NULL))

    FROM tblRPT_TXNDetails)

    UPDATE Updater SET RFMTerminalDesc = 'Counter ' +CAST(CounterNo AS VARCHAR(10))

    ;

    --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)

  • eligiable (3/2/2014)


    Hi Jeff Moden

    1st alteration will allow to store the Counter values in CounterDesc as an INT or BigINT depends on the records (I got 1 Million and increasing), so I declared BIGINT.

    ROW_NUMBER returns BIGINT. One million is comfortably held by INT.

    The 2nd Time I need to put a Phrase 'Counter' with the generated value in CounterDesc so I need to alter it to NVARCHAR.

    The expression 'Counter' is VARCHAR.

    The Updater is not allowing to store the value in NVARCHAR or VARCHAR, that's why I need to alter it again.

    Your code probably gets the job done but as Jeff has shown you and fully explained, it's waaaay overkill for what you want to do.

    Since you want the same TerminalNo values per MerchantNo I think DENSE_RANK should serve the purpose:

    -- always check an update as the equivalent SELECT before running it.

    -- you may wish to replace the ORDER BY to, say, TerminalNo

    SELECT

    MerchantNo,

    TerminalNo,

    CounterNo = 'Counter ' + CAST(DENSE_RANK() OVER(PARTITION BY MerchantNo ORDER BY TerminalNo) AS VARCHAR(10))

    FROM tblRPT_TXNDetails;

    -- when you're satisfied that CounterNo looks correct, convert the query into an UPDATE like this:

    WITH Updater AS (

    SELECT

    RFMTerminalDesc,

    CounterNo = 'Counter ' + CAST(DENSE_RANK() OVER(PARTITION BY MerchantNo ORDER BY TerminalNo) AS VARCHAR(10))

    FROM tblRPT_TXNDetails)

    UPDATE Updater SET RFMTerminalDesc = CounterNo


    [font="Arial"]Low-hanging fruit picker and defender of the moggies[/font]

    For better assistance in answering your questions, please read this[/url].


    Understanding and using APPLY, (I)[/url] and (II)[/url] Paul White[/url]

    Hidden RBAR: Triangular Joins[/url] / The "Numbers" or "Tally" Table: What it is and how it replaces a loop[/url] Jeff Moden[/url]

  • Thank you Jeff Moden

    The explanation really helped, no need to store the Counter phrase into the database, I can display it on run-time, I didn't really know about the:DENSE_RANK()as mentioned by ChrisM@home.

    Thanx for the help, really appreciated.

  • Thank you ChrisM@home

    I really need to update, the statement works perfectly.

    Thanks to you all.

Viewing 12 posts - 1 through 11 (of 11 total)

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