Related
I have dates in the following format:
2019-09-01 00:00:00.000
2019-09-02 00:00:00.000
2019-10-22 00:00:00.000
I want to floor the dates to get the following:
2019-09-01 00:00:00.000
2019-09-01 00:00:00.000
2019-10-01 00:00:00.000
How can I do this in a select statement? I tried DATEPART but I can only extract the month number.
Another option: Use DateFromParts:
SELECT DATEFROMPARTS(YEAR(DateColumn), MONTH(DateColumn), 1) As FirstOfMonth
FROM TableName;
I prefer this option over the EOMonth() approach suggested in gvee's answer just because it's more readable and doesn't involve any date calculations.
EOMonth() is your friend!
SELECT '2019-10-22 00:00:00.000' AS original_value
, EOMonth('2019-10-22 00:00:00.000') AS end_of_month
, DateAdd(dd, 1, EOMonth('2019-10-22 00:00:00.000')) AS start_of_next_month
, DateAdd(mm, -1, DateAdd(dd, 1, EOMonth('2019-10-22 00:00:00.000'))) AS start_of_this_month
;
To keep the datetime format the same, you can use DATEADD(month, DATEDIFF(month, 0, your_date_column), 0)
This method counts the months from day 0 (01/01/1900) to your original date, then adds that number of months back to day 0, which returns the beginning of the month
There are no straight forward function to find the first day of a month in SQL but one can make use of LAST_DAY() function to calculate it by using these steps:
First, get the last day of the month of a date.
Second, add 1 day to get the first day of the next month using DATE_ADD() function
Third, subtract 1 month to get the first day of the month of the date.
At last, format the date calculated into yyyy-mm-dd HH:mm:ss format as required
The following query illustrates how to get the first day of the month of 2017-07-14.
SELECT DATE_FORMAT(DATE_ADD(DATE_ADD(LAST_DAY('2017-07-14'),
INTERVAL 1 DAY),
INTERVAL - 1 MONTH),
"%Y-%m-%d %T") AS first_day;
I used the following links to write the answer.
https://www.mysqltutorial.org/mysql-last_day/ ,
https://www.w3schools.com/sql/func_mysql_date_format.asp
I am using the following query to get the difference between two dates. The date ranges are tolling 12 months interval.
CY stand for Current year while PY stands for Previous Year. The dates in Current year are used to calculate the previous year dates
When I execute my query I have the following output, where the month is 11 and day 364. But I want my months to be twelve and the day 365 or (366 for leap year).
DECLARE #CY_StartDate date =CAST(DATEADD(MONTH, DATEDIFF(MONTH, -1, GETDATE())-13, 0) AS DATE),
#CY_EndDate date =CAST(DATEADD(MONTH, DATEDIFF(MONTH, -1, GETDATE())-1, -1) AS DATE); --- Rolling 12 months
DECLARE #PY_startDate date =DATEADD(YEAR,-1,#CY_StartDate),
#PY_EndDate date =DATEADD(YEAR,-1,#CY_EndDate)
SELECT
#CY_StartDate AS CY_Start,
#CY_EndDate AS CY_End,
#PY_StartDate AS PY_Start,
#PY_EndDate AS PY_End,
DATEDIFF(year, #CY_StartDate, #CY_EndDate) AS yr,
DATEDIFF(month, #CY_StartDate, #CY_EndDate) AS month,
DATEDIFF(day, #CY_StartDate, #CY_EndDate) AS day
Current Output
CY_Start CY_End PY_Start PY_End yr month day
2017-10-01 2018-09-30 2016-10-01 2017-09-30 1 11 364
Expected output
CY_Start CY_End PY_Start PY_End yr month day
2017-10-01 2018-09-30 2016-10-01 2017-09-30 1 12 365
The values you are getting make sense. DATEDIFF counts the ticks between 2 dates, where a tick is the value of the first parameter. So, for example: DATEDIFF(MONTH, '20180101','20180228') will return 1, as only 1 tick has occured (2 - 1 = 1). Seems, here, you simply need to add 1:
DECLARE #CY_StartDate date =CAST(DATEADD(MONTH, DATEDIFF(MONTH, -1, GETDATE())-13, 0) AS DATE),
#CY_EndDate date =CAST(DATEADD(MONTH, DATEDIFF(MONTH, -1, GETDATE())-1, -1) AS DATE); --- Rolling 12 months
DECLARE #PY_startDate date =DATEADD(YEAR,-1,#CY_StartDate),
#PY_EndDate date =DATEADD(YEAR,-1,#CY_EndDate)
select
#CY_StartDate as CY_Start,
#CY_EndDate AS CY_End,
#PY_StartDate AS PY_Start,
#PY_EndDate AS PY_End,
DATEDIFF(year,#CY_StartDate,DATEADD(DAY,1,#CY_EndDate)) as yr,
DATEDIFF(month,#CY_StartDate,DATEADD(DAY,1,#CY_EndDate)) as month,
DATEDIFF(day,#CY_StartDate,DATEADD(DAY,1,#CY_EndDate)) as day
The reason I used a further DATEADD is because this makes it consistent with every expression. The value of yr was correct, however, for dates like 20170101 and 20171231, the value of yr would be 0. Hence adding 1 day the the value of #CY_EndDate makes this far more reliable, should the dates move.
Common sense. How many numbers are there between 1 and 10 including both? You might say that there are 10 - 1 = 9 which is incorrect. The correct answer is (10 - 1) + 1 = 10.
Likewise, if you have two inclusive dates e.g. 2017-10-01 and 2018-09-30 you add one to DATEDIFF(DAY, '2017-10-01', '2018-09-30') to get 365 instead of 364.
However, as suggested in the other answer, it is much better to the end date exclusive (not counted) which makes date calculations straight forward. In your example, you should add 1 day to the last date so that you have [2017-10-01, 2018-10-01) and DATEDIFF will produce desired results.
Delphi's WeekOfTheYear function uses the ISO 8601 methodology where the week starts on a Monday, and the first week of a year is defined as the first week with four or more days in that year.
To get the week of the year in Microsoft SQL Server, you use the DATEPART(wk, ...) function, but this uses a different mechanism, in that the default for U.S. English starts the week on Sunday, and that the 2nd week of the year is the first Sunday of the year, unless that Sunday is the 1st of January, then the 2nd week starts on the 2nd Sunday of the year, as can be seen if you run this example:
SELECT '2010-1-3', DATEPART(wk, '2010-1-3'), DATENAME(dw, '2010-1-3')
SELECT '2011-1-2', DATEPART(wk, '2011-1-2'), DATENAME(dw, '2011-1-2')
SELECT '2012-1-1', DATEPART(wk, '2012-1-1'), DATENAME(dw, '2012-1-1')
SELECT '2013-1-6', DATEPART(wk, '2013-1-6'), DATENAME(dw, '2013-1-6')
SELECT '2014-1-5', DATEPART(wk, '2014-1-5'), DATENAME(dw, '2014-1-5')
SELECT '2015-1-4', DATEPART(wk, '2015-1-4'), DATENAME(dw, '2015-1-4')
SELECT '2016-1-3', DATEPART(wk, '2016-1-3'), DATENAME(dw, '2016-1-3')
SELECT '2017-1-1', DATEPART(wk, '2017-1-1'), DATENAME(dw, '2017-1-1')
SELECT '2018-1-7', DATEPART(wk, '2018-1-7'), DATENAME(dw, '2018-1-7')
SELECT '2019-1-6', DATEPART(wk, '2019-1-6'), DATENAME(dw, '2019-1-6')
SELECT '2020-1-5', DATEPART(wk, '2020-1-5'), DATENAME(dw, '2020-1-5')
The results from above show every Sunday being the 2nd week of the year, except for the 2 that are the 1st January, which show the 1st week of the year.
I looked at the answer for the question below, that seemed to indicate it would return the week of the year depending on what day the week starts,
Delphi week number function based on system start of week
but that is also based on using the Delphi functions, and does not return the same results as SQL Server.
What SQL Server currently shows for the dates 1st January 2018 to 8th January 2018 is:
1/1/2018 = 1
2/1/2018 = 1
3/1/2018 = 1
4/1/2018 = 1
5/1/2018 = 1
6/1/2018 = 1
7/1/2018 = 2
8/1/2018 = 2
Delphi does have a DayOfWeek function that uses Sunday as the first day of the week, compared to the DayofTheWeek function which uses Monday, but I just can't seem to work out the logic that is needed to get the same results as SQL Server. There has to at least be a condition in there for dealing with Sunday 1st January being week 1, but for the first other Sundays being week 2.
Does anyone have any Delphi source code that returns the week of the year exactly as SQL Server does when using U.S. English default of DATEFIRST 7?
The code is deceptively simple, but the logic of it is a little difficult. The rule about the first Sunday means that, in all cases the first Saturday is in week 1. So the idea is that we go to the next Saturday and count the number of Saturdays from the start of the year.
function SQLWeekOfYear(const pDate: TDate): integer;
var
iYear, iMonth, iDay : word;
iDOW : integer;
iDays : integer;
begin
// this is based on First Sunday in year being in week 2
// unless it is the first of Jan, which is equivalent of saying that the
// first saturday is always in week 1.
//
// This means that we count the number of saturdays prior to this date and add one,
// which is equivalent to finding the next saturday
DecodeDate( pDate, iYear, iMonth, iDay );
iDOW := DayOfWeek( pDate );
// Find how many days so far since 1st jan
iDays := Trunc(pDate - EncodeDate( iYear, 1, 1 )) + 1;
// now adjust for day of week to get to next saturday
iDays := iDays + (7-iDOW);
// add 6 and divide by 7 to find actual number saturdays
Result := (iDays + 6) div 7;
end;
For any given date I need to determine what day of the week it is.
I already know how to get the day of the week with this DATENAME(dw,MyDate.Field)and the number of the day with this DATEPART(dw,MyDate.Field).
Once I've got the day of the week if it is before Wednesday I want to return that Wednesday's date.
If the day of the week is Wednesday or after then I want to return next Wednesdays date.
Monday is day 1 in my system.
Use DATEPART() to determine day of the week.
Use CASE() for the different cases.
Calculate the day delta. Hint: It's either 3-dw or 7+3-dw.
Use DATEADD() to get from the current day (returned by DATEPART) to Wednesday or Wednesday of next week.
the easiest way, without any calculating:
set datefirst 1;
with dates as (
select CAST('20170906' as datetime) d
union all
select DATEADD(day, 1, dates.d)
from dates
where DATEADD(day, 1, dates.d) <= '20170930'
)
select dates.d, DATEADD(day, v.valueToAdd, dates.d) nextWed
from dates
join (values(1,2),(2,1),(3,7),(4,6),(5,5),(6,4),(7,3))v(dayOfWeek,valueToAdd) on v.dayOfWeek = DATEPART(weekday, dates.d)
order by 1
How can i create backward scheduling using TSQL. In my old system i have something like this. Is it possible to implement the same logic using TSQL?
Field Name: A-NEST BY
and here is the formula for A-NEST BY. note [A-CUT BY] is a date field
IF(weekday(datesub(A-CUT BY, 7)) = 7, datesub(datesub(A-CUT BY, 7),1),
if(weekday(datesub(A-CUT BY, 7)) = 1, datesub(datesub(A-CUT BY, 7),2),
datesub(A-CUT BY, 7)))
[A-CUT BY] is calculated date field which on the first line here IF(weekday(datesub(A-CUT BY, 7)) = 7, datesub(datesub(A-CUT BY, 7),1), if the date is 7 thge seventh date or Saturday moves the schedule to Friday. if sunday or 1 move the schedule to Monday
weekday(date) or weekday(date,format):
Returns the day of week of a date. The second input is optional, but if given must be one of "number",
"name", or "abbrev". If "number" or omitted, the result will be a number, with 1 for Sunday, 2 for
Monday, up to 7 for Saturday. If "name", the result will be the word Sunday, Monday, etc. If "abbrev",
the result will be the word Sun, Mon, etc.
datesub(date, N)or datesub(date, N,unit):
Subtracts N days from date, that is, returns the date N days before the input date. If N is negative, it
will return the date N days after the input date. The third input is optional; if provided, it must be one of
years, months, days, hours, or minutes. If not provided, days will be assumed.
I am totally guessing here because this question lacks any clarity. I think you just want to use a case expression. Here is an example of what I think you might want.
set datefirst 7 --Sunday
declare #ACUTBY datetime = getdate()
select
case datepart(WEEKDAY, #ACUTBY)
when 1 then 2
when 7 then 5
else datepart(WEEKDAY, #ACUTBY)
end as [A-CUT BY]
--EDIT--
SqlZim made an excellent suggestion to add datefirst to avoid any issues if using another language or the first day of the week is changed for whatever reason.
You can read more about DATEFIRST here. https://learn.microsoft.com/en-us/sql/t-sql/statements/set-datefirst-transact-sql