My table will be structured like this
temp
ID | Date
---|-----------
1 | 2018-01-01
2 | 2018-01-01
3 | 2018-01-01
4 | 2018-01-02
5 | 2018-01-02
6 | 2018-01-03
And I will have an input from the user for start and end dates:
#StartDate DATE = '2018-01-01'
#EndDate DATE = '2018-01-03'
And I want my return structured like so:
ID | Date
---|-----------
1 | 2018-01-01
4 | 2018-01-02
6 | 2018-01-03
I've tried doing this:
select distinct temp.ID, joinTable.Date
from temp
inner join (
select min(innerTemp.Date), innerTemp.ID
from temp innerTemp
where innerTemp.Date >= #StartDate
and innerTemp.Date < #EndDate
group by innerTemp.ID, innerTemp.Date
) as joinTable on joinTable.ID = temp.ID and joinTable.Date = temp.Date
where temp.Date >= #StartDate
and temp.Date < #EndDate
order by temp.Date desc
To try to join the table to itself with only one entry per day then choose from that but that isn't working. I am pretty stumped on this one. Any ideas?
That seems very complicated. This returns the result set you want:
select min(id), date
from temp
where date >= #StartDate and date < #EndDate
group by date;
If you have other columns you want to keep (so group by is not appropriate), a simple method with good performance is:
select t.*
from temp t
where t.id = (select min(t2.id) from temp t2 where t2.date = t.date and t2.date >= #StartDate and t2.date < #EndDate);
Of course, you can also use row_number(), but with an index on temp(date, id) and temp(id), the above should be pretty fast.
WITH cte AS
(
SELECT
*
, ROW_NUMBER() OVER(PARTITION BY date ORDER BY id asc) rn
FROM
temp )
SELECT
id,
date
FROM
rn = 1
Related
I have a table with a calendar which look like this in SQL Server
Date WorkingDay
20200514 1
20200515 1
20200516 0
20200517 0
20200518 1
20200519 1
20200520 1
20200521 0
20200522 1
I am trying to select the third working day from a specific date.
If I start the 20200514, result must be the 20200518.
I try with a query like this but I do not have the date, only a list of result
select top 3 *
from tmp_workingdays
where workingday = 1 and date >= 20200514
order by date asc
How can I select only the date?
Try the following, here is the demo.
select
date
from
(
select
*,
row_number() over (order by date) as rnk
from tmp_workingdays
where workingday=1
and date >= 20200514
) val
where rnk = 3
output:
| Date |
------------
|2020-05-18|
DECLARE #date AS DATE = '2020-05-14' ;
WITH cte_WorkingDates AS
(
SELECT [Date]
FROM tmp_workingdays
WHERE WorkingDay = 1
)
SELECT [Date]
FROM cte_WorkingDates
WHERE [Date] >= #date
ORDER BY [Date] ASC
OFFSET 2 ROWS FETCH NEXT 1 ROWS ONLY ;
select date
from tmp_workingdays
where workingday = 1
and date >= 20200514
order by date asc
OFFSET 2 ROWS
FETCH NEXT 1 ROWS ONLY
SQL Server
I have a table with 2 time stamps, time_start and time_end.
e.g.
ID time_start time_end
---- ------------------- -------------------
1 2019-01-01 08:30:00 2019-01-01 09:40:00
2 2019-01-01 09:10:24 2019-01-01 15:14:19
3 2019-01-01 09:21:15 2019-01-01 09:21:19
4 2019-01-01 10:39:45 2019-01-01 10:58:12
5 2019-01-01 11:39:45 2019-01-01 11:40:10
and I would like to group them so I can have the number of rows grouped by a variable time interval.
e.g.
time_interval row_count
------------------- ---------
2019-01-01 07:00:00 0
2019-01-01 08:00:00 1
2019-01-01 09:00:00 3
2019-01-01 10:00:00 2
2019-01-01 11:00:00 1
2019-01-01 12:00:00 0
My interval could be 1 hour, 1 minute, 30 minutes, 1 day, etc...
Think of it as a log-in/log-out situation, and I want to see how may users were logged at any given minute, hour, day, etc...
Try this,
DECLARE #start_date datetime='2019-01-01',
#end_date datetime='2019-01-02',
#i_minutes int=60
DECLARE #t TABLE
(
id int identity(1,1),time_start datetime,time_end datetime
)
INSERT INTO #t(time_start,time_end)VALUES
('2019-01-01 08:30:00','2019-01-01 09:40:00'),
('2019-01-01 09:10:24','2019-01-01 15:14:19'),
('2019-01-01 09:21:15','2019-01-01 09:21:19'),
('2019-01-01 10:39:45','2019-01-01 10:58:12'),
('2019-01-01 11:39:45','2019-01-01 11:40:10')
--SELECT #start_date=min(time_start),#end_date=max(time_end)
--FROM #t
;WITH CTE_time_Interval AS
(
SELECT #start_date AS time_int,#i_minutes AS i_minutes
UNION ALL
SELECT dateadd(minute,#i_minutes,time_int),i_minutes+ #i_minutes
FROM CTE_time_Interval
WHERE time_int<=#end_date
)
,CTE1 AS
(
SELECT ROW_NUMBER()OVER(ORDER BY time_int)AS r_no,time_int
FROM CTE_time_Interval
)
,CTE2 AS
(
SELECT a.time_int AS Int_start_time,b.time_int AS Int_end_time
FROM CTE1 a
INNER JOIN CTE1 b ON a.r_no+1=b.r_no
)
SELECT a.Int_start_time,a.Int_end_time,sum(iif(b.time_start is not null,1,0)) AS cnt
FROM CTE2 a
LEFT JOIN #t b ON
(
b.time_start BETWEEN a.Int_start_time AND a.Int_end_time
OR
b.time_end BETWEEN a.Int_start_time AND a.Int_end_time
OR
a.Int_start_time BETWEEN b.time_start AND b.time_end
OR
a.Int_end_time BETWEEN b.time_start AND b.time_end
)
GROUP BY a.Int_start_time,a.Int_end_time
Hi this is my workaround.
I created a table "test" with your data.
First I get the min and max intervals and after, I get all the intervals between these values with a CTE.
Finally, with this CTE and a left join with the intervals between time_start and time_end I got the answer.
This is for intervals of 1 hour
DECLARE #minDate AS DATETIME;
DECLARE #maxDate AS DATETIME;
SET #minDate = (select case
when (select min(time_start) from test) < (select min(time_end) from test)
then (select min(time_start) from test)
else (select min(time_end) from test) end )
SET #minDate = FORMAT(#minDate, 'dd-MM.yyyy HH:00:00')
SET #maxDate = (select case
when (select max(time_start) from test) > (select max(time_end) from test)
then (select max(time_start) from test)
else (select max(time_end) from test) end )
SET #maxDate = FORMAT(#maxDate, 'dd-MM.yyyy HH:00:00')
;WITH Dates_CTE
AS (SELECT #minDate AS Dates
UNION ALL
SELECT Dateadd(hh, 1, Dates)
FROM Dates_CTE
WHERE Dates < #maxDate)
SELECT d.Dates as time_interval, count(*) as row_count
FROM Dates_CTE d
LEFT JOIN test t on d.Dates
between (FORMAT(t.time_start, 'dd-MM.yyyy HH:00:00'))
and (FORMAT(t.time_end, 'dd-MM.yyyy HH:00:00'))
GROUP BY d.Dates
For intervals of 10 minutes you need some changes.
First I format dates getting minutes (dd-MM.yyyy HH:mm:00 instead of dd-MM.yyyy HH:00:00)
and in the left join I approach time_start and time_end to their 10 minutes time (9:32:00 in 9:30:00) (dateadd(minute, 10 * (datediff(minute, 0, time_start) / 10), 0)):
DECLARE #minDate AS DATETIME;
DECLARE #maxDate AS DATETIME;
SET #minDate = (select case
when (select min(time_start) from test) < (select min(time_end) from test)
then (select min(time_start) from test)
else (select min(time_end) from test) end )
SET #minDate = FORMAT(#minDate, 'dd-MM.yyyy HH:mm:00')
SET #maxDate = (select case
when (select max(time_start) from test) > (select max(time_end) from test)
then (select max(time_start) from test)
else (select max(time_end) from test) end )
SET #maxDate = FORMAT(#maxDate, 'dd-MM.yyyy HH:mm:00')
;WITH Dates_CTE
AS (SELECT #minDate AS Dates
UNION ALL
SELECT Dateadd(minute, 10, Dates)
FROM Dates_CTE
WHERE Dates < #maxDate)
SELECT d.Dates as time_interval, count(*) as row_count
FROM Dates_CTE d
LEFT JOIN test t on d.Dates
between dateadd(minute, 10 * (datediff(minute, 0, time_start) / 10), 0)
and dateadd(minute, 10 * (datediff(minute, 0, time_end) / 10), 0)
GROUP BY d.Dates
And finally I get this results for 1 hour intervals:
+---------------------+-----------+
| time_interval | row_count |
+---------------------+-----------+
| 01/01/2019 08:00:00 | 1 |
| 01/01/2019 09:00:00 | 3 |
| 01/01/2019 10:00:00 | 2 |
| 01/01/2019 11:00:00 | 2 |
| 01/01/2019 12:00:00 | 1 |
| 01/01/2019 13:00:00 | 1 |
| 01/01/2019 14:00:00 | 1 |
| 01/01/2019 15:00:00 | 1 |
+---------------------+-----------+
I hope it works for you.
You need to specify the time intervals. The rest is a LEFT JOIN/GROUP BY or correlated subquery:
with dates as (
select convert(datetime, '2019-01-01 07:00:00') as dt
union all
select dateadd(hour, 1, dt)
from dates
where dt < '2019-01-01 12:00:00'
)
select dates.dt, count(t.id)
from dates left join
t
on dates.dt < t.time_end and
dates.dt >= dateadd(hour, 1, t.time_start)
group by dates.dt
order by dates.dt;
If you have lots of data and lots of time periods, you might find that this has poor performance. If this is the case, ask a new question, with more information about sizing and performance.
I am trying to write SQL to generate the following data
Date Count
2018-09-24 2
2018-09-25 2
2018-09-26 2
2018-09-27 2
2018-09-28 2
2018-09-29 1
A sample of the base table I am using is
ID StartDate EndDate
187267 2018-09-24 2018-10-01
187270 2018-09-24 2018-09-30
So I'm trying to get a list of dates between 2 dates and then count how many base data records there are in each date.
I started using a temporary table and attempting to loop through the records to get the results but I'm not sure if this is the right approach.
I have this code so far
WITH ctedaterange
AS (SELECT [Dates] = (select ea.StartWork from EngagementAssignment ea where ea.EngagementAssignmentId IN(SELECT ea.EngagementAssignmentId
FROM EngagementLevel el INNER JOIN
EngagementAssignment ea ON el.EngagementLevelID = ea.EngagementLevelId
WHERE el.JobID = 15072 and ea.AssetId IS NOT NULL))
UNION ALL
SELECT [dates] + 1
FROM ctedaterange
WHERE [dates] + 1 < = (select ea.EndWork from EngagementAssignment ea where ea.EngagementAssignmentId IN(SELECT ea.EngagementAssignmentId
FROM EngagementLevel el INNER JOIN
EngagementAssignment ea ON el.EngagementLevelID = ea.EngagementLevelId
WHERE el.JobID = 15072 and ea.AssetId IS NOT NULL)))
SELECT [Dates], Count([Dates])
FROM ctedaterange
GROUP BY [Dates]
But I get this error
Subquery returned more than 1 value. This is not permitted when the subquery follows =, !=, <, <= , >, >= or when the subquery is used as an expression.
I get correct results when the job I use only generates one record in the subselect in the where clause, ie:
SELECT ea.EngagementAssignmentId
FROM EngagementLevel el INNER JOIN
EngagementAssignment ea ON el.EngagementLevelID = ea.EngagementLevelId
WHERE el.JobID = 15047 and ea.AssetId IS NOT NULL
generates one record.
The results look like this:
Dates (No column name)
2018-09-24 02:00:00.000 1
2018-09-25 02:00:00.000 1
2018-09-26 02:00:00.000 1
2018-09-27 02:00:00.000 1
2018-09-28 02:00:00.000 1
2018-09-29 02:00:00.000 1
2018-09-30 02:00:00.000 1
2018-10-01 02:00:00.000 1
you can generate according to your range by changing from and to date
DECLARE
#DateFrom DATETIME = GETDATE(),
#DateTo DATETIME = '2018-10-30';
WITH DateGenerate
AS (
SELECT #DateFrom as MyDate
UNION ALL
SELECT DATEADD(DAY, 1, MyDate)
FROM DateGenerate
WHERE MyDate < #DateTo
)
SELECT
MyDate
FROM
DateGenerate;
Well, if you only have a low date range, you can use a recursive CTE as demonstrated in the other answers. The problem with a recursive CTE is with large ranges, where it starts to be ineffective - So I wanted to show you a different approach, that builds the calendar CTE without using recursion.
First, Create and populate sample table (Please save us this step in your future questions):
DECLARE #T AS TABLE
(
ID int,
StartDate date,
EndDate date
)
INSERT INTO #T (ID, StartDate, EndDate) VALUES
(187267, '2018-09-24', '2018-10-01'),
(187270, '2018-09-24', '2018-09-30')
Then, get the first start date and the number of dates you need in the calendar cte:
DECLARE #DateDiff int, #StartDate Date
SELECT #DateDiff = DATEDIFF(DAY, MIN(StartDate), Max(EndDate)),
#StartDate = MIN(StartDate)
FROM #T
Now, construct the calendar cte based on row_number (that is, unless you already have a numbers (tally) table you can use):
;WITH Calendar(TheDate)
AS
(
SELECT TOP(#DateDiff + 1) DATEADD(DAY, ROW_NUMBER() OVER(ORDER BY ##SPID)-1, #StartDate)
FROM sys.objects t0
-- unremark the next row if you don't get enough records...
-- CROSS JOIN sys.objects t1
)
Note that I'm using row_number() - 1 and therefor have to select top(#DateDiff + 1)
Finally - the query:
SELECT TheDate, COUNT(ID) As NumberOfRecords
FROM Calendar
JOIN #T AS T
ON Calendar.TheDate >= T.StartDate
AND Calendar.TheDate <= T.EndDate
GROUP BY TheDate
Results:
TheDate | NumberOfRecords
2018-09-24 | 2
2018-09-25 | 2
2018-09-26 | 2
2018-09-27 | 2
2018-09-28 | 2
2018-09-29 | 2
2018-09-30 | 2
2018-10-01 | 1
You can see a live demo on rextester.
Can you please try following SQL CTE query where I have used a SQL dates table function [dbo].[DatesTable] which produces a list of dates between min date and max date in the source table
;with boundaries as (
select
min(StartDate) minD, max(EndDate) maxD
from DateRanges
), dates as (
select
dates.[date]
from boundaries
cross apply [dbo].[DatesTable](minD, maxD) as dates
)
select dates.[date], count(*) as [count]
from dates
inner join DateRanges
on dates.date between DateRanges.StartDate and DateRanges.EndDate
group by dates.[date]
order by dates.[date]
The output is as expected
Try this: demo
WITH cte1
AS (SELECT id,sdate,edate from t
union all
select c.id,DATEADD(DAY, 1, c.sdate),c.edate from cte1 c where DATEADD(DAY, 1, c.sdate)<=c.edate
)
SELECT sdate,count(id) as total FROM cte1
group by sdate
OPTION (MAXRECURSION 0)
Output:
sdate total
2018-09-24 2
2018-09-25 2
2018-09-26 2
2018-09-27 2
2018-09-28 2
2018-09-29 2
2018-09-30 1
I need to create a report and I am struggling with the SQL script.
The table I want to query is a company_status_history table which has entries like the following (the ones that I can't figure out)
Table company_status_history
Columns:
| id | company_id | status_id | effective_date |
Data:
| 1 | 10 | 1 | 2016-12-30 00:00:00.000 |
| 2 | 10 | 5 | 2017-02-04 00:00:00.000 |
| 3 | 11 | 5 | 2017-06-05 00:00:00.000 |
| 4 | 11 | 1 | 2018-04-30 00:00:00.000 |
I want to answer to the question "Get all companies that have been at least for some point in status 1 inside the time period 01/01/2017 - 31/12/2017"
Above are the cases that I don't know how to handle since I need to add some logic of type :
"If this row is status 1 and it's date is before the date range check the next row if it has a date inside the date range."
"If this row is status 1 and it's date is after the date range check the row before if it has a date inside the date range."
I think this can be handled as a gaps and islands problem. Consider the following input data: (same as sample data of OP plus two additional rows)
id company_id status_id effective_date
-------------------------------------------
1 10 1 2016-12-15
2 10 1 2016-12-30
3 10 5 2017-02-04
4 10 4 2017-02-08
5 11 5 2017-06-05
6 11 1 2018-04-30
You can use the following query:
SELECT t.id, t.company_id, t.status_id, t.effective_date, x.cnt
FROM company_status_history AS t
OUTER APPLY
(
SELECT COUNT(*) AS cnt
FROM company_status_history AS c
WHERE c.status_id = 1
AND c.company_id = t.company_id
AND c.effective_date < t.effective_date
) AS x
ORDER BY company_id, effective_date
to get:
id company_id status_id effective_date grp
-----------------------------------------------
1 10 1 2016-12-15 0
2 10 1 2016-12-30 1
3 10 5 2017-02-04 2
4 10 4 2017-02-08 2
5 11 5 2017-06-05 0
6 11 1 2018-04-30 0
Now you can identify status = 1 islands using:
;WITH CTE AS
(
SELECT t.id, t.company_id, t.status_id, t.effective_date, x.cnt
FROM company_status_history AS t
OUTER APPLY
(
SELECT COUNT(*) AS cnt
FROM company_status_history AS c
WHERE c.status_id = 1
AND c.company_id = t.company_id
AND c.effective_date < t.effective_date
) AS x
)
SELECT id, company_id, status_id, effective_date,
ROW_NUMBER() OVER (PARTITION BY company_id ORDER BY effective_date) -
cnt AS grp
FROM CTE
Output:
id company_id status_id effective_date grp
-----------------------------------------------
1 10 1 2016-12-15 1
2 10 1 2016-12-30 1
3 10 5 2017-02-04 1
4 10 4 2017-02-08 2
5 11 5 2017-06-05 1
6 11 1 2018-04-30 2
Calculated field grp will help us identify those islands:
;WITH CTE AS
(
SELECT t.id, t.company_id, t.status_id, t.effective_date, x.cnt
FROM company_status_history AS t
OUTER APPLY
(
SELECT COUNT(*) AS cnt
FROM company_status_history AS c
WHERE c.status_id = 1
AND c.company_id = t.company_id
AND c.effective_date < t.effective_date
) AS x
), CTE2 AS
(
SELECT id, company_id, status_id, effective_date,
ROW_NUMBER() OVER (PARTITION BY company_id ORDER BY effective_date) -
cnt AS grp
FROM CTE
)
SELECT company_id,
MIN(effective_date) AS start_date,
CASE
WHEN COUNT(*) > 1 THEN DATEADD(DAY, -1, MAX(effective_date))
ELSE MIN(effective_date)
END AS end_date
FROM CTE2
GROUP BY company_id, grp
HAVING COUNT(CASE WHEN status_id = 1 THEN 1 END) > 0
Output:
company_id start_date end_date
-----------------------------------
10 2016-12-15 2017-02-03
11 2018-04-30 2018-04-30
All you want know is those records from above that overlap with the specified interval.
Demo here with somewhat more complicated use case.
Maybe this is what you are looking for? For these kind of questions, you need to join two instance of your table, in this case I am just joining with next record by Id, which probably is not totally correct. To do it better, you can create a new Id using a windowed function like row_number, ordering the table by your requirement criteria
If this row is status 1 and it's date is before the date range check
the next row if it has a date inside the date range
declare #range_st date = '2017-01-01'
declare #range_en date = '2017-12-31'
select
case
when csh1.status_id=1 and csh1.effective_date<#range_st
then
case
when csh2.effective_date between #range_st and #range_en then true
else false
end
else NULL
end
from company_status_history csh1
left join company_status_history csh2
on csh1.id=csh2.id+1
Implementing second criteria:
"If this row is status 1 and it's date is after the date range check
the row before if it has a date inside the date range."
declare #range_st date = '2017-01-01'
declare #range_en date = '2017-12-31'
select
case
when csh1.status_id=1 and csh1.effective_date<#range_st
then
case
when csh2.effective_date between #range_st and #range_en then true
else false
end
when csh1.status_id=1 and csh1.effective_date>#range_en
then
case
when csh3.effective_date between #range_st and #range_en then true
else false
end
else null -- ¿?
end
from company_status_history csh1
left join company_status_history csh2
on csh1.id=csh2.id+1
left join company_status_history csh3
on csh1.id=csh3.id-1
I would suggest the use of a cte and the window functions ROW_NUMBER. With this you can find the desired records. An example:
DECLARE #t TABLE(
id INT
,company_id INT
,status_id INT
,effective_date DATETIME
)
INSERT INTO #t VALUES
(1, 10, 1, '2016-12-30 00:00:00.000')
,(2, 10, 5, '2017-02-04 00:00:00.000')
,(3, 11, 5, '2017-06-05 00:00:00.000')
,(4, 11, 1, '2018-04-30 00:00:00.000')
DECLARE #StartDate DATETIME = '2017-01-01';
DECLARE #EndDate DATETIME = '2017-12-31';
WITH cte AS(
SELECT *
,ROW_NUMBER() OVER (PARTITION BY company_id ORDER BY effective_date) AS rn
FROM #t
),
cteLeadLag AS(
SELECT c.*, ISNULL(c2.effective_date, c.effective_date) LagEffective, ISNULL(c3.effective_date, c.effective_date)LeadEffective
FROM cte c
LEFT JOIN cte c2 ON c2.company_id = c.company_id AND c2.rn = c.rn-1
LEFT JOIN cte c3 ON c3.company_id = c.company_id AND c3.rn = c.rn+1
)
SELECT 'Included' AS RangeStatus, *
FROM cteLeadLag
WHERE status_id = 1
AND effective_date BETWEEN #StartDate AND #EndDate
UNION ALL
SELECT 'Following' AS RangeStatus, *
FROM cteLeadLag
WHERE status_id = 1
AND effective_date > #EndDate
AND LagEffective BETWEEN #StartDate AND #EndDate
UNION ALL
SELECT 'Trailing' AS RangeStatus, *
FROM cteLeadLag
WHERE status_id = 1
AND effective_date < #EndDate
AND LeadEffective BETWEEN #StartDate AND #EndDate
I first select all records with their leading and lagging Dates and then I perform your checks on the inclusion in the desired timespan.
Try with this, self-explanatory. Responds to this part of your question:
I want to answer to the question "Get all companies that have been at
least for some point in status 1 inside the time period 01/01/2017 -
31/12/2017"
Case that you want to find those id's that have been in any moment in status 1 and have records in the period requested:
SELECT *
FROM company_status_history
WHERE id IN
( SELECT Id
FROM company_status_history
WHERE status_id=1 )
AND effective_date BETWEEN '2017-01-01' AND '2017-12-31'
Case that you want to find id's in status 1 and inside the period:
SELECT *
FROM company_status_history
WHERE status_id=1
AND effective_date BETWEEN '2017-01-01' AND '2017-12-31'
This problem is related to this, which has no solution in sight: here
I have a table that shows me all sessions of an area.
This session has a start date.
I need to get all the days of month of the start date of the session by specific area (in this case)
I have this query:
SELECT idArea, idSession, startDate FROM SessionsPerArea WHERE idArea = 1
idArea | idSession | startDate |
1 | 1 | 01-01-2013 |
1 | 2 | 04-01-2013 |
1 | 3 | 07-02-2013 |
And i want something like this:
date | Session |
01-01-2013 | 1 |
02-01-2013 | NULL |
03-01-2013 | NULL |
04-01-2013 | 1 |
........ | |
29-01-2013 | NULL |
30-01-2013 | NULL |
In this case, the table returns me all the days of January.
The second column is the number of sessions that occur on that day, because there may be several sessions on the same day.
Anyone can help me?
Please try:
DECLARE #SessionsPerArea TABLE (idArea INT, idSession INT, startDate DATEtime)
INSERT #SessionsPerArea VALUES (1,1,'2013-01-01')
INSERT #SessionsPerArea VALUES (1,2,'2013-01-04')
INSERT #SessionsPerArea VALUES (1,3,'2013-07-02')
DECLARE #RepMonth as datetime
SET #RepMonth = '01/01/2013';
WITH DayList (DayDate) AS
(
SELECT #RepMonth
UNION ALL
SELECT DATEADD(d, 1, DayDate)
FROM DayList
WHERE (DayDate < DATEADD(d, -1, DATEADD(m, 1, #RepMonth)))
)
SELECT *
FROM DayList t1 left join #SessionsPerArea t2 on t1.DayDate=startDate and t2.idArea = 1
This will work:
DECLARE #SessionsPerArea TABLE (idArea INT, idSession INT, startDate DATE)
INSERT #SessionsPerArea VALUES
(1,1,'2013-01-01'),
(1,2,'2013-01-04'),
(1,3,'2013-07-02')
;WITH t1 AS
(
SELECT startDate
, DATEADD(MONTH, DATEDIFF(MONTH, '1900-01-01', startDate), '1900-01-01') firstInMonth
, DATEADD(DAY, -1, DATEADD(MONTH, DATEDIFF(MONTH, '1900-01-01', startDate) + 1, '1900-01-01')) lastInMonth
, COUNT(*) cnt
FROM #SessionsPerArea
WHERE idArea = 1
GROUP BY
startDate
)
, calendar AS
(
SELECT DISTINCT DATEADD(DAY, c.number, t1.firstInMonth) d
FROM t1
JOIN master..spt_values c ON
type = 'P'
AND DATEADD(DAY, c.number, t1.firstInMonth) BETWEEN t1.firstInMonth AND t1.lastInMonth
)
SELECT d date
, cnt Session
FROM calendar c
LEFT JOIN t1 ON t1.startDate = c.d
It uses simple join on master..spt_values table to generate rows.
Just an example of calendar table. To return data for a month adjust the number of days between < 32, for a year to 365+1. You can calculate the number of days in a month or between start/end dates with query. I'm not sure how to do this in SQL Server. I'm using hardcoded values to display all dates in Jan-2013. You can adjust start and end dates for diff. month or to get start/end dates with queries...:
WITH data(r, start_date) AS
(
SELECT 1 r, date '2012-12-31' start_date FROM any_table --dual in Oracle
UNION ALL
SELECT r+1, date '2013-01-01'+r-1 FROM data WHERE r < 32 -- number of days between start and end date+1
)
SELECT start_date FROM data WHERE r > 1
/
START_DATE
----------
1/1/2013
1/2/2013
1/3/2013
...
...
1/31/2013