How to calculate End and start Week? - sql

how does one calculate the beginning of a week from a given date e.g 23 March 2010 beginning of the week is 21 March 2010

Depending on the value of DATEFIRST (I'm assuming 7 here, i.e. Sunday):
SELECT columnname - (DATEPART(WEEKDAY, columnname) - 1) AS week_start
FROM Table1
Then week_end is of course 7 days later.

Related

Custom month numbers that take last 30 days instead of Number of month (SQL Server)

I am trying to create a lag function to return current month and last month streams for an artist.
Instead of returning streams for Feb vs Jan, I wan the function to use the last 30 days as a period for current month, and the previous 30 days as the previous month.
The query that I am currently using is this:
SELECT
DATEPART(month, date) AS month,
artist,
SUM([Streams]) AS streams,
LAG(SUM([Streams])) OVER (PARTITION BY artist ORDER BY DATEPART(month, date)) AS previous_month_streams
FROM combined_artist
WHERE date > DATEADD(m, -2, DATEADD(DAY, 2 - DATEPART(WEEKDAY, GETDATE()-7), CAST(GETDATE()-7 AS DATE)))
GROUP BY DATEPART(month, date), artist;
While this works, it is not giving me the data I need. This is returning the sum of streams for February vs the Streams for the month of January. February seems very low because we only have one week worth of data in February.
My goal is to get the last 30 days from the max date in the table using a lag function. So if the max date is Feb. 7 2023, I want the current month to include data from Jan. 7 2023 - Feb. 7 2023, and the previous month to include data from Dec. 7 2022 - Jan. 7 2023. I am thinking to create a custom month date part that will start from the max date and give a month number to the last 30 days . (2 for Jan 7 - Feb 7, 1 for Dec 7 - Jan-7...) I am not sure how to go about this. This is in SQL Server and I am looking to use the lag function for performance reasons.
I think you could probably use something like datediff(d, date_you_care_about, max_date)/30 in your group by and partition by clauses.
The basic idea is that integer division rounds down, so if the difference between the dates is < 30, dividing it by 30 is 0. If the difference is >=30 but less than 60, dividing it by 30 is 1. And so forth.
You can see a proof of concept in this Fiddle.

Find number of days in a given week according to the month for a given date. in SQL

Consider the date "2022-07-02"
For the month July first week only have 3 days in it.
I need to find the number of days in the week for the given date.
In above date the week has 3 days where "2022-07-02" day reside.
Example 2 :
For month June in 2022 first week has 5 days in the week
Therefore if i declare a date as "2022-06-03" it should pass the number of days in the week as 5
I need a query to find the number of days for the specific week.
set datefirst 1 -- assumes Monday is set as start of week
declare #myDate date = getdate();
-- calculate the next last day of week
declare #nextSunday date = dateadd(day, 7 - datepart(weekday, #myDate), #myDate);
select case
-- advancing into the next month implies a partial week
-- datediff(month, #myDate, nextSunday) = 1 would be equivalent
when day(#nextSunday) < day(#myDate) then 7 - day(#nextSunday)
-- else see if still within first week
when day(#nextSunday) < 7 then day(#nextSunday)
else 7 end;
Within a query you might use it this way:
select case
when day(nextSunday) < day(dateColumn) then 7 - day(nextSunday)
when day(nextSunday) < 7 then day(nextSunday)
else 7 end
from
myData cross apply (
values (dateadd(day, 7 - datepart(weekday, dateColumn), dateColumn))
) v(nextSunday);
https://dbfiddle.uk/?rdbms=sqlserver_2019&fiddle=ee5bfb52dabe31dd619cfd136689db59
If you don't want the shorthand form then just replace every instance of nextSunday in the final step with its full expression.
There's nothing in the logic that prevents this from working with another first day of week. I just chose a variable name that helped ellucidate this particular problem.

SQL - check if an order date occurs after the second Saturday in July

I am querying against a table of 4 yrs of order transactions (pk = order number) and I'm looking to tag each record with particular date flags based on the order date - e.g., calendar year, calendar month, fiscal year, etc. There are date attributes that are specific to our business (e.g., not easily solved by a datepart function) that I'm having trouble with.
I was able to add "School Year" (for us that runs Aug 1 - July 31) using a case statement:
case
when datepart(month, oline.order_date_ready) between 8 and 12 then datepart(year, oline.order_date_ready)
else (datepart(year, oline.order_date_ready)-1)
end as school_yr
So for 1/19/2017, the above would return "2016", because to us the 2016 school year runs from Aug 1 2016 to July 31 2017.
But now I'm having trouble repeating the same kind of case statement for something called "Rollover Year". All of our order history tables are reset/"rolled over" on the 2nd Saturday in July every calendar year, so for example the most recent rollover date was Saturday July 9th 2016. Click to view - rollover year date ranges
My above case statement doesn't apply anymore because I can't just add "datepart(month, oline.order_date_ready) = 7" - I don't need the whole month of July, I just need all the orders occurring after the 2nd Saturday in that July. So in this example, I need everything occurring from Sat July 9 2016 to today to be flagged as rollover_date = 2016.
Is there a flexible way to do this without hard coding previous/future rollover dates into another table? That's the only way I can think to solve it currently, but I'm sure there must be a better way.
Thanks!
If you ask for the day-of-the-week of July 1st, then from there it's simple arithmetic, right? This query gives results matching your image:
SELECT y,
CONCAT(y, '-07-01')::timestamp +
CONCAT(6 - EXTRACT(DOW FROM CONCAT(y, '-07-01')::timestamp) + 7, ' days')::interval
FROM generate_series(2013, 2020) s(y)
ORDER BY y DESC
;
So given any date d from year y, if it comes before the 2nd Saturday of July, give it fiscal year y - 1. Otherwise give it fiscal year (school year?) y.

Select Date Between Just Day and Month Excluding Year

The following is the pseudo code for what I want to do:
When Date is Between 04-01 and 03-31 of the following year then output as Q1.
I know how to do this with the year but not excluding the year.
I have no idea what you mean by output "Q1". However, if you want your years to start on April 1st (which seems like a reasonable interpretation of what you are sking), the easiest way is to subtract a number of days. For most years you will deal with, you can do:
select year(dateadd(day, - (31 + 28 + 31), date) as theyear
Of course, this only works three years out of four, because of leap years. One way to fix this is with explicit logic -- but that gets messy. Another way is to add the remaining months and subtract one year:
select year(dateadd(day, (30 + 31 + 30 + 31 + 31 + 30 + 31 + 30 + 31), date) - 1 as theyear
It's unclear exactly what you're trying to do. Q1 usually indicates a quarter, a three-month period. A quarter running from 1 April to 31 March of the following year isn't much of a quarter :)
However, assuming you're trying to select stuff within a certain span of time starting from a particular date, you might try a little date/time arithmetic. First, a few notes:
datetime values have a nominal precision of 1 millisecond (and an actual precision of approximately 3ms). That means that something like '31 March 2014 23:59:59.999' is rounded up to '1 April 2014 00:00:00.000'. The largest time value for a given day is `23:59:59.997'. This can have...deleterious effects on your queries if you're not cognizant of it. Don't ask me how I know this.
datetime literals without a time component, such as '1 April 2013', are interpreted as start-of-day ('1 April 2014 00:00:00.000').
So, something like this:
declare
#dtFrom datetime ,
#dtThru datetime
set #dtFrom = '1 April 2013'
set #dtThru = dateAdd(year,1,dtFrom)
select *
from foo t
where t.someDateTimeValue >= #dtFrom
and t.someDateTimevalue < #dtThru
should probably do you.
You might want to adjust the setting of #dtThru to suit your requirements: if you're actually looking for the end of a quarter, you migh change it to something like
set #dtThru = dateAdd(month,3,dtFrom)
If you have a fiscal year that runs from 1 April through 31 March and want to figure out, say, what fiscal year and quarter your data represents, you might do something like this:
select FiscalYear = datepart(year,t.someDateTimeValue)
- case datepart(month,t.someDateTimeValue) / 4
when 0 then 1 -- jan/feb/mar is quarter 4 of the prev FY
else 0 -- everything else is this FY
end ,
FiscalQuarter = case datepart(month,t.someDateTimevalue) / 4
when 0 then 4 -- jan/feb/mar is Q4 of the prev FY
when 1 then 1 -- apr/may/jun is Q1 of the curr FY
when 2 then 2 -- jul/aug/sep is Q2 of the curr FY
when 3 then 3 -- oct/nov/dec is Q3 of the curr FY
end ,
*
from foo t
I think what you want is the following:
SELECT year(dateadd(q, -1, mydate)) AS yearEndingQ1
FROM mytable
This would give the year as 2014 for all dates between 04/01/2014 and 03/31/2015. Of course it's possible you want a result of 2015 instead in which case you want:
SELECT year(dateadd(q, 3, mydate)) AS yearEndingQ1
FROM mytable
Hope this helps.
UPDATE per OP's comment: "I am tracking data for a year ending Quarter x. Our fiscal year is a bit weird around here. So basically it would be fiscal year ending Q1, fiscal year ending Q2, etc. Perhaps I could have provided more clarity in my question."
This would give results in three separate columns for fiscal year ending Q1, fiscal year ending Q2, and fiscal year ending Q3. (I assume you don't need anything for fiscal year ending Q4!!)
SELECT year(dateadd(q, -1, mydate)) AS yearEndingQ1
, year(dateadd(q, -2, mydate)) AS yearEndingQ2
, year(dateadd(q, -3, mydate)) AS yearEndingQ3
FROM mytable

day of the week in the week numbering

in oracle there is a possibility to change starting day of the week.
In US first day is SUN, and in EU - MON
This change works by changing nls_territory
However if I consider day of the week and week numbering at once, I have following situation:
day week_number
7 9 <- here it should be still week 8
1 9
2 9
3 9
4 9
5 9
6 9
7 10 <- here it should still be week 9
Do you have any idea which nls should could affect this start_day of the week?
Similar post
Get First Day Of Week From Week Number
Oracle has two date formats which give us the Week Number.
'WW' starts from the first of January and increments every seven days. As 01-JAN-2013 was a Tuesday that means the Week Number increments on a Tuesday. I guess this is the version you're using at the moment.
But there is also the 'IW' format, which is the ISO standard. In this version, the week number starts from the Monday of the week where the first of January falls. This has the peculiar side-effect of making 31-DEC-2012 the first date of Week 1 on 2013, but it does mean the week number always increments on day 1 of the week.
Find out more.
Not sure I understand your question and I see no connection between day# and week# in your example, sorry... Maybe this will help you to get ISO week. This builds annual week table. You may add 'WW' format to the query and compare dates, weeks in diff. NLS settings:
-- ISO_WK# --
SELECT mydate
, TRUNC(mydate, 'w') wk_starts
, TRUNC(mydate, 'w') + 7 - 1/86400 wk_ends
, TO_NUMBER (TO_CHAR (mydate, 'IW')) ISO_wk#
FROM
(
SELECT TRUNC(SYSDATE, 'YEAR')-1 + LEVEL AS mydate
FROM dual
CONNECT BY LEVEL <=
(
SELECT TRUNC(SYSDATE, 'YEAR')-TRUNC(ADD_MONTHS (SYSDATE, -12), 'YEAR') "Num of Days"
FROM dual
)
)
/