LAST_DAY('WEEK') ignores WEEK_OF_YEAR_POLICY session parameter - snowflake-cloud-data-platform

I realized that on our Time table we had days of December as part of 1st week of 2022. Even after setting WEEK_OF_YEAR_POLICY = 1 variable to, I still get that the last day for week 53 of 2021 is 01-01-2022:
-- Jan 1st always starts the 1st week; Week starts on Sunday
ALTER SESSION SET week_of_year_policy=1, week_start=7;
WITH cte_my_date AS (
SELECT
Dateadd(day, Seq4(), '2021-01-01 00:00:00') AS MY_DATE
FROM
TABLE(Generator(rowcount => 365))
)
select my_date, week(my_date), Last_day(my_date, 'WEEK') from cte_my_date
Is there an out-of-the-box way to retrieve 31-Dec-2021 as last day of the week?

You could use least() to enforce the restriction of last day of year vs last day of week:
WITH cte_my_date AS (
SELECT
Dateadd(day, Seq4(), '2021-12-16 00:00:00') AS MY_DATE
FROM TABLE(Generator(rowcount => 15))
)
select my_date, week(my_date)
, least(last_day(my_date, 'WEEK'), last_day(my_date, 'YEAR')) last
from cte_my_date
;

Related

How to find the past 4 weeks of the same weekday value starting today

I am trying to select records from today and the same day of each week for the last 4 weeks.
Today (Tuesday)
Last Tuesday
The Tuesday before that
The Tuesday before that
I need this to be tied to current date because I am going to run this query every day so I don't want to use a between or something where I manually specify the date range.
Everything I have found or tried so far has pulled the last month of data but not the last 4 weeks of the same weekday.
select *
from table
where thedatecolumn >= DATEADD(mm, -1, GETDATE())
This works but pulls everything from the last month.
If today's date is 7/10/2019 I need
Data from 7/10/2019
Data from 7/3/2019
Data from 6/26/2019
Data from 6/19/2019
Every day I will run this query, so I need it to be dynamic based on the current date.
I believe you want to look back 21 days and then filter those dates that have the same day of week:
select * from table
where thedatecolumn >= DATEADD(DAY, -21, CAST(GETDATE() AS DATE))
and DATEPART(WEEKDAY, thedatecolumn) = DATEPART(WEEKDAY, GETDATE())
You Can try using a recursive cte which starts today and repeatedly substracts 7 days - so you ensure you always land on the same weekday. Following an example:
WITH cteFromToday AS(
SELECT 0 AS WeeksBack, GETDATE() AS MyDate
UNION ALL
SELECT WeeksBack + 1 AS WeeksBack, DATEADD(d, -7, MyDate) AS MyDate
FROM cteFromToday
)
SELECT TOP 5 *
FROM cteFromToday
OPTION ( MaxRecursion 0 );
This is quite simple. Substitute CURRENT_TIMESTAMP here for any given date.
SELECT CONVERT(DATE,CURRENT_TIMESTAMP) AS Today,
DATEADD(DAY,-7,CONVERT(DATE,CURRENT_TIMESTAMP)) AS LastWeek ,
DATEADD(DAY,-14,CONVERT(DATE,CURRENT_TIMESTAMP)) AS TwoWeeksAgo,
DATEADD(DAY,-21,CONVERT(DATE,CURRENT_TIMESTAMP)) AS ThreeWeeksAgo
SO, if you want to get data for a set of ranges for one entire day with those dates:
SELECT something
WHERE
datetimecolumn >= CONVERT(DATE,CURRENT_TIMESTAMP) AND datetimecolumn < DATEADD(DAY,1, CONVERT(DATE,CURRENT_TIMESTAMP)) -- Todays range,
OR datetimecolumn >= DATEADD(DAY,-7,CONVERT(DATE,CURRENT_TIMESTAMP)) AND datetimecolumn < DATEADD(DAY,1,DATEADD(DAY,-7,CONVERT(DATE,CURRENT_TIMESTAMP)))-- LastWeek ,
OR datetimecolumn >= DATEADD(DAY,-14,CONVERT(DATE,CURRENT_TIMESTAMP)) AND datetimecolumn < DATEADD(DAY,1,DATEADD(DAY,-14,CONVERT(DATE,CURRENT_TIMESTAMP)))-- TwoWeeksAgo,
OR datetimecolumn >= DATEADD(DAY,-21,CONVERT(DATE,CURRENT_TIMESTAMP)) AND datetimecolumn < DATEADD(DAY,1, DATEADD(DAY,-21,CONVERT(DATE,CURRENT_TIMESTAMP))) -- ThreeWeeksAgo

SET DATEFIST = 1 not working correctly

I'm having trouble to make SET DATEFIRST to work properly on some simple queries I working on at the moment.
Here is my first example:
SET DATEFIRST = 1
SELECT count(Distinct ID)
FROM Products
WHERE
Location in (12)
and YEAR (CREATED) = '2018'
Group by datepart(wk, created), year(created)
The above code gives me some results correct and some others wrong. It's basically counting from Monday to Monday, but I want it to count from Monday to Sunday. I still don't understand why it's counting 8 days instead of 7 days on some of the weeks.
Also I have multiple selections with SET DATEFIRST = 1 which also doesn't work:
SET DATEFIRST = 1
--Products finished this week
SELECT count(Distinct ID)
FROM Products
WHERE
Created >= dateadd(day, 1-datepart(dw, getdate()), CONVERT(date,getdate()))
AND Created < dateadd(day, 8-datepart(dw, getdate()), CONVERT(date,getdate()))
AND Location in (15,16,17) AND (Location IS NOT NULL OR Location NOT IN(18))
UNION ALL
--------------------------------------------
-- Products received this week
SELECT count(Distinct ID)
FROM Products
WHERE
Created >= dateadd(day, 1-datepart(dw, getdate()), CONVERT(date,getdate()))
AND Created < DATEADD(DAY,0,DATEDIFF(DAY,0,dateadd(day, 8-datepart(dw, getdate()),getdate())))
AND Location in(1)
The above code is not reacting to SET DATEFIRST = 1
It's not counting from Monday to sunday, instead it's counting from Sunday to sunday (8 days)
Here is your issue:
DATEADD(day, 8-datepart(DW, GETDATE()), CONVERT(DATE,GETDATE())))
You are comparing a DATETIME from the database with a TIME to a DATE with no TIME. So the DATE portions on next Monday will be equal and your < will fail.
This will not work because the DB field is a DATETIME and you are comparing to a DATE.
SET #D= '06/25/2018 01:20:23 AM' --Monday next week with a time
IF(#D < DATEADD(day, 8-datepart(DW, GETDATE()), CONVERT(GETDATE()))) --<--The whole day
SELECT 1 -- '06/25/2018 12:00:23 AM' < (DATE)2018-06-25
ELSE
SELECT 2
Returns 2
If you do want to include everything through Sunday then you need to set the cutoff to 12:00:00 AM Monday morning. One way to do that would be something like:
DATEADD(DAY,0,DATEDIFF(DAY,0,dateadd(day, 8-datepart(dw, getdate()),getdate())))

Determine number of Days in month returns different count for Month of March

Here is a weird one for you all.
I need to determine the number of days in a Month
;WITH cteNetProfit AS
(
---- NET PROFIT
SELECT DT.CreateDate
, SUM(DT.Revenue) as Revenue
, SUM(DT.Cost) as Cost
, SUM(DT.GROSSPROFIT) AS GROSSPROFIT
FROM
(
SELECT CAST([createDTG] AS DATE) as CreateDate
, SUM(Revenue) as Revenue
, SUM(Cost) as Cost
, SUM(REVENUE - COST) AS GROSSPROFIT
FROM [dbo].[CostRevenueSpecific]
WHERE CAST([createDTG] AS DATE) > CAST(GETDATE() - 91 AS DATE)
AND CAST([createDTG] AS DATE) <= CAST(GETDATE() - 1 AS DATE)
GROUP BY createDTG
UNION ALL
SELECT CAST([CallDate] AS DATE) AS CreateDate
, SUM(Revenue) as Revenue
, SUM(Cost) as Cost
, SUM(REVENUE - COST) AS GROSSPROFIT
FROM abc.PublisherCallByDay
WHERE CAST([CallDate] AS DATE) > CAST(GETDATE() - 91 AS DATE)
AND CAST([CallDate] AS DATE) <= CAST(GETDATE() - 1 AS DATE)
GROUP BY CALLDATE
) DT
GROUP BY DT.CreateDate
)
select distinct MONTH(CREATEDATE), DateDiff(Day,CreateDate,DateAdd(month,1,CreateDate))
FROM cteNetProfit
For some reason it is returning two different results for the month of March 2016 one result is 30 and the other 31(which of course is correct) I validate that the underlying data only has 31 days worth of data for the Month of March. Since Feb is a leap year can this affect the DATEDIFF function. The remaining months return the correct #.
2 29
3 31
3 30
4 30
5 31
Thanks for the input, however, I found the solution elsewhere
select Distinct MONTH(CREATEDATE), Day(EOMONTH(CreateDate))
FROM cteNetProfit
The difference comes when you hit the 2016-03-31 date. If you run the query below for 2016-03-30 and 2016-03-31, the results of adding 1 MONTH using DATEADD, in both instances, is 2016-04-30. It returns the last day of the next month.
SELECT DATEADD(MONTH,1,'2016-03-30') , DATEADD(MONTH,1,'2016-03-31')
This syntax seemed to work (courtesy of https://raresql.com/2013/01/06/sql-server-get-number-of-days-in-month/).
SELECT DAY(DATEADD(ms,-2,DATEADD(MONTH, DATEDIFF(MONTH,0,#DATE)+1,0))) AS [Current Month]

MSSQL order by previous 7 days

I run this query in MSSQL to get the items, grouping by the last 7 days of the week:
SELECT COUNT(Date_Entered), DATENAME(WEEKDAY, Date_Entered)
FROM my_table
WHERE Board_Name = 'Board'
AND DATEDIFF(DAY,Date_Entered,GETDATE()) <= 7
GROUP BY DATENAME(WEEKDAY, Date_Entered)
In the result, days of the week are sorted in alphabetical order: Friday > Monday > Saturday > Sunday > Thursday > Tuesday > Wednesday
How do I sort by the normal/correct/common sense order, starting with the weekday of 7 days ago and ending with yesterday?
Ordering by MAX(Date_Entered) should work too:
SELECT
COUNT(Date_Entered),
DATENAME(WEEKDAY, Date_Entered)
FROM my_table
WHERE Board_Name = 'Board' AND DATEDIFF(DAY,Date_Entered,GETDATE()) <= 7
GROUP BY DATENAME(WEEKDAY, Date_Entered)
ORDER BY MAX(Date_Entered);
Normally you would want to order by the date ascending, but since you use an aggregate function you would need to group by the date which would ruin it, but since the max(date) in every group is the date you can do max(date) to order.
DATEPART is your friend, try it like this:
SELECT COUNT(Date_Entered), DATENAME(WEEKDAY, Date_Entered),DATEPART(WEEKDAY,Date_Entered)
FROM my_table
WHERE Board_Name = 'Board'
AND DATEDIFF(DAY,Date_Entered,GETDATE()) <= 7
GROUP BY DATEPART(WEEKDAY,Date_Entered),DATENAME(WEEKDAY, Date_Entered)
ORDER BY DATEPART(WEEKDAY,Date_Entered)
If you can't count on data being available for every week then you'd need to do something more based on date calculations. Off the top of my head I think this will be more reliable:
ORDER BY (DATEDIFF(dd, MAX(Date_Entered), CURRENT_TIMESTAMP) + 77777) % 7
EDIT: I wrote that not realizing that the data was already limited to a single week. I thought the intention was to group in buckets by day of week for a longer range of dates.
I'll also comment that to me it is more natural to do the grouping on cast(Date_Entered as date) rather than on a string value and I wouldn't be surprised if it's a more efficient query.

Get current year's birthday in SQL

I have the date of birth (2/3/1967) and I want to convert that to this years birthday.
The select statement would return something like this
userid date_of_birth current_bday
abc123 2/3/1967 2/3/2011
I tried playing around with datepart to get month and day but didn't succeed.
SQL Server 2008 R2
Here's one way to do it:
Use a DATEADD function to add (in years) the difference between the current year and the birth year (to the birth date)
SELECT userid,
date_of_birth,
DATEADD(YY, DATEPART(YYYY, GETDATE()) - DATEPART(YYYY,date_of_birth), date_of_birth) AS current_bday
FROM Users
One thing to worry about is trying to create a date by the individual month and day with the current year. The one problem with that would be trying to create a February 29th birthdate on a year the isn't a leap year. I did test this, and it appears that you will need to specifically account for this, since the DATEADD function gives a date of '2011-02-28' for a birthdate of '2000-02-29'
i think this is more neat:
SELECT userid
,date_of_birth
,DATEADD(YEAR,DATEDIFF(YEAR,date_of_birth,SYSDATETIME()),date_of_birth)
AS current_bday
If you want to grab the "next birthday" from today (not just this year, but possibly next year if the birthday was before today, in this year), you need additional checks.
Here's an example which computes the next birthday for each 29th day of each month in the year 1988 in two ways:
- no check if the bday has come to pass (bday)
- check if bday has come to pass (bday2)
with dobs (dob) as (
select convert(datetime,dob)
from (values
('1988-01-29')
, ('1988-02-29')
, ('1988-03-29')
, ('1988-04-29')
, ('1988-05-29')
, ('1988-06-29')
, ('1988-07-29')
, ('1988-08-29')
, ('1988-09-29')
, ('1988-10-29')
, ('1988-11-29')
, ('1988-12-29')
) as X(dob)
)
select
dob,
dateadd(YY, datepart(YYYY, getdate()) - datepart(YYYY,dob), dob) as bday1
, case when
datepart(month, dateadd(year,datediff(year,dob,getdate()),dob))
<
datepart(month, getdate())
or ( datepart(month, dateadd(year,datediff(year,dob,getdate()),dob))
<
datepart(month, getdate())
and
datepart(day, dateadd(year,datediff(month,dob,getdate()),dob))
<
datepart(day, getdate())
)
then dateadd(year,1+datediff(year,dob,getdate()),dob)
else dateadd(year,datediff(year,dob,getdate()),dob)
end as bday2
from dobs

Resources