Count distinct customer based on last three month sales - sql

I need to get monthly count of distinct customers based on last three month Sales.
To show the result by adding current month customer count and adding last three month customer to the count as below:
In month of APRIL ,distinct customers count of (APRIL+MARCH+FEBRUARY)
In month of MAY,distinct customers count of (MAY+APRIL+MARCH)
In month of JUNE,distinct customers count of (JUNE+MAY+APRIL)
In month of JULY,distinct customers count of (JULY+JUNE+MAY)
Here what I tried:
SELECT MonNumber = MONTH(h.Invoicedate) ,
YearNumber = YEAR(h.Invoicedate) ,
PartyCount = ( SELECT COUNT(DISTINCT s.CustomerID)
FROM salesdata s
WHERE s.Invoicedate BETWEEN DATEADD(month, -6,
h.Invoicedate)
AND h.Invoicedate
)
FROM salesdata h
GROUP BY MONTH(h.Invoicedate) ,
YEAR(h.Invoicedate)
ORDER BY YEAR(h.Invoicedate) ,
MONTH(h.Invoicedate)
| Year | Month | COUNT |
|-----------|----------|-------------|
| 2014 | Jan | 6 |
| 2014 | Feb | 6 |
| 2014 | Mar | 6 |
| 2014 | Apr | 4 |
| 2014 | May | 6 |
| 2014 | Jun | 6 |
View the table on SQL Fiddle

Here it is.
WITH dt AS (
-- set invoice to BOM to get one row per month
SELECT DISTINCT DATEADD(mm,DATEDIFF(mm,0,InvoiceDate),0) AS InvoiceDate
FROM salesdata
)
SELECT YEAR(dt.InvoiceDate) AS YEAR
, MONTH(dt.InvoiceDate) AS MONTH
, COUNT(DISTINCT CustomerId) AS PARTYCOUNT
FROM salesdata s
INNER JOIN dt
-- Define your window
ON s.InvoiceDate >= DATEADD(MM, -2, dt.InvoiceDate)
AND s.InvoiceDate < DATEADD(MM, 1, dt.InvoiceDate)
GROUP BY YEAR(dt.InvoiceDate)
, MONTH(dt.InvoiceDate)
ORDER BY 1, 2

You need to apply some aggregate function to invoicedate.
This should work: fiddle
SELECT
-- get the first of the current month and substract two months
dateadd(month, -2, DATEADD(day, -day(h.Invoicedate) + 1, h.Invoicedate)) as first_of_month,
PartyCount = ( SELECT COUNT(DISTINCT s.CustomerID)
FROM salesdata s
WHERE s.Invoicedate >= dateadd(month, -2, DATEADD(day, -day(h.Invoicedate) + 1, h.Invoicedate))
AND s.Invoicedate < min(dateadd(month, 1, DATEADD(day, -day(h.Invoicedate) + 1, h.Invoicedate)))
)
FROM salesdata h
group by
dateadd(month, -2, DATEADD(day, -day(h.Invoicedate) + 1, h.Invoicedate))
order by 1
I would prefer to create a table with the month names and date ranges first and then simply use this instead.

Related

Combining Dates and Ordering by Date

I have the below Query
SELECT distinct
COUNT(Status) AS [Transactions],
left(DATENAME(mm, Date_Reported), 3) AS Month,
DATENAME(yyyy, Date_Reported) AS Year
FROM [Transactions]
GROUP BY DATENAME(mm, Date_Reported), DATENAME(yyyy,Date_Reported)
ORDER BY Year, Month DESC;
My output is as below:
Transaction | Month | Year
123 | Jan | 2000
1234 | Mar | 2000
12 | Feb | 2000
How can I alter the query so I can get the month and year together like "Jan 2000" and then order it by the date so Jan 2000, Feb 2000 and Mar 2000
Thank you in advance
I think you want :
SELECT COUNT(Status) AS [Transactions], t1.MonthYear
FROM [Transactions] t
CROSS APPLY ( VALUES (CONCAT(DATENAME(mm, Date_Reported),' ',
DATENAME(yyyy, Date_Reported)),
DATEPART(mm, Date_Reported)
)
) t1 (MonthYear, Morder)
GROUP BY t1.MonthYear, t1.Morder
ORDER BY t1.Morder;

SQL split number over date range

I have a table with, for example this data
ID |start_date |end_date |amount
---|------------|-----------|-------
a1 |2013-12-01 |2014-03-31 |100
Iwant to have a query that split the dates so I have the amount splitted out over the year like this :
ID |org_start_date |org_end_date |new_start_date |new_end_date |amount
---|----------------|---------------|----------------|----------------|-------
a1 |2013-12-01 |2014-03-31 |2013-12-01 |2013-12-31 |25
a1 |2013-12-01 |2014-03-31 |2014-01-01 |2014-03-31 |75
The 25 in 2013 is because 2013 has one month and 75 in 2014 because this has 3 months
Is there a way to do this in T-SQL?
Thx in advance!
Use spt_values table to create a calendar table, then join to your table to split date range into any part you want.
If split by year and divide amount by months you could:
with dates as
(
select number,DATEADD(day,number,'20130101') as dt
from master..spt_values
where number between 0 and 1000 AND TYPE='P'
)
select
m.start_date as org_start_date,
m.end_date as org_end_date,
min(d.dt) as new_start_date,
max(d.dt) as new_end_date,
m.amount*count(distinct month(d.dt))/(datediff(month,m.start_date,m.end_date)+1) as amount
from
MonthSplit m
join
dates d
on
d.dt between m.start_date and m.end_date
group by
m.start_date, m.end_date, year(d.dt),m.amount
Here is the SQL FIDDLE DEMO.
Here is a solution using a numbers table:
SQL Fiddle Example
DECLARE #STARTYR INT = (SELECT MIN(YEAR([Start Date])) FROM Table1)
DECLARE #ENDYR INT = (SELECT MAX(YEAR([End Date])) FROM Table1)
SELECT [Id]
, #STARTYR + Number AS [Year]
, CASE WHEN YEAR([Start Date]) < #STARTYR + Number
THEN DATEADD(YEAR, #STARTYR - 1900 + Number,0)
ELSE [Start Date] END AS [Start]
, CASE WHEN YEAR([End Date]) > #STARTYR + Number
THEN DATEADD(YEAR, #STARTYR - 1900 + Number + 1,0)
ELSE [End Date] END AS [End]
, DATEDIFF(MONTH,CASE WHEN YEAR([Start Date]) < #STARTYR + Number
THEN DATEADD(YEAR, #STARTYR - 1900 + Number,0)
ELSE [Start Date] END
,CASE WHEN YEAR([End Date]) > #STARTYR + Number
THEN DATEADD(YEAR, #STARTYR - 1900 + Number + 1,0)
ELSE DATEADD(MONTH,DATEDIFF(MONTH,0,DATEADD(MONTH,1,[End Date])),0) END) AS [Months]
, DATEDIFF(MONTH,[Start Date],[End Date]) + 1 [Total Months]
, ([Amount] / (DATEDIFF(MONTH,[Start Date],[End Date]) + 1))
*
DATEDIFF(MONTH,CASE WHEN YEAR([Start Date]) < #STARTYR + Number
THEN DATEADD(YEAR, #STARTYR - 1900 + Number,0)
ELSE [Start Date] END
,CASE WHEN YEAR([End Date]) > #STARTYR + Number
THEN DATEADD(YEAR, #STARTYR - 1900 + Number + 1,0)
ELSE DATEADD(MONTH,DATEDIFF(MONTH,0,DATEADD(MONTH,1,[End Date])),0) END) AS [Proportion]
FROM Numbers
LEFT JOIN Table1 ON YEAR([Start Date]) <= #STARTYR + Number
AND YEAR([End Date]) >= #STARTYR + Number
WHERE Number <= #ENDYR - #STARTYR
I don't have a ready made SQL for you but just a thought to solve this problem. If you have some experience with SQL it won't be hard to express it in SQL.
You could do this by defining a reference table for the months that are between your begin and end date:
ID | month | year | month start | month end | count |
-----------------------------------------------------------------------------
1001 | dec-2013 | 2013 | 1-12-2013 | 31-12-2013 | 1 |
1001 | jan-2014 | 2014 | 1-1-2014 | 31-1-2014 | 1 |
1001 | feb-2014 | 2014 | 1-2-2014 | 28-2-2014 | 1 |
1001 | mar-2014 | 2014 | 1-3-2014 | 31-3-2014 | 1 |
Maybe you already have such a time ref table in your DWH.
When you join (with a between statement) your table with the record that contains the start and end date per row, with this reference table, you'll have the split from 1 row to the number of months contained in the range correct. The count column will help you to get the split ratio per year right by grouping over the year afterwards (like : 1/4 for 2013 and 3/4 for 2014). You'll have to apply the ratio to the field 'amount' that you want to split up.
It's very similar to this question:
Split date range into one row per month in sql server
Although you are doing grouping by year, so based on that answer you can modify it to do what you want by adding MIN, MAX to your date values and grouping by the YEAR():
SQL Fiddle Demo
Schema Setup:
CREATE TABLE MonthSplit
([ID] varchar(2), [start_date] datetime, [end_date] datetime, [amount] int)
;
INSERT INTO MonthSplit
([ID], [start_date], [end_date], [amount])
VALUES
('a1', '2013-12-01 00:00:00', '2014-03-31 00:00:00', 100),
('a2', '2013-10-01 00:00:00', '2015-05-01 00:00:00', 400)
;
Recursive CTE to group by Year:
WITH cte AS
(SELECT ID
, start_date
, end_date
, start_date AS from_date
, DATEADD(day, day(start_date)* -1 + 1, start_date) AS first_of_month
FROM MonthSplit
UNION ALL
SELECT ID
, start_date
, end_date
, DATEADD(month,1,first_of_month)
, DATEADD(month,1,first_of_month)
FROM cte
WHERE DATEADD(month,1,from_date) < end_date
)
SELECT ID as ID,
min(start_date) as org_start_date,
min(end_date) as org_end_date,
min(from_date) AS new_start_date,
CASE when max(end_date) < DATEADD(month,1,max(first_of_month)) THEN
max(end_date)
ELSE
DATEADD(day, -1, DATEADD(month,1,max(first_of_month)))
END AS new_end_date
FROM cte
group by year(from_date), ID
Results:
| ID | ORG_START_DATE | ORG_END_DATE | NEW_START_DATE | NEW_END_DATE |
|----|-------------------|----------------|-------------------|-------------------|
| a1 | December, 01 2013 | March, 31 2014 | December, 01 2013 | December, 31 2013 |
| a1 | December, 01 2013 | March, 31 2014 | January, 01 2014 | March, 31 2014 |
| a2 | October, 01 2013 | May, 01 2015 | October, 01 2013 | December, 31 2013 |
| a2 | October, 01 2013 | May, 01 2015 | January, 01 2014 | December, 31 2014 |
| a2 | October, 01 2013 | May, 01 2015 | January, 01 2015 | April, 30 2015 |

How to get the date for the previous month which are closed in the current month

Iam using SSRS Report builder application to create BI Report for my System which is tracking the numbers of incidents logged and closed based on each month.
the below is the table which i need to create the query
Month Logged Received Closed Remaining
January 200 220 150 70
February 150 220 200 20
March 110 130 100 30
April 200 230 200 30
and each column define as follow:
Logged= Open Incident in the Current Month for example open from 1/1/2014 to 31/1/2014 (Contain only the current month data )
Received = Logged incident+ the remaining from the previous months which are still open not close for example the month febreuary will be 150 for the current moth+70 from previous month remaining will give me total 220 which is received.
Closed= incident which are opened in the current month and closed in the current month + the remaining from the previous month which closed in this month
Remaining= Received – closed
the code which i used is not giving me the close incident for the previous months also its only giving me which were closed in the current month
the below is the code which i used for my query:
SELECT group_id, YEAR(Opendate) AS Year, MONTH(Opendate) AS Month,
COUNT(CASE WHEN Month(Closedate) = Month(Opendate)
AND Month(closedate)> Month (opendate) THEN 1 ELSE NULL END) AS closed,
COUNT(*) AS Logged,
FROM Incidents
WHERE (Opendate >= #YearStart) AND (Opendate <= #YearEnd)
GROUP BY YEAR(Opendate), MONTH(Opendate), group_id
ORDER BY Year, Month,group_id
Logged is working fine the closed, Received and remaining i am stuck on it.
I tried to use Union and got the Logged and Closed Data
Select count(*) logged,year(opendate) as year1,MONTH(opendate) as
month1,'Logged' as status1
From Incidents
where opendate is not null
GROUP BY year(opendate),MONTH(opendate)
UNION
Select count(*) closed,year(Closedate) as year1,MONTH(Closedate) as
month1,'All_Closed' as status1
From Incidents
where Closedate is not null
GROUP BY year(Closedate),MONTH(Closedate)
UNION
Select count(*) Remaining,year(opendate) as year1,MONTH(opendate) as
month1,'Current_Month_Not_Closed' as status1
From Incidents
where Month(Closedate) > MONTH(Opendate)
GROUP BY year(opendate),MONTH(opendate)
UNION
Select count(*) Month_Closed,year(opendate) as year1,MONTH(opendate) as
month1,'Current_Month_Close' as status1
From Incidents
where MONTH(Closedate) = MONTH(Opendate)
GROUP BY year(opendate),MONTH(opendate)
order by year1,month1
the data which I received are as follow:
logged | year1 | month1 | status1
-------+-------+--------+-------------------------
1093 | 2014 | 1 | Logged
1089 | 2014 | 1 | All_Closed
997 | 2014 | 1 | Current_Month_Close
96 | 2014 | 1 | Current_Month_Not_Closed
1176 | 2014 | 2 | Logged
1176 | 2014 | 2 | All_Closed
91 | 2014 | 2 | Current_Month_Not_Closed
1085 | 2014 | 2 | Current_Month_Close
1340 | 2014 | 3 | Logged
1327 | 2014 | 3 | All_Closed
107 | 2014 | 3 | Current_Month_Not_Closed
1232 | 2014 | 3 | Current_Month_Close
116 | 2014 | 4 | Current_Month_Not_Closed
1320 | 2014 | 4 | Current_Month_Close
1424 | 2014 | 4 | All_Closed
1441 | 2014 | 4 | Logged
1167 | 2014 | 5 | Current_Month_Close
105 | 2014 | 5 | Current_Month_Not_Closed
1277 | 2014 | 5 | Logged
1283 | 2014 | 5 | All_Closed
To have a reliable data a calendar table as anchor can help, and is needed in case the tickets can be alive for months from their opening date or there can be a month without ticket created.
For example with the fake data
CREATE TABLE Incidents (
id int identity(1, 1)
, group_id nvarchar(100)
, Opendate Datetime
, Closedate Datetime
)
INSERT INTO Incidents
VALUES ('Service Desk', '20140107', '20140120')
, ('Service Desk', '20140117', '20140123')
, ('Service Desk', '20140127', '20140313')
, ('Service Desk', '20140310', '')
-- from an OP comment the open tickets have the Closedate '' (1900-01-01)
without a calendar table (or a temp, or a CTE) there is no way to add february in the resultset, even if the third record is both "Received" and "Remaining" in that month.
To create a calendar there are several way, in this case we need the some information about months but nothing about the days, so those are not generated.
declare #YearStart date = '20140101'
declare #YearEnd date = '20140430'
;WITH D(N) AS (
SELECT 0 UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3
UNION ALL SELECT 4 UNION ALL SELECT 5 UNION ALL SELECT 6 UNION ALL SELECT 7
UNION ALL SELECT 8 UNION ALL SELECT 9
)
SELECT EOM
= DATEADD(D, -1, DATEADD(M, u.N + 10 * t.N + 1
, DATEADD(Y, DATEDIFF(Y, 0, #YearStart), 0)))
, pMonth = u.N + 10 * t.N
FROM D u
CROSS JOIN D t
WHERE u.N + 10 * t.N <= DATEDIFF(M, #YearStart, #YearEnd)
Here EOM is the date of the end of the month, it'll be used to check if the incidents are closed in the month and pMonth is the progressive month starting from #YearStart.
Now we need to prepare the data in the incident table to be used
SELECT ID
, OpenDate
, Closedate = COALESCE(NULLIF(Closedate, ''), '99991231')
, pOpenDate = DATEDIFF(M, #YearStart, OpenDate)
, pClosedate = DATEDIFF(M, #YearStart
, COALESCE(NULLIF(Closedate, ''), '99991231'))
FROM Incidents
the Closedate need to always have a value higher than the OpenDate, for this is used the constant date 9999-12-31, pOpenDate and pClosedate, as pMonth before, are the progressive month starting from #YearStart respectively of OpenDate and Closedate.
Putting those togheter it's possible to create the main query
declare #YearStart date = '20140101'
declare #YearEnd date = '20140430'
;WITH D(N) AS (
SELECT 0 UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3
UNION ALL SELECT 4 UNION ALL SELECT 5 UNION ALL SELECT 6 UNION ALL SELECT 7
UNION ALL SELECT 8 UNION ALL SELECT 9
), CM AS (
SELECT EOM
= DATEADD(D, -1, DATEADD(M, u.N + 10 * t.N + 1
, DATEADD(Y, DATEDIFF(Y, 0, #YearStart), 0)))
, pMonth = u.N + 10 * t.N
FROM D u
CROSS JOIN D t
WHERE u.N + 10 * t.N <= DATEDIFF(M, #YearStart, #YearEnd)
), I AS (
SELECT ID
, OpenDate
, Closedate = COALESCE(NULLIF(Closedate, ''), '99991231')
, pOpenDate = DATEDIFF(M, #YearStart, OpenDate)
, pClosedate = DATEDIFF(M, #YearStart
, COALESCE(NULLIF(Closedate, ''), '99991231'))
FROM Incidents
)
SELECT MONTH(CM.EOM) [Month]
, Logged = SUM(CASE WHEN pOpenDate = pMonth
THEN 1
ELSE 0
END)
, Received = Count(i.id)
, Closed = SUM(CASE WHEN pClosedate = pMonth
AND i.Closedate < CM.EOM
THEN 1
ELSE 0
END)
, Remaining = SUM(CASE WHEN i.Closedate > CM.EOM
THEN 1
ELSE 0
END)
FROM CM
INNER JOIN I ON CM.pMonth
BETWEEN i.pOpenDate AND i.pClosedate
WHERE CM.EOM <= #YearEnd
GROUP BY CM.EOM
ORDER BY CM.EOM
SQLFiddle Demo
using a JOIN to get the month from the calendar table between #YearStart and #YearEnd and all the incident alive in the month. Their attribute are calculated with the CASE logic, in case of Received if a ticket is alive it's received so no logic is needed.
All the CASE can be transformed in BIT logic
declare #YearStart date = '20140101'
declare #YearEnd date = '20140430'
;WITH D(N) AS (
SELECT 0 UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3
UNION ALL SELECT 4 UNION ALL SELECT 5 UNION ALL SELECT 6 UNION ALL SELECT 7
UNION ALL SELECT 8 UNION ALL SELECT 9
), CM AS (
SELECT EOM
= DATEADD(D, -1, DATEADD(M, u.N + 10 * t.N + 1
, DATEADD(Y, DATEDIFF(Y, 0, #YearStart), 0)))
, pMonth = u.N + 10 * t.N
FROM D u
CROSS JOIN D t
WHERE u.N + 10 * t.N <= DATEDIFF(M, #YearStart, #YearEnd)
), I AS (
SELECT ID
, OpenDate
, Closedate = COALESCE(NULLIF(Closedate, ''), '99991231')
, pOpenDate = DATEDIFF(M, #YearStart, OpenDate)
, pClosedate = DATEDIFF(M, #YearStart
, COALESCE(NULLIF(Closedate, ''), '99991231'))
FROM Incidents
)
SELECT MONTH(CM.EOM) [Month]
, Logged = SUM(1 - CAST(pOpenDate - pMonth AS BIT))
, Received = Count(i.id)
, Closed = SUM(1 - CAST(pClosedate - pMonth AS BIT))
, Remaining = SUM(0 + CAST(i.pClosedate / (CM.pMonth + 1) AS BIT))
FROM CM
INNER JOIN I ON CM.pMonth
BETWEEN i.pOpenDate AND i.pClosedate
WHERE CM.EOM <= #YearEnd
GROUP BY CM.EOM
ORDER BY CM.EOM;
SQLFiddle Demo
The bit logic is base on how the CAST to BIT works:
0 go to 0
everything else go to 1
based on that (with A and B integer):
1 - CAST(A - B AS BIT) is 1 when A = B
CAST(A / (B + 1) AS BIT) is 1 when A > B (the 0 + is to force an implicit cast to INT as BIT cannot be SUMmed)
Received would be the number of tickets that were opened before the end of the month, and not closed before the start of the month.
count(case when OpenDate <= #EndOfMonth and
(#StartOfMonth >= CloseDate or CloseDate is null) then 1 end)
as Received
Closed is straightforward:
count(case when CloseDate between #StartOfMonth and #EndOfMonth
then 1 end) as Closed
You should be able to figure out how to calculate the start and end of a month using Google.

sql server calculate cumulative number per month for different year

I have a table with "date" column. Each row represents a survey.
date
11/19/2013 5:51:41 PM
11/22/2013 1:30:38 PM
11/23/2013 3:09:17 PM
12/2/2014 5:24:17 PM
12/25/2014 11:42:56 AM
1/6/2014 2:24:49 PM
I want to count the number of survey per month cumulatively. As you see from the above table, there are 3 surveys for Nov 2013, 2 surveys for Dec 2013, 1 survey for Jan 2014. The cumulative number of survey per month would be:
month | year | number_of_survey
11 | 2013 | 3
12 | 2013 | 5
1 | 2014 | 6
I have this query which shows correct number of surveys for 2013, and number of survey for 2014 is not cumulative.
with SurveyPerMonth as -- no of Survey per month
(
select datepart(month, s.date) as month,
datepart(year, s.date) as year,
count(*) as no_of_surveys
from myTable s
group by datepart(year, s.date), datepart(month, s.date)
)
select p1.month, p1.year, sum(p2.no_of_surveys) as surveys -- cumulatively
from SurveyPerMonth p1
inner join SurveyPerMonth p2 on p1.month >= p2.month and p1.year>=p2.year **-- the problem is probably comes from this line of code**
group by p1.month, p1.year
order by p1.year, p1.month;
This query returns:
month | year | surveys
11 | 2013 | 3
12 | 2013 | 5
1 | 2014 | 1 // 2014 is not cumulative
How can I calculate cumulative number of surveys per month for 2014 as well?
Something like this ?
SELECT date = create_date INTO #myTable FROM master.sys.objects
;WITH perMonth ( [year], [month], [no_of_surveys])
AS (SELECT DatePart(year, s.date) ,
DatePart(month, s.date),
COUNT(*)
FROM #myTable s
GROUP BY datepart(year, s.date),
datepart(month, s.date))
SELECT [year],
[month],
[no_of_surveys] = ( SELECT SUM([no_of_surveys])
FROM perMonth agg
WHERE (agg.[year] < pm.[year])
OR (agg.[year] = pm.[year] AND agg.[month] <= pm.[month]))
FROM perMonth pm
ORDER BY [year], [month]
Edit: seems I missed the ball with < and >, fixed it and added small example
'--This should work.I have added a new column 'monthyear'
with surveypermonth as -- no of survey per month
(
select datepart(month, s.date) as month,
datepart(year, s.date) as year,
datepart(year, s.date) *100 + datepart(month, s.date) as monthyear,
count(*) as no_of_surveys
from test s
group by datepart(year, s.date), datepart(month, s.date),datepart(year, s.date)*100 + datepart(month, s.date)
)
select a.month,substring(cast(monthyear as varchar(6)),1,4) as year,surveys from
(
select p1.month, p1.monthyear as monthyear, sum(p2.no_of_surveys) as surveys
from surveypermonth p1
inner join surveypermonth p2 on p1.monthyear>=p2.monthyear
group by p1.month, p1.monthyear
--order by p1.monthyear, p1.month
)a

Query to return all the days of a month

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