Help please, I have the following data in one of my MSSQL Data tables.
ID |StartDateTime |EndDateTime |OrderNo|
1 |12-08-01 08:00 |12-08-01 08:00 |6001 |
5 |12-08-01 09:00 |12-08-01 10:00 |6001 |
7 |12-08-01 10:00 |12-08-01 11:00 |6001 |
10 |12-08-01 11:00 |12-08-01 12:00 |6002 |
15 |12-08-01 12:00 |12-08-01 13:00 |6002 |
22 |12-08-01 13:00 |12-08-01 14:00 |6003 |
29 |12-08-01 14:00 |12-08-01 15:00 |6001 |
33 |12-08-01 15:00 |12-08-01 16:00 |6001 |
36 |12-08-01 16:00 |12-08-01 17:00 |6004 |
The problem is currently I have no way to tell if the OrderNo has been used multiple times. I cant tell that Order 6001 has been run twice.
I'd like to be able to add a new field to uniquely identify each run of an order from now on. But also go back over previous records and update them as well.
ID |StartDateTime |EndDateTime |OrderNo|Run|
1 |12-08-01 08:00 |12-08-01 08:00 |6001 |1 |
5 |12-08-01 09:00 |12-08-01 10:00 |6001 |1 |
7 |12-08-01 10:00 |12-08-01 11:00 |6001 |1 |
10 |12-08-01 11:00 |12-08-01 12:00 |6002 |1 |
15 |12-08-01 12:00 |12-08-01 13:00 |6002 |1 |
22 |12-08-01 13:00 |12-08-01 14:00 |6003 |1 |
29 |12-08-01 14:00 |12-08-01 15:00 |6001 |2 |
33 |12-08-01 15:00 |12-08-01 16:00 |6001 |2 |
36 |12-08-01 16:00 |12-08-01 17:00 |6004 |1 |
The idea being I can group by OrderNo and Run and will recieve the following.
OrderNo |Run |RunStart |RunEnd |
6001 | 1 |12-08-01 08:00 |12-08-01 11:00 |
6001 | 2 |12-08-01 14:00 |12-08-01 16:00 |
6002 | 1 |12-08-01 11:00 |12-08-01 13:00 |
6003 | 1 |12-08-01 13:00 |12-08-01 14:00 |
6004 | 1 |12-08-01 16:00 |12-08-01 17:00 |
I have tried multiple ways using ROW_NUMBER, CTE, cursors etc to run through the data. I have a feeling there is a simple solution but I can't figure it out.
I Hope this makes sense.
EDIT
I have changed the data tables to reference an extra complication, which I didn't include first time round. Aaron's solution provided would have worked fine. But it assumes the runs can only last for upto 2 hours (or rows). In my database these runs for n hours (or rows). Im sorry I wasn't clear first time around, and I appreciate the help given thus far.
Your edit actually makes the problem simpler to me (possibly just because I missed a simpler island approach initially).
;WITH x AS
(
SELECT OrderNo, StartDateTime, EndDateTime,
rn1 = ROW_NUMBER() OVER (ORDER BY StartDateTime),
rn = ROW_NUMBER() OVER (PARTITION BY OrderNo ORDER BY StartDateTIme)
FROM dbo.table_name -- you need to change this
),
y AS
(
SELECT OrderNo, Island = rn1 - rn,
rs = MIN(StartDateTime),
re = MAX(EndDateTime)
FROM x GROUP BY OrderNo, rn1 - rn
)
SELECT
OrderNo,
Run = ROW_NUMBER() OVER (PARTITION BY OrderNo ORDER BY rs),
RunStart = rs,
RunEnd = rs
FROM y
ORDER BY OrderNo, Run;
Leaving my original answer for posterity.
There is probably a simpler way, but this gets the answer you're after using window functions.
;WITH x AS
(
SELECT ID, StartDateTime, EndDateTime, OrderNo,
rn = ROW_NUMBER() OVER (PARTITION BY OrderNo ORDER BY StartDateTime)
FROM dbo.table_name -- you need to change this
), y AS
(
SELECT x.ID, x.StartDateTime, x.EndDateTime, x.OrderNo, x.rn,
x2ID = x2.ID, x2S = x2.StartDateTime, x2E = x2.EndDateTime,
x2O = x2.OrderNo, x2rn = x2.rn
FROM x LEFT OUTER JOIN x AS x2
ON x.OrderNo = x2.OrderNo
AND x.rn = x2.rn - 1
AND x.ID = x2.ID - 1
)
SELECT
OrderNo,
Run = ROW_NUMBER() OVER (PARTITION BY OrderNo ORDER BY StartDateTime),
RunStart = StartDateTime,
RunEnd = COALESCE(x2E, EndDateTime)
FROM y
WHERE x2ID IS NOT NULL
OR NOT EXISTS
(
SELECT 1 FROM y AS y2 WHERE y2.OrderNo = y.OrderNo AND y2.x2rn = y.rn
)
ORDER BY OrderNo, Run;
Related
How to calculate working hours between two dates in snowflake without creating tables?
i have tried function like (datediff) and timestamp but i could not reach the solution
i would like to get something like that
+---------------------+---------------------+---------------+
| create_Task | Solved_Task | BusinessHours |
+---------------------+---------------------+---------------+
| 2012-03-05 09:00:00 | 2012-03-05 15:00:00 | 6.000000 |
| 2012-03-05 10:00:00 | 2012-03-06 10:00:00 | 8.000000 |
| 2012-03-05 11:00:00 | 2012-03-06 10:00:00 | 7.000000 |
| 2012-03-05 10:00:00 | 2012-03-06 15:00:00 | 13.000000 |
| 2012-03-09 16:00:00 | 2012-03-12 10:00:00 | 2.000000 |
| 2012-03-06 16:00:00 | 2012-03-15 10:00:00 | 50.000000 |
| 2012-03-09 16:00:00 | 2012-03-19 10:00:00 | 42.000000 |
+---------------------+---------------------+---------------+
and i would like to specify the working hours so then i can calculate the business hours
One way to do this is by creating a working hours table. Then you can run a fairly simple query:
select
t.id
, sum(datediff(‘second’,
-- calculate the max of the two start time
(case when t.start <=
w.working_day_start_timestamp
then w.working_day_start_timestamp
else t.start
end),
-- calculate the min of the two end times
(case when t.end >=
w.working_day_end_timestamp
then w.working_day_end_timestamp
else t.end
end)
)) / 3600 -- convert to hourly
as working_hour_diff
from
working_days_times w,
cross join time_intervals t
where -- select all intersecting intervals
(
t.start <= w.working_day_end_timestamp
and
t.end >= w.working_day_start_timestamp
)
and -- select only working days
w.is_working_day
group by
t.id
If you need a function, this article describes the implementation of a Javascript UDF in Snowflake:
https://medium.com/dandy-engineering-blog/how-to-calculate-the-number-of-working-hours-between-two-timestamps-in-sql-b5696de66e51
I need help computing a date difference across different rows with variable lag (specifically, rows that are not on the same day) without subqueries, joins, etc. I think this should be possible with some inline t-SQL aggregates that use OVER(PARTITION BY) clause, such as LAG, DENSE_RANK, etc., but I can't quite put a finger on it. This is for a SQL Server 2017 Developer's edition.
A clarifying example:
Consider a dataset with Job beginning and end dates (across various projects). Some jobs start and end on the same day (such as jobs 2 & 3, 4 & 5). I need to compute the idle time between consequent jobs that started on different days (per project). That is the days between last job's ending time and current job's beginning time. If the previous job started on the same day, then look further back in history of the same project. I.e. the jobs that started on the same day can be considered as parts of the same job.
UPDATE: I simplified the code/output by dropping time values (question's history has original dataset).
IF OBJECT_ID('tempdb..#t') IS NOT NULL DROP TABLE #t;
CREATE TABLE #t(Prj TINYINT, Beg DATE, Eñd DATE);
INSERT INTO #t SELECT 1, '1/1/17', '1/2/17';
INSERT INTO #t SELECT 1, '1/5/17', '1/7/17';
INSERT INTO #t SELECT 1, '1/5/17', '1/7/17';
INSERT INTO #t SELECT 1, '1/15/17', '1/15/17';
INSERT INTO #t SELECT 1, '1/15/17', '1/18/17';
INSERT INTO #t SELECT 1, '1/20/17', '1/24/17';
INSERT INTO #t SELECT 2, '2/2/17', '2/5/17';
INSERT INTO #t SELECT 2, '2/7/17', '2/9/17';
ALTER TABLE #t ADD Job INT NOT NULL IDENTITY (1,1) PRIMARY KEY;
A LAG(.,1) function uses precisely the previous job's ending time, which is not what I want. It yields incorrect idle duration for jobs 2 & 3, 4 & 5. Jobs 2 & 3 should both use the ending time of job 1. Jobs 4 & 5 should both use the ending time of job 3. The joined query computes idle duration correctly, but an inline calculation is desirable here (without joins, subqueries).
SELECT c.Job, c.Prj, c.Beg, c.Eñd,
-- in-line computation with OVER clause
PrvEñd_lg=LAG(c.Eñd,1) OVER(PARTITION BY c.Prj ORDER BY c.Beg),
Idle_lg=DATEDIFF(DAY, LAG(c.Eñd,1) OVER(PARTITION BY c.Prj ORDER BY c.Beg), c.Beg),
-- calculation over current and (joined) previous records
PrvEñd_j=MAX(p.Eñd),
IdleDur_j=DATEDIFF(DAY, MAX(p.Eñd), c.Beg)
FROM #t c LEFT JOIN #t p ON c.Prj=p.Prj AND c.Beg > p.Eñd
GROUP BY c.Job, c.Prj, c.Beg, c.Eñd
ORDER BY c.Prj, c.Beg
Job Prj Beg Eñd PrvEñd_lg Idle_lg PrvEñd_j IdleDur_j
1 1 2017-01-01 2017-01-02 NULL NULL NULL NULL
2 1 2017-01-05 2017-01-07 2017-01-02 3 2017-01-02 3
3 1 2017-01-05 2017-01-07 2017-01-07 -2 2017-01-02 3
4 1 2017-01-15 2017-01-15 2017-01-07 8 2017-01-07 8
5 1 2017-01-15 2017-01-18 2017-01-15 0 2017-01-07 8
6 1 2017-01-20 2017-01-24 2017-01-18 2 2017-01-18 2
7 2 2017-02-02 2017-02-05 NULL NULL NULL NULL
8 2 2017-02-07 2017-02-09 2017-02-05 2 2017-02-05 2
Please let me know, if I can further clarify any specific details.
Many thanks!
You can use a self-join.
select a.Job
, a.Prj
, a.Beg
, a.Eñd
, max(b.Eñd) as PrevEñd
, min(datediff(mi, b.Eñd, a.Beg) / (60*24.0)) as IdleDur
from #t as a
left join #t as b on a.Prj = b.Prj
and cast(a.Beg as date) > cast(b.Eñd as date)
group by a.Job
, a.Prj
, a.Beg
, a.Eñd
This produces the following output:
+-----+-----+---------------------+---------------------+---------------------+-----------+
| Job | Prj | Beg | Eñd | PrevEñd | IdleDur |
+-----+-----+---------------------+---------------------+---------------------+-----------+
| 1 | 1 | 2017-01-01 01:00:00 | 2017-01-02 02:00:00 | NULL | NULL |
| 2 | 1 | 2017-01-05 02:00:00 | 2017-01-07 03:00:00 | 2017-01-02 02:00:00 | 3.0000000 |
| 3 | 1 | 2017-01-05 03:00:00 | 2017-01-07 02:00:00 | 2017-01-02 02:00:00 | 3.0416666 |
| 4 | 1 | 2017-01-15 04:00:00 | 2017-01-15 03:00:00 | 2017-01-07 03:00:00 | 8.0416666 |
| 5 | 1 | 2017-01-15 15:00:00 | 2017-01-18 03:00:00 | 2017-01-07 03:00:00 | 8.5000000 |
| 6 | 1 | 2017-01-20 05:00:00 | 2017-01-24 02:00:00 | 2017-01-18 03:00:00 | 2.0833333 |
| 7 | 2 | 2017-02-02 06:00:00 | 2017-02-05 03:00:00 | NULL | NULL |
| 8 | 2 | 2017-02-07 07:00:00 | 2017-02-09 02:00:00 | 2017-02-05 03:00:00 | 2.1666666 |
+-----+-----+---------------------+---------------------+---------------------+-----------+
I need to sum up values from Money column for each WeekNumber.
Now I have view:
WeekNumber | DayTime | Money
---------------------------------------
1 | 2012-01-01 | 20.4
1 | 2012-01-02 | 30.5
1 | 2012-01-03 | 55.1
2 | 2012-02-01 | 67.3
2 | 2012-02-02 | 33.4
3 | 2012-03-01 | 11.8
3 | 2012-03-04 | 23.9
3 | 2012-03-05 | 34.3
4 | 2012-04-01 | 76.6
4 | 2012-04-02 | 90.3
Tsql:
SELECT datepart(week,DayTime) AS WeekNumber, DayTime, Money FROM dbo.Transactions
In conclusion, I would like to get something like this:
WeekNumber | DayTime | Sum
---------------------------------------
1 | 2012-01-01 | 106
2 | 2012-02-02 | 100.7
3 | 2012-03-03 | 470
4 | 2012-04-01 | 166.9
DayTime should be random for each Week Number but exists in column DayTime from view above.
Please, be free to write your ideas. Thanks.
SELECT datepart(week,DayTime) AS WeekNumber
, MIN(DayTime) DayTime --<-- Instead of random get first date from your data in that week
, SUM(Money) AS [Sum]
FROM dbo.Transactions
GROUP BY datepart(week,DayTime)
Try this
SELECT datepart(week,DayTime) AS WeekNumber, SUM(Money) FROM dbo.Transactions GROUP BY WeekNumber
As you will have number of rows for each week you cannot get DayTime with the same table. There are other ways to add that too like JOIN
Change your SQL to sum the money column. Like this
SELECT
datepart(week,DayTime) AS WeekNumber,
DayTime, Money = SUM(Money)
FROM dbo.Transactions
GROUP BY datepart(week,DayTime),DayTime
SELECT datepart(week, DayTime) AS WeekNumber
,MIN(DayTime)
,SUM(MONEY)
FROM dbo.Transactions
GROUP BY datepart(week, DayTime)
I have a query which combines data from two tables.
Policy table
PolicyID PolicyNumber PolicyStartDate
48 FCO100009 2015-06-01 00:00:00.000
49 FCO100009 2016-06-01 00:00:00.000
Claim Table
ClaimID ClaimReference PolicyNumber IncidentDatetime NotificationDatetime Version
30 287 FCO100009 2015-11-06 00:00:00.000 2015-11-27 00:00:00.000 4. Claim - Incident Date
223 259 FCO100009 2015-11-03 00:00:00.000 2015-11-20 00:00:00.000 4. Claim - Incident Date
1367 988 FCO100009 2016-04-15 00:00:00.000 2016-04-21 00:00:00.000 4. Claim - Incident Date
1561 1859 FCO100009 2016-09-14 00:00:00.000 2016-09-19 00:00:00.000 4. Claim - Incident Date
1741 443275 FCO100009 2016-05-11 00:00:00.000 2016-05-12 00:00:00.000 4. Claim - Incident Date
1742 991 FCO100009 2016-04-20 00:00:00.000 2016-04-21 00:00:00.000 4. Claim - Incident Date
2038 287 FCO100009 2015-11-06 00:00:00.000 2015-11-27 00:00:00.000 5. Claim - Notification Date
3744 259 FCO100009 2015-11-03 00:00:00.000 2015-11-20 00:00:00.000 5. Claim - Notification Date
3745 991 FCO100009 2016-04-20 00:00:00.000 2016-04-21 00:00:00.000 5. Claim - Notification Date
4502 1859 FCO100009 2016-09-14 00:00:00.000 2016-09-19 00:00:00.000 5. Claim - Notification Date
4639 988 FCO100009 2016-04-15 00:00:00.000 2016-04-21 00:00:00.000 5. Claim - Notification Date
6600 443275 FCO100009 2016-05-11 00:00:00.000 2016-05-12 00:00:00.000 5. Claim - Notification Date
There are 2 records for the Policy with different Policy Start Dates and a 2 versions of each claim record where the Version field is either Claim Incident Date or Claim Notification Date.
What I am attempting to accomplish is joining the two tables on PolicyNumber and then setting the PolicyStartDate value in the results to be the maximum value from Policy.PolicyStartDate where the PolicyStartDate is less than the NotificationDate when Version = NotificationDate OR PolicyStartDate is less than the Incident Date when Version = IncidentDate.
Please note that this is using financial NOT Calendar years and in this case the year of account runs from April to March.
Here is my current query which doesn't produce the correct answer:
SELECT cds.ClaimID,
cds.ClaimReference,
p.policyID,
p.PolicyStartDate,
cds.IncidentDatetime,
cds.NotificationDatetime,
cds.[Version]
FROM dbo.ClaimDataStaging cds
INNER JOIN dbo.[Policy] p
ON p.PolicyNumber = cds.PolicyNumber
AND p.PolicyStartDate < CASE WHEN cds.[Version] = '4. Claim - Incident Date' THEN cds.IncidentDatetime
WHEN cds.[Version] = '5. Claim - Notification Date' THEN cds.NotificationDatetime END
WHERE cds.PolicyNumber = 'FCO100009'
ORDER BY cds.[Version], cds.ClaimReference;
GO
Any help or advice much appreciated.
As far as I understood your question and your problem (duplicates caused by condition on date < date which catches former policy too), you can try this query too.
It use LEAD() function to calculate a sort of Enddate (I used IS NULL to to catch last policy, but you can change it adapting to your needs).
I moved the CASE in an inner query to avoid repeating of it in the WHERE clause.
SELECT cds.ClaimID,
cds.ClaimReference,
p.policyID,
p.PolicyStartDate,
cds.IncidentDatetime,
cds.NotificationDatetime,
cds.[Version]
FROM (SELECT ClaimID, ClaimReference, IncidentDatetime,NotificationDatetime,[Version], PolicyNumber
, CASE WHEN [Version] = '4. Claim - Incident Date' THEN IncidentDatetime
WHEN [Version] = '5. Claim - Notification Date' THEN NotificationDatetime END AS CheckDate
FROM dbo.ClaimDataStaging) cds
INNER JOIN (SELECT policyID, PolicyNumber, PolicyStartDate
, LEAD(PolicyStartDate) OVER (PARTITION BY PolicyNumber ORDER BY PolicyStartDate) AS PolicyEndDate FROM dbo.Policy ) p
ON p.PolicyNumber = cds.PolicyNumber
AND p.PolicyStartDate < CheckDate
AND (p.PolicyEndDate IS NULL OR p.PolicyEndDate>=CheckDate)
WHERE cds.PolicyNumber = 'FCO100009'
ORDER BY cds.[Version], cds.ClaimReference;
Output:
+---------+----------------+----------+-------------------------+-------------------------+-------------------------+------------------------------+
| ClaimID | ClaimReference | policyID | PolicyStartDate | IncidentDatetime | NotificationDatetime | Version |
+---------+----------------+----------+-------------------------+-------------------------+-------------------------+------------------------------+
| 223 | 259 | 48 | 2015-06-01 00:00:00.000 | 2015-11-03 00:00:00.000 | 2015-11-20 00:00:00.000 | 4. Claim - Incident Date |
| 30 | 287 | 48 | 2015-06-01 00:00:00.000 | 2015-11-06 00:00:00.000 | 2015-11-27 00:00:00.000 | 4. Claim - Incident Date |
| 1367 | 988 | 48 | 2015-06-01 00:00:00.000 | 2016-04-15 00:00:00.000 | 2016-04-21 00:00:00.000 | 4. Claim - Incident Date |
| 1742 | 991 | 48 | 2015-06-01 00:00:00.000 | 2016-04-20 00:00:00.000 | 2016-04-21 00:00:00.000 | 4. Claim - Incident Date |
| 1561 | 1859 | 49 | 2016-06-01 00:00:00.000 | 2016-09-14 00:00:00.000 | 2016-09-19 00:00:00.000 | 4. Claim - Incident Date |
| 1741 | 443275 | 48 | 2015-06-01 00:00:00.000 | 2016-05-11 00:00:00.000 | 2016-05-12 00:00:00.000 | 4. Claim - Incident Date |
| 3744 | 259 | 48 | 2015-06-01 00:00:00.000 | 2015-11-03 00:00:00.000 | 2015-11-20 00:00:00.000 | 5. Claim - Notification Date |
| 2038 | 287 | 48 | 2015-06-01 00:00:00.000 | 2015-11-06 00:00:00.000 | 2015-11-27 00:00:00.000 | 5. Claim - Notification Date |
| 4639 | 988 | 48 | 2015-06-01 00:00:00.000 | 2016-04-15 00:00:00.000 | 2016-04-21 00:00:00.000 | 5. Claim - Notification Date |
| 3745 | 991 | 48 | 2015-06-01 00:00:00.000 | 2016-04-20 00:00:00.000 | 2016-04-21 00:00:00.000 | 5. Claim - Notification Date |
| 4502 | 1859 | 49 | 2016-06-01 00:00:00.000 | 2016-09-14 00:00:00.000 | 2016-09-19 00:00:00.000 | 5. Claim - Notification Date |
| 6600 | 443275 | 48 | 2015-06-01 00:00:00.000 | 2016-05-11 00:00:00.000 | 2016-05-12 00:00:00.000 | 5. Claim - Notification Date |
+---------+----------------+----------+-------------------------+-------------------------+-------------------------+------------------------------+
Based on the suggestions received, I was able to adjust my query to provide the required result. Here is my final code for reference:
SELECT DISTINCT
cds.ClaimID,
MAX(p.PolicyID) OVER (PARTITION BY cds.PolicyNumber) AS PolicyID,
MAX(p.PolicyStartDate) OVER (PARTITION BY cds.PolicyNumber) AS
PolicyStartDate, cds.ClaimKey, cds.ClaimReference, cds.ClaimStatus,
cds.IncidentDatetime, cds.NotificationDatetime, cds.UW_Date,
cds.IncidentType, cds.IncidentDescription, cds.OwnDamagePaid,
cds.OwnDamageReserve, cds.OwnDamageIncurred, cds.TPDamagePaid,
cds.TPDamageReserve, cds.TPDamageIncurred,
cds.BodilyInjuryPaid, cds.BodilyInjuryReserve,
cds.BodilyInjuryIncurred, cds.TotalPaid, cds.TotalReserve,
cds.EstimatedRecovery, cds.ActualRecovery, cds.TotalIncurred, cds.TotalIncurredBand,
CONVERT(VARCHAR(16), 'Current Period') AS TimeView, 1 AS ClaimCount, CONVERT(VARCHAR(48), [Version]) AS [Version]
FROM dbo.ClaimDataStaging cds
INNER JOIN dbo.UW_Calendar u
ON u.UW_Date = cds.UW_Date
LEFT OUTER JOIN dbo.[Policy] p
ON p.PolicyNumber = cds.PolicyNumber
AND p.PolicyStartDate <= CASE
WHEN cds.[Version] = '4. Claim - Incident Date' THEN cds.IncidentDatetime
WHEN cds.[Version] = '5. Claim - Notification Date' THEN cds.NotificationDatetime
END;
GO
I think you're looking for something like the query below. It will give you the max policy number based on the filter you had for the dates.
SELECT cds.ClaimID,
cds.ClaimReference,
p.policyID,
p.PolicyStartDate,
cds.IncidentDatetime,
cds.NotificationDatetime,
cds.[Version]
FROM dbo.ClaimDataStaging cds
CROSS APPLY (
SELECT PolicyStartDate = MAX(fp.PolicyStartDate)
FROM dbo.[Policy] fp
WHERE fp.PolicyNumber = cds.PolicyNumber
AND ((fp.PolicyStartDate < cds.IncidentDatetime AND cds.[Version] = '4. Claim - Incident Date')
OR (fp.PolicyStartDate < cds.NotificationDatetime AND cds.[Version] = '5. Claim - Notification Date')) sp
INNER JOIN dbo.[Policy] p ON p.PolicyNumber = cds.PolicyNumber AND p.PolicyStartDate = sp.PolicyStartDate
WHERE cds.PolicyNumber = 'FCO100009'
ORDER BY cds.[Version], cds.ClaimReference;
GO
Using T-SQL, I want a new column that will show me the first day of each month, for the current year of getdate().
After that I need to count the rows on this specific date. Should I do it with CTE or a temp table?
If 2012+, you can use DateFromParts()
To Get a List of Dates
Select D = DateFromParts(Year(GetDate()),N,1)
From (values (1),(2),(3),(4),(5),(6),(7),(8),(9),(10),(11),(12)) N(N)
Returns
D
2017-01-01
2017-02-01
2017-03-01
2017-04-01
2017-05-01
2017-06-01
2017-07-01
2017-08-01
2017-09-01
2017-10-01
2017-11-01
2017-12-01
Edit For Trans Count
To get Transactions (assuming by month). It becomes a small matter of a left join to created Dates
-- This is Just a Sample Table Variable for Demonstration.
-- Remove this and Use your actual Transaction Table
--------------------------------------------------------------
Declare #Transactions table (TransDate date,MoreFields int)
Insert Into #Transactions values
('2017-02-18',6)
,('2017-02-19',9)
,('2017-03-05',5)
Select TransMonth = A.MthBeg
,TransCount = count(B.TransDate)
From (
Select MthBeg = DateFromParts(Year(GetDate()),N,1)
,MthEnd = EOMonth(DateFromParts(Year(GetDate()),N,1))
From (values (1),(2),(3),(4),(5),(6),(7),(8),(9),(10),(11),(12)) N(N)
) A
Left Join #Transactions B on TransDate between MthBeg and MthEnd
Group By A.MthBeg
Returns
TransMonth TransCount
2017-01-01 0
2017-02-01 2
2017-03-01 1
2017-04-01 0
2017-05-01 0
2017-06-01 0
2017-07-01 0
2017-08-01 0
2017-09-01 0
2017-10-01 0
2017-11-01 0
2017-12-01 0
For an adhoc table of months for a given year:
declare #year date = dateadd(year,datediff(year,0,getdate() ),0)
;with Months as (
select
MonthStart=dateadd(month,n,#year)
from (values(0),(1),(2),(3),(4),(5),(6),(7),(8),(9),(10),(11)) t(n)
)
select MonthStart
from Months
rextester demo: http://rextester.com/POKPM51023
returns:
+------------+
| MonthStart |
+------------+
| 2017-01-01 |
| 2017-02-01 |
| 2017-03-01 |
| 2017-04-01 |
| 2017-05-01 |
| 2017-06-01 |
| 2017-07-01 |
| 2017-08-01 |
| 2017-09-01 |
| 2017-10-01 |
| 2017-11-01 |
| 2017-12-01 |
+------------+
The first part: dateadd(year,datediff(year,0,getdate() ),0) adds the number of years since 1900-01-01 to the date 1900-01-01. So it will return the first date of the year. You can also swap year for other levels of truncation: year, quarter, month, day, hour, minute, second, et cetera.
The second part uses a common table expression and the table value constructor (values (...),(...)) to source numbers 0-11, which are added as months to the start of the year.
Not sure why you require recursive... But for first day of month you can try query like below:
Select Dateadd(day,1,eomonth(Dateadd(month, -1,getdate())))
declare #year date = dateadd(year,datediff(year,0,getdate() ),0)
;WITH months(MonthNumber) AS
(
SELECT 0
UNION ALL
SELECT MonthNumber+1
FROM months
WHERE MonthNumber < 11
)
select dateadd(month,MonthNumber,#year)
from months