i have following query in postgresql for dates between 2 ranges.
select generate_series('2019-04-01'::timestamp, '2020-03-31', '1 month')
as g_date
I need to generate specific date in every month .i.e 15 th of every month. Following is my query to generate series
DO $$
DECLARE
compdate date = '2019-04-15';
BEGIN
CREATE TEMP TABLE tmp_table ON COMMIT DROP AS
select *,
case
when extract('day' from d) <> extract('day' from compdate) then 0
when ( extract('month' from d)::int - extract('month' from compdate)::int ) % 1 = 0 then 1
else 0
end as c
from generate_series('2019-04-01'::timestamp, '2020-03-31', '1 day') d;
END $$;
SELECT * FROM tmp_table
where c=1;
;
But every thing is perfect if input date between (1..29)-04-2019 ..
2019-04-25
2019-05-25
2019-06-25
2019-07-25
2019-08-25
2019-09-25
2019-10-25
2019-11-25
2019-12-25
2020-01-25
2020-02-25
2020-03-25
but if i give compdate: 31-04-2019 or 30-04-2019 giving out put:
2019-05-31
2019-07-31
2019-08-31
2019-10-31
2019-12-31
2020-01-31
2020-03-31
Expected Output:
date flag
2019-04-01 0 ----start_date
2019-04-30 1
2019-05-31 1
2019-06-30 1
2019-07-31 1
2019-08-31 1
2019-09-30 1
2019-10-31 1
2019-11-30 1
2019-12-31 1
2020-01-31 1
2020-02-29 1
2020-03-31 0 ---end_date
If matched day not found in the result it should take last day of that month..i.e if 31 not found in month of feb it
should take 29-02-2019 and also in april month instead of 31 it should take 2019-04-30.
Please suggest.
to generate the last days of the month, just generate first days & subtract a 1 day interval
example: the following generates all last day of month in the year 2010
SELECT x - interval '1 day' FROM
GENERATE_SERIES('2010-02-01', '2011-01-01', interval '1 month') x
You cannot accomplish what you want with generate_series. This results due to that process applying a fixed increment from the previous generated value. Your case 1 month. Now Postgres will successfully compute correct end-of-month date from 1 month to the next. So for example 1month from 31-Jan yields 28-Feb (or 29), because 31-Feb would be an invalid date, Postgres handles it. However, that same interval from 28-Feb gives the valid date 28-Mar so no end-of-month adjustment is needed. Generate_Series will return 28th of the month from then on. The same applies to 30 vs. 31 day months.
But you can achieve what your after with a recursive CTE by employing a varying interval to the same initial start date. If the resulting date is invalid for date the necessary end-of-month adjustment will be made. The following does that:
create or replace function constant_monthly_date
( start_date timestamp
, end_date timestamp
)
returns setof date
language sql strict
as $$
with recursive date_set as
(select start_date ds, start_date sd, end_date ed, 1 cnt
union all
select (sd + cnt*interval '1 month') ds, sd, ed, cnt+1
from date_set
where ds<end_date
)
select ds::date from date_set;
$$;
-- test
select * from constant_monthly_date(date '2020-01-15', date '2020-12-15' );
select * from constant_monthly_date(date '2020-01-31', date '2020-12-31' );
Use the least function to get the least one between the computed day and end of month.
create or replace function test1(day int) returns table (t timestamptz) as $$
select least(date_trunc('day', t) + make_interval(days => day-1), date_trunc('day', t) + interval '1 month' - interval '1 day') from generate_series('2019-04-01', '2020-03-31', interval '1 month') t
$$ language sql;
select test1(31);
So let's say you have a table with the column below (type is datetimeoffset(3)).
DTO_Created
2017-04-28 03:16:56.942 -05:00
2017-05-01 00:20:54.925 -05:00
2017-05-01 12:17:52.752 -05:00
2017-05-01 23:21:00.198 -05:00
2017-05-02 01:19:23.254 -05:00
How would you query to only get the rows created on 2017-05-01? (3 rows total) I am attempting this, but am not getting all 3.
SELECT * FROM MyTable WHERE DTO_Created >= '2017-05-01 00:00:00.000' AND DTO_Created <= '2017-05-01 23:59:59.999'
The problem seems to be caused by the type (datetimeoffset) because this doesn't happen with regular datetime columns.
Environment: SQL Server 2016
You need to use datetimeoffset for the where clause as well.
First, create and populate sample table (Please save us this step in your future questions):
DECLARE #T AS TABLE
(
DTO_Created DateTimeOffset
)
INSERT INTO #T(DTO_Created) VALUES
('2017-04-28 03:16:56.942 -05:00'),
('2017-05-01 00:20:54.925 -05:00'),
('2017-05-01 12:17:52.752 -05:00'),
('2017-05-01 23:21:00.198 -05:00'),
('2017-05-02 01:19:23.254 -05:00')
The query:
SELECT *
FROM #T
WHERE DTO_Created >= '2017-05-01 00:00:00.000 -05:00'
AND DTO_Created < '2017-05-02 00:00:00.000 -05:00'
Results:
DTO_Created
2017-05-01 00:20:54.925 -05:00
2017-05-01 12:17:52.752 -05:00
2017-05-01 23:21:00.198 -05:00
Another option for 2016 or higher version is to use AT TIME ZONE, but you'll have to add the hours difference to the search value, and beware of daylight savings time:
SELECT *
FROM #T
WHERE DTO_Created >= CAST('2017-05-01 05:00:00' AS DateTime2) AT TIME ZONE 'Easter Island Standard Time' -- -05:00
AND DTO_Created < CAST('2017-05-02 05:00:00' AS DateTime2) AT TIME ZONE 'Easter Island Standard Time'
If you want to ignore local time and treat the UTC part of the DateTimeOffset as if it was DateTime you can simply cast the DateTimeOffset to Date, though that would be a non-sargable predicate:
SELECT *
FROM #T
WHERE CAST(DTO_Created As Date) = '2017-05-01'
Try this
CONVERT(DATETIME, DTO_Created, 1) >= '2017-05-01 00:00:00.000' AND CONVERT(DATETIME, DTO_Created, 1) <= '2017-05-01 23:59:59.999'
OR this
cast(DTO_Created as datetime) >= '2017-05-01 00:00:00.000' AND cast(DTO_Created as datetime) <= '2017-05-01 23:59:59.999'
I'm trying to take a time frame the user selects and then group the selection into time periods - in this case: 2 weeks.
For instance, today is 5/4/2018 and if I set that as my start date and 5/31/2018 as my end date, I get the following:
DECLARE #StartDate DATE ,
#EndDate DATE ,
#ToDate DATE;
SET #StartDate = GETDATE ();
SET #EndDate = '20180531';
SET #ToDate = DATEADD ( DAY, 1, #EndDate );
SELECT dd.Date ,
ROW_NUMBER () OVER ( ORDER BY DATEPART ( WEEK, dd.Date )) AS [rownumb]
FROM dbo.DateDimension AS [dd]
WHERE dd.Date >= #StartDate
AND dd.Date < #ToDate;
And the results look like:
Date rownumb
2018-05-04 1
2018-05-05 2
2018-05-06 3
2018-05-07 4
2018-05-08 5
2018-05-09 6
2018-05-10 7
2018-05-11 8
2018-05-12 9
2018-05-13 10
2018-05-14 11
2018-05-15 12
2018-05-16 13
2018-05-17 14
2018-05-18 15
2018-05-19 16
2018-05-20 17
2018-05-21 18
2018-05-22 19
2018-05-23 20
2018-05-24 21
2018-05-25 22
2018-05-26 23
2018-05-27 24
2018-05-28 25
2018-05-29 26
2018-05-30 27
2018-05-31 28
I was playing around with ROW_NUMBER ( along with RANK and DENSE_RANK ) but I have not been able to get these functions to accomplish what I am looking for but what I am hoping to do is have an additional column called "TimePeriod" where the dates are grouped together in 2-week increments ( or 14 days ) so that 5/4/18 through 5/17/18 have a value of 1 for the "TimePeriod" column and 5/18/18 through 5/31/18 would have a value of 2 for the "TimePeriod" column. And this should be dynamic so that wider date ranges are grouped in periods of two weeks with each period increasing by 1.
Suggestions?
If there's no requirement to use the ordering and ranking functions in sql, you can easily implement as below.
get the total number of days between the start and end date
for each date you subtract the days difference of the current date from the total days difference, then divide this by 14
so this basically will give you the interval (2 weeks) to which the current date belongs, it's zero based so you might want to add a 1 to it
DECLARE #StartDate DATE ,
#EndDate DATE ,
#ToDate DATE;
DECLARE #DaysDiff INT;
SET #StartDate = GETDATE ();
SET #EndDate = '20180531';
SET #ToDate = DATEADD ( DAY, 1, #EndDate );
--GET the difference in days between the start and end date
SET #DaysDiff = DATEDIFF( Day, #StartDate,#ToDate )
SELECT dd.Date ,
( #DaysDiff - DATEDIFF(Day,dd.Date,#ToDate) )/14
FROM dbo.DateDimension AS [dd]
WHERE dd.Date >= #StartDate
AND dd.Date < #ToDate;
SQL Server is showing week 53 for first week of 2011 except 1th of January, and needs to be week 1.
Below is the query and output:
declare #T table (dt datetime)
insert into #T values
('2010-12-26'),
('2010-12-27'),
('2010-12-28'),
('2010-12-29'),
('2010-12-30'),
('2010-12-31'),
('2011-01-01'),
('2011-01-02'),
('2011-01-03'),
('2011-01-04'),
('2011-01-05'),
('2011-01-06'),
('2011-01-07'),
('2011-01-08')
select dt,DATEPART(wk,dt) from #T
Output:
2010-12-26 00:00:00.000 53
2010-12-27 00:00:00.000 53
2010-12-28 00:00:00.000 53
2010-12-29 00:00:00.000 53
2010-12-30 00:00:00.000 53
2010-12-31 00:00:00.000 53
2011-01-01 00:00:00.000 1
2011-01-02 00:00:00.000 2
2011-01-03 00:00:00.000 2
2011-01-04 00:00:00.000 2
2011-01-05 00:00:00.000 2
2011-01-06 00:00:00.000 2
2011-01-07 00:00:00.000 2
2011-01-08 00:00:00.000 2
I want SQL Server to show week 1 from Dec 26th - Jan 1th. Does anybody know how to accomplish this?
Thanks and regards,
Aschwin.
It was alot harder than I first expected. I am comparing the end of last year to see if it is qualified to be part of the new year. If so i set the week as 1, otherwise i just use the normal week.
declare #T table (dt datetime)
insert into #T values
('2010-12-25'),
('2010-12-26'),
('2010-12-27'),
('2010-12-28'),
('2010-12-29'),
('2010-12-30'),
('2010-12-31'),
('2011-01-01'),
('2011-01-02'),
('2011-01-03'),
('2011-01-04'),
('2011-01-05'),
('2011-01-06'),
('2011-01-07'),
('2011-01-08'),
('2011-12-31'),
('2012-01-01')
select dt,
week = case when dt + 6 - datediff(day, -1, dt) % 7 = dateadd(year, datediff(year,-1, dt), 0)
then 1 else datepart(week, dt) end from #t
Proof:
https://data.stackexchange.com/stackoverflow/q/110527/
I am not sure it holds for all years (but it looks like it) but you could solve this using a CASE statement.
SELECT dt
, CASE WHEN DATEPART(wk, dt) <> 53
THEN DATEPART(wk, dt)
ELSE 1
END
FROM #T
The new ISO_WEEK datepart doesn't apply to your requested output.
I Created 2 functions to deal with this issue
1) to get First or last day of the week
2) to get the week number or year
function 1
CREATE FUNCTION [dbo].[fn_GetDayOf]
(
#Date datetime,
--#FirstDayOfWeek int = 7,
#Mode int =1
)
/*
Mode 1: First Day Of Week
Mode 2: Last Day Of Week
*/
RETURNS datetime
WITH EXECUTE AS CALLER
BEGIN
Declare #Return datetime
--SET DATEFIRST #FirstDayOfWeek
IF #Mode = 1
BEGIN
select #Return = dateadd(day,-(datepart(weekday,#date)-1),convert(date,#date))
END
ELSE IF #Mode = 2
BEGIN
select #Return = dateadd(SECOND,-1,convert(datetime,dateadd(day,(datepart(weekday,#date)),convert(date,#date))))
END
ELSE
BEGIN
SET #Return = #Date
END
--SET DATEFIRST 7
RETURN #Return
END
Function 2
CREATE FUNCTION [dbo].[fn_GetYearWeek]
(
#Date datetime,
--#FirstDayOfWeek int = 7,
#Mode int =1
)
/*
Mode 1 = Week Number
Mode 2 = Year
*/
RETURNS INT
BEGIN
declare #Return int
IF #Mode = 1
BEGIN
select #Return = case when datepart(week,[dbo].[fn_GetDayOf] (#Date,1)) <> datepart(week,[dbo].[fn_GetDayOf] (#Date,2)) then datepart(week,[dbo].[fn_GetDayOf] (#Date,1)) else datepart(week,[dbo].[fn_GetDayOf] (#Date,2)) end
END
ELSE IF #Mode = 2
BEGIN
select #Return = case when datepart(WEEK,[dbo].[fn_GetDayOf] (#Date,1)) <> datepart(week,[dbo].[fn_GetDayOf] (#Date,2)) then datepart(YEAR,[dbo].[fn_GetDayOf] (#Date,1)) else datepart(YEAR,[dbo].[fn_GetDayOf] (#Date,2)) end
END
ELSE
BEGIN
SET #Return = -1
END
Return #Return
END
Running Example
declare #T table (dt datetime)
insert into #T values
('2010-12-25'),
('2010-12-26'),
('2010-12-27'),
('2010-12-28'),
('2010-12-29'),
('2010-12-30'),
('2010-12-31'),
('2011-01-01'),
('2011-01-02'),
('2011-01-03'),
('2011-01-04'),
('2011-01-05'),
('2011-01-06'),
('2011-01-07'),
('2011-01-08'),
('2011-12-31'),
('2012-01-01'),
('2012-01-02'),
('2012-12-31'),
('2013-01-01')
select
dt,
datepart(week,dt),
--case when datepart(week,[dbo].[fn_GetDayOf] (dt,1)) <> datepart(week,[dbo].[fn_GetDayOf] (dt,2)) then datepart(week,[dbo].[fn_GetDayOf] (dt,1)) else datepart(week,[dbo].[fn_GetDayOf] (dt,2)) end
[dbo].[fn_GetYearWeek] (dt,1),
[dbo].[fn_GetYearWeek] (dt,2)
from #T
result:
Another way to retrieve the total number of weeks in current year:
DECLARE #LASTDAY DATETIME
DECLARE #weeks INT
SET #LASTDAY = DATEADD(ms,-3,DATEADD(yy,0,DATEADD(yy,DATEDIFF(yy,0,GETDATE())+1,0)))
SELECT #weeks = CASE DATEname(dw,#LASTDAY)
WHEN 'MONDAY' THEN DATEPART(WK, DATEADD(wk,DATEDIFF(wk,7,#LASTDAY),5))
WHEN 'TUESDAY' THEN DATEPART(WK, DATEADD(wk,DATEDIFF(wk,7,#LASTDAY),5))
WHEN 'WEDNESDAY' THEN DATEPART(WK, DATEADD(wk,DATEDIFF(wk,7,#LASTDAY),5))
ELSE DATEPART(WK, #LASTDAY)
END
select #weeks
I use DATEDIFF function to filter records added this week only:
DATEDIFF(week, DateCreated, GETDATE()) = 0
and I noticed what it's assumed what week starts on Sunday. But in my case I would prefer to set start of week on Monday. Is it possible somehow in T-SQL?
Thanks!
Update:
Below is an example showing what DATEDIFF doesn't check ##DATEFIRST variable so I need another solution.
SET DATEFIRST 1;
SELECT
DateCreated,
DATEDIFF(week, DateCreated, CAST('20090725' AS DATETIME)) AS D25,
DATEDIFF(week, DateCreated, CAST('20090726' AS DATETIME)) AS D26
FROM
(
SELECT CAST('20090724' AS DATETIME) AS DateCreated
UNION
SELECT CAST('20090725' AS DATETIME) AS DateCreated
) AS T
Output:
DateCreated D25 D26
----------------------- ----------- -----------
2009-07-24 00:00:00.000 0 1
2009-07-25 00:00:00.000 0 1
(2 row(s) affected)
26 Jul 2009 is Sunday, and I want DATEDIFF returns 0 in third column too.
Yes it possible
SET DATEFIRST 1; -- Monday
from http://msdn.microsoft.com/en-us/library/ms181598.aspx
It appears datediff doesn't respect the Datefirst, so make it do so run it like this
create table #testDates (id int identity(1,1), dateAdded datetime)
insert into #testDates values ('2009-07-09 15:41:39.510') -- thu
insert into #testDates values ('2009-07-06 15:41:39.510') -- mon
insert into #testDates values ('2009-07-05 15:41:39.510') -- sun
insert into #testDates values ('2009-07-04 15:41:39.510') -- sat
SET DATEFIRST 7 -- Sunday (Default
select * from #testdates where datediff(ww, DATEADD(dd,-##datefirst,dateadded), DATEADD(dd,-##datefirst,getdate())) = 0
SET DATEFIRST 1 -- Monday
select * from #testdates where datediff(ww, DATEADD(dd,-##datefirst,dateadded), DATEADD(dd,-##datefirst,getdate())) = 0
Stolen from
http://social.msdn.microsoft.com/Forums/en-US/transactsql/thread/8cc3493a-7ae5-4759-ab2a-e7683165320b
I have another solution.
This should be easier to understand, correct me if I am wrong
SET DATEFIRST 1
select DATEDIFF(week, 0, DATEADD(day, -##DATEFIRST, '2018-04-15 00:00:00.000'))
We subtract '-1' from date and Sunday will become Saturday (which is 7nth day of week)
and Mondфy(2) will first day of week
So if i'm getting this correctly,
the only thing we need to do is remove 1 day from both dates on our datediff as following :
DATEDIFF(week,dateadd(day,-1,cast(GETDATE() as date)),
dateadd(day,-1,cast([Date] as date))) as RollingWeek