I am using SQL SERVER 2008. I have a table called Cut-Off table. This table only shows the StartDate of cut-off for every month. So i am creating a temp table that will store StartDate, EndDate, Month and Year. But i don't know how to get the EndDate with datetime.
TABLE
----------------------------------------------------------
StartDate EndDate Month Year
----------------------------------------------------------
2014-12-15 00:00:00 NULL 12 2014
2015-01-26 00:00:00 NULL 1 2015
2015-02-26 00:00:00 NULL 2 2015
---------------------------------------------------------
EXPECTED OUTPUT
--------------------------------------------------------------------
StartDate EndDate Month Year
------------------------------------------------------------------
2014-12-15 00:00:00 2015-01-25 23:59:99 12 2014
2015-01-26 00:00:00 2015-02-25 23:59:99 1 2015
2015-02-26 00:00:00 2015-03-25 23:59:99 2 2015
------------------------------------------------------------------
How i get the EndDate??
Was unable to get 99 seconds in '23:59:99'
Try this:
CREATE TABLE #temp(StartDate datetime, EndDate datetime, Month int, Year int)
INSERT #temp values
('2014-12-15 00:00:00', NULL, 12, 2014),
('2015-01-26 00:00:00', NULL, 1, 2015),
('2015-02-26 00:00:00', NULL, 2, 2015)
UPDATE t
SET EndDate = DateAdd(m, DateDiff(m, 0, StartDate), '1900-02-25T23:59:59')
FROM #temp t
SELECT * FROM #temp
Result:
StartDate EndDate Month Year
2014-12-15 00:00:00.000 2015-01-25 23:59:59.000 12 2014
2015-01-26 00:00:00.000 2015-02-25 23:59:59.000 1 2015
2015-02-26 00:00:00.000 2015-03-25 23:59:59.000 2 2015
Edit:
If you want to calculate the EndDate from next StartDate, here is your UPDATE statement:
UPDATE t
SET EndDate = (SELECT top 1 DateAdd(s, -1, StartDate) FROM #temp
WHERE t.StartDate < StartDate
ORDER BY startdate)
FROM #temp t
Result:
StartDate EndDate Month Year
2014-12-15 00:00:00.000 2015-01-25 23:59:59.000 12 2014
2015-01-26 00:00:00.000 2015-02-25 23:59:59.000 1 2015
2015-02-26 00:00:00.000 null 2 2015
SAMPLE TABLE
CREATE TABLE #TEMP(DATES DATETIME,DATES DATETIME,[MONTH] INT,[YEAR] INT)
INSERT INTO #TEMP
SELECT '2014-12-15 00:00:00', NULL, 12, 2014
UNION ALL
SELECT '2015-01-26 00:00:00', NULL, 1, 2015
UNION ALL
SELECT '2015-02-26 00:00:00', NULL, 2, 2015
QUERY
;WITH CTE AS
(
SELECT ROW_NUMBER() OVER(ORDER BY STARTDATE)RNO, STARTDATE
FROM #TEMP
)
,CTE2 AS
(
SELECT C1.STARTDATE,
ISNULL(DATEADD(S,-1,C2.STARTDATE),DATEADD(S,-1,DATEADD(MONTH,1,C1.STARTDATE))) ENDDATE
FROM CTE C1
LEFT JOIN CTE C2 ON C1.RNO=C2.RNO-1
)
UPDATE #TEMP
SET EndDate = CTE2.ENDDATE
FROM #TEMP T1
JOIN CTE2 ON T1.STARTDATE=CTE2.STARTDATE
RESULT
You can also do it with OUTER APPLY
select
c.StartDate
DATEADD(SECOND,-1,e.StartDate) EndDate
DATEPART(MONTH,StartDate) [Month],
DATEPART(YEAR,StartDate) [Year]
from [Cut-Off] c
outer apply (
select TOP 1
e.StartDate
from [Cut-Off] e
where
e.StartDate > c.StartDate
order by
e.StartDate
) e
Related
I have following 2019 data.
Date Calendar_year Weekend_indicator
2019-01-01 2019 weekday
2019-01-01 2019 weekday
2019-01-02 2019 weekday
2019-01-02 2019 weekday
and so on.
I need to give one record a day and another a night value and have it repeated for the entire year's data so that it would look like this.
Date Calendar_year Weekend_indicator day_night
2019-01-01 2019 weekday Day
2019-01-01 2019 weekday Night
2019-01-02 2019 weekday Day
2019-01-02 2019 weekday Night
Here is my code.
DECLARE #Year AS INT,
#FirstDateOfYear DATETIME,
#LastDateOfYear DATETIME
-- You can change #year to any year you desire
SELECT #year = 2019
SELECT #FirstDateOfYear = DATEADD(yyyy, #Year - 1900, 0)
SELECT #LastDateOfYear = DATEADD(yyyy, #Year - 1900 + 1, 0)
-- Creating Query to Prepare Year Data;
WITH cte AS (
SELECT 1 AS DayID,
#FirstDateOfYear AS FromDate,
DATENAME(dw, #FirstDateOfYear) AS Dayname
UNION ALL
SELECT cte.DayID + 1 AS DayID,
DATEADD(d, 1 ,cte.FromDate),
DATENAME(dw, DATEADD(d, 1 ,cte.FromDate)) AS Dayname
FROM cte
WHERE DATEADD(d,1,cte.FromDate) < #LastDateOfYear
)
SELECT c.FromDate AS Date
,#Year as calendar_year
,CHOOSE(datepart(dw, c.FromDate), 'WEEKEND', 'WEEKDAY', 'WEEKDAY',
'WEEKDAY', 'WEEKDAY', 'WEEKDAY', 'WEEKEND') as weekend_indicator
FROM CTE c
CROSS JOIN ( values (1), (2) ) tb (FromDate)
WHERE DayName IN ('Saturday','Sunday')
or dayname not in ('Saturday', 'Sunday')
order by c.FromDate
OPTION (MaxRecursion 1000)
How do I assign the day and the night value and have it repeated?
This simple query might point you in the right direction:
USE TEMPDB
CREATE TABLE #T (DateCol DATE, Calender_Year INT)
INSERT INTO #T VALUES ('20180101', 2018 )
SELECT *
FROM #T
CROSS APPLY (VALUES ('Day'), ('Night') ) AS C (Val)
If you want just to change your code without rewriting it,
add one more row as below:
DECLARE #Year AS INT,
#FirstDateOfYear DATETIME,
#LastDateOfYear DATETIME
-- You can change #year to any year you desire
SELECT #year = 2019
SELECT #FirstDateOfYear = DATEADD(yyyy, #Year - 1900, 0)
SELECT #LastDateOfYear = DATEADD(yyyy, #Year - 1900 + 1, 0);
-- Creating Query to Prepare Year Data;
WITH cte AS (
SELECT 1 AS DayID,
#FirstDateOfYear AS FromDate,
DATENAME(dw, #FirstDateOfYear) AS Dayname
UNION ALL
SELECT cte.DayID + 1 AS DayID,
DATEADD(d, 1 ,cte.FromDate),
DATENAME(dw, DATEADD(d, 1 ,cte.FromDate)) AS Dayname
FROM cte
WHERE DATEADD(d,1,cte.FromDate) < #LastDateOfYear
)
SELECT c.FromDate AS Date
,#Year as calendar_year
,CHOOSE(datepart(dw, c.FromDate), 'WEEKEND', 'WEEKDAY', 'WEEKDAY',
'WEEKDAY', 'WEEKDAY', 'WEEKDAY', 'WEEKEND') as weekend_indicator,
case tb.FromDate when 1 then 'Day' else 'Night' end as day_night -----<<<<<<-----
FROM CTE c
CROSS JOIN ( values (1), (2) ) tb (FromDate)
WHERE DayName IN ('Saturday','Sunday')
or dayname not in ('Saturday', 'Sunday')
order by c.FromDate
OPTION (MaxRecursion 1000)
I use a table variable for create this solution.
DECLARE
#Year int
,#FirstDateOfYear date
,#LastDateOfYear date
,#date_loop date
SET #YEAR = 2019
SELECT
#FirstDateOfYear = DATEADD(yyyy, #Year - 1900, 0)
,#LastDateOfYear = DATEADD(yyyy, #Year - 1900 + 1, 0)
,#date_loop = DATEADD(yyyy, #Year - 1900, 0) --initialize variable for loop
DECLARE #date_table TABLE ([Date] date, [Calendar_year] int, [Weekend_indicator] varchar(10), [day_night] varchar(5))
WHILE #date_loop < #LastDateOfYear
BEGIN
INSERT #date_table
SELECT d.[Date], d.[Calendar_year], d.[Weekend_indicator], ca.[day_night]
FROM (
SELECT
#date_loop AS [Date]
,YEAR(#date_loop) AS [Calendar_year]
,CHOOSE(datepart(dw, #date_loop), 'weekend', 'weekday', 'weekday','weekday', 'weekday', 'weekday', 'weekend') AS [Weekend_indicator]) AS d
CROSS APPLY (
SELECT 'Day' AS [day_night]
UNION
SELECT 'Night'
) AS ca
SET #date_loop = DATEADD(day,1,#date_loop)
END
SELECT *
FROM #date_table
If you don't have a calendar or numbers table, you can use an ad-hoc table
Example
Declare #Date1 date = '2019-01-01'
Declare #Date2 date = '2019-12-31'
Select [Date] = d
,Calendar_year = datepart(YEAR,d)
,Weekend_indicator = case when datename(WEEKDAY,d) in ('Saturday','Sunday') then 'weekend' else 'weekday' end
,day_night
From (
Select Top (DateDiff(DAY,#Date1,#Date2)+1) D=DateAdd(DAY,-1+Row_Number() Over (Order By (Select Null)),#Date1)
From master..spt_values n1,master..spt_values n2
) A
Cross Join (values ('Day'),('Night') ) b(day_night)
Order by D,day_night
Returns
Date Calendar_year Weekend_indicator day_night
2019-01-01 2019 weekday Day
2019-01-01 2019 weekday Night
2019-01-02 2019 weekday Day
2019-01-02 2019 weekday Night
...
2019-12-30 2019 weekday Day
2019-12-30 2019 weekday Night
2019-12-31 2019 weekday Day
2019-12-31 2019 weekday Night
Revised answer: I see an unused (VALUES ...) clause which could be used like so:
CROSS JOIN (VALUES ('Day'), ('Night') ) whatever(day_night)
DB Fiddle
I have a table that stores an Id and an effective period indicating when it is active
PortfolioId StartDate EndDate
1 2018-01-01 00:00:00.000 2018-05-31 00:00:00.000
2 2017-01-01 00:00:00.000 2018-05-31 00:00:00.000
I have another table that stores a component related to the above Id and that too has an effective period. Table 2 can have more that on entry for any given entry in table 1.
PortfolioComponentId PortfolioId SplitDate
1 1 2018-02-28 00:00:00.000
2 1 2018-03-31 00:00:00.000
3 2 2017-03-31 00:00:00.000
4 2 2017-09-20 00:00:00.000
5 2 2018-01-15 00:00:00.000
I have a period where I am running the query for
i.e
StartDate : 30-JUN-2017
End date : 15-MAY-2018
I have looking for a result like the below, where data in table 1 is split based on the data from table 2
PortfolioId StartDate EndDate
1 2018-01-01 00:00:00.000 2018-02-28 00:00:00.000
1 2018-03-01 00:00:00.000 2018-03-31 00:00:00.000 - Starts from End date + 1 from the prev row
1 2018-04-01 00:00:00.000 2018-05-15 00:00:00.000
2 2017-06-30 00:00:00.000 2017-09-20 00:00:00.000 - Starts from Seach date [Portfolio component Id 3 ignored as it falls outside of search date range]
2 2017-09-21 00:00:00.000 2018-01-15 00:00:00.000
2 2018-01-16 00:00:00.000 2018-05-15 00:00:00.000 - Ends by seach end date
Data setup - In case it helps
DECLARE #SearchStartDate DATETIME = '30-JUN-2017'
DECLARE #SearchEndDate DATETIME = '15-MAY-2018'
DECLARE #Portfolio TABLE
(
PortfolioId INT PRIMARY KEY IDENTITY(1,1),
StartDate DATETIME,
EndDate DATETIME
)
INSERT INTO #Portfolio
SELECT '01-JAN-2018', '31-MAY-2018'
INSERT INTO #Portfolio
SELECT '01-JAN-2017', '31-MAY-2018'
DECLARE #PortfolioComponents TABLE
(
PortfolioComponentId INT PRIMARY KEY IDENTITY(1,1),
PortfolioId INT,
SplitDate DATETIME
)
INSERT INTO #PortfolioComponents
SELECT 1, '28-FEB-2018'
INSERT INTO #PortfolioComponents
SELECT 1, '31-MAR-2018'
INSERT INTO #PortfolioComponents
SELECT 2, '31-MAR-2017'
INSERT INTO #PortfolioComponents
SELECT 2, '20-SEP-2017'
INSERT INTO #PortfolioComponents
SELECT 2, '15-JAN-2018'
SELECT * from #Portfolio
SELECT * from #PortfolioComponents
I believe I have got a result close to what I want, though not
the most ideal approach
the best approach from a performance perspective
DECLARE #Temp TABLE (BenchmarkDate Datetime, ComponentType int, PortfolioId INT)
INSERT INTO #Temp
SELECT StartDate , 1, PortfolioId FROM #Portfolio
UNION
SELECT EndDate , 2, PortfolioId FROM #Portfolio
INSERT INTO #Temp
SELECT SplitDate , 2, PortfolioId FROM #PortfolioComponents
UNION
SELECT SplitDate + 1 , 1, PortfolioId FROM #PortfolioComponents
DECLARE #Results TABLE
(
Id INT IDENTITY(1,1),
StartDate DATETIME,
EndDate DATETIME,
PortfolioId INT
)
INSERT INTO #Results
SELECT rset1.BenchmarkDate [Startdate],
( SELECT MIN(rset2.BenchmarkDate)
FROM #Temp rset2
WHERE rset2.ComponentType = 2
AND rset2.BenchmarkDate > rset1.BenchmarkDate
AND rset1.PortfolioId = rset2.PortfolioId) [Enddate],
rset1.PortfolioId
FROM #Temp rset1
WHERE rset1.ComponentType = 1
ORDER BY rset1.PortfolioId, rset1.BenchmarkDate
SELECT
CASE WHEN (#SearchStartDate BETWEEN StartDate AND EndDate ) THEN #SearchStartDate ELSE StartDate END StartDate,
CASE WHEN (#SearchEndDate BETWEEN StartDate AND EndDate) THEN #SearchEndDate ELSE EndDate END EndDate,
PortfolioId ,
(
SELECT PortfolioComponentId
FROM #PortfolioComponents pc
WHERE
(pc.PortfolioID = r.PortfolioId AND
(DATEADD(d, 1, pc.SplitDate) = r.StartDate ))
) PortfolioComponentId
FROM #Results r
WHERE
(#SearchStartDate < StartDate AND #SearchStartDate < EndDate AND #SearchEndDate > EndDate)
OR
(#SearchEndDate BETWEEN StartDate AND EndDate)
OR
(#SearchStartDate BETWEEN StartDate AND EndDate )
Week ends on Saturday and new week starts on Sunday.
I would like to determine date which will be a Saturday and split dates in SQL Server. Example below:
Start_date - 09/11/2018 - Friday
End_date - 12/11/2018 - Monday
Total number of days = 4
I would like split the days and end results as
Start_date - 09/11/2018 - Friday
Date1 - 10/11/2018 - Saturday
Total number of days = 2
Date2 - 11/11/2018 - Sunday
End_date - 12/11/2018 - Monday
Total number of days = 2
Another example ( start_date and end_date if there are more number of Saturdays e.g. )
start-date - 04/05/2017
end_date - 31/05/2017
Then results should be like below :-
Date1 Date2 no. of days.
------------------------------------
04/05/2017 06/05/2017 3
07/05/2017 13/05/2017 7
14/05/2017 20/05/2017 7
21/05/2017 27/05/2017 7
28/05/2017 31/05/2017 4
Please help.
Thanks & Regards,
VG
If I am not mistaken you are looking for something like this:
DECLARE #StartDate DATE = '2017-05-04'
DECLARE #EndDate DATE = '2017-05-31'
DECLARE #OutputTABLE AS TABLE
(
StartDate DATE NOT NULL,
EndDate DATE NOT NULL
)
DECLARE #NumberOfWeeks INT = DATEDIFF(week, #StartDate, #EndDate)
DECLARE #Counter INT = 0
DECLARE #TempDate DATE = DATEADD(week, #Counter, #StartDate)
WHILE #NumberOfWeeks >= 0
BEGIN
IF #NumberOfWeeks = 0
BEGIN
INSERT INTO #OutputTABLE VALUES (#TempDate, #EndDate);
END
ELSE
BEGIN
INSERT INTO #OutputTABLE VALUES (#TempDate, DATEADD(DAY, -1, DATEADD(week, DATEDIFF(week ,0 , #TempDate) + 1, -1)));
END
SET #TempDate = DATEADD(week, #Counter + 1, DATEADD(day, -1, DATEADD(week, DATEDIFF(week, 0, #StartDate), 0)))
SET #NumberOfWeeks = #NumberOfWeeks - 1
SET #Counter = #Counter + 1
END
SELECT StartDate,
EndDate,
DATEDIFF(day, StartDate, EndDate) + 1 AS NumberOfDays
FROM #OutputTABLE
Here is one way. I couldn't figure out a way with a recursive CTE since you can't aggregate in the recursive part of a recursive CTE.
DECLARE #MinDate DATE = '20170504',
#MaxDate DATE = '20170531'
DECLARE #StartDate datetime, #EndDate datetime
--DATE TABLE... ONE ROW FOR EVERY DAY IN RANGE
IF OBJECT_ID('tempdb..#DateTable') IS NOT NULL DROP TABLE #DateTable
SELECT TOP (DATEDIFF(DAY, #MinDate, #MaxDate) + 1)
Dates = DATEADD(DAY, ROW_NUMBER() OVER(ORDER BY a.object_id) - 1, #MinDate)
INTO #DateTable
FROM sys.all_objects a
CROSS JOIN sys.all_objects b
IF OBJECT_ID('tempdb..#Results') IS NOT NULL DROP TABLE #Results
CREATE TABLE #Results (Date1 DATETIME, Date2 DATETIME, NumOfDays INT)
--INSERT FIRST ROW IN CASE IT STARTS ON A DAY OTHER THAN SUNDAY
INSERT INTO #Results
SELECT
MIN(Dates) as Date1
,MIN(CASE WHEN DATEPART(WEEKDAY,Dates) = 7 THEN Dates END) as Date2
,DATEDIFF(DAY,MIN(Dates),MIN(CASE WHEN DATEPART(WEEKDAY,Dates) = 7 THEN Dates END)) + 1 as NoOfDays
FROM #DateTable
SET #StartDate = (SELECT MIN(CASE WHEN DATEPART(WEEKDAY,Dates) = 1 THEN Dates END) FROM #DateTable)
SET #EndDate = (SELECT MAX(CASE WHEN DATEPART(WEEKDAY,Dates) = 1 THEN Dates END) FROM #DateTable)
--INSERT ALL FULL WEEKS
WHILE #StartDate < #EndDate
BEGIN
INSERT INTO #Results
SELECT
MIN(Dates) as Date1
,MIN(CASE WHEN DATEPART(WEEKDAY,Dates) = 7 THEN Dates END) as Date2
,DATEDIFF(DAY,MIN(Dates),MIN(CASE WHEN DATEPART(WEEKDAY,Dates) = 7 THEN Dates END)) + 1 as NoOfDays
FROM #DateTable
WHERE Dates >= #StartDate
SET #StartDate = DATEADD(DAY,7,#StartDate)
END
--INSERT LAST ROW IF IT ISN'T A FULL WEEK
IF (SELECT MAX(Date2) FROM #Results) <> #MaxDate
BEGIN
INSERT INTO #Results
SELECT
MIN(Dates) as Date1
,MAX(Dates) as Date2
,DATEDIFF(DAY,MIN(Dates),MAX(Dates)) + 1 as NoOfDays
FROM #DateTable
WHERE Dates > (SELECT MAX(Date2) FROM #Results)
END
SELECT * FROM #Results
DROP TABLE #Results
DROP TABLE #DateTable
RETURNS
+-------------------------+-------------------------+-----------+
| Date1 | Date2 | NumOfDays |
+-------------------------+-------------------------+-----------+
| 2017-05-04 00:00:00.000 | 2017-05-06 00:00:00.000 | 3 |
| 2017-05-07 00:00:00.000 | 2017-05-13 00:00:00.000 | 7 |
| 2017-05-14 00:00:00.000 | 2017-05-20 00:00:00.000 | 7 |
| 2017-05-21 00:00:00.000 | 2017-05-27 00:00:00.000 | 7 |
| 2017-05-28 00:00:00.000 | 2017-05-31 00:00:00.000 | 4 |
+-------------------------+-------------------------+-----------+
This will compute your Date2 from your Date1
print dateadd(day,-1,dateadd(wk,datepart(wk,'4/5/2017'),'1/1/2017'))
I need help modifying a script to find the missing RevenuePeriod by ID within a given time frame. I have found a similar script that finds the missing date for a given ID but I don't know how to modify the script where it will give the missing dates per ID.
create table #WorksheetHistory (WorksheetID [int] IDENTITY(1,1) ,ID varchar(6), RevenuePeriod datetime)
insert into #WorksheetHistory (ID,RevenuePeriod)
SELECT '000001','2015-06-01 00:00:00.00' Union All
SELECT '000001','2015-07-01 00:00:00.00' Union All
SELECT '000001','2015-11-01 00:00:00.00' Union All
SELECT '000001','2015-12-01 00:00:00.00' Union All
SELECT '000002','2015-06-01 00:00:00.00' Union All
SELECT '000002','2015-12-01 00:00:00.00'
DECLARE #EndDate datetime
DECLARE #StartDate datetime
SET #StartDate = '2015-06-01 00:00:00.00'
SET #EndDate = '2015-12-01 00:00:00.00'
;WITH Dates as
(
SELECT #StartDate AS dt
UNION ALL
SELECT DATEADD(month, 1, dt) as dt
FROM Dates
WHERE dt < (select dateadd(month,-1,#EndDate) enddate)
)
select Month(dt) as dtMonth, Year(dt) dtYear
from Dates d left outer join #WorksheetHistory w
on dateadd(month, datediff(month,0,d.dt),0) = dateadd(month, datediff(month,0,w.RevenuePeriod),0) Where RevenuePeriod is null
Drop Table #WorksheetHistory
The current output returns the following output. I realize in the script it doesnt have an ID returned, but even if I do, it would return null value as the script indicates return null dates. I don't know how to put the associated ID in it too.
dt
2015-08-01 00:00:00.000
2015-09-01 00:00:00.000
2015-10-01 00:00:00.000
My desired result would be return the Missing ID's with the respective missing dates.
ID dt
000001 2015-08-01 00:00:00.00
000001 2015-09-01 00:00:00.00
000001 2015-10-01 00:00:00.00
000002 2015-07-01 00:00:00.00
000002 2015-08-01 00:00:00.00
000002 2015-09-01 00:00:00.00
000002 2015-10-01 00:00:00.00
000002 2015-11-01 00:00:00.00
Use EXCEPT:
WITH Dates as
(
SELECT #StartDate AS dt
UNION ALL
SELECT DATEADD(month, 1, dt) as dt
FROM Dates
WHERE dt < DATEADD(m, -1, #EndDate)
)
-- all the possible combinations
SELECT w.ID, d.dt
FROM Dates d
CROSS JOIN (
SELECT ID
FROM #WorksheetHistory
GROUP BY ID
) w
EXCEPT
-- the combinations you actually have
SELECT w.ID, d.dt
FROM Dates d
JOIN #WorksheetHistory w
ON d.dt = w.RevenuePeriod;
We have a list of items. Each item may be checked/examined one or more times in the last #FromDate to #ToDate (12 months, for example). How do we count the number of items which were checked in a month that had never been check since #FromDate and display that count monthly.
The result would look like below:
2014-05-01 00:00:00.000 1 May 2014 381
2014-06-01 00:00:00.000 2 June 2014 296
2014-07-01 00:00:00.000 3 July 2014 24
2014-08-01 00:00:00.000 4 August 2014 260
2014-09-01 00:00:00.000 5 September 2014 249
2014-10-01 00:00:00.000 6 October 2014 177
2014-11-01 00:00:00.000 7 November 2014 298
2014-12-01 00:00:00.000 8 December 2014 274
2015-01-01 00:00:00.000 9 January 2015 41
2015-02-01 00:00:00.000 10 February 2015 0
2015-03-01 00:00:00.000 11 March 2015 0
2015-04-01 00:00:00.000 12 April 2015 0
So far we could count the number of items which were checked monthly by using the following query:
;WITH d AS
(
SELECT DATEADD(MONTH, n, DATEADD(MONTH, DATEDIFF(MONTH, 0, '2014-05-15'), 0)) as d, ROW_NUMBER() OVER (ORDER BY n) as rn
FROM ( SELECT TOP (DATEDIFF(MONTH, '2014-05-15', '2015-04-15') + 1)
n = ROW_NUMBER() OVER (ORDER BY [object_id]) - 1
FROM sys.all_objects ORDER BY [object_id] ) AS n
)
SELECT
d.d,
d.rn,
DATENAME(MONTH, d.d) as [Month],
YEAR(d.d) as [Year],
COUNT(DISTINCT ItemNumber) AS Count
FROM d LEFT OUTER JOIN ItemCheck
ON CheckedTime >= d.d
AND CheckedTime < DATEADD(MONTH, 1, d.d)
GROUP BY d.d, d.rn
ORDER BY d.d;
However, we still can't figure out how to count the number of items which were checked say in July 2014 but had never been checked in May and June 2014.
A simple single query below would be able to display that for a particular month but that doesn't display in monthly.
select count(distinct ItemNumber) from ItemCheck
where CheckTime >= '2014-07-01' AND CheckTime < '2014-08-01' and
ItemNumber NOt in (SELECT ItemNumber FROM ItemCheck as t1 where t1.CheckTime >= '2014-05-01' AND t1.CheckTime < '2014-07-01')
order by ItemNumber
Update: The ItemCheck table looks like below:
CheckID | ItemNumber | CheckTime
1 i1 2014-05-02
2 i4 2014-06-12
3 i5 2014-07-03
4 i1 2014-08-01
5 i1 2014-08-02
6 i2 2014-09-15
7 i3 2014-10-11
Suppose you have a table called months with one column called month.
Then you could do it something along these lines:
DECLARE #date DATE
SET #date = '2014-01-01'
SELECT months.month AS currentMonth, COUNT(ItemNumber) ItemNumber from ItemCheck
CROSS JOIN months
WHERE CheckTime >= dateadd(m, months.month - 1, #date) AND CheckTime < dateadd(m, months.month, #date) and
ItemNumber NOT IN (SELECT ItemNumber FROM ItemCheck AS t1 WHERE t1.CheckTime >= DATEADD(m, months.month - 3, #date) AND t1.CheckTime < dateadd(m, months.month - 1, #date))
GROUP BY month
ORDER BY ItemNumber
You could also do it with a function, but you have to write it (function is called dbo.Split and you could write it this way: select data from dbo.Split(',', '1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12'). This way you could avoid creating another table (in-memory or a real one..)
If you want the code for dbo.Split I can provide it.