简体   繁体   中英

How to query data for every x month in SQL?

I have a table that displays hits on a link for a every day a long with an "origin" column

hitdate      originid    hitcount
==================================
2011-01-05          2          25
2011-01-05          3           3
2011-04-06          2           1          
2011-04-06          6          11
2011-05-06          7           9
2011-05-09          2          25
2011-07-10          3           3
2011-17-11          2           1          
2011-01-12          6          11
2012-01-06          7           9

What I want is to programmatically sum the hitcount for every 3 month, for each originID, since the first day of the set (which is May 1st 2011) until today's date

The results I want to get

Trimester                 originid    hitcount
==============================================
2011-01-05 to 2011-31-07         1           0
                                 2          26 
                                 3           3
                                 6          11
2011-01-08 to 2011-31-10        ..          ..

I think a possible source of trouble with using datepart(qq, ... as the other answers do is that (1) this doesn't seem to account for the possibility of the date set spanning multiple years, and (2) it makes assumptions about the earliest date in your data set. I've tried to write a solution that will work for any start date, even if it's not the first day of a month. It's a bit more complicated, so perhaps someone can suggest a way to simplify it.

-- Sample data from the question. 
declare @hits table (hitdate date, originid bigint, hitcount int);
insert @hits values
    (convert(date, '20110501', 112), 2, 25),
    (convert(date, '20110501', 112), 3,  3),
    (convert(date, '20110604', 112), 2,  1),
    (convert(date, '20110604', 112), 6, 11),
    (convert(date, '20110605', 112), 7,  9),
    (convert(date, '20110905', 112), 2, 25),
    (convert(date, '20111007', 112), 3,  3),
    (convert(date, '20111117', 112), 2,  1),
    (convert(date, '20111201', 112), 6, 11),
    (convert(date, '20120601', 112), 7,  9);

-- Define the date range that we're concerned with.
declare @beginDate date, @endDate date;
select
    @beginDate = min(hitdate),
    @endDate = convert(date, getdate())
from 
    @hits;

-- Build a list of three-month periods that encompass the date range defined
-- above. Each period will be inclusive on the lower end of the range and
-- exclusive on the upper end.
declare @Trimester table (beginDate date, endDate date);
while @beginDate <= @endDate
begin
    insert @Trimester values (@beginDate, dateadd(m, 3, @beginDate));
    set @beginDate = dateadd(m, 3, @beginDate);
end;

-- Finally, assign each hit to one of these periods.
select
    [Trimester] = 
        convert(varchar, T.beginDate) + 
        ' to ' + 
        convert(varchar, dateadd(d, -1, T.endDate)),
    H.originid,
    hitcount = sum(H.hitcount)
from
    @hits H
    inner join @Trimester T on 
        H.hitdate >= T.beginDate and
        H.hitdate < T.endDate
group by
    T.beginDate,
    T.endDate,
    H.originid
order by
    T.beginDate,
    H.originid;

The result set is:

查询结果

One difference between my result set and yours is that yours contains an entry saying that there were zero hits for originid = 1 . I haven't done anything with this since I don't know where you're storing the set of valid originid values, but it should be fairly easy for you to tweak this query to support that if you want, just by meddling with the joins in the final step.

Update: A little more on my concerns with using datepart :

  • Using datepart(qq, hitdate) by itself assumes that quarter boundaries are calendar quarters, eg 1 Apr–30 Jun, as has already been noted in another answer.

  • Using something like datepart(qq, dateadd(mm,1,hitdate)) shifts the quarter by a month so you get one starting on 1 May as in your sample data, but suppose you want to start in June instead of May. You have to change the query.

  • datepart(qq, ... by itself will return the same value for two dates that are in the same quarter but different years. So hits on 1 May 2011 and those on 1 May 2012 will be grouped together. You can adjust for this, but at the time of this writing, the other answers aren't doing so.

  • Using datepart in any of the ways described above assumes that quarters begin on the first day of a month. Suppose the earliest date in your result set was 15 May. My query will treat 15 May–14 Aug as the first quarter, 15 Aug–14 Nov as the second quarter, etc.

That last one, I think, might be the most difficult to overcome if you want a solution that uses datepart(qq, ...) . If you're comfortable with the assumption that the start date is always the first day of a month, then a simpler solution than I have suggested is possible.

In the query above, if you want to use a different start date, you simply assign the date you want to @beginDate .

Use Group BY and DATEPART

SELECT DATEPART(qq,hitdate) AS Trimester, originid, SUM(hitcount)
FROM yourtable
GROUP BY DATEPART(qq,hitdate), originid

Use DATEPART to break the date into quarters. As your 3 month periods are not standard quarters (Jan-Mar, Apr-June), you can use DATEADD in order to offset your hitdate so that it falls in the appropriate three month period:

Select DATEPART(qq,dateadd(mm,1,hitdate)) as QRTR, originid, SUM(hitcount)
from tablename
group by DATEPART(qq,dateadd(mm,1,hitdate)), originid

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