sql query to get bi-weekly data - sql

enter image description hereI want to know the start bi-week date and end bi-week date.
means i want to get bi-weekly data from the below table structure.
create table #temp(
EmployeeID int,
TEDate datetime,
EmpFNamenvarchar(100),
EmpLName nvarchar(100)
)
go
insert into #temp (EmployeeID ,EmpFName,EmpLName,TEDate)
Select 2019,'roz','Ahmad','2019-04-23'
union all
Select 2019,'roz','Ahmad','2019-04-17'
union all
Select 2019,'roz','Ahmad','2019-04-29'
select * from #temp
How to do it? I have used below approach but still getting wrong results at the end.
Select *,
DATEADD(WEEK, DATEPART(wk, TEDate),
DATEADD(YEAR, year(TEDate) - 1900, 0)) - 4 - DATEPART(DW, DATEADD(WEEK, DATEPART(wk, TEDate),
DATEADD(YEAR, year(TEDate) - 1900, 0)) - 4) + 1 AS [BiWEEK_START],
DATEADD(WEEK, DATEPART(wk, TEDate),
DATEADD(YEAR, year(TEDate) - 1900, 0)) - 4 - DATEPART(DW, DATEADD(WEEK, DATEPART(wk, TEDate),
DATEADD(YEAR, year(TEDate) - 1900, 0)) - 4) + 14 AS [BiWEEK_END]
from #temp
Desired results:
4/17/2019
4/14/2019
4/27/2019
4/22/2019
4/14/2019
4/27/2019
4/23/2019
4/14/2019
4/27/2019
4/29/2019
4/28/2019
5/11/2019
5/3/2019
4/28/2019
5/11/2019
5/6/2019
4/28/2019
5/11/2019
5/8/2019
4/28/2019
5/11/2019
5/13/2019
5/12/2019
5/25/2019

Ok, here are two solutions. The problem is, you have to be able to tell which bi week a TEDate belongs to just by looking at the TEDate, otherwise you have to define either a list of the bi weeks or tell your query where to start. For the second query you should be able to put in any bi week start date and it should still work.
DECLARE #bi_weeks TABLE
(
StartDate DATE,
EndDate DATE
);
INSERT INTO #bi_weeks VALUES
('20190317','20190330'),
('20190331','20190413'),
('20190414','20190427'),
('20190428','20190511'),
('20190512','20190525');
DECLARE #emp_data TABLE
(
EmployeeID INT,
TEDate DATETIME,
EmpFName NVARCHAR(100),
EmpLName NVARCHAR(100)
);
INSERT INTO #emp_data (EmployeeID ,EmpFName,EmpLName,TEDate) VALUES
(2019,'roz','Ahmad','2019-03-20'),
(2019,'roz','Ahmad','2019-04-01'),
(2019,'roz','Ahmad','2019-04-13'),
(2019,'roz','Ahmad','2019-04-23'),
(2019,'roz','Ahmad','2019-04-17'),
(2019,'roz','Ahmad','2019-04-29');
--This is the first way where we join to a list of known bi week start and end dates
SELECT ed.*, bw.*
FROM #emp_data ed
LEFT JOIN #bi_weeks bw ON ed.TEDate BETWEEN bw.StartDate AND bw.EndDate
ORDER BY 2
--This is the second way where we tell our query a known start date and then use it to
--calculate the rest of the start and end dates.
DECLARE #initial_bi_week_start_date DATE = '20190414';
With empdataCTE AS
(
SELECT *,
[initial_bi_week_start_date] = #initial_bi_week_start_date ,
[bi_week_start] =
DATEADD(DAY,
(DATEDIFF(DAY, #initial_bi_week_start_date, TEDate) / 14 +
IIF(TEDate < #initial_bi_week_start_date, 1, 0) * -1) * 14,
#initial_bi_week_start_date)
FROM #emp_data ed
)
SELECT *, DATEADD(DAY, 13, [bi_week_start]) [bi_week_end]
FROM empdataCTE;

Related

Return all rows with no activity in the past 12 months but have an activity in the current month?

I have a table that looks like this:
SALESPERSON
CUSTOMER
LOAD_ID
DATE
ABC
CUST1
001
5/10/2021
ABC
CUST2
002
8/18/2020
DFG
CUST3
003
6/12/2018
I want my query to return all customers who have no loads within the previous 12 months but have loads in the current month of May. So basically the query should return Loads 001 and 003.
You can use lag(). I'm not sure if "previous 12 months" is 12 months before the date or the previous 12 calendar months, but the logic is like this:
select t.*
from (select t.*,
lag(date) over (partition by customer order by date) as prev_date
from t
) t
where date >= datefromparts(year(getdate()), month(getdate()), 1) and
date < dateadd(day, 1, datefromparts(year(getdate()), month(getdate()), 1)) and
(prev_date is null or
prev_date < dateadd(month, -12, datefromparts(year(getdate()), month(getdate()), 1))
)
There are many ways of obtaining the result. I used one approach and this is quite simple. Our logic is to get the rows which are greater than the previous period end and less than previous period start. Basically data does not fall in between the previous 12 months, if I am not wrong.
DECLARE #table_sales AS TABLE
(
SALESPERSON VARCHAR(100)
, CUSTOMER VARCHAR(100)
, LOAD_ID INT
,[DATE] DATE )
INSERT INTO #table_sales VALUES ('ABC', 'CUST1',001,'2021-05-10')
,('ABC', 'CUST2',002,'2020-08-18')
,('DFG', 'CUST3',003, '2018-06-12')
--Define the date periods. I used getdate and prepared the data from and date to
DECLARE #StartDate AS DATE = DATEADD(DAY, 1, EOMONTH(GETDATE() , -1))
DECLARE #PreviousFrom AS DATE = DATEADD(YEAR, -1, DATEADD(MONTH, -1, #StartDate) )
DECLARE #PreviousTo AS DATE = DATEADD(DAY,-1, #StartDate)
SELECT * FROM
#table_sales
WHERE [DATE] > #PreviousTo
UNION ALL
SELECT * FROM
#table_sales
WHERE [DATE] < #PreviousFrom

How to get the summation of days in specific month of year in range

If i have Vacation table with the following structure :
emp_num start_date end_date
234 8-2-2015 8-5-2015
234 6-28-2015 7-1-2015
234 8-29-2015 9-2-2015
115 6-7-2015 6-7-2015
115 8-7-2015 8-10-2015
considering date format is: m/dd/yyyy
How could i get the summation of vacations for every employee during specific month .
Say i want to get the vacations in 8Aug-2015
I want the result like this
emp_num sum
234 7
115 4
7 = all days between 8-2-2015 and 8-5-2015 plus all days between 8-29-2015 AND 8-31-2015 the end of the month
i hope this will help you
declare #temp table
(emp_num int, startdate date, enddate date)
insert into #temp values (234,'8-2-2015','8-5-2015')
insert into #temp values (234,'6-28-2015','7-1-2015')
insert into #temp values (234,'8-29-2015','9-2-2015')
insert into #temp values (115,'6-7-2015','6-7-2015')
insert into #temp values (115,'8-7-2015','8-10-2015')
-- i am passing 8 as month number in your case is August
select emp_num,
SUM(
DATEDIFF (DAY , startdate,
case when MONTH(enddate) = 8
then enddate
else DATEADD(s,-1,DATEADD(mm, DATEDIFF(m,0,startdate)+1,0))--end date of month
end
)+1) AS Vacation from #temp
where (month(startdate) = 8 OR month(enddate) = 8) AND (Year(enddate)=2015 AND Year(enddate)=2015)
group by emp_num
UPDATE after valid comment: This will fail with these dates: 2015-07-01, 2015-09-30 –#t-clausen.dk
i was assumed OP wants for month only which he will pass
declare #temp table
(emp_num int, startdate date, enddate date)
insert into #temp values (234,'8-2-2015','8-5-2015')
insert into #temp values (234,'6-28-2015','7-1-2015')
insert into #temp values (234,'8-29-2015','9-2-2015')
insert into #temp values (115,'6-7-2015','6-7-2015')
insert into #temp values (115,'8-7-2015','8-10-2015')
insert into #temp values (116,'07-01-2015','9-30-2015')
select emp_num,
SUM(
DATEDIFF (DAY , startdate,
case when MONTH(enddate) = 8
then enddate
else DATEADD(s,-1,DATEADD(mm, DATEDIFF(m,0,startdate)+1,0))
end
)+1) AS Vacation from #temp
where (Year(enddate)=2015 AND Year(enddate)=2015)
AND 8 between MONTH(startdate) AND MONTH(enddate)
group by emp_num
This will work for sqlserver 2012+
DECLARE #t table
(emp_num int, start_date date, end_date date)
INSERT #t values
( 234, '8-2-2015' , '8-5-2015'),
( 234, '6-28-2015', '7-1-2015'),
( 234, '8-29-2015', '9-2-2015'),
( 115, '6-7-2015' , '6-7-2015'),
( 115, '8-7-2015' , '8-10-2015')
DECLARE #date date = '2015-08-01'
SELECT
emp_num,
SUM(DATEDIFF(day,
CASE WHEN #date > start_date THEN #date ELSE start_date END,
CASE WHEN EOMONTH(#date) < end_date
THEN EOMONTH(#date)
ELSE end_date END)+1) [sum]
FROM #t
WHERE
start_date <= EOMONTH(#date)
and end_date >= #date
GROUP BY emp_num
Using a Tally Table:
SQL Fiddle
DECLARE #month INT,
#year INT
SELECT #month = 8, #year = 2015
--SELECT
-- DATEADD(MONTH, #month - 1, DATEADD(YEAR, #year - 1900, 0)) AS start_day,
-- DATEADD(MONTH, #month, DATEADD(YEAR, #year - 1900, 0)) AS end_d
;WITH CteVacation AS(
SELECT
emp_num,
start_date = CONVERT(DATE, start_date, 101),
end_date = CONVERT(DATE, end_date, 101)
FROM vacation
)
,E1(N) AS(
SELECT * FROM(VALUES
(1),(1),(1),(1),(1),(1),(1),(1),(1),(1)
)t(N)
),
E2(N) AS(SELECT 1 FROM E1 a CROSS JOIN E1 b),
E4(N) AS(SELECT 1 FROM E2 a CROSS JOIN E2 b),
Tally(N) AS(
SELECT TOP(SELECT MAX(DATEDIFF(DAY, start_date, end_date)) FROM vacation)
ROW_NUMBER() OVER(ORDER BY (SELECT NULL))
FROM E4
)
SELECT
v.emp_num,
COUNT(*)
FROM CteVacation v
CROSS JOIN Tally t
WHERE
DATEADD(DAY, t.N - 1, v.start_date) <= v.end_date
AND DATEADD(DAY, t.N - 1, v.start_date) >= DATEADD(MONTH, #month - 1, DATEADD(YEAR, #year - 1900, 0))
AND DATEADD(DAY, t.N - 1, v.start_date) < DATEADD(MONTH, #month, DATEADD(YEAR, #year - 1900, 0))
GROUP BY v.emp_num
First, you want to use the correct data type to ease your calculation. In my solution, I used a CTE to format your data type. Then build a tally table from 1 up to the max duration of the all the vacations. Using that tally table, do a CROSS JOIN on the vacation table to generate all vacation dates from its start_date up to end_date.
After that, add a WHERE clause to filter dates that falls on the passed month-year parameter.
Here, #month and #year is declared as INT. What you want is to get all dates from the first day of the month-year up to its last day. The formula for first day of the month is:
DATEADD(MONTH, #month - 1, DATEADD(YEAR, #year - 1900, 0))
And for the last day of the month, add one month to the above and just use <:
DATEADD(MONTH, #month, DATEADD(YEAR, #year - 1900, 0))
Some common date routines.
More explanation on tally table.
Select(emp_name,start_date,end_date) AS sum_day from table_Name Group by emp_num,start_date,end_date
Try this
with cte(
Select emp_num,DATEDIFF(day,start_date,end_date) AS sum_day from table_Name
Group by emp_num,start_date,end_date
)
Select emp_num,sum(sum_day) as sum_day from cte group by emp_num

Get all dates between two dates in SQL Server

How to get all the dates between two dates?
I have a variable #MAXDATE which is storing the maximum date from the table. Now I want to get the all dates between #Maxdate and GETDATE() and want to store these dates in a cursor.
So far I have done as follows:
;with GetDates As
(
select DATEADD(day,1,#maxDate) as TheDate
UNION ALL
select DATEADD(day,1, TheDate) from GetDates
where TheDate < GETDATE()
)
This is working perfectly but when I am trying to store these values in a cursor
SET #DateCurSor = CURSOR FOR
SELECT TheDate
FROM GetDates
Compilation Error
Incorrect syntax near the keyword 'SET'.
How to solve this?
My first suggestion would be use your calendar table, if you don't have one, then create one. They are very useful. Your query is then as simple as:
DECLARE #MinDate DATE = '20140101',
#MaxDate DATE = '20140106';
SELECT Date
FROM dbo.Calendar
WHERE Date >= #MinDate
AND Date < #MaxDate;
If you don't want to, or can't create a calendar table you can still do this on the fly without a recursive CTE:
DECLARE #MinDate DATE = '20140101',
#MaxDate DATE = '20140106';
SELECT TOP (DATEDIFF(DAY, #MinDate, #MaxDate) + 1)
Date = DATEADD(DAY, ROW_NUMBER() OVER(ORDER BY a.object_id) - 1, #MinDate)
FROM sys.all_objects a
CROSS JOIN sys.all_objects b;
For further reading on this see:
Generate a set or sequence without loops – part 1
Generate a set or sequence without loops – part 2
Generate a set or sequence without loops – part 3
With regard to then using this sequence of dates in a cursor, I would really recommend you find another way. There is usually a set based alternative that will perform much better.
So with your data:
date | it_cd | qty
24-04-14 | i-1 | 10
26-04-14 | i-1 | 20
To get the quantity on 28-04-2014 (which I gather is your requirement), you don't actually need any of the above, you can simply use:
SELECT TOP 1 date, it_cd, qty
FROM T
WHERE it_cd = 'i-1'
AND Date <= '20140428'
ORDER BY Date DESC;
If you don't want it for a particular item:
SELECT date, it_cd, qty
FROM ( SELECT date,
it_cd,
qty,
RowNumber = ROW_NUMBER() OVER(PARTITION BY ic_id
ORDER BY date DESC)
FROM T
WHERE Date <= '20140428'
) T
WHERE RowNumber = 1;
You can use this script to find dates between two dates. Reference taken from this Article:
DECLARE #StartDateTime DATETIME
DECLARE #EndDateTime DATETIME
SET #StartDateTime = '2015-01-01'
SET #EndDateTime = '2015-01-12';
WITH DateRange(DateData) AS
(
SELECT #StartDateTime as Date
UNION ALL
SELECT DATEADD(d,1,DateData)
FROM DateRange
WHERE DateData < #EndDateTime
)
SELECT DateData
FROM DateRange
OPTION (MAXRECURSION 0)
GO
Just saying...here is a more simple approach to this:
declare #sdate date = '2017-06-25'
, #edate date = '2017-07-24';
with dates_CTE (date) as (
select #sdate
Union ALL
select DATEADD(day, 1, date)
from dates_CTE
where date < #edate
)
select *
from dates_CTE;
Easily create a Table Value Function that will return a table with all dates.
Input dates as string
You can customize the date in the the format you like '01/01/2017' or '01-01-2017' in string formats (103,126 ...)
Try this
CREATE FUNCTION [dbo].[DateRange_To_Table] ( #minDate_Str NVARCHAR(30), #maxDate_Str NVARCHAR(30))
RETURNS #Result TABLE(DateString NVARCHAR(30) NOT NULL, DateNameString NVARCHAR(30) NOT NULL)
AS
begin
DECLARE #minDate DATETIME, #maxDate DATETIME
SET #minDate = CONVERT(Datetime, #minDate_Str,103)
SET #maxDate = CONVERT(Datetime, #maxDate_Str,103)
INSERT INTO #Result(DateString, DateNameString )
SELECT CONVERT(NVARCHAR(10),#minDate,103), CONVERT(NVARCHAR(30),DATENAME(dw,#minDate))
WHILE #maxDate > #minDate
BEGIN
SET #minDate = (SELECT DATEADD(dd,1,#minDate))
INSERT INTO #Result(DateString, DateNameString )
SELECT CONVERT(NVARCHAR(10),#minDate,103), CONVERT(NVARCHAR(30),DATENAME(dw,#minDate))
END
return
end
To execute the function do this:
SELECT * FROM dbo.DateRange_To_Table ('01/01/2017','31/01/2017')
The output will be
01/01/2017 Sunday
02/01/2017 Monday
03/01/2017 Tuesday
04/01/2017 Wednesday
05/01/2017 Thursday
06/01/2017 Friday
07/01/2017 Saturday
08/01/2017 Sunday
09/01/2017 Monday
10/01/2017 Tuesday
11/01/2017 Wednesday
12/01/2017 Thursday
13/01/2017 Friday
14/01/2017 Saturday
15/01/2017 Sunday
16/01/2017 Monday
17/01/2017 Tuesday
18/01/2017 Wednesday
19/01/2017 Thursday
20/01/2017 Friday
21/01/2017 Saturday
22/01/2017 Sunday
23/01/2017 Monday
24/01/2017 Tuesday
25/01/2017 Wednesday
26/01/2017 Thursday
27/01/2017 Friday
28/01/2017 Saturday
29/01/2017 Sunday
30/01/2017 Monday
31/01/2017 Tuesday
This can be considered as bit tricky way as in my situation, I can't use a CTE table, so decided to join with sys.all_objects and then created row numbers and added that to start date till it reached the end date.
See the code below where I generated all dates in Jul 2018. Replace hard coded dates with your own variables (tested in SQL Server 2016):
select top (datediff(dd, '2018-06-30', '2018-07-31')) ROW_NUMBER()
over(order by a.name) as SiNo,
Dateadd(dd, ROW_NUMBER() over(order by a.name) , '2018-06-30') as Dt from sys.all_objects a
You can try this:
SET LANGUAGE SPANISH
DECLARE #startDate DATE = GETDATE() -- Your start date
DECLARE #endDate DATE = DATEADD(MONTH, 16, GETDATE()) -- Your end date
DECLARE #years INT = YEAR(#endDate) - YEAR(#startDate)
CREATE TABLE #TMP_YEARS (
[year] INT
)
-- Get all posible years between the start and end date
WHILE #years >= 0
BEGIN
INSERT INTO #TMP_YEARS
([year])
SELECT YEAR(#startDate) + #years
SET #years = #years - 1
END
;WITH [days]([day]) AS -- Posible days at a month
(
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 UNION ALL -- days lower than 10
SELECT 10 UNION ALL SELECT 11 UNION ALL SELECT 12 UNION ALL SELECT 13 UNION ALL SELECT 14 UNION ALL SELECT 15 UNION ALL SELECT 16 UNION ALL SELECT 17 UNION ALL SELECT 18 UNION ALL SELECT 19 UNION ALL -- days lower than 20
SELECT 20 UNION ALL SELECT 21 UNION ALL SELECT 22 UNION ALL SELECT 23 UNION ALL SELECT 24 UNION ALL SELECT 25 UNION ALL SELECT 26 UNION ALL SELECT 27 UNION ALL SELECT 28 UNION ALL SELECT 29 UNION ALL -- days lower than 30
SELECT 30 UNION ALL SELECT 31 -- days higher 30
),
[months]([month]) AS -- All months at a year
(
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 UNION ALL SELECT 10 UNION ALL SELECT 11 UNION ALL SELECT 12
)
SELECT CONVERT(VARCHAR, a.[year]) + '-' + REPLICATE('0', 2 - LEN(CONVERT(VARCHAR, n.[month]))) + CONVERT(VARCHAR, n.[month]) + '-' + REPLICATE('0', 2 - LEN(CONVERT(VARCHAR, d.[day]))) + CONVERT(VARCHAR, d.[day]) as [date]
FROM #TMP_YEARS a
CROSS JOIN [months] n -- Join all years with all months
INNER JOIN [days] d on DAY(EOMONTH(CONVERT(VARCHAR, a.[year]) + '-' + REPLICATE('0', 2 - LEN(CONVERT(VARCHAR, n.[month]))) + CONVERT(VARCHAR, n.[month]) + '-' + CONVERT(VARCHAR, DAY(EOMONTH(CAST(CONVERT(VARCHAR, a.[year]) + '-' + CONVERT(varchar, n.[month]) + '-15' AS DATE)))))) >= d.[day] AND -- The number of the day can't be higher than the last day of the current month and the current year
CONVERT(VARCHAR, a.[year]) + '-' + REPLICATE('0', 2 - LEN(CONVERT(VARCHAR, n.[month]))) + CONVERT(VARCHAR, n.[month]) + '-' + REPLICATE('0', 2 - LEN(CONVERT(VARCHAR, d.[day]))) + CONVERT(VARCHAR, d.[day]) <= ISNULL(#endDate, GETDATE()) AND -- The current date can't be higher than the end date
CONVERT(VARCHAR, a.[year]) + '-' + REPLICATE('0', 2 - LEN(CONVERT(VARCHAR, n.[month]))) + CONVERT(VARCHAR, n.[month]) + '-' + REPLICATE('0', 2 - LEN(CONVERT(VARCHAR, d.[day]))) + CONVERT(VARCHAR, d.[day]) >= ISNULL(#startDate, GETDATE()) -- The current date should be higher than the start date
ORDER BY a.[year] ASC, n.[month] ASC, d.[day] ASC
The output will be something like this, you can format the date as you like:
2019-01-24
2019-01-25
2019-01-26
2019-01-27
2019-01-28
2019-01-29
2019-01-30
2019-01-31
2019-02-01
2019-02-02
2019-02-03
2019-02-04
2019-02-05
2019-02-06
2019-02-07
2019-02-08
2019-02-09
...
create procedure [dbo].[p_display_dates](#startdate datetime,#enddate datetime)
as
begin
declare #mxdate datetime
declare #indate datetime
create table #daterange (dater datetime)
insert into #daterange values (#startdate)
set #mxdate = (select MAX(dater) from #daterange)
while #mxdate < #enddate
begin
set #indate = dateadd(day,1,#mxdate)
insert into #daterange values (#indate)
set #mxdate = (select MAX(dater) from #daterange)
end
select * from #daterange
end
I listed dates of 2 Weeks later. You can use variable #period OR function datediff(dd, #date_start, #date_end)
declare #period INT, #date_start datetime, #date_end datetime, #i int;
set #period = 14
set #date_start = convert(date,DATEADD(D, -#period, curent_timestamp))
set #date_end = convert(date,current_timestamp)
set #i = 1
create table #datesList(dts datetime)
insert into #datesList values (#date_start)
while #i <= #period
Begin
insert into #datesList values (dateadd(d,#i,#date_start))
set #i = #i + 1
end
select cast(dts as DATE) from #datesList
Drop Table #datesList
This is the method that I would use.
DECLARE
#DateFrom DATETIME = GETDATE(),
#DateTo DATETIME = DATEADD(HOUR, -1, GETDATE() + 2); -- Add 2 days and minus one hour
-- Dates spaced a day apart
WITH MyDates (MyDate)
AS (
SELECT #DateFrom
UNION ALL
SELECT DATEADD(DAY, 1, MyDate)
FROM MyDates
WHERE MyDate < #DateTo
)
SELECT
MyDates.MyDate
, CONVERT(DATE, MyDates.MyDate) AS [MyDate in DATE format]
FROM
MyDates;
Here is a similar example, but this time the dates are spaced one hour apart to further aid understanding of how the query works:
-- Alternative example with dates spaced an hour apart
WITH MyDates (MyDate)
AS (SELECT #DateFrom
UNION ALL
SELECT DATEADD(HOUR, 1, MyDate)
FROM MyDates
WHERE MyDate < #DateTo
)
SELECT
MyDates.MyDate
FROM
MyDates;
As you can see, the query is fast, accurate and versatile.
You can use SQL Server recursive CTE
DECLARE
#MinDate DATE = '2020-01-01',
#MaxDate DATE = '2020-02-01';
WITH Dates(day) AS
(
SELECT CAST(#MinDate as Date) as day
UNION ALL
SELECT CAST(DATEADD(day, 1, day) as Date) as day
FROM Dates
WHERE CAST(DATEADD(day, 1, day) as Date) < #MaxDate
)
SELECT* FROM dates;
declare #start_dt as date = '1/1/2021'; -- Date from which the calendar table will be created.
declare #end_dt as date = '1/1/2022'; -- Calendar table will be created up to this date (not including).
declare #dates as table (
date_id date primary key,
date_year smallint,
date_month tinyint,
date_day tinyint,
weekday_id tinyint,
weekday_nm varchar(10),
month_nm varchar(10),
day_of_year smallint,
quarter_id tinyint,
first_day_of_month date,
last_day_of_month date,
start_dts datetime,
end_dts datetime
)
while #start_dt < #end_dt
begin
insert into #dates(
date_id, date_year, date_month, date_day,
weekday_id, weekday_nm, month_nm, day_of_year, quarter_id,
first_day_of_month, last_day_of_month,
start_dts, end_dts
)
values(
#start_dt, year(#start_dt), month(#start_dt), day(#start_dt),
datepart(weekday, #start_dt), datename(weekday, #start_dt), datename(month, #start_dt), datepart(dayofyear, #start_dt), datepart(quarter, #start_dt),
dateadd(day,-(day(#start_dt)-1),#start_dt), dateadd(day,-(day(dateadd(month,1,#start_dt))),dateadd(month,1,#start_dt)),
cast(#start_dt as datetime), dateadd(second,-1,cast(dateadd(day, 1, #start_dt) as datetime))
)
set #start_dt = dateadd(day, 1, #start_dt)
end
-- sample of the data
select
top 50 *
--into master.dbo.DimDate
from #dates d
order by date_id
DECLARE #FirstDate DATE = '2018-01-01'
DECLARE #LastDate Date = '2018-12-31'
DECLARE #tbl TABLE(ID INT IDENTITY(1,1) PRIMARY KEY,CurrDate date)
INSERT #tbl VALUES( #FirstDate)
WHILE #FirstDate < #LastDate
BEGIN
SET #FirstDate = DATEADD( day,1, #FirstDate)
INSERT #tbl VALUES( #FirstDate)
END
INSERT #tbl VALUES( #LastDate)
SELECT * FROM #tbl

SQL function for last 12 months

I am looking for a SQL-function that gives the last 12 months with Start Date and End Date. Say you pick 10.Dec, it will give a result in:
- StartDate -- EndDate
- 2013-11-01 - 2013-11-30
- 2013-10-01 - 2013-10-31
- 2013-09-01 - 2013-09-30
and so it goes for the last 12 months.
I tried modifying an old function we had, but I got totally off and confused in the end.
ALTER FUNCTION [dbo].[Last12Months](#Date date) RETURNS TABLE
AS
Return
(
with cte as (
SELECT DATEADD(mm, DATEDIFF(mm, 01, #Date), 01) AS Start,
DATEADD(mm, DATEDIFF(mm, -12, #Date), -12) AS EndDate
union all
select Start - 1, EndDate - 1 from cte
where Start >= #Date )
select CAST(Start as DATE) StartDate, CAST(EndDate as DATE) EndDate from cte)
Runned it like this:
select * from dbo.Last12Months ('2013-12-10')
and got:
- StartDate - EndDate
- 2013-12-02 - 2013-12-20
Anyone know what to do?
Please try using CTE:
ALTER FUNCTION [dbo].[Last12Months]
(
#Date datetime
) RETURNS #tbl TABLE (Start datetime, EndDate datetime)
AS
BEGIN
WITH T AS(
SELECT
DATEADD(month, DATEDIFF(month, 0, #Date), 0) AS Start,
DATEADD(d, -DAY(DATEADD(m,1,#date)),DATEADD(m,1,#date)) AS EndDate,
12 Cnt
UNION ALL
SELECT
DATEADD(month, -1, Start),
DATEADD(d, -DAY(DATEADD(m,1,Start-1)),DATEADD(m,1,Start-1)),
Cnt-1
FROM
T
WHERE
Cnt-1>0
)
INSERT INTO #tbl
(Start, EndDate)
SELECT
Start, EndDate
FROM T
RETURN
END
This seems to do the job - whether you want to put it in a function or just wherever you need to have the data:
; With Numbers as (
select ROW_NUMBER() OVER (ORDER BY number ) as n
from master..spt_values
), Months as (
select DATEADD(month,n,'20010101') as start_date,
DATEADD(month,n,'20010131') as end_date
from Numbers
)
select * from Months
where DATEDIFF(month,start_date,GETDATE()) between 0 and 11
(Substitute any other date for GETDATE() if you want to get it based on some other date)
(On my machine, this can generate any month from January 2001 on to at least the next century - it can be adjusted if you need earlier or later dates also)
Damn, you've got to be quick on SO!
Good use of CTEs: i've learnt a bit answering this...
alter function Last12Months(#d date) returns table
as
return(
with cte as (
select
dateadd(month, datepart(mm,#d)-13,
dateadd(year,datepart(yyyy,#d)-1900,0)
)
as start
union all
select dateadd(mm, 1, start) from cte
where start < #d)
select start, dateadd(mm, 1, start) ends from cte
where start < #d
)
go
select * from Last12Months('2014-06-04')
Removed conversion to varchar thanks to
Date serial in SQL?
This returns 13 months: from say June last year to this June, inclusive.
To return the previous 12 months, not including the current June, change the final start<#d to
where start < dateadd(month, datepart(mm,#d)-1,
dateadd(year,datepart(yyyy,#d)-1900,0))
The end is 00:00 hours on the first day of the next month.
check this,
Declare #i date='2013-12-10'
;with cte as
(Select dateadd(month,datediff(month,0,#i)-1,0) StartDate
,dateadd(day,-1,dateadd(month,datediff(month,0,#i),0)) EndDate ,1 rownum
Union all
select dateadd(month,-1,StartDate),dateadd(day,-1,StartDate),rownum+1 rownum from cte where rownum<12 )
select * from cte
#Lebowski Below script will give you start and end date of specified calendar months from today in chronological order
DECLARE #nMonths TINYINT
SET #nMonths = 60
SELECT FORMAT(DATEADD(month, n.n - #nMonths+1+ DATEDIFF(month, 0, GETDATE()) -1 ,0), 'yyyy-MM-dd') AS MonthStartDate
, FORMAT(DATEADD(dd, -1, DATEADD(month, n.n - #nMonths+1 + DATEDIFF(month, 0, GETDATE()),0)), 'yyyy-MM-dd') AS MonthEndDate
FROM (SELECT TOP(#nMonths) n = ROW_NUMBER() OVER (ORDER BY NAME)
FROM master.dbo.syscolumns) n
Sample output
MonthStartDate MonthEndDate
2011-04-01 2011-04-30
2011-05-01 2011-05-31
2011-06-01 2011-06-30
2011-07-01 2011-07-31
2011-08-01 2011-08-31
2011-09-01 2011-09-30
2011-10-01 2011-10-31
2011-11-01 2011-11-30
2011-12-01 2011-12-31
....
Try this it might help you
select top 12 *
from YourTable
where dateOf between #DateFrom and #DateTo
order by dateOf desc

SQL fill days for dates not found in Database

I'm getting data from my function as follows:
Date | Number
06-02-2012 | 2
06-05-2012 | 5
06-08-2012 | 5
If i want to include all dates that are not found in DB in the following matter how would i do it?:
Date | Number
06-02-2012 | 2
06-03-2012 | 0
06-04-2012 | 0
06-05-2012 | 5
06-06-2012 | 0
06-07-2012 | 0
06-08-2012 | 5
SELECT convert(varchar, MIN(DATEADD(wk, DATEDIFF(wk, 0, person.date), 0)), 1), Count(person.ID)
FROM [dbo].[Person] person
WHERE (DATEDIFF(D, person.date, #dateFrom) <=0 AND DATEDIFF(D, person.date, #dateTo) >=0)
GROUP BY DATEPART(WK, person.date)
You would create a temporary table, or subquery, containing all the dates in your chosen range, and use a left join against your source data
I recommend that you create a table of dates -- a one column table containing dates from, say 2000-01-01 to 2050-12-31. You can then use that table on the left hand side of a LEFT JOIN query like this:
SELECT date_table.date AS [Date], COUNT(your_table.primary_key) AS [Number]
FROM date_table
LEFT JOIN your_table ON date_table.date = your_table.date
WHERE date_table.date BETWEEN '2012-01-01' AND '2012-06-30'
Index the date table wisely and you'll end up with a very efficient query.
If you need just a small interval, you can try a function like this:
DECLARE #minDate date
DECLARE #maxDate date
SET #minDate = '2012-09-01'
SELECT #maxDate = CAST( CONVERT( CHAR(8), GetDate(), 112) AS DATETIME)
DECLARE #Numbers TABLE(
Date date,
Number int)
WHILE #minDate < #maxDate
BEGIN
INSERT INTO #Numbers
SELECT #minDate, 0
WHERE NOT EXISTS( SELECT Number FROM Numbers WHERE [Date] = #minDate )
SET #minDate = DATEADD(day, 1, #minDate)
END
SELECT n.[Date], ISNULL(n.Number, 0)
FROM #Numbers n
UNION ALL
SELECT Numbers.[Date], ISNULL(Numbers.Number, 0)
FROM Numbers
ORDER BY [Date]
If you need more month and year, then I think the best way to make a prefilled permanent helper table with the dates what you need. And make only an easy join on them like it is posted in an other answer.