Calculate the end day of a week with DATE_TRUNC in PostgreSQL - sql

Is there a way to change the showing date to the end of each week instead of the beginning of the week.
Here's my code:
SELECT date_trunc('week', day + '1 day'::interval)::date - '1 day'::interval AS anchor, AVG(value) AS average
FROM daily_metrics
WHERE metric = 'daily-active-users'
GROUP BY anchor
ORDER BY anchor
And the result is as below:
What I want to achieve is to make it 2018-03-03 (the end of the self defined week) instead of 2018-02-25 (the beginning of the self defined week), 2018-03-10 instead of 2018-03-04...

Your trick with shifting back and forth by one day works just fine to get the start of your custom week. You get the end or your custom week (Saturday) by adding 5 instead of subtracting 1:
SELECT date_trunc('week', day + interval '1 day')::date + 5 AS anchor ...
Adding an integer to the date, signifying days.
Related:
PostgreSQL custom week number - first week containing Feb 1st

Simply try
SELECT date_trunc('week', day::DATE + 1)::date + 5 AS anchor, AVG(value) AS average
FROM daily_metrics
WHERE metric = 'daily-active-users'
GROUP BY anchor
ORDER BY anchor
When a date is the start date of a week adding 6 (1 + 5) days will move the date to the last date of the week. The the addition of one is to move sundays to the following week and the 5 to get the end of the week from the start date.
Note, PostgreSQL allows the addition of integers (= days) to dates.

Related

Get the end date for an ISO week number in Google Data studio/Looker Studio

I google data studio, I have a field called week that contains the week number the value is string 'Week 2' for example. How can I extract the last day of the week based on the week number. In this case I want to get 2023-01-14 which is the last day of week 2?
enter image description here
Have you tried the last_day function?
I would build from the using the date using last_day, because from the week number it would require a bit more code.
SELECT *,
LAST_DAY(today, WEEK) AS last_day_of_week,
EXTRACT(WEEK FROM today) AS week_number
FROM (SELECT CURRENT_DATE() AS today)
But if you need to go with just the week number, I recommend taking a look into this other question How to create date based on year, week number and day in bigquery .
The following code works:
DATEtime_ADD(DATEtime_TRUNC(DATEtime_ADD(DATEtime_TRUNC(date, WEEK), INTERVAL (week-1) * 1 DAY), WEEK), INTERVAL 6 DAY)

Is there a way to add in column for Week number depending on a date column?

I was wondering if it is possible in SQL to read in a date column and based on that date create a new column and automatically have the Week number as well. For example today is 4/7/2020 , so the query would have Week 15 populated for that?
]1
In the picture the week column would ideally be populated beside 'datestr'.
Thank you]2
In redshift, you can use date_part() with the w specifier to get the week number of a date or timestamp:
select t.*, date_part(w, datestr) week_number from mytable t
Note that weeks starts on Monday in Redshift. If you want the week to start on Sunday:
select t.*, date_part(w, datestr + interval '1' day) week_number from mytable t
You could use extract. I am not 100% sure if weeks in Redshift start from Sunday or Monday, but you can adjust the interval to test the edge cases.
select datestr, extract(week from datestr + interval '1 day') as weeknum
from your_table

SQLite query for n-th day of month

Say, I have the following table:
CREATE TABLE Data (
Id INTEGER PRIMARY KEY,
Value DECIMAL,
Date DATE);
Since the application is finance-related, user may choose, which day would be the first day of the month. For instance, if he receives salary every 10th of the month, he may set the first day of the month to be 10th.
I'd like to create a query, which returns average value for n-th day of month, as defined by user. For instance:
Date | Value
---------------+------
10.01.2016 | 10
11.01.2016 | 15
10.02.2016 | 20
11.03.2016 | 10
Result of the query should be:
Day | Average
----+--------
1 | 15
2 | 12.5
Note, that if user sets first day to 10th, 9th of the month may be 28th, 29th, 30th or 31st day of a month (depending on which month we're talking about). So this is not as simple as extracting day number from the date.
Assuming that the date values do not use the format dd.mm.yyyy but one of the supported date formats, you can use the built-in date functions to compute this.
To compute the difference, in days, between two dates, convert them into a date format that uses days as a number, i.e., Julian days.
To get the 'base' day for a month, we can use modifiers:
> SELECT julianday('2001-02-11') -
julianday('2001-02-11', 'start of month', '+10 days') + 2;
2.0
(The +2 is needed because we add to the 1st of the month, not the 0th, and we count beginning at 1, not 0.)
If the day is before the tenth, the computed value would become zero or negative, and we have to use the previous month instead:
> SELECT julianday('2001-02-09') -
julianday('2001-02-09', 'start of month', '-1 month', '+10 days') + 2;
31.0
Combining these results in this expression to compute the n for a date Date:
CASE
WHEN julianday(Date) -
julianday(Date, 'start of month', '+10 days') + 2 > 0
THEN julianday(Date) -
julianday(Date, 'start of month', '+10 days') + 2
ELSE julianday(Date) -
julianday(Date, 'start of month', '-1 month', '+10 days') + 2
END
You can the use this in your query:
SELECT CASE...END AS Day,
AVG(Value) AS Average
FROM Data
GROUP BY Day;
Seems like I found in parallel a different solution:
SELECT avg(Amount),
strftime("%d", Date, "-9 days") AS day
FROM (
SELECT sum(Amount) AS Amount,
strftime("%Y-%m-%d", Date, "start of day") AS Date
FROM Operations
GROUP BY strftime("%Y-%m-%d", Date, "start of day")
)
GROUP BY day;
Where "-9 days" is for 10th of the month (so first_day-1).

SQL in postgres convert datetime for recurring event to future datetime

I'm keep track of recurring weekly events in a table using just a DATETIME. I only care about the TIME and the day of the week it falls on.
I need to be able to convert the set DATETIME into the current or upcoming future one.
IE How can I convert a date stored as 2013-02-22 12:00:00 using the current date to the next occurrence? Ie this next Friday at 12:00:00 or 2013-03-01 12:00:00 so that I can then order events by date?
Or I could store the TIME and day of the week separately as a number 0-6.
UPDATE:
From Erwin I got something like:
Event.order("date_trunc('week', now()::timestamp) + (start_at - date_trunc('week', start_at))")
Which seems order them except that the first dates I get are Monday skipping over events I know exist for Sunday which it puts as last.
Your best choice is to store a timestamp or timestamptz (timestamop with time zone). If you have or ever will have to deal with more than one time zone, make that timestamptz and define whether you want to operate with local time or UTC or whatever. More details in this related answer:
Ignoring timezones altogether in Rails and PostgreSQL
Demo how to transpose a timestamp into the current week efficiently (same day of week and time). Assuming timestamp here:
SELECT date_trunc('week', now()::timestamp) + (t - date_trunc('week', t))
FROM (SELECT '2013-02-15 12:00:00'::timestamp AS t) x;
The trick is to compute the interval between the start of the corresponding week and the given timestamp and add that to the start of the current week with the help of date_trunc().
The ISO week starts with Monday, putting Sunday last.
Or, to just add a week to a given timestamp:
SELECT t + interval '1 week';
If You just want to ORDER BY, you only need the interval:
ORDER BY (t - date_trunc('week', t))
If you want to put Sunday first (shifting days):
ORDER BY ((t + interval '1d') - date_trunc('week', (t + interval '1d'))
Or simpler:
ORDER BY EXTRACT(dow FROM t), t::time
Quoting the manual on EXTRACT():
dow
The day of the week as Sunday(0) to Saturday(6)
isodow
The day of the week as Monday(1) to Sunday(7)
Answer to question in comment
I'm only interested in ordering them relative to the current date. Ie
if it's tuesday, I want tuesday first, monday last.
Wrapping at midnight of "today":
ORDER BY (EXTRACT(dow FROM t)::int + 7 - EXTRACT(dow FROM now())::int) % 7
,t::time
Using the modulo operator % to shift the day according to "today".
Using dowinstead of isodow, because starting with 0 makes % simpler.
Keep using the datetime. It's simple and gives you flexibility. You can use the extract function to get your time of day and day of week results. This page will help you. http://www.postgresql.org/docs/9.3/static/functions-datetime.html

Group SQL results by week and specify "week-ending" day

I'm trying to select data grouped by week, which I have working, but I need to be able to specify a different day as the last day of the week. I think something needs to go near INTERVAL (6-weekday('datetime')) but not sure. This kind of SQL is above my pay-grade ($0) :P
SELECT
sum(`value`) AS `sum`,
DATE(adddate(`datetime`, INTERVAL (6-weekday(`datetime`)) DAY)) AS `dt`
FROM `values`
WHERE id = '123' AND DATETIME BETWEEN '2010-04-22' AND '2010-10-22'
GROUP BY `dt`
ORDER BY `datetime`
Thanks!
select
sum(value) as sum,
CASE WHEN (weekday(datetime)<=3) THEN date(datetime + INTERVAL (3-weekday(datetime)) DAY)
ELSE date(datetime + INTERVAL (3+7-weekday(datetime)) DAY)
END as dt
FROM values
WHERE id = '123' and DATETIME between '2010-04-22' AND '2010-10-22'
GROUP BY dt
ORDER BY datetime
This does look pretty evil but, this query will provide you with a sum of value grouped by a week ending on a Thursday (weekday() return of 3).
If you wish to change what day the end of the week is you just need to replace the 3's in the case statement, ie if you wanted Tuesday you would have it say
CASE WHEN (weekday(datetime)<=1) THEN date(datetime + INTERVAL (1-weekday(datetime)) DAY)
ELSE date(datetime + INTERVAL (1+7-weekday(datetime)) DAY)
I hope this helps.
Simple solution that I like. This will return the date for the start of the week assuming the week ends Sunday and starts Monday.
DATE(`datetime`) - INTERVAL WEEKDAY(`datetime`) AS `dt`
This can easily be adjusted to have a week ending on Thursday because Thursday is 3 days earlier than Sunday
DATE(`datetime`) - INTERVAL WEEKDAY(`datetime` + INTERVAL 3 DAY) AS `dt`
this returns for the start of the week that starts on Friday and ends on Thursday.
You can group on this no problem. If you want to use get the end of the week based on the start you do this
DATE(`datetime`) - INTERVAL -6 + WEEKDAY(`datetime` + INTERVAL 3 DAY) AS `dt`
I think you must choose between Sunday and Monday? When you can use DATE_FORMAT for grouping by string format of date, and use %v for grouping by Mondays and %v for grouping by Sundays.
SELECT
sum(`value`) AS `sum`,
DATE_FORMAT(`datetime`,'%v.%m.%Y') AS `dt`
FROM `values`
WHERE id = '123' AND DATETIME BETWEEN '2010-04-22' AND '2010-10-22'
GROUP BY DATE_FORMAT(`datetime`,'%v.%m.%Y')
ORDER BY `datetime`
How to use DATE_FORMAT
I don't remember the exact math, but you can get WEEKDAY to wrap around on different days of the week by adding or subtracting days to its argument. You'll need to tinker with different values of x and y in the expression:
x-weekday(adddate(`datetime`, INTERVAL y DAY))