Using T-SQL Cursors to find first, highest and last values

  • Hi all,

    I have basic knowledge of T-SQL and I am using Cursors to get the first value, the last value and the peak value and some other values from other tables. I found some examples on google but the code I am using is mixed up. I am using multiple Cursors. Could you please look at this code and provide me better solution by using Cursors. I need to join three tables to get the result set into the Cursor. The first example uses 2 tables.

    @FirstName NVARCHAR,

    @LastName NVARCHAR,

    @FirstValue decimal,

    @HighestValue decimal,

    @LastValue decimal

    -- First Cursor

    DECLARE TESTCURSOR CURSOR

    DYNAMIC

    FOR

    SELECT x.value AS FirstValue

    FROM TABLE x

    INNER JOIN TABLE y

    ON x.id = y.id

    WHERE someid = '123xyx'

    OPEN TESTCURSOR

    -- get the first value

    FETCH FIRST FROM TESTCURSOR INTO @FirstValue

    -- second Cursor

    DECLARE TESTCURSOR2 CURSOR

    DYNAMIC

    FOR

    SELECT MAX(x.value) AS HighestValue

    FROM TABLE x

    INNER JOIN TABLE y

    ON x.id = y.id

    WHERE someid = '123xyx' -- used same id as above in the first cursor in where clause

    OPEN TESTCURSOR2

    -- get the next highest value by using MAX function above

    FETCH NEXT FROM TESTCURSOR2 INTO @HighestValue -- Getting the MAX highest value

    CLOSE TESTCURSOR2

    DEALLOCATE TESTCURSOR2

    -- get the last value from TESTCURSOR (The first Cursor)

    FETCH LAST FROM TESTCURSOR INTO @LastValue

    -- close the first cursor

    CLOSE TESTCURSOR

    DEALLOCATE TESTCURSOR

    -- select all the values

    SELECT @FirstValue AS InitialValue, @HighestValue AS Hisghest, , @LastValue AS Last

    The above code seems totally inefficient but it gives the correct result. Now I want to pull some more value and join a third table (TABLE z) in the above CURSORS

    and not sure how to make it working using CURSORS.I would like to use the following in the CURSORS above.

    SELECT x.publishdate, y.firstname, y.lastname, y.age, z.initialValue AS FirstValue, z.HighestValue AS Highest, z.LastValue AS Last

    FROM TABLE x

    LEFT OUTER JOIN TABLE y

    ON x.id = y.id

    INNER JOIN TABLE z

    ON x.id = z.id

    Could you please provide some help and make the above code working?

    Thank you.

  • Why do you want to use cursors for this? "Cursor" is a dirty word around here. You could probably do all this with either aggregate or window functions.

  • Could you provide some examples with Aggregates or Windows functions using the code I provided?

    Thank you.

  • Got some table definitions and some fake data? (Fake if it's confidential... representative is fine). And expected output?

  • You can have a look at the code example I provided in the first post.

    Thanks.

  • Maybe start here:

    http://www.sqlservercentral.com/articles/Best+Practices/61537/

    and maybe this:

    http://spaghettidba.com/?s=how+to+post

    Look at your question from our perspective. How are we to test any of it? Without data, we can't. If you set up the question so that folks can run the code you provide to set up your scenario and run what you tried, they can offer tested answers.

  • Depending on the query there is a processing order of the rows of a table. It is not determined which is the first row to be processed and which is the last row to be processed.

    So code should not depend on this.

    Use a ORDER BY clause to determine in which order the table should be processed.

    The code below uses a ORDER BY clause and a first and last row can be produced, but you should not depend on that this produces the same results everytime.

    (The ORDER BY clause orders on a (SELECT NULL), so effectively the table is not ordered on anything).

    --

    -- Create a table.

    --

    select 3 as nr, 'a' as kar into ##SS

    insert into ##SS values

    (9, 'C'),

    (15,'h'),

    (7, 'e'),

    (6, 'd'),

    (12,'z'),

    (4, 'b'),

    (5, 'q')

    select * from ##SS

    --

    -- Get the first row of a table. This might be 'any' row

    --

    ;

    With

    A as (select

    ROW_NUMBER() OVER(PARTITION BY 1 ORDER BY (SELECT NULL)) a_number, * from ##SS)

    select * from a where a_number = (select min(a_number) from a)

    --

    -- Get the last row of a table. This might be 'any' row

    --

    ;

    With

    A as (select

    ROW_NUMBER() OVER(PARTITION BY 1 ORDER BY (SELECT NULL)) a_number, * from ##SS)

    select * from a where a_number = (select MAX(a_number) from a)

    --

    -- Get the highest and the lowest values.

    --

    select MAX(nr) as Max_nr, MIN(nr) as Min_nr, MAX(kar) as Max_kar, MIN(kar) as min_kar from ##SS

    Ben

  • You overcomplicate things, Ben. It can all be done in a single query.

    This won't solve the whole problem, but it can show how it could be done.

    --

    -- Create a table.

    --

    select 3 as id, 'a' as value into #SampleData

    insert into #SampleData values

    (9, 'C'),

    (15,'h'),

    (7, 'e'),

    (6, 'd'),

    (12,'z'),

    (4, 'b'),

    (5, 'q')

    select *

    from #SampleData

    ORDER BY id

    SELECT first_value,

    last_value,

    max_value

    FROM

    ( SELECT TOP 1

    value AS first_value,

    MAX(value) OVER() AS max_value

    FROM #SampleData

    ORDER BY id) a,

    ( SELECT TOP 1

    value AS last_value

    FROM #SampleData

    ORDER BY id DESC) b

    GO

    DROP TABLE #SampleData

    Luis C.
    General Disclaimer:
    Are you seriously taking the advice and code from someone from the internet without testing it? Do you at least understand it? Or can it easily kill your server?

    How to post data/code on a forum to get the best help: Option 1 / Option 2
  • @ben, @luis

    Thank you guys for your great examples. I will test these examples and will let you know as soon as possible.

    Thanks.

  • drop table ##ss

    select 3 as nr, 'a' as kar, 1 as groupingcode into ##SS

    insert into ##SS values

    (9, 'C',1),

    (15,'h',1),

    (7, 'e',1),

    (6, 'd',1),

    (12,'z',1),

    (4, 'b',1),

    (5, 'q',1)

    select * from ##SS

    --

    -- Get the first row of a table. This might be 'any' row

    --

    ;

    Select top 1

    min(nr) over (partition by 1 order by groupingcode desc),

    max(nr) over (partition by 1 order by groupingcode desc),

    first_value(nr) over (partition by 1 order by groupingcode desc),

    last_value(nr) over (partition by 1 order by groupingcode desc)

    from ##ss

    just another idea to play with

    :w00t:

  • Hello Luis

    (and ITU_dk2012)

    The first time I anwsered, the anwser got lost because my regular work interrupted and when 'posting the anwser' this was 'too late'. Second time I shortened the anwser.

    The point was not the code, but that there is no defined first and/or last row.

    The code showed how to get 'a first' and 'a last' row without sorting and without a cursor.

    Ben

    select top 1 * from ##SS

    This could replace the first statement.

    For the last row the code becomes more complicated.

    But I used the same construction twice to limit the number of constructions.

  • Ben,

    I agree completely with you in the sense that there's no way to define a first or last row without an ORDER BY. Rows in a table aren't organized in a sequential way like in a spreadsheet.

    I still think that you overcomplicate things (and I probably did, too). ๐Ÿ˜€

    WITH CTE AS(

    SELECT *,

    ROW_NUMBER() OVER( ORDER BY (SELECT NULL)) row_num, --no real order for the rows. Could change at any moment.

    COUNT(*) OVER() AS row_count

    FROM #SampleData

    )

    SELECT MAX( CASE WHEN row_num = 1 THEN value END) AS first_value,

    MAX( CASE WHEN row_num = row_count THEN value END) AS last_value,

    MAX( value) AS first_value

    FROM CTE;

    Steve,

    FIRST_VALUE and LAST_VALUE are good options, but this was posted in a SQL 2008 forum, so they might not apply.

    Luis C.
    General Disclaimer:
    Are you seriously taking the advice and code from someone from the internet without testing it? Do you at least understand it? Or can it easily kill your server?

    How to post data/code on a forum to get the best help: Option 1 / Option 2
  • DoH :ermm:

  • steve.tarry (9/22/2015)


    drop table ##ss

    select 3 as nr, 'a' as kar, 1 as groupingcode into ##SS

    insert into ##SS values

    (9, 'C',1),

    (15,'h',1),

    (7, 'e',1),

    (6, 'd',1),

    (12,'z',1),

    (4, 'b',1),

    (5, 'q',1)

    select * from ##SS

    --

    -- Get the first row of a table. This might be 'any' row

    --

    ;

    Select top 1

    min(nr) over (partition by 1 order by groupingcode desc),

    max(nr) over (partition by 1 order by groupingcode desc),

    first_value(nr) over (partition by 1 order by groupingcode desc),

    last_value(nr) over (partition by 1 order by groupingcode desc)

    from ##ss

    just another idea to play with

    :w00t:

    Lose the expensive sort:

    SELECT TOP 1

    min(nr) OVER (),

    max(nr) OVER (),

    first_value(nr) OVER (ORDER BY (SELECT NULL)),

    last_value(nr) OVER (ORDER BY (SELECT NULL))

    FROM #ss

    โ€œ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

Viewing 14 posts - 1 through 13 (of 13 total)

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