Related
I use the DATEADD function a lot when working with date ranges, especially if I'm working with rolling time periods. If I wanted to see all results from the past 6 months, I'd of course use...
WHERE [DateField] BETWEEN DATEADD(mm,-6,GETDATE()) AND GETDATE()
...and that would show me everything from July 20, 2022 to today, January 20, 2023.
But what If I wanted to see everything from the beginning of July? What if I wanted to see everything from the beginning of whatever month was 6 months ago, or whatever time period I specify, without having to manually plug in dates?
I tried the following...
WHERE [DateField] BETWEEN DATEADD(mm,-6,(DATEPART(m,GETDATE()))) AND GETDATE()
...in an attempt to get it to focus on the month, but for some reason now I'm suddenly getting all the results from this table I'm working with, with data going back to 2014. I think I'm on the right track in terms of logic, but something's obviously not quite right.
The Why
A good troubleshooting step is to see the value your date function is returning. For example, you can execute this SELECT statement:
SELECT DATEADD(mm,-6,(DATEPART(m,GETDATE())))
This returns a date of 1899-07-02 00:00:00:00 which effectively turns your WHERE clause into:
WHERE [DateField] BETWEEN '1899-07-02' AND '2023-01-20
Thus explaining why your query returned all the records in the table.
Solution
First, you can truncate the current date to the first day of the current month:
SELECT DATEADD(MONTH, DATEDIFF(MONTH, 0, GETDATE()), 0) AS FOM;
Output: 2023-01-01 00:00:00
Now add another DATEADD function to subtract six months:
SELECT DATEADD(MONTH, -6, DATEADD(MONTH, DATEDIFF(MONTH, 0, GETDATE()), 0)) AS FOM_SIX_MNTHS_AGO;
Output: 2022-07-01 00:00:00
Putting it all together:
SELECT *
FROM [TABLE_NAME]
WHERE [DateField] BETWEEN DATEADD(MONTH, -6, DATEADD(MONTH, DATEDIFF(MONTH, 0, GETDATE()), 0)) AND GETDATE();
This is a great use case for a calendar table (or function if you don't want to materialize a actual table).
Consider:
CREATE OR ALTER FUNCTION Calendar (#StartYear INT, #EndYear INT)
RETURNS #Calender TABLE (
Date DATE, DateEndTime DATETIME2, Year INT, Month INT, Day INT, Quarter INT, WeekNumber INT, MonthName NVARCHAR(20), DayName NVARCHAR(20), WeekStartDate DATE, WeekEndDate DATE, MonthStartDate DATE, MonthEndDate DATE,
QuarterStartDate DATE, QuarterEndDate DATE, YearStartDate DATE, YearEndDate DATE, WeekStartTime DATETIME2, WeekEndTime DATETIME2, MonthStartTime DATETIME2, MonthEndTime DATETIME2, QuarterStartTime DATETIME2, QuarterEndTime DATETIME2,
YearStartTime DATETIME2, YearEndTime DATETIME2, IsWeekDay BIT)
AS
BEGIN
WITH CalendarHistory AS (
SELECT CAST(GETUTCDATE() AS DATE) AS Date
UNION ALL
SELECT DATEADD(DAY,-1,Date)
FROM CalendarHistory
WHERE DATEADD(DAY,-1,Date) > DATEADD(DAY,-1,DATEFROMPARTS(DATEPART(YEAR,DATEADD(YEAR,-#StartYear,GETUTCDATE())),1,1))
), CalendarFuture AS (
SELECT CAST(DATEADD(DAY,1,GETUTCDATE()) AS DATE) AS Date
UNION ALL
SELECT DATEADD(DAY,1,Date)
FROM CalendarFuture
WHERE DATEADD(DAY,1,Date) < DATEFROMPARTS(DATEPART(YEAR,DATEADD(YEAR,#EndYear,GETUTCDATE())),1,1)
), Calendar AS (
SELECT Date
FROM CalendarHistory
UNION ALL
SELECT Date
FROM CalendarFuture
)
INSERT INTO #Calender
SELECT Date,
DATEADD(MICROSECOND,-3,CAST(DATEADD(DAY,1,Date) AS DATETIME2)) AS DateEndTime,
DATEPART(YEAR,Date) AS Year, DATEPART(MONTH,Date) AS Month, DATEPART(DAY,Date) AS Day, DATEPART(QUARTER,Date) AS Quarter, DATEPART(WEEK,Date) AS WeekNumber,
DATENAME(MONTH,Date) AS MonthName, DATENAME(WEEKDAY,Date) AS DayName,
DATEADD(DAY,1-DATEPART(WEEKDAY,Date),Date) AS WeekStartDate, DATEADD(DAY,7-DATEPART(WEEKDAY,Date),Date) AS WeekEndDate,
DATEADD(DAY,1-DATEPART(DAY,Date),Date) AS MonthStartDate, DATEADD(DAY,-1,DATEADD(MONTH,1,DATEADD(DAY,1-DATEPART(DAY,Date),Date))) AS MonthEndDate,
DATEADD(MONTH,(((DATEPART(MONTH,Date)-1)/3))*3,DATEFROMPARTS(DATEPART(YEAR,Date),1,1)) AS QuarterStartDate, DATEADD(DAY,-1,DATEADD(MONTH,3,DATEADD(MONTH,(((DATEPART(MONTH,Date)-1)/3))*3,DATEFROMPARTS(DATEPART(YEAR,Date),1,1)))) AS QuarterEndDate,
DATEFROMPARTS(DATEPART(YEAR,Date),1,1) AS YearStartDate, DATEADD(DAY,-1,DATEFROMPARTS(DATEPART(YEAR,Date)+1,1,1)) AS YearEndDate,
CAST(DATEADD(DAY,1-DATEPART(WEEKDAY,Date),Date) AS DATETIME2) AS WeekStartTime, DATEADD(MICROSECOND,-3,CAST(DATEADD(DAY,1,DATEADD(DAY,7-DATEPART(WEEKDAY,Date),Date)) AS DATETIME2)) AS WeekEndTime,
CAST(DATEADD(DAY,1-DATEPART(DAY,Date),Date) AS DATETIME2) AS MonthStartTime, DATEADD(MICROSECOND,-3,CAST(DATEADD(DAY,0,DATEADD(MONTH,1,DATEADD(DAY,1-DATEPART(DAY,Date),Date))) AS DATETIME2)) AS MonthEndTime,
CAST(DATEADD(MONTH,(((DATEPART(MONTH,Date)-1)/3))*3,DATEFROMPARTS(DATEPART(YEAR,Date),1,1)) AS DATETIME2) AS QuarterStartTime, DATEADD(MICROSECOND,-3,CAST(DATEADD(DAY,0,DATEADD(MONTH,3,DATEADD(MONTH,(((DATEPART(MONTH,Date)-1)/3))*3,DATEFROMPARTS(DATEPART(YEAR,Date),1,1)))) AS DATETIME2)) AS QuarterEndTime,
CAST(DATEFROMPARTS(DATEPART(YEAR,Date),1,1) AS DATETIME2) AS YearStartTime, DATEADD(MICROSECOND,-3,CAST(DATEADD(DAY,0,DATEFROMPARTS(DATEPART(YEAR,Date)+1,1,1)) AS DATETIME2)) AS YearEndTime,
CASE WHEN DATEPART(WEEKDAY,Date) IN (1,7) THEN 1 ELSE 0 END AS IsWeekDay
FROM Calendar
ORDER BY Date
OPTION (MAXRECURSION 0)
RETURN
END;
GO
This can then be used like so:
SELECT *
FROM dbo.Calendar(1,1)
ORDER BY Date
In this case I've asked for a year before, and a year after the current date by passing 1 for both #StartYear and #EndYear
Date DateEndTime Year Month Day Quarter WeekNumber MonthName DayName WeekStartDate WeekEndDate MonthStartDate MonthEndDate QuarterStartDate QuarterEndDate YearStartDate YearEndDate WeekStartTime WeekEndTime MonthStartTime MonthEndTime QuarterStartTime QuarterEndTime YearStartTime YearEndTime IsWeekDay
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
2022-01-01 2022-01-01 23:59:59.9999970 2022 1 1 1 1 January Saturday 2021-12-26 2022-01-01 2022-01-01 2022-01-31 2022-01-01 2022-03-31 2022-01-01 2022-12-31 2021-12-26 00:00:00.0000000 2022-01-01 23:59:59.9999970 2022-01-01 00:00:00.0000000 2022-01-31 23:59:59.9999970 2022-01-01 00:00:00.0000000 2022-03-31 23:59:59.9999970 2022-01-01 00:00:00.0000000 2022-12-31 23:59:59.9999970 1
2022-01-02 2022-01-02 23:59:59.9999970 2022 1 2 1 2 January Sunday 2022-01-02 2022-01-08 2022-01-01 2022-01-31 2022-01-01 2022-03-31 2022-01-01 2022-12-31 2022-01-02 00:00:00.0000000 2022-01-08 23:59:59.9999970 2022-01-01 00:00:00.0000000 2022-01-31 23:59:59.9999970 2022-01-01 00:00:00.0000000 2022-03-31 23:59:59.9999970 2022-01-01 00:00:00.0000000 2022-12-31 23:59:59.9999970 1
2022-01-03 2022-01-03 23:59:59.9999970 2022 1 3 1 2 January Monday 2022-01-02 2022-01-08 2022-01-01 2022-01-31 2022-01-01 2022-03-31 2022-01-01 2022-12-31 2022-01-02 00:00:00.0000000 2022-01-08 23:59:59.9999970 2022-01-01 00:00:00.0000000 2022-01-31 23:59:59.9999970 2022-01-01 00:00:00.0000000 2022-03-31 23:59:59.9999970 2022-01-01 00:00:00.0000000 2022-12-31 23:59:59.9999970 0
....
2023-12-29 2023-12-29 23:59:59.9999970 2023 12 29 4 52 December Friday 2023-12-24 2023-12-30 2023-12-01 2023-12-31 2023-10-01 2023-12-31 2023-01-01 2023-12-31 2023-12-24 00:00:00.0000000 2023-12-30 23:59:59.9999970 2023-12-01 00:00:00.0000000 2023-12-31 23:59:59.9999970 2023-10-01 00:00:00.0000000 2023-12-31 23:59:59.9999970 2023-01-01 00:00:00.0000000 2023-12-31 23:59:59.9999970 0
2023-12-30 2023-12-30 23:59:59.9999970 2023 12 30 4 52 December Saturday 2023-12-24 2023-12-30 2023-12-01 2023-12-31 2023-10-01 2023-12-31 2023-01-01 2023-12-31 2023-12-24 00:00:00.0000000 2023-12-30 23:59:59.9999970 2023-12-01 00:00:00.0000000 2023-12-31 23:59:59.9999970 2023-10-01 00:00:00.0000000 2023-12-31 23:59:59.9999970 2023-01-01 00:00:00.0000000 2023-12-31 23:59:59.9999970 1
2023-12-31 2023-12-31 23:59:59.9999970 2023 12 31 4 53 December Sunday 2023-12-31 2024-01-06 2023-12-01 2023-12-31 2023-10-01 2023-12-31 2023-01-01 2023-12-31 2023-12-31 00:00:00.0000000 2024-01-06 23:59:59.9999970 2023-12-01 00:00:00.0000000 2023-12-31 23:59:59.9999970 2023-10-01 00:00:00.0000000 2023-12-31 23:59:59.9999970 2023-01-01 00:00:00.0000000 2023-12-31 23:59:59.9999970 1
You can then use this by joining to it from your query:
SELECT *
FROM MyOrders m
INNER JOIN dbo.Calendar(1,1) c
ON m.OrderDate = c.Date
INNER JOIN MyOrders d
ON d.OrderDate BETWEEN c.MonthStartDate AND c.MonthEndTime
WHERE m.OrderID = 1
If you are using SQL Server 2012 or later you can utilise the EOMONTH function as follows.
WHERE [DateField] BETWEEN DATEADD(day, 1, EOMONTH(GETDATE(), -7)) AND GETDATE()
Personally I like to split this sort of thing out into variables, and I never use between. So
-- By declaring variables I know they are dates not datetimes
-- `getdate()` returns a datetime which can cause issues with datetime windows
declare #StartDate date, #EndDate date = getdate();
-- By setting #startdate relative to #EndDate I can test different date windows easily
set #EndDate dateadd(d, 1, eomonth(#StartDate, -7));
select *
from dbo.MyTable
-- This form of window handles date and datetimes correctly without any guessing
-- I find `between` to be unintuitive
where DateColumn >= #StartDate
and DateColumn < dateadd(day, 1, #EndDate);
I would like to get the number of a week from a [Dates] column, starting with Monday as the first day of the week. I would like each week to be uniquely identified. Rather than rolling over at 52 to 1, to continue counting to week 54, 55, etc.
P.S. I need this for correct sort of [Week Date Range] column in Tableau.
Otherwise, Tableau sorts grouped data this way:
08/08/2022 - 08/14/2022
08/09/2021 - 08/15/2021
08/15/2022 - 08/21/2022
08/16/2021 - 08/22/2021
Code:
CREATE TABLE Dates
(Id INT, Dates DATE);
INSERT INTO Dates
VALUES
(1, '2022-12-19'),
(2, '2022-12-22'),
(3, '2022-12-25'),
(4, '2022-12-26'),
(5, '2022-12-29'),
(6, '2022-12-31'),
(7, '2023-01-01'),
(8, '2023-01-06'),
(9, '2023-01-07'),
(10, '2023-01-09')
SELECT *,
CONCAT(CONVERT(VARCHAR(10), DATEADD(DAY, DATEDIFF(DAY, '19000101', Dates) / 7 * 7, '19000101'), 101), ' - ', CONVERT(VARCHAR(10), DATEADD(DAY, (DATEDIFF(DAY, '19000101', Dates) / 7 + 1) * 7, '18991231'), 101)) AS [Week Date Range (Monday - Sunday)],
DATEPART(ISO_WEEK, Dates) AS [ISO Week Number]
FROM Dates
Id
Dates
Week Date Range (Monday - Sunday)
ISO Week Number
1
2022-12-19
12/19/2022 - 12/25/2022
51
2
2022-12-22
12/19/2022 - 12/25/2022
51
3
2022-12-25
12/19/2022 - 12/25/2022
51
4
2022-12-26
12/26/2022 - 01/01/2023
52
5
2022-12-29
12/26/2022 - 01/01/2023
52
6
2022-12-31
12/26/2022 - 01/01/2023
52
7
2023-01-01
12/26/2022 - 01/01/2023
52
8
2023-01-06
01/02/2023 - 01/08/2023
1
9
2023-01-07
01/02/2023 - 01/08/2023
1
10
2023-01-09
01/09/2023 - 01/15/2023
2
Your problem is not really that you are in need of a running week number. Your problem is that the "Week Date Range" that you have asked Tableau to sort is a string formatted in part as MM/DD/YYYY, which will never sort correctly across years.
The simplest solution is to provide either a true date value such as the calculated week start date or that same date as a string formatted as "yyyy-mm-dd" using CONVERT(VARCHAR(10), WeekStart, 120).
However, if you really want a unique week number, you can use the DATEDIFF(day, ...) / 7 function relative to a "reference Monday" to give you a continuous integer week number. (The "reference Monday" must precede all dates in the data to avoid negative diffs.)
The following includes both:
SET DATEFIRST 1
DECLARE #ReferenceMonday DATE = '1900-01-01' -- This just happens to be a Monday
SELECT
*,
DATENAME(weekday, DATES) AS Weekday,
CONCAT(CONVERT(VARCHAR(10), DATEADD(DAY, DATEDIFF(DAY, '19000101', Dates) / 7 * 7, '19000101'), 101), ' - ', CONVERT(VARCHAR(10), DATEADD(DAY, (DATEDIFF(DAY, '19000101', Dates) / 7 + 1) * 7, '18991231'), 101)) AS [Week Date Range (Monday - Sunday)],
DATEDIFF(day, #ReferenceMonday, Dates) / 7 AS SortableWeek,
CONVERT(VARCHAR(10), DATEADD(DAY, DATEDIFF(DAY, '19000101', Dates) / 7 * 7,'19000101'), 120) AS [SortableWeekStart]
FROM Dates
ORDER BY Id
(I have also added weekday name to the select list as a reference.)
The week start and finish date calculations can also be simplified by combining DATEPART(weekday,...) with DATEADD(day).
DATEADD(day, 1-DATEPART(weekday, D.Dates), D.Dates) AS WeekStart
DATEADD(day, 7-DATEPART(weekday, D.Dates), D.Dates) AS WeekFinish
A technique that may improve readability and reduce duplication of expressions is to use CROSS APPLY to separate calculations from the final select list.
Combining the above could result in a query rewritten as follows:
SET DATEFIRST 1
DECLARE #ReferenceMonday DATE = '1900-01-01' -- This just happens to be a Monday
SELECT
D.*,
DATENAME(weekday, DATES) AS Weekday,
DS.[Week Date Range (Monday - Sunday)],
WK.SortableWeek,
DS.SortableWeekStart
FROM Dates D
CROSS APPLY (
SELECT
DATEADD(day, 1-DATEPART(weekday, D.Dates), D.Dates) AS WeekStart,
DATEADD(day, 7-DATEPART(weekday, D.Dates), D.Dates) AS WeekFinish,
DATEDIFF(day, #ReferenceMonday, Dates) / 7 AS SortableWeek
) WK
CROSS APPLY (
SELECT
CONCAT(CONVERT(VARCHAR(10), WK.WeekStart, 101), ' - ', CONVERT(VARCHAR(10), WK.WeekFinish, 101)) AS [Week Date Range (Monday - Sunday)],
CONVERT(VARCHAR(10), WeekStart, 120) AS SortableWeekStart
) DS
ORDER BY Id
Both of the above queries yield the following results:
Id
Dates
Weekday
Week Date Range (Monday - Sunday)
SortableWeek
SortableWeekStart
1
2022-12-19
Monday
12/19/2022 - 12/25/2022
6416
2022-12-19
2
2022-12-22
Thursday
12/19/2022 - 12/25/2022
6416
2022-12-19
3
2022-12-25
Sunday
12/19/2022 - 12/25/2022
6416
2022-12-19
4
2022-12-26
Monday
12/26/2022 - 01/01/2023
6417
2022-12-26
5
2022-12-29
Thursday
12/26/2022 - 01/01/2023
6417
2022-12-26
6
2022-12-31
Saturday
12/26/2022 - 01/01/2023
6417
2022-12-26
7
2023-01-01
Sunday
12/26/2022 - 01/01/2023
6417
2022-12-26
8
2023-01-06
Friday
01/02/2023 - 01/08/2023
6418
2023-01-02
9
2023-01-07
Saturday
01/02/2023 - 01/08/2023
6418
2023-01-02
10
2023-01-09
Monday
01/09/2023 - 01/15/2023
6419
2023-01-09
See this db<>fiddle for a working example.
I think you're mostly on the right path. Logic as follows
Find the start of the week
With that start of the week, calculate the relevant week number
Convert that week number into a 'grouping' value that is sorted
You've done the first two steps above; to get the third, I get the 'grouping value' for Tableau to be an int value calculated as 100 * YEAR(date) + (week number) - meaning that in your data above, the grouping value will range from 202251 to 202302.
Note that this needs to be done on the 'WeekStart' field, not 'Dates', because 20230101 will return a YEAR of 2023 rather than 2022.
-- Make Monday the first day of week
SET DATEFIRST 1;
-- Calculate 'ISOwkRef' as sort variable
WITH Dates_with_weekstart AS
(SELECT ID,
Dates,
DATEADD(day, -1 * (DATEPART(weekday, dates)-1), Dates) AS WeekStart
FROM dates)
SELECT *,
100*YEAR(Weekstart) + DATEPART(iso_week, WeekStart) AS ISOwkRef
FROM Dates_with_weekstart
ORDER BY ISOwkRef;
db<>fiddle with answer above here.
Results
ID Dates WeekStart ISOwkRef
1 2022-12-19 2022-12-19 202251
2 2022-12-22 2022-12-19 202251
3 2022-12-25 2022-12-19 202251
4 2022-12-26 2022-12-26 202252
5 2022-12-29 2022-12-26 202252
6 2022-12-31 2022-12-26 202252
7 2023-01-01 2022-12-26 202252
8 2023-01-06 2023-01-02 202301
9 2023-01-07 2023-01-02 202301
10 2023-01-09 2023-01-09 202302
Edit: Alternatively, if you prefer to have the weeks numbered from the first week and then increasingly, you can do it similar to above, but
First calculate the week number for the earliest week
Calculate the difference in weeks from that earliest week, and add it to the number
I think it's a bit more convoluted, but here is an approach to do it:
WITH Dates_with_weekstart AS
(SELECT ID,
Dates,
DATEADD(day, -1 * (DATEPART(weekday, dates)-1), Dates) AS WeekStart
FROM dates)
SELECT Dates_with_weekstart.ID,
Dates_with_weekstart.Dates,
Dates_with_weekstart.Weekstart,
DATEPART(ISO_week, First_Weekstart) + DATEDIFF(week, First_Weekstart, Dates_with_weekstart.Weekstart) AS ISOWkNum_rolling
FROM Dates_with_weekstart
CROSS JOIN
(SELECT MIN(WeekStart) AS First_Weekstart
FROM Dates_with_weekstart
) AS FirstWkStart
ORDER BY ISOWkNum_rolling;
Results:
ID Dates Weekstart ISOWkNum_rolling
1 2022-12-19 2022-12-19 51
2 2022-12-22 2022-12-19 51
3 2022-12-25 2022-12-19 51
4 2022-12-26 2022-12-26 52
5 2022-12-29 2022-12-26 52
6 2022-12-31 2022-12-26 52
7 2023-01-01 2022-12-26 52
8 2023-01-06 2023-01-02 53
9 2023-01-07 2023-01-02 53
10 2023-01-09 2023-01-09 54
Fiddle above has been adjusted with this second approach.
Feel free to add the varchar 'Date range' field you have; this answer is just working with the key data.
I need to get last 6 weeks data from some table, right now the logic that I use is this
WEEK([date column]) BETWEEN WEEK(NOW()) - 6 AND WEEK(NOW())
It run as I want, but January is near and I realize that this query will not working as it is. I try to run my query on 15th January 2022, I only get data from 1st January to 15th January when I use my logic.
TGL MINGGU_KE
2022-01-01 | 1
2022-01-02 | 2
2022-01-03 | 2
2022-01-04 | 2
2022-01-05 | 2
2022-01-06 | 2
2022-01-07 | 2
2022-01-08 | 2
2022-01-09 | 3
2022-01-10 | 3
2022-01-11 | 3
2022-01-12 | 3
2022-01-13 | 3
2022-01-14 | 3
2022-01-15 | 3
Can I get the last 6 weeks data including last year?
This is my dbfiddle: https://dbfiddle.uk/o9BeAFJF
You can round the dates to the first day of the week using ROUND, TRUNC or THIS_WEEK
WITH
SEARCH_WEEK (TGL) AS (
VALUES date '2020-12-01'
UNION ALL
SELECT tgl + 1 DAY FROM SEARCH_WEEK WHERE tgl < CURRENT date
),
BASE_DATE (base_date) AS (
VALUES date '2022-01-15'
),
OPTIONS (OPTION, OPTION_BASE_DATE) AS (
SELECT OPTION, option_base_date FROM base_date CROSS JOIN LATERAL (
VALUES
('ROUND D', ROUND(base_date, 'D')),
('ROUND IW', ROUND(base_date, 'IW')),
('ROUND W', ROUND(base_date, 'W')),
('ROUND WW', ROUND(base_date, 'WW')),
('TRUNC D', TRUNC(base_date, 'D')),
('TRUNC IW', TRUNC(base_date, 'IW')),
('TRUNC W', TRUNC(base_date, 'W')),
('TRUNC WW', TRUNC(base_date, 'WW')),
('THIS_WEEK', THIS_WEEK(base_date)),
('THIS_WEEK + 1 DAY', THIS_WEEK(base_date) + 1 DAY)
) a (OPTION, OPTION_BASE_DATE)
)
SELECT
OPTION,
MIN(TGL) BEGIN,
max(tgl) END,
dayname(MIN(TGL)) day_BEGIN,
dayname(max(tgl)) day_end,
days_between(max(tgl), min(tgl)) + 1 duration_in_days
FROM
SEARCH_WEEK
CROSS JOIN options
WHERE
TGL BETWEEN option_base_date - 35 DAYS AND option_base_date + 6 DAYS
GROUP BY OPTION
OPTION
BEGIN
END
DAY_BEGIN
DAY_END
DURATION_IN_DAYS
ROUND D
2021-12-12
2022-01-22
Sunday
Saturday
42
ROUND IW
2021-12-13
2022-01-23
Monday
Sunday
42
ROUND W
2021-12-11
2022-01-21
Saturday
Friday
42
ROUND WW
2021-12-11
2022-01-21
Saturday
Friday
42
THIS_WEEK
2021-12-05
2022-01-15
Sunday
Saturday
42
THIS_WEEK + 1 DAY
2021-12-06
2022-01-16
Monday
Sunday
42
TRUNC D
2021-12-05
2022-01-15
Sunday
Saturday
42
TRUNC IW
2021-12-06
2022-01-16
Monday
Sunday
42
TRUNC W
2021-12-11
2022-01-21
Saturday
Friday
42
TRUNC WW
2021-12-11
2022-01-21
Saturday
Friday
42
fiddle
you can use dateadd to get first day of week six weeks ago like this:
Select * from tableName
where [dateColumn] between dateadd(WEEK,-6,getdate()) and getdate()
You can use DATEADD to get last 6 weeks of data as follows:
Select * from [TableName] where [DateColumn] between
DATEADD(WEEK,-6,GETDATE()) and GETDATE();
I am looking for some T-SQL code that should pick the date which is the "last Sunday in the month of January".
For example:
Current day expected result
2017-01-29 2016-01-31
2017-02-05 2017-01-29
2017-02-19 2017-01-29
2018-01-28 2017-01-29
2018-02-04 2018-01-28
This is because the year starts from last Sunday in January
I have some T-SQL code which is being used in SQL Server 2014:
select
convert(varchar(10), DATEADD(day, DATEDIFF(day, '19000107', DATEADD(month, DATEDIFF(MONTH, 0, CONVERT(date, CONVERT(VARCHAR(4), GETDATE(), 112) + '0101')), 30)) / 7 * 7, '19000107'), 120)
If the current date is between February to December then the above code returns the correct answer. But if we set current date as '2017-01-25' (or any other dates in January), then the result is wrong.
For the above date (2017-01-25) answer should be '2016-01-31'
The issue might be that the query is finding the first day of Jan for the year supplied, but this is going to be different if Jan is the supplied month.
The change is to determine if Jan is the supplied month, and return last year as the year.
select
convert(varchar(10), DATEADD(day, DATEDIFF(day, '19000107', DATEADD(month, DATEDIFF(MONTH, 0, CONVERT(date, CONVERT(VARCHAR(4), (CASE WHEN MONTH(GetDate()) = 1 THEN CONVERT(VARCHAR(4), GetDate(), 112) - 1 ELSE CONVERT(VARCHAR(4), GetDate(), 112) END), 112) + '0101')), 30)) / 7 * 7, '19000107'), 120)
The CASE statement within the CONVERT(date, CONVERT(VARCHAR(4)...is where the change was made.
A lookup table is the best way to solve this type of problem
WITH lookup(d) AS
(
VALUES( ('2016-01-31'),
('2017-01-29'),
('2018-01-28')
)
select MAX(d)
FROM lookup
WHERE d <= GETDATE()
The rightmost column of this will calculate the previous last Sunday in January for any date. I've left the other columns in as a simple way to document and experiment with the logic.
There is no branching logic, it is all pure date math so it should aggregate reasonably fast and should be able to use indexes on a date column.
DECLARE #Date DATE = '20170205';
SELECT SampleDate= #Date ,
PreviousLastSundayInJanuaryForSampleDate = DATEADD(DAY,(DATEPART(WEEKDAY,EOMONTH(DATEADD(MONTH,(DATEPART(MONTH
,DATEADD(DAY,((DATEPART(DAYOFYEAR,DATEADD(DAY,(DATEPART(WEEKDAY,EOMONTH(DATEADD(MONTH,(DATEPART(MONTH,#Date)-1)*-1,#Date)))-1)*-1,EOMONTH(DATEADD(MONTH,(DATEPART(MONTH,#Date)-1)*-1,#Date)))))-1) * -1 ,#Date))-1)*-1
,DATEADD(DAY,((DATEPART(DAYOFYEAR,DATEADD(DAY,(DATEPART(WEEKDAY,EOMONTH(DATEADD(MONTH,(DATEPART(MONTH,#Date)-1)*-1,#Date)))-1)*-1,EOMONTH(DATEADD(MONTH,(DATEPART(MONTH,#Date)-1)*-1,#Date)))))-1) * -1 ,#Date))))-1)*-1,EOMONTH(DATEADD(MONTH,(DATEPART(MONTH
,DATEADD(DAY,((DATEPART(DAYOFYEAR,DATEADD(DAY,(DATEPART(WEEKDAY,EOMONTH(DATEADD(MONTH,(DATEPART(MONTH,#Date)-1)*-1,#Date)))-1)*-1,EOMONTH(DATEADD(MONTH,(DATEPART(MONTH,#Date)-1)*-1,#Date)))))-1) * -1 ,#Date))-1)*-1
,DATEADD(DAY,((DATEPART(DAYOFYEAR,DATEADD(DAY,(DATEPART(WEEKDAY,EOMONTH(DATEADD(MONTH,(DATEPART(MONTH,#Date)-1)*-1,#Date)))-1)*-1,EOMONTH(DATEADD(MONTH,(DATEPART(MONTH,#Date)-1)*-1,#Date)))))-1) * -1 ,#Date))))
To use this against a table, replace #Date with your own date field.
Updated with sample results:
SampleDate PreviousLastSundayInJanuaryForSampleDate
2008-06-03 2008-01-27
2008-12-20 2008-01-27
2009-07-08 2009-01-25
2010-01-24 2009-01-25
2010-08-12 2010-01-31
2011-02-28 2011-01-30
2011-09-16 2011-01-30
2012-04-03 2012-01-29
2012-10-20 2012-01-29
2013-05-08 2013-01-27
2013-11-24 2013-01-27
2014-06-12 2014-01-26
2014-12-29 2014-01-26
2015-07-17 2015-01-25
2016-02-02 2016-01-31
2016-08-20 2016-01-31
2017-03-08 2017-01-29
2017-09-24 2017-01-29
2018-04-12 2018-01-28
2018-10-29 2018-01-28
2019-05-17 2019-01-27
2019-12-03 2019-01-27
2020-06-20 2020-01-26
I have a query like this:
SELECT 'Last 7 Days' AS Date_Range, CONVERT(smalldatetime, GETDATE()) - 6 AS Begin_Date, CONVERT(smalldatetime, GETDATE())
AS End_Date
FROM sys.columns
produces output
Last 7 Days 2011-03-20 07:35:00 2011-03-26 07:35:00
Last 7 Days 2011-03-20 07:35:00 2011-03-26 07:35:00
How to get this?
Last 7 Days 2011-03-20 00:00:00 2011-03-26 00:00:00
Last 7 Days 2011-03-20 00:00:00 2011-03-26 00:00:00
Do a DateAdd operation on the DT value you're getting back. This essentially removes the time component:
DateAdd(Day, DateDiff(Day, 0, GetDate()), 0)
You could just use date in the convert function instead of smalldatetime and then append "00:00:00" as string to the result.