user1729486
user1729486

Reputation: 21

need to get the min and max date for an id with a datavalue for min(date) and max(date) datavalue in the same column

Given this data:

ID      FirstDate       LastDate       ItemId
12A     05-11-2011      05-11-2011        0
12A     12-19-2011      12-19-2011        3
12A     01-04-2012      01-04-2012        3
12A     01-19-2012      01-19-2012       12
64B     06-15-2010      06-15-2010        0
64B     08-19-2011      08-19-2011        3

I want to see:

ID     FirstDate    FirstItemId     LastDate     LastItemId
12A    05-11-2011       0           01-19-2012       12
64B    06-15-2010       0           08-19-2011        3

Upvotes: 0

Views: 2050

Answers (2)

Tim Lehner
Tim Lehner

Reputation: 15251

In it's most basic form, you will probably want to combine the SQL min and group by functionality:

select ID
    , min(FirstDate) as FirstDate
    , min(ItemId) as FirstItemId
    , max(LastDate) as LastDate
    , max(ItemId) as LastItemId
from MyTable
group by ID

Note, however, that this will return the absolute min and max values for each column, not necessarily the ItemId that corresponds to the FirstDate and such, unless the data happens to be that way. Here is one possible alternative to that to get ItemIDs based upon the first/last dates:

-- Get ItemIDs that correspond to First/Last Dates
select ID
    , FirstDate
    , (select min(ItemID) from Mytable where ID = a.ID and FirstDate = a.FirstDate) as FirstItemID
    , LastDate
    , (select max(ItemID) from Mytable where ID = a.ID and LastDate = a.LastDate) as LastItemID
from (
    select ID
        , min(FirstDate) as FirstDate
        , max(LastDate) as LastDate
    from Mytable
    group by ID
) as a

I find that correlated subqueries can often be faster than windowing functions (and possibly easier to understand), but you must test in your environment with your data.

Upvotes: 0

Taryn
Taryn

Reputation: 247690

You can use a combination of windowing functions to get this result:

select id,
  max(case when FirstRowNumber= 1 then firstdate end) firstdate,
  max(case when FirstRowNumber= 1 then itemid end) firstitemId,
  max(case when LastRowNumber= 1 then lastdate end) lastdate,
  max(case when LastRowNumber= 1 then itemid end) lastitemId
from 
(
  select id, firstdate, lastdate, itemid,
    row_number() over(partition by id order by firstdate) FirstRowNumber,
    row_number() over(partition by id order by lastdate desc) LastRowNumber
  from yourtable
) x
where FirstRowNumber= 1
  or LastRowNumber= 1
group by id

See SQL Fiddle with Demo.

This solution assigns the row_number to the records in both ASC/DESC date order. Then you only care about the records where the row_number = 1. I then applied an aggregate and a CASE statement to the values to get the correct result.

Or you can use a very ugly UNPIVOT and PIVOT solution:

select *
from 
(
  select id,
    val,
    case when firstrownumber = 1 and col = 'firstdate'
          then 'firstdate'
        when firstrownumber = 1 and col = 'itemid'
          then 'firstitemid'
        when LastRowNumber = 1 and col = 'lastdate'
          then 'lastdate'
        when LastRowNumber = 1 and col = 'itemid'
          then 'lastitemid'
        else '' end col
  from
  (
    select id, 
      convert(varchar(10), firstdate, 120) firstdate, 
      convert(varchar(10), lastdate, 120) lastdate, 
      cast(itemid as varchar(10)) itemid,
      row_number() over(partition by id order by firstdate) FirstRowNumber,
      row_number() over(partition by id order by lastdate desc) LastRowNumber
    from yourtable
  ) x
  unpivot
  (
    val for col in (firstdate, lastdate, itemid)
  ) u
) x1
pivot
(
  max(val)
  for col in ([firstdate], [firstitemid], 
              [lastdate], [lastitemid])
) p

See SQL Fiddle with Demo

Upvotes: 1

Related Questions