views:

145

answers:

3

The Table:

declare @Table table (    
 id int,    
 ticketid int,    
 sponsor int,    
 dev int,
 qa int,
 savedate datetime
)

insert into @Table values (1,100,22,0, 0, '2008-10-29 11:17:59.527')
insert into @Table values (2,100,5,0, 0, '2008-10-29 11:00:37.030')
insert into @Table values (3,101,22,0, 0, '2009-10-29 11:10:27.687')
insert into @Table values (5,101,44,0, 0, '2008-10-31 12:07:52.917')
insert into @Table values (6,101,32,0, 0, '2009-06-30 08:16:12.343')
insert into @Table values (7,101,44,0, 0, '2009-10-31 10:12:11.369')

I'm trying to select the top 1 max of savedate where recordid is a certain record, grouped by sponsor.

My progress:

select max(savedate)
from @Table
where ticketid = 101
group by sponsor

Returns

2009-10-29 11:10:27.687
2009-06-30 08:16:12.343
2009-10-31 10:12:11.370

Close, I'm grouped correctly but I want the top 1 most recent date. So I do this:

select top 1 max(savedate)
from @Table
where ticketid = 101
group by sponsor

Returns

2009-10-29 11:10:27.687

Woohoo, got it, time for a break.. wait.. that's not thie most recent date! Let's try to order these by savedate

select top 1 max(savedate)
from @Table
where ticketid = 101
group by sponsor
order by savedate desc

Oh no! The dreaded:

"Column "@Table.savedate" is invalid in the ORDER BY clause because it is not contained in either an aggregate function or the GROUP BY clause."

But savedate IS aggregated in the select list! How do I do what I want to do?

+1  A: 

After typing this thing out and making sure I don't miss a detail, I came up with the answer right at the end. I figured I'd add it anyway so I or anyone else can find it later if they are as short sighted as I was in this instance.

select top 1 max(savedate) as date
from @Table
where ticketid = 101
group by sponsor
order by date desc

max(savedate) is not the same as savedate! Aliasing then refering to the aggregate worked perfectly:

2009-10-31 10:12:11.370

Hope this helps someone.

Dzejms
A: 
select top 1 max(savedate)
from @Table
where ticketid = 101
group by sponsor
order by max(savedate) desc
Maximilian Mayerl
Yup, that's pretty much it. Out of curiosity, do you know if the max(savedate) in the order by clause will recalculate? If so I guess the alias would be the way to go. I imagine MS would have thought of that though and cached it somehow. Do query plans in SQL manager give you answers to questions like this? That query plan diagram is a mystery to me.
Dzejms
A: 

You probably wanted to return the sponsor along with the MAX(savedate).

If you didn't then note that this query:

SELECT  MAX(savedate)
FROM    @Table
WHERE   ticketid = 101

is completely identical to your solution

select top 1 max(savedate) as date
from @Table
where ticketid = 101
group by sponsor
order by date desc

, while being much more efficient.

Alternatively, you can just do the following:

SELECT  TOP 1 sponsor, savedate
FROM    @Table
WHERE   ticketid = 101
ORDER BY
        savedate DESC

Update:

This query will return the date when the task was last assigned to a current sponsor:

declare @Table table (    
 id int PRIMARY KEY,    
 ticketid int,    
 sponsor int,    
 dev int,
 qa int,
 savedate datetime
)

insert into @Table values (1,100,22,0, 0, '2008-10-29 11:17:59.527')
insert into @Table values (2,100,5,0, 0, '2008-10-29 11:00:37.030')
insert into @Table values (3,101,22,0, 0, '2009-10-29 11:10:27.687')
insert into @Table values (5,101,44,0, 0, '2008-10-31 12:07:52.917')
insert into @Table values (6,101,32,0, 0, '2009-06-30 08:16:12.343')
insert into @Table values (7,101,44,0, 0, '2009-10-30 10:12:11.369')
insert into @Table values (8,101,44,1, 0, '2009-10-31 10:12:11.369')

;WITH    rows AS
        (
        SELECT  *, ROW_NUMBER() OVER (PARTITION BY ticketid ORDER BY savedate DESC) AS rn
        FROM    @Table
        )
SELECT  rl.sponsor, ro.savedate
FROM    rows rl
CROSS APPLY
        (
        SELECT  TOP 1 rc.savedate
        FROM    rows rc
        JOIN    rows rn
        ON      rn.ticketid = rc.ticketid
                AND rn.rn = rc.rn + 1
                AND rn.sponsor <> rc.sponsor
        WHERE   rc.ticketid = rl.ticketid
        ORDER BY
                rc.rn
        ) ro
WHERE   rl.rn = 1

I added one more record for sponsor 44 to the test data.

The query will return the rows 1 and 7, since the sponsor did not change in row 8.

Quassnoi
Yeah, sort of forgot to add that. I want to group by sponsor because I care about when the value of that field changes from record to record. If dev or qa changes, but sponsor does not, then I don't want it in my result set.
Dzejms
Mine query is completely identical to yours in terms of the results returned. What do you mean by "dev or qa changes"?
Quassnoi
Perhaps some context will help. This table tracks when a ticket in a bug tracking program has had one of the assignment fields changed. It can be assigned to a sponsor, a QA team member, or a Developer. If any of these assignments are changed a record is inserted into this table with the current assigned users for each assigned type (probably bad design). I want to see how long the sponsor has had the ticket assigned to them, regardless of whether the dev or qa assignement has changed. that is why I'm grouping by sponsor. I then compare savedate to current date and get the number of days.
Dzejms
Woah, fancy stuff. So I've since realized, as you have, that what I'm after and what I came up with aren't the same :)In this case, what I'm ultimately after is the last time that the sponsor changed, not when anything but the sponsor has changed. So can I accomplish that by changing AND rn.sponsor <> rc.sponsorto AND rn.sponsor = rc.sponsor?
Dzejms
This will return you exactly the last time the sponsor changed. Look into my test data. The sponsor does not change between the rows `7` and `8`, that's why I don't return row `8`. But the sponsor does change between rows `6` and `7`, and row `7` hold the date when the sponsor changed, that's why I return row `7`.
Quassnoi
Ok. I'm obviously fishing because I don't understand the with rows over partiontion syntax. But what I want to end up with is a one column, one row savedate value that is the last date that sponsor was changed. I think that I can accomplish that by changing SELECT rl.sponsor, ro.savedate to SELECT top 1 Max(ro.savedate). Am I headed down the right path? Also, this has veered from the original question. Is there any way to break one question into two?
Dzejms
`@Dzejms`: my query will return one row per ticket. Just add the `AND ticket = 101` condition to the end of the query and remove `rl.sponsor` from the `SELECT` list. To break a question in two just post another question.
Quassnoi
Ok, I've submitted a new question, #1650425. I'll wait for you to give it a shot so you can get the points you deserve for helping me out!
Dzejms