简体   繁体   中英

SQL - aggregate function - GROUP BY clause

I have the following query:

select vkbr.vkID, vkbr.bid, vkbr.Date, vkbr.agID 
FROM camp c (NOLOCK)
JOIN ag (NOLOCK) ON ag.campID = c.id
JOIN vkbr WITH (NOLOCK) ON vkbr.agID = ag.id
where c.id = 648322
order by vkbr.vkID;

Which has the following results:

vkID        bid     Date                    agID
1072845175  NULL    2012-12-04 20:20:12.390 16074852
1072845177  0.74    2012-12-01 23:36:11.280 16074852
1072845177  0.18    2012-12-02 23:01:26.123 16074852
1072845177  0.72    2012-12-09 23:38:52.503 16074852
1072845177  0.62    2012-12-14 15:26:49.643 16074852
1072845178  2.91    2012-12-08 19:37:00.877 16074852
1072845178  0.73    2012-12-13 17:54:11.240 16074852
1072845178  2.70    2012-12-14 15:26:49.643 16074852

I need to group by vkID, get the max(Date) and the bid on the max(Date) to get this result:

1072845175  NULL    2012-12-04 20:20:12.390 16074852
1072845177  0.62    2012-12-14 15:26:49.643 16074852
1072845178  2.70    2012-12-14 15:26:49.643 16074852


select vkbr.vkID, MAX(vkbr.Date) as Date, MIN(vkbr.agID) as agID
FROM camp c (NOLOCK)
JOIN    ag (NOLOCK) ON ag.campID = c.id
JOIN    vkbr WITH (NOLOCK) ON vkbr.agID = ag.id
where c.id=648322
group by vkbr.vkID
having Max(vkbr.Date) is not null
and Max(Date) <= '2012-12-18';

Since I cannot add the bid column in SELECT statement - receive this error: Column 'bid' is invalid in the select list because it is not contained in either an aggregate function or the GROUP BY clause.

So I tried to do this with temp tables:

create table #getBids ( 
vkID bigint not null,
Date datetime null,
agID bigint null);

insert into #getBids (vkID, Date, agID) 
select vkbr.vkID, MAX(vkbr.Date) as Date, MIN(vkbr.agID) as agID
FROM camp c (NOLOCK)
JOIN    ag (NOLOCK) ON ag.campID = c.id
JOIN    vkbr WITH (NOLOCK) ON vkbr.agID = ag.id
where c.id = 648322
group by vkbr.vkID
having Max(vkbr.Date) is not null
and Max(Date) <= '2012-12-18';

Now this gives me the result I want:

select vkbr.vkID, vkbr.bid from vkbr
inner join #getBids on vkbr.Date = #getBids.Date
and vkbr.agID = #getBids.agID
and vkbr.vkID = #getBids.vkID

I was wondering is there anyway to accomplish the same result in one query w/o creating the temp table? Any help is greatly appreciated.

If you're using SQL Server 2005 or higher you can use the ROW_NUMBER() function to select the row with MAX(Date) for each vkID .

I haven't been able to test this with your schema but I think it would work something like this:

First, declare a Common Table Expression (CTE) to add a row number to your query results

with cte as
(    
    select vkbr.vkID, vkbr.bid, vkbr.Date, vkbr.agID, ROW_NUMBER() OVER (PARTITION BY vkbr.vkID ORDER BY vkbr.Date DESC) AS RowNumber
    FROM camp c (NOLOCK)
    JOIN ag (NOLOCK) ON ag.campID = c.id
    JOIN vkbr WITH (NOLOCK) ON vkbr.agID = ag.id
    where c.id = 648322
)

Then, SELECT only where RowNumber = 1 . This will give you the latest record for each vkbr.vkID

select vkID, bid, Date, agID
from cte
where RowNumber = 1

Query:

SELECT vkbr.vkID,
       vkbr.bid,
       vkbr.Date,
       vkbr.agID
FROM camp c (NOLOCK)
JOIN ag (NOLOCK) ON ag.campID = c.id
JOIN vkbr WITH (NOLOCK) ON vkbr.agID = ag.id
WHERE c.id = 648322
AND vkbr.Date = (SELECT MAX(v.Date)
                 FROM vkbr v2
                 WHERE v2.vkID = vkbr.vkID
                 AND v2.agID = vkbr.agID)
ORDER BY vkbr.vkID

Example Query:

SQLFIDDLEExample

SELECT t1.*
FROM Table1 t1
WHERE t1.Date = (SELECT MAX(t2.Date)
                 FROM Table1 t2
                 WHERE t1.vkID = t2.vkID
                 AND t1.agID = t2.agID)

Result:

|       VKID |    BID |                            DATE |     AGID |
--------------------------------------------------------------------
| 1072845178 |   2.70 | December, 14 2012 15:26:49+0000 | 16074852 |
| 1072845177 |   0.62 | December, 14 2012 15:26:49+0000 | 16074852 |
| 1072845175 | (null) | December, 04 2012 20:20:12+0000 | 16074852 |

You can do this with one query. First, the query must gather the max date by vkID. Next, it must join back to vkbr using the date to get the actual bid. You could use a sub-query , ie something lIke this:

select vkID, bid, Date, agID
from
(
    select vkbr.vkID, MAX(vkbr.Date) as Date, MIN(vkbr.agID) as agID
    from camp c with (nolock)
    inner join ag with (nolock)
        on ag.campID = c.id
    inner join vkbr with (nolock)
        on vkbr.agID = ag.id
    where c.id = 648322
    group by vkbr.vkID
    having Max(vkbr.Date) is not null
    and Max(vkbr.Date) <= '2012-12-18';
) MaxDate
    inner join vkbr with (nolock)
        on vkbr.agID = MaxDate.agID
        and vkbr.Date = MaxDate.Date

I would recommend using a common table expression (CTE) instead of a sub-query. The CTE approach would look something like this:

;with MaxDate (vkID, Date, agID) as
(
    select vkbr.vkID, MAX(vkbr.Date) as Date, MIN(vkbr.agID) as agID
    from ...
)
select ...
from MaxDate
inner join vkbr
    on vkbr.agID = MaxDate.agID
    and vkbr.Date = MaxDate.Date

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM