I have specific date range like
From Date To Date
---------------------------
2012-11-10 2012-11-15
2012-11-21 2012-11-22
2012-11-30 2012-12-01
I want to write a SQL query which calculates the total no of days between two dates and sum total number of days of particular month
The output I wanted is,
No of days month
--------------------
9 11
1 12
Can anyone help me to write this SQL query?
Ideally, you have a table named "Dates" with all the dates you will ever use, eg year 1950 through 2100. This query will give you the result you want:
select dateadd(m,datediff(m, 0, d.thedate),0) themonth, count(1)
from dates d
join ranges r on d.thedate between r.[from date] and r.[to date]
group by datediff(m, 0, d.thedate)
order by themonth;
Result:
| themonth | COLUMN_1 |
-------------------------
| 2012-11-01 | 9 |
| 2012-12-01 | 1 |
Note that instead of just showing "11" or "12" as month, which doesn't work well if you have ranges going above 12 months, or doesn't help sorting when it crosses a new year, this query shows the first day of the month instead.
If not, you can virtually create a dates
table on the fly, per the expanded query below:
;with dates(thedate) as (
select dateadd(yy,years.number,0)+days.number
from master..spt_values years
join master..spt_values days
on days.type='p' and days.number < datepart(dy,dateadd(yy,years.number+1,0)-1)
where years.type='p' and years.number between 100 and 150
-- note: 100-150 creates dates in the year range 2000-2050
-- adjust as required
)
select dateadd(m,datediff(m, 0, d.thedate),0) themonth, count(1)
from dates d
join ranges r on d.thedate between r.[from date] and r.[to date]
group by datediff(m, 0, d.thedate)
order by themonth;
The full working sample is given here: SQL Fiddle
尝试这个
select ((day(date_to)) - (day(date_from))) as no_of_days,month(date_from)as month from tablename
Pardon me for badly written SQL.
The assumption is that the month diff. between fromdate and todate is 1.
Schema
CREATE TABLE dateData
(fromdate datetime, todate datetime)
;
INSERT INTO dateData
(fromdate, todate)
VALUES
('2012-11-10', '2012-11-15'),
('2012-11-21', '2012-11-22'),
('2012-11-30', '2012-12-01')
;
SQL
select mth, sum(days) as daysInMth
from
(
select month(fromdate) as mth,
sum(case
when month(fromdate) = month(todate) then datediff(dd, fromdate, todate)+1
else datediff(dd, fromdate, dateadd(mm, 1, fromdate) - day(fromdate)) + 1 end)
as days
from dateData
group by month(fromdate)
union
select month(todate) as mth,
sum(case when month(todate) <> month(fromdate) then
datediff(dd, fromdate, dateadd(mm, 1, fromdate) - day(fromdate)) + 1
else
case when month(todate) = month(fromdate) then 0 else
datediff(dd, convert(datetime, year(todate) + '-' + month(todate) + '-1'), todate)
end
end) as days
from dateData
group by month(todate)
) aggregated
group by mth
View on SQLFiddle: http://www.sqlfiddle.com/#!3/9f7da/56
I'd break it down into several steps (each given a separate CTE):
declare @Ranges table (FromDate date not null,ToDate date not null)
insert into @Ranges (FromDate,ToDate) values
('20121110','20121115'),
('20121121','20121122'),
('20121130','20121201')
;with Months as (
select
DATEADD(month,DATEDIFF(month,'20010101',FromDate),'20010101') as MonthStart,
DATEADD(month,DATEDIFF(month,'20010101',FromDate),'20010131') as MonthEnd
from @Ranges
union /* not all */
select
DATEADD(month,DATEDIFF(month,'20010101',ToDate),'20010101') as MonthStart,
DATEADD(month,DATEDIFF(month,'20010101',ToDate),'20010131') as MonthEnd
from @Ranges
), MonthRanges as (
select
CASE WHEN r.FromDate > m.MonthStart then r.FromDate ELSE m.MonthStart END as StartRange,
CASE WHEN r.ToDate < m.MonthEnd then r.ToDate ELSE m.MonthEnd END as EndRange
from
@Ranges r
inner join
Months m
on
r.ToDate >= m.MonthStart and
r.FromDate <= m.MonthEnd
)
select
DATEPART(month,StartRange),
SUM(DATEDIFF(day,StartRange,EndRange)+1) /* Inclusive */
from
MonthRanges
group by
DATEPART(month,StartRange)
First, the Months
CTE finds the first and last days of each month that we might be interested in(*). Then, MonthRanges
recombines this data with the original ranges and splits them as required so that each period we're dealing with only represents days from a single month. Then we can just use DATEDIFF
to calculate the number of days that each range spans (and add 1 since we're dealing with dates and want inclusive values)
(*) the Months
CTE will work provided that we're not dealing with any ranges that span over multiple months and where no other ranges start or end in the intervening months. If you need to cope with this situation, I need to revise the Months
CTE. Eg if we add ('20120115','20120315')
(and no other range) to the above sample, we won't get a result for February using the above. Do we need to cope with this situation?
To cope with the situation noted in (*), we can replace the Months
CTE in the above query with:
;With LastMonth as (
select MAX(ToDate) as Mx from @Ranges
), MultiMonths as (
select
DATEADD(month,DATEDIFF(month,'20010101',FromDate),'20010101') as MonthStart,
DATEADD(month,DATEDIFF(month,'20010101',FromDate),'20010131') as MonthEnd
from @Ranges
union all
select
DATEADD(month,1,MonthStart),
DATEADD(month,1,MonthEnd)
from MultiMonths
where MonthStart <= (select Mx from LastMonth)
), Months as (
select distinct MonthStart,MonthEnd from MultiMonths
)
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.