TSQL: How to Split dates and count months in between - sql-server

I need to split these dates by FY and get the number of months between the dates. I have the FY split answered previously at How to duplicate Rows with new entries link.
Having the following dates:-
ID Start dt End dt
2550 10/1/2010 9/30/2011
2551 8/1/2014 7/31/2015
2552 6/1/2013 5/31/2015
2553 5/10/2012 6/11/2014
I would like the following result set:
ID FY # of Months Start Dt End Dt
2550 2011 12 10/1/2010 9/30/2011
2551 2014 2 8/1/2014 9/30/2014
2551 2015 10 10/1/2014 7/31/2015
2552 2013 4 6/1/2013 9/30/2013
2552 2014 12 10/1/2013 9/30/2014
2552 2015 8 10/1/2014 5/31/2015
2553 2012 5 5/10/2012 9/30/2012
2553 2013 12 10/1/2012 9/30/2013
2553 2014 9 10/1/2013 6/11/2014
An FY is considered from Oct to Sept of the next year.
Thanks in advance!

It was a fun query to build:
declare #t table(id int, start_dt datetime, end_dt datetime)
Insert into #t(id, start_dt, end_dt) Values
(2550, '2010/10/1', '2011/9/30')
, (2551, '2014/8/1', '2015/7/31')
, (2552, '2013/6/1', '2015/5/31')
, (2553, '2012/5/10', '2014/6/11')
, (2554, '2012/5/10', '2012/11/1')
, (2555, '2012/5/10', '2012/8/11')
; with data as(
Select id, start_dt, end_dt, next_y = DATEADD(month, 9, DATEADD(year, DATEDIFF(year, 0, DATEADD(month, 3, start_dt)), 0))
From #t
), split as (
Select id, start_dt
, end_dt = case when next_y > end_dt then end_dt else DATEADD(day, -1, next_y) end
, next_y = DATEADD(year, 1, next_y)
From data as d
Union All
Select s.id
, DATEADD(day, 1, s.end_dt)
, case when DATEADD(day, -1, next_y) < t.end_dt then DATEADD(day, -1, next_y) else t.end_dt end
, next_y = DATEADD(year, 1, s.next_y)
From split as s
Inner Join #t as t on t.id = s.id
Where s.end_dt < t.end_dt
)
Select ID, FY = year(end_dt), '# of Months' = DATEDIFF(month, start_dt, end_dt)+1, 'Start Dt' = start_dt, 'End Dt' = end_dt
From split
Order by id, start_dt, end_dt

Related

FiscalYear Calendar in SQL Server

I have a requirement to create a Financial year Calendar with
start date = 2019-03-31 and end_date = 2020-03-28
declare #startdate date = '2019-03-30'
declare #enddate date = '2020-03-28'
declare #dates table (date date, MonthNo int)
while #startdate < #enddate
BEGIN
set #startdate = dateadd(dd,1,#startdate)
insert #dates (date)
select #startdate
END
select * from #dates
I will need to populate month number column
MonthNumber : In this case, It can not be obtained through typical query using
DATEPART(MONTH, [date])
Requirement : Month number should be starting at 1 and should be assigned as shown below
NoOfDays trading_month
28 1
28 2
35 3
28 4
28 5
35 6
28 7
28 8
35 9
28 10
28 11
35 12
Ex: Trading month = 1 is between 2019-03-31 and 2019-04-28 and next 28 days = month 2 and next 35 days month = 3 etc..
ps: I do not want to use case statement 12 times to populate month number, is there any alternate way to achieve this
can achieve this way
select *, case when ROW_NUMBER() over (order by date) between 1 and 28 then 1
when ROW_NUMBER() over (order by date) between 29 and 28+28 then 2
when ROW_NUMBER() over (order by date) between 28+28+1 and 28+29+35 then 3
----so on
end MonthNo
from #dates
Here is one option.
Example
Declare #Date1 date = '2019-03-31'
Select [Date]=DateAdd(DAY,Row_Number() over (Order by M,D)-1,#Date1)
,[Year]=DatePart(YEAR,#Date1)
,[Month]=M
From (
Select *
From (Select Top (28) D=Row_Number() Over (Order By (Select Null)) From master..spt_values n1 ) A
Cross Join (Values (1),(2),(4),(5),(7),(8),(10),(11)) B(M)
Union All
Select *
From ( Select Top (35) D=Row_Number() Over (Order By (Select Null)) From master..spt_values n1 ) A
Cross Join (Values (3),(6),(9),(12)) B(M)
) A
Order by 1,2
Returns
Date Year Month
2019-03-31 2019 1
2019-04-01 2019 1
2019-04-02 2019 1
2019-04-03 2019 1
2019-04-04 2019 1
2019-04-05 2019 1
2019-04-06 2019 1
2019-04-07 2019 1
2019-04-08 2019 1
2019-04-09 2019 1
...
2020-03-21 2019 12
2020-03-22 2019 12
2020-03-23 2019 12
2020-03-24 2019 12
2020-03-25 2019 12
2020-03-26 2019 12
2020-03-27 2019 12
2020-03-28 2019 12
Lucky you ... Download and execute the code in my article SQL Server Calendar Table, then download and modify the code in SQL Server Calendar Table: Fiscal Years to fit your definition of a fiscal year and execute that.
I once had a client that ran on 'Crop year' where their fiscal years started the last week of May, and the only way to pull that off was to create a wompload of T-SQL to populate a Calendar table. Once that was done for any given dataset all I had to do was JOIN on the date column and then I could get all the fiscal-related column values I wanted without having to recalculate them every time.
{edit: Here's the relevant code that creates the 4-4-5 from the above Fiscal Weeks article}
USE calendar
GO
/*
Calendar table: Populate the six fiscal_ columns
2015-09-16
*/
Declare #dtYearStart date, #dtStart date, #dtEnd date, #dt date
Declare #fiscal_month tinyint = 1, #fiscal_year smallint , #fiscal_week_in_month tinyint, #fiscal_week_in_year tinyint, #fiscal_day_in_week tinyint, #fiscal_day_in_month tinyint
Declare #counter int = 1, #counter_year int = 1, #counter_month int = 1, #counter_week int = 1, #counter_day int = 1
-- Run this for 19 years from May 2000 to May 2020
WHILE #counter_year <= 19
begin
-- Per the article image, the last day of the year is the last Sunday in May.
SELECT #dtYearStart = MAX(PKDate), #dtEnd = MAX(PKDate)
FROM days
WHERE continuous_year = #counter_year AND calendar_month = 5 AND calendar_day_in_week = 1
-- YEARS and MONTHS
-- Set the year
SELECT #fiscal_year = YEAR(#dtYearStart) + 1, #fiscal_month = 1
SET #counter = 1
WHILE #counter <= 12
begin
SELECT #dtStart = DATEADD(day, 1, #dtEnd)
SELECT #dtEnd = DATEADD(day, CASE WHEN #fiscal_month IN (1, 4, 7, 10) THEN 34 ELSE 27 END, #dtStart)
UPDATE days
SET fiscal_year = #fiscal_year, fiscal_month = #fiscal_month
FROM days
WHERE PKDate >= #dtStart AND PKDate <= #dtEnd
;WITH ro AS (SELECT PKDate, RANK() OVER (ORDER BY PKDate) as row_order FROM days WHERE fiscal_year = #fiscal_year AND fiscal_month = #fiscal_month)
UPDATE days
SET fiscal_day_in_month = row_order
FROM days
JOIN ro ON days.PKDate = ro.PKDate
-- TESTING ONLY, comment the below line out in production
-- SELECT 'Year and Month' as label, PKDate, fiscal_year, fiscal_month, fiscal_day_in_month FROM days WHERE PKDate >= #dtStart AND PKDate <= #dtEnd
SELECT #counter = #counter + 1, #fiscal_month = #fiscal_month + 1
end
-- WEEKS
SELECT #counter = 1, #counter_week = 1, #dtEnd = #dtYearStart
WHILE #counter <= 52
begin
SELECT #dtStart = DATEADD(day, 1, #dtEnd)
SELECT #dtEnd = DATEADD(day, 6, #dtStart)
UPDATE days
SET fiscal_week_in_month = #counter_week, fiscal_week_in_year = #counter
FROM days
WHERE PKDate >= #dtStart AND PKDate <= #dtEnd
-- TESTING ONLY, comment the below line out in production
-- SELECT 'Week' as label, PKDate, fiscal_week_in_year, fiscal_week_in_month FROM days WHERE PKDate >= #dtStart AND PKDate <= #dtEnd
SELECT #counter = #counter + 1
-- Get the fiscal month of the row to determine if the month has 4 or 5 weeks.
SELECT #fiscal_month = fiscal_month FROM days WHERE PKDate = #dtStart
SELECT #counter_week = CASE
WHEN #fiscal_month IN (1, 4, 7, 10) AND #counter_week = 5 THEN 1
WHEN #fiscal_month IN (1, 4, 7, 10) AND #counter_week < 5 THEN #counter_week + 1
WHEN #fiscal_month NOT IN (1, 4, 7, 10) AND #counter_week = 4 THEN 1
WHEN #fiscal_month NOT IN (1, 4, 7, 10) AND #counter_week < 4 THEN #counter_week + 1 END
end
-- DAYS
;WITH ro AS (SELECT PKDate, RANK() OVER (ORDER BY PKDate) as row_order FROM days WHERE fiscal_year = #fiscal_year)
UPDATE days
SET fiscal_day_in_year = row_order
FROM days
JOIN ro ON days.PKDate = ro.PKDate
SELECT #counter_year = #counter_year + 1
end
Good luck.
Jim
I was able to get the expected results with the following query. The only addition to the original table was an identity column. Using most of your original code:
declare #startdate date = '2019-03-30'
declare #enddate date = '2020-03-28'
declare #dates table (pkindex int IDENTITY(1,1), [date] date, MonthNo tinyint)
while #startdate < #enddate
BEGIN
set #startdate = dateadd(dd,1,#startdate)
insert #dates ([date])
select #startdate
END
DECLARE #requirments TABLE (NoOfDays tinyint, trading_month tinyint)
INSERT INTO #requirments VALUES
(28, 1), (28, 2), (35, 3), (28, 4), (28, 5), (35, 6), (28, 7), (28, 8), (35, 9)
,(28, 10), (28, 11), (35, 12)
UPDATE #dates
SET MonthNo =
(SELECT MIN(R.trading_month)
FROM #requirments R
WHERE pkindex <
(SELECT SUM(R2.NoOfDays)
FROM #requirments R2
WHERE R2.trading_month < R.trading_month + 1
) + 1
)
SELECT * FROM #dates
Not sure if I understand your goal here exactly, why 1 = 28 specifically. However it seems like your datepart idea was inline with your desired solution if you just do this I get the layout you wanted.
declare #startdate date = '2019-03-30'
declare #enddate date = '2020-03-28'
declare #dates table (NoOfDays date, trading_mo int)
while #startdate < #enddate
BEGIN
set #startdate = dateadd(dd,1,#startdate)
insert #dates (NoOfDays)
select #startdate
END
select count(NoOfDays) noOfDays,
datepart(month,NoOfDays) trading_mo
from #dates
group by datepart(month,NoOfDays)
order by trading_mo

Always show empty weeks

I need to be able to show all week numbers per month regardless of whether there are hours for that week or not.
Change this:
2.00 2 July
2.25 3 July
7.25 5 July
To this:
0.00 1 July
2.00 2 July
2.25 3 July
0.00 4 July
7.25 5 July
Here is the current query I am working with:
SELECT SUM(te.Hours_Bill) AS HoursBilled
, DATEPART(dd, DATEDIFF(dd, 0, te.Date_Start) / 7 * 7 ) / 7 + 1 AS HoursWeekNumber
, DATENAME(mm, te.Date_Start) AS HoursMonthName
FROM AGR_Header ah
INNER
JOIN Time_Entry te
ON te.Agr_Header_RecID = ah.AGR_Header_RecID
INNER
JOIN Company co
ON co.Company_RecID = ah.Company_RecID
WHERE (te.Date_Start >= DATEADD(mm, DATEDIFF(mm, 0, GETDATE()) -6, 0) AND te.Date_Start <= DATEADD(mm, DATEDIFF(mm, -1, GETDATE()) -1, -1))
GROUP
BY DATEPART(dd, DATEDIFF(dd, 0, te.Date_Start) / 7 * 7 ) / 7 + 1
, DATENAME(mm, te.Date_Start)
, DATEPART(mm, te.Date_Start)
ORDER
BY DATEPART(mm, te.Date_Start) ASC
, DATEPART(dd, DATEDIFF(dd, 0, te.Date_Start) / 7 * 7 ) / 7 + 1 ASC
By default I want to show all week numbers for a month and if no results for the week, display 0.
This may help you
CREATE TABLE #t
(
hr NUMERIC(22, 6),
weekno INT,
mon VARCHAR(20)
)
INSERT INTO #t
VALUES
(2.00 , 2 , 'October'),
(2.25 , 3 , 'October'),
(7.25 , 5 , 'October')
WITH dates AS (
SELECT 1 id,datename(month,(getdate())) mon
UNION ALL
SELECT id+1, mon FROM dates WHERE id<5
)
SELECT Isnull(hr, 0) hrs,
id AS weekno,
d.mon AS [month]
FROM #t t
RIGHT JOIN dates d
ON t.mon = d.mon
AND t.weekno = d.id
output
hrs weekno month
0.000000 1 October
2.000000 2 October
2.250000 3 October
0.000000 4 October
7.250000 5 October

DAY shift calculations

Input table
Start time End Time
8/12/14 17:00 8/14/14 12:00
I need to show the output as below
Date 11:00 to 23:00 23:00 to 11:00
8/12/14 6 1
8/13/14 12 12
8/14/14 1 11
I know you all will say "What did you try ?"
But the answer "I Can't think of where to start"
Using recursive CTE:
SQL Fiddle
MS SQL Server 2012 Schema Setup:
Query 1:
DECLARE #StartDate DateTime = '2014-08-12 17:00'
DECLARE #EndDate DateTime = '2014-08-14 12:00'
;WITH CTE
AS
(
SELECT #StartDate AS [Date],
CAST(#StartDate As time) AS [Time]
UNION ALL
SELECT DATEADD(HH, 1, [Date]) As [Date],
CAST(DATEADD(HH, 1, [Date]) AS Time) AS [Time]
FROM CTE
WHERE DATEADD(HH, 1, [Date]) < #EndDate
)
SELECT CAST([Date] AS Date) [Date],
SUM(CASE WHEN [Time] BETWEEN '11:00' AND '22:59'
THEN 1 ELSE 0 END) as [11:00 to 23:00],
SUM(CASE WHEN [Time] BETWEEN '00:00' AND '10:59'
THEN 1
WHEN [Time] Between '23:00' and '23:59' THEN 1
ELSE 0 END) as [23:00 to 11:00]
FROM CTE
GROUP BY CAST([Date] AS Date)
ORDER BY [Date]
Results:
| DATE | 11:00 TO 23:00 | 23:00 TO 11:00 |
|------------|----------------|----------------|
| 2014-08-12 | 6 | 1 |
| 2014-08-13 | 12 | 12 |
| 2014-08-14 | 1 | 11 |
The following shows one way to do with using a Tally tables (aka "table of numbers" table.)
Key assumptions I made:
Start and End times are both known
Start and End times are for precise hours (e.g. 1:00, 2:00, and not 13:23 or 00:00.00.007). (This is why I used the smalldatetime datatype)
End time is always greater than Start time
I went further and assumed that the data is stored in a table for multiple entities--that is, you'd ultimately want to process multiple items in one query. If you only ever want to do this for one item at a time, (a) the query below can be readily chopped down, and (b) it'd probably be easier to do in C# or whatever the calling language is.
Setting up testing data:
-- DROP TABLE Testing
CREATE TABLE Testing
(
EntryId int not null
,StartTime smalldatetime not null
,EndTime smalldatetime not null
)
INSERT Testing values
(1, 'Aug 12, 2014 17:00', 'Aug 14, 2014 12:00') -- Original problem
,(2, 'Aug 11, 2014 00:00', 'Aug 11, 2014 23:00') -- 23 hours
,(3, 'Aug 11, 2014 00:00', 'Aug 12, 2014 00:00') -- 24 hour shift
,(4, 'Aug 11, 2014 12:00', 'Aug 12, 2014 12:00') -- Noon to Noon
,(11, 'Aug 22, 2014 4:00', 'Aug 22, 2014 5:00') -- One-hour problem cases
,(12, 'Aug 22, 2014 10:00', 'Aug 22, 2014 11:00') -- One-hour problem cases
,(13, 'Aug 22, 2014 11:00', 'Aug 22, 2014 12:00') -- One-hour problem cases
,(14, 'Aug 22, 2014 12:00', 'Aug 22, 2014 13:00') -- One-hour problem cases
,(21, 'Aug 23, 2014 18:00', 'Aug 23, 2014 19:00') -- One-hour problem cases
,(22, 'Aug 23, 2014 22:00', 'Aug 23, 2014 23:00') -- One-hour problem cases
,(23, 'Aug 23, 2014 23:00', 'Aug 24, 2014 00:00') -- One-hour problem cases
,(24, 'Aug 24, 2014 00:00', 'Aug 24, 2014 1:00') -- One-hour problem cases
My routine:
DECLARE
#Earliest smalldatetime
,#Latest smalldatetime
-- This could be thrown in as a first CTE, but doing so would make the overall query that much less comprehensible.
SELECT
#Earliest = min(StartTime)
,#Latest = max(EndTime)
from Testing
--where <filtering criteria, if you're not parsing the whole table)
;WITH
Pass0 as (select 1 as C union all select 1), --2 rows
Pass1 as (select 1 as C from Pass0 as A, Pass0 as B),--4 rows
Pass2 as (select 1 as C from Pass1 as A, Pass1 as B),--16 rows
Pass3 as (select 1 as C from Pass2 as A, Pass2 as B),--256 rows
Pass4 as (select 1 as C from Pass3 as A, Pass3 as B),--65536 rows
Tally as (select row_number() over(order by C) as Number from Pass4),
DateRange as (select
dateadd(hh, ta.Number, #Earliest) ShiftHour
from Tally ta
where dateadd(hh, ta.Number, #Earliest) <= #Latest)
SELECT
te.EntryId
,cast(dateadd(hh, -1, dr.Shifthour) as date) [Date]
,sum(case when datepart(hh, dateadd(hh, -1, dr.Shifthour)) between 11 and 22 then 1 else 0 end) [11:00 to 23:00]
,sum(case when datepart(hh, dateadd(hh, -1, dr.Shifthour)) between 11 and 22 then 0 else 1 end) [23:00 to 11:00]
from Testing te
inner join DateRange dr
on dr.ShiftHour > te.StartTime
and dr.ShiftHour <= te.Endtime
group by
te.EntryId
,cast(dateadd(hh, -1, dr.Shifthour) as date)
order by
te.EntryId
,cast(dateadd(hh, -1, dr.Shifthour) as date)
Plug this in to show the results without the grouping, invaluable in figuring out what's going on in there:
;WITH
Pass0 as (select 1 as C union all select 1), --2 rows
Pass1 as (select 1 as C from Pass0 as A, Pass0 as B),--4 rows
Pass2 as (select 1 as C from Pass1 as A, Pass1 as B),--16 rows
Pass3 as (select 1 as C from Pass2 as A, Pass2 as B),--256 rows
Pass4 as (select 1 as C from Pass3 as A, Pass3 as B),--65536 rows
Tally as (select row_number() over(order by C) as Number from Pass4),
DateRange as (select
dateadd(hh, ta.Number, #Earliest) ShiftHour
from Tally ta
where dateadd(hh, ta.Number, #Earliest) <= #Latest)
SELECT
te.EntryId
,dateadd(hh, -1, dr.Shifthour)
,cast(dateadd(hh, -1, dr.Shifthour) as date) [Date]
,datepart(hh, dateadd(hh, -1, dr.Shifthour))
from Testing te
inner join DateRange dr
on dr.ShiftHour > te.StartTime
and dr.ShiftHour <= te.Endtime
order by
te.EntryId
,cast(dateadd(hh, -1, dr.Shifthour) as date)
,dateadd(hh, -1, dr.Shifthour)
The hard parts were:
Getting the 24th hour (Aug 23, 00:00) into the previous day (Aug 24). That's why I shift everything back one day [edit] one HOUR
< and > logic when dealing with multiple entries
Reconciling with the fact that the results for a "One day Noon to Noon" sample just looks goofy.
Yeah, this may be over-engineering, but some times you just have to give it a try. I couldn't say how well it would perform on large datasets, and if your first and last dates are more than four years apart you'll need to add "Pass5" when building the tally table.
DECLARE #StartTime DATETIME='08/01/2013 00:00:00'
,#EndTime DATETIME='08/03/2013 23:00:00'
DECLARE #StartDate DATE,
#Starthour INT,
#Startmin INT,
#EndDate DATE,
#Endhour INT,
#Endmin INT
SELECT #StartDate=CONVERT(DATE,#StartTime,101),#Starthour=DATEPART(HOUR,#StartTime),#Startmin=DATEPART(MINUTE,#StartTime)
SELECT #EndDate=CONVERT(DATE,#EndTime,101),#Endhour=DATEPART(HOUR,#EndTime),#Endmin=DATEPART(MINUTE,#EndTime)
DECLARE #T TABLE (StartTime DATETIME, EndTime DATETIME)
INSERT #T VALUES (DATEADD(MINUTE,-#Startmin,#StartTime), DATEADD(MINUTE,-#Endmin,#EndTime))
DECLARE #Final1 TABLE (ShiftDate DATETIME,[11:00 to 23:00] INT,[23:00 to 11:00] INT)
DECLARE #Final2 TABLE (ShiftDate DATETIME,[11:00 to 23:00] INT,[23:00 to 11:00] INT)
DECLARE #Final3 TABLE (ShiftDate DATETIME,[11:00 to 23:00] INT,[23:00 to 11:00] INT)
;WITH CTE AS
(
SELECT StartTime AS dt, EndTime,StartTime
FROM #T
UNION ALL
SELECT DATEADD(MINUTE, 60, dt) AS dt, EndTime,DATEADD(MINUTE, 60,StartTime) AS StartTime
FROM CTE
WHERE dt < DATEADD(HOUR, -1, EndTime)
)
INSERT INTO #Final1([11:00 to 23:00],[23:00 to 11:00],ShiftDate)
SELECT
CASE
WHEN CAST(dt AS TIME) >= '11:00' AND CAST(dt AS TIME) < '23:00' THEN 1
ELSE 0
END AS '11:00 to 23:00',
CASE
WHEN CAST(dt AS TIME) >= '11:00' AND CAST(dt AS TIME) < '23:00' THEN 0
ELSE 1
END AS '23:00 to 11:00'
,StartTime
FROM CTE
INSERT INTO #Final2([11:00 to 23:00],[23:00 to 11:00],ShiftDate)
SELECT SUM([11:00 to 23:00])*60 AS [11:00 to 23:00]
,SUM([23:00 to 11:00])*60 AS [23:00 to 11:00]
,CONVERT(DATE,DATEADD(MINUTE,-660,ShiftDate)) AS ShiftDate
FROM #Final1
GROUP BY CONVERT(DATE,DATEADD(MINUTE,-660,ShiftDate))
INSERT INTO #Final3([11:00 to 23:00],[23:00 to 11:00],ShiftDate)
SELECT CASE WHEN ShiftDate=#StartDate AND #Starthour BETWEEN 11 AND 22 THEN [11:00 to 23:00]-#Startmin ELSE [11:00 to 23:00] END AS [11:00 to 23:00],
CASE WHEN ShiftDate=#StartDate AND #Starthour BETWEEN 23 AND 10 THEN [23:00 to 11:00]-#Startmin ELSE [23:00 to 11:00] END AS [23:00 to 11:00],
ShiftDate
FROM #Final2
SELECT ShiftDate,
CONVERT(VARCHAR(10),
(CASE WHEN ShiftDate=#EndDate AND #Endhour BETWEEN 11 AND 22
THEN [11:00 to 23:00]+#Endmin
ELSE [11:00 to 23:00] END)) AS [11:00 to 23:00],
CONVERT(VARCHAR(10),
(CASE WHEN ShiftDate=#EndDate AND #Endhour BETWEEN 23 AND 10
THEN [23:00 to 11:00]+#Endmin
ELSE [23:00 to 11:00] END)) AS [23:00 to 11:00]
FROM #Final3

How to get the records grouping dates for a span of 3 days in SQL?

It's getting difficult to group and display records every 5 days.
Here is my data:
FLIGHT_DATE LANDINGS PILOTID COPILOTNAME MONT DPT
11/16/2013 1 A B 11 5.5
11/17/2013 1 A B 11 13
11/19/2013 1 A B 11 12.55
11/19/2013 1 A B 11 4
11/21/2013 1 A B 12 6
11/24/2013 1 A B 12 6.03
11/25/2013 1 A B 11 5.5
11/26/2013 1 A B 11 13
11/26/2013 1 A B 11 12.55
11/30/2013 1 A B 11 4
12/1/2013 1 A B 12 6
12/2/2013 1 A B 12 6.03
I want to show it as below:
Week Start Week End DPT
11/17/2013 11/21/2013 35.55
11/22/2013 11/26/2013 37.08
11/27/2013 12/1/2013 6
12/2/2013 12/6/2013 6.03
Here it is my proposed solution:
DECLARE #MinDate AS DATETIME = (SELECT MIN(flight_date) FROM flights);
WITH cte
AS
(
SELECT
flight_date, DATEDIFF(DAY, #MinDate, flight_date) AS NoDays,
DATEDIFF(DAY, #MinDate, flight_date)/5 AS NoGroup,
DPT
FROM flights
)
SELECT
DATEADD(DAY, NoGroup*5, #MinDate) AS [Week Start],
DATEADD(DAY, NoGroup*5+4, #MinDate) AS [Weed End],
SUM(DPT)
FROM cte
GROUP BY NoGroup;
The idea is to form groups of 5 days, then associate a record to a specific group based on division with 5. NoDays represents the days spent from MinDate to Flight_Date.
You can use this query. You need to specify the start date from which you want to count and the number of days in each period (which seems to be 5 in your case) but please adjust those numbers as needed.
declare #startdate date = '20131117'
declare #interval int = 5
select dateadd(dd, #interval * (o.number - 1), #startdate) WeekStart,
dateadd(dd, #interval * o.number - 1, #startdate) WeekEnd,
sum(d.DPT) DPT
from yourtable d
inner join
(select ROW_NUMBER() over (order by object_id) as number from sys.all_objects) as o
on d.FLIGHT_DATE >= dateadd(dd, #interval * (o.number - 1), #startdate)
and d.FLIGHT_DATE < dateadd(dd, #interval * o.number, #startdate)
group by o.number
order by dateadd(dd, #interval * (o.number - 1), #startdate)

How to count the number of days in a given month and in a given year in MSSQL

EmployeeID RecordID DateRecord
1 1 2/19/2013 12:00:00 AM
1 2 2/21/2013 12:00:00 AM
1 3 2/23/2013 12:00:00 AM
1 4 2/27/2013 12:00:00 AM
1 5 3/3/2013 12:00:00 AM
2 11 3/10/2013 12:00:00 AM
2 12 3/14/2013 12:00:00 AM
1 14 3/16/2013 12:00:00 AM
How can I count the number of days?
Example in February 2013 which has "19, 21, 23, 27" that should be count to "4" days .. ??
I found this method ..
SELECT DATEPART(yy, Daterecord),
DATEPART(mm, Daterecord),
DATEPART(dd, Daterecord),
COUNT(*)
FROM Records
GROUP BY DATEPART(yy, Daterecord),
DATEPART(mm, Daterecord),
DATEPART(dd, Daterecord)
and resulted to ..
2013 2 19 1
2013 2 21 1
2013 2 23 1
2013 2 27 1
2013 3 3 1
2013 3 10 1
2013 3 14 1
2013 3 16 1
it just get the specific dates but didm't count the total number of days in each month .. help me .. pls
I have change few names hopr you won't mind
WITH Emp_CTE AS (
SELECT EmployeeID ,DATEPART(yy, Daterecord) AS years,
DATEPART(mm, Daterecord) AS months
-- DATEPART(dd, Daterecord) AS days
FROM testTrial
)
SELECT COUNT(months) AS noOfMonths ,* FROM Emp_CTE GROUP BY months,EmployeeID,years
SqlFiddle
Let you try this:-
1: Find the number of days in whatever month we're currently in
DECLARE #dt datetime
SET #dt = getdate()
SELECT #dt AS [DateTime],
DAY(DATEADD(mm, DATEDIFF(mm, -1, #dt), -1)) AS [Days in Month]Solution
2: Find the number of days in a given month-year combo
DECLARE #y int, #m int
SET #y = 2012
SET #m = 2
SELECT #y AS [Year],
#m AS [Month],
DATEDIFF(DAY,
DATEADD(DAY, 0, DATEADD(m, ((#y - 1900) * 12) + #m - 1, 0)),
DATEADD(DAY, 0, DATEADD(m, ((#y - 1900) * 12) + #m, 0))
) AS [Days in Month]
If your table is called Employee then this will do the trick:
select convert(varchar, DateRecord, 112)/ 100, count(*)
from Employee
group by convert(varchar, DateRecord, 112)/ 100
Your initial query was almost right, just needed to remove the DATEPART(dd, Daterecord) from the grouping and it would work. Add in a HAVING clause to find the records from the month of February:
SELECT
DATEPART(yy, Daterecord),
DATEPART(mm, Daterecord),
COUNT(1)
FROM
Records
GROUP BY
DATEPART(yy, Daterecord),
DATEPART(mm, Daterecord)
HAVING
DATEPART(yy, eCreationTime) = 2013
AND DATEPART(mm, Daterecord) = 2
there is no 'yearmonth' in the suggested code ??
try this perhaps
select
datename(month,daterecord) as [Month]
, year(DateRecord) as [Year]
, count(distinct DateRecord ) as day_count
, count(distinct dateadd(day, datediff(day,0, DateRecord ), 0)) as daytime_count
from your_table
where ( DateRecord >= '20130201' and DateRecord < '20130301' )
group by
datename(month,daterecord)
, year(DateRecord)
note the column [daytime_count] is only required if the field [DateRecord] has times othe than 12:00 AM (i.e. it "trims off" times so you deal with dates at 12:AM)
Regarding date range selections: many people will feel that using 'between' is the solution however that isn't true and the safest most reliable method is as I shown above. Note that the higher date is 1st March, but we are asking for information that is less than the 1st March, so we don't need to worry about leap years and we don't have to worry about hours and minutes either.
see: What do BETWEEN and the devil have in common?
try this...
declare #date2 nvarchar(max)
set #date2 = (select getdate())
select DateDiff(Day,#date2,DateAdd(month,1,#date2))

Resources