I have a set of records which store weekly events by using a datetime... start_at from the first week they began.
It's not the REAL DATE, it's storing the DAY (DOW) and TIME, but is not the date I'm actually looking for.
I'm trying to figure out how I can convert/abstract the start_at date in a way so using that I can find all of the next events that fall within the next 24 hours.
START_AT -> 2010-09-12 16:00:00 -> CONVERT into 2013-09-08 16:00:00
but I'm not sure how to abstract the start_at for the upcoming day of the week.
I figured out how to order them but I'm not sure how to do what I want:
SELECT * FROM events ORDER BY (EXTRACT(dow FROM start_at)::int + 7 - EXTRACT(dow FROM now())::int) % 7,start_at::time
Any help would be greatly appreciated!
If you mean the next 24 hours:
SELECT *
FROM events
WHERE start_at BETWEEN now() AND now() + INTERVAL '1 day'
If you mean the next 24 hour period starting at midnight (i.e. all day tomorrow):
SELECT *
FROM events
WHERE start_at BETWEEN now()::date + INTERVAL '1 day' AND
now()::date + INTERVAL '2 days'
If I understand you correctly you may be over thinking it. If all you're looking for is a recurring datetime 24 hour before you could do something like:
SELECT * FROM events EXTRACT(dow FROM start_at) = 1
Where 1 is the DOW (for Monday)... you abstract the DOW from the date
The day of the week as Sunday(0) to Saturday(6)
and unless you very fussed about the Time, you could just search for the DOW before the day you want (~24 hours).
If I've understood you correctly, you store the date a recurring event starts on, and you need to know which recurring events happen in the next 24 hours.
This is probably easiest to handle with a join on a calendar table. The CTE "events" below takes the place of whatever your table is called. It stores events we expect to recur weekly, starting on Jan 3, Jan 4, and Jan 5. As I write this, it's Thursday.
with events as (
select date '2013-01-03' start_at
union all
select date '2013-01-04'
union all
select date '2013-01-05'
), event_days as (
select events.start_at, c.day_of_week
from events
inner join calendar c on c.cal_date = events.start_at
)
select *
from event_days
inner join calendar on event_days.day_of_week = calendar.day_of_week
where cal_date between current_date and current_date + interval '1 days';
start_at day_of_week cal_date year_of_date month_of_year day_of_month day_of_week
2013-01-03 Thu 2013-09-12 2013 9 12 Thu
2013-01-04 Fri 2013-09-13 2013 9 13 Fri
Code for a calendar table in PostgreSQL.
"The next 24 hours" is a little fuzzy if you're storing just the date.
Related
I need to pull out first date and last date of the month from the given from_date and to_date as input, For Example:-
I have my psql output table as the following:
Year
Term
Start Date
End Date
2022
Odd
01-02-2022
30-04-2022
2022
Even
01-07-2022
30-09-2022
I need the output as the following:-
Year
Term
Start Date
End Date
2022
Odd
01-02-2022
28-02-2022
2022
Odd
01-03-2022
31-03-2022
2022
Odd
01-04-2022
30-04-2022
2022
Even
01-07-2022
30-07-2022
2022
Even
01-08-2022
31-08-2022
2022
Even
01-09-2022
30-09-2022
I need the ouput in Postgresql, Pls help
Thanks
Your issue boils down to given a period with start and end dates, determine the first and last dates for each month in that period. In Postgres given a date you can determine the first (with date_trunc function) and last of the a month with the expressions:
-- for a given date
date_trunc('month', given_date) -- returns first day of month
date_trunc('month', given_date + interval '1 month' - interval '1 day') -- returns last day of month
Use the first expression above, with generate_series with dates, to create the first of each month in the period. The use the second expression to generate the end of each month. (see demo)
with range_dates (year, term, gsom) as
( select year
, term
, generate_series( date_trunc('month', od.start_date)::date
, date_trunc('month', od.end_date )::date
, interval '1 month'
)::date
from output_data od
)
select year
, term
, gsom start_date
, (gsom + interval '1 month' - interval '1 day')::date end_date
from range_dates
order by term desc, start_date;
I am currently trying to figure out the equivalent functions in PostgresSQL for the below MariaDB functions:
SELECT
CONCAT(DATE_ADD(DATE_ADD(LAST_DAY('2021-02-15 00:00:00'),INTERVAL 1 DAY),INTERVAL -1 MONTH), ' ', '00:00:00') AS first_day_of_the_month,
CONCAT(LAST_DAY('2021-02-15 23:59:59'), ' ' ,'23:59:59') AS last_day_of_the_month;
Expected Result:
first_day_of_the_month | last_day_of_the_month
------------------------|-------------------------------
2021-02-01 00:00:00 | 2021-02-28 23:59:59
I want to get the last day and the first day of the month of a certain date including the time 00:00:00 for the first day and 23:549:59 for the last day.
So far I am comming close to the expected result with these queries:
SELECT
(date_trunc('MONTH', '2021-02-15 00:00:00'::TIMESTAMP) + INTERVAL '1 MONTH - 1 day')::TIMESTAMP
+ '1 days'::INTERVAL - '1 months'::INTERVAL AS first_day_of_the_month,
(date_trunc('MONTH', '2021-02-15 23:59:59'::TIMESTAMP) + INTERVAL '1 MONTH - 1 day')::TIMESTAMP AS last_day_of_the_month;
However, as you can see in the DB-Fiddle for the last_day_of_the_month I am getting 00:00:00 instead of 23:59:59.
How do I need to change the query to get the correct timestamp?
demo:db<>fiddle
/* Option 1: Certain Date */
SELECT
date_trunc('month', timestamp '2021-02-15 00:00:00') AS first_timestamp,
date_trunc('month', timestamp '2021-02-15 23:59:59') + interval '1 month - 1 second' AS last_timestamp;
/* Option 2: Current_date */
SELECT
date_trunc('month', current_date)::timestamp AS first_timestamp,
date_trunc('month', current_date)::timestamp + interval '1 month - 1 second' AS last_timestamp;
First timestamp of month
date_trunc('month', ...) normalizes the date to the first possible timestamp of the month. So it returns 00:00:00 of the first day of the month
Last timestamp of month
If you add a month to the previous result, you get the first day/timestamp (00:00:00) of the next month. If you subtract a day from this, you get the last day of the current month. Of course, you can subtract just a second to get 23:59:59 of the last day of the month as you expected.
For the first day of the month:
date_trunc('month', the_date_column)
For the last day:
date_trunc('month', the_date_column) + interval '1 month' - interval '1 day'
Or if you prefer:
date_trunc('month', date) + interval '1 month - 1 day'
If you really one one second from midnight on the last day, replace day with second in the logic. I don't recommend that. In fact, if you want to define a range of date, define an open range and just use the first day of the next month. Then you can learn about tsrange which builds this into the language.
WITH dates AS (
SELECT `day`
FROM UNNEST(GENERATE_DATE_ARRAY('2020-11-11', CURRENT_DATE(), INTERVAL 1 DAY)) `day`
)
The above gets the dates till current day.
The below where I add +60 at the end_date gets dates till after 60 days from current date.
WITH dates AS (
SELECT `day`
FROM UNNEST(GENERATE_DATE_ARRAY('2020-11-11', CURRENT_DATE()+60, INTERVAL 1 DAY)) `day`
)
I want to count records that had set_at_date from current_date to future. For example, the number of bookings from current day till 60 days later but without getting me at the results the future dates. Just dates and counts till today like this:
date
bookings
2021-02-26
30
2021-02-25
32
2021-02-24
28
I have a table which has all the purchases of my costumers. I want to select all entries from the last week, (week start from Sunday).
id value date
5907 1.20 "2015-06-05 09:08:34-03"
5908 120.00 "2015-06-09 07:58:12-03"
I've tried this:
SELECT id, valor, created, FROM compras WHERE created >= now() - interval '1 week' and parceiro_id= '1'
But I got the data from the last week including data from this week, I only want data from the last week.
How to get data only from last week ?
This condition will return records from Sunday till Saturday last week:
WHERE created BETWEEN
NOW()::DATE-EXTRACT(DOW FROM NOW())::INTEGER-7
AND NOW()::DATE-EXTRACT(DOW from NOW())::INTEGER
There is an example:
WITH compras AS (
SELECT ( NOW() + (s::TEXT || ' day')::INTERVAL )::TIMESTAMP(0) AS created
FROM generate_series(-20, 20, 1) AS s
)
SELECT to_char( created, 'DY'::TEXT), created
FROM compras
WHERE created BETWEEN
NOW()::DATE-EXTRACT(DOW FROM NOW())::INTEGER-7
AND NOW()::DATE-EXTRACT(DOW from NOW())::INTEGER
In answer to #d456:
Wouldn't using BETWEEN include midnight on Sunday at both ends of the interval?
That right, BETWEEN includes midnight on Sunday at both ends of the interval. To exclude midnight on Sunday at end of interval it is necessary to use operators >= and <:
WITH compras AS (
SELECT s as created
FROM generate_series( -- this would produce timestamps with 20 minutes step
(now() - '20 days'::interval)::date,
(now() + '20 days'::interval)::date,
'20 minutes'::interval) AS s
)
SELECT to_char( created, 'DY'::TEXT), created
FROM compras
WHERE TRUE
AND created >= NOW()::DATE-EXTRACT(DOW FROM NOW())::INTEGER-7
AND created < NOW()::DATE-EXTRACT(DOW from NOW())::INTEGER
Postgres by default starts weeks on a Sunday, so you are in luck. You can use date_trunc() to get the beginning of the previous week:
WHERE (created >= date_trunc('week', CURRENT_TIMESTAMP - interval '1 week') and
created < date_trunc('week', CURRENT_TIMESTAMP)
)
EDIT:
Postgres by default starts week for date_trunc on Monday, but for dow on Sunday. So, you can do what you want by using that logic, which Nicolai has in his answer.
In a Postgres 9.1 database, I am trying to generate a series of weeks for a given month but with some constraints. I need all weeks to start on Monday and get cut when they start or end in another month.
Example:
For February, 2013 I want to generate a series like this:
start
------------------------
2013-02-01 00:00:00+00
2013-02-04 00:00:00+00
2013-02-11 00:00:00+00
2013-02-18 00:00:00+00
2013-02-25 00:00:00+00
The query that I have now looks like this:
SELECT GREATEST(date_trunc('week', dates.d),
date_trunc('month',dates.d)) as start
FROM generate_series(to_timestamp(1359676800),to_timestamp(1362095999), '1 week') as dates(d)
This query gets me the first 4 weeks but it's missing the week from the 25th. Is it possible to get the last week?
SELECT generate_series(date_trunc('week', date '2013-02-01' + interval '6 days')
, date_trunc('week', date '2013-02-01' + interval '1 month - 1 day')
, interval '1 week')::date AS day
UNION SELECT date '2013-02-01'
ORDER BY 1;
This variant does not need a subselect, GREATEST or GROUP BY and only generates the required rows. Simpler, faster. It's cheaper to UNION one row.
Add 6 days to the first day of the month before date_trunc('week', ...) to compute the first Monday of the month.
Add 1 month and subtract 1 day before date_trunc('week', ...) to get the last Monday of the month.
This can conveniently be stuffed into a single interval expression: '1 month - 1 day'
UNION (not UNION ALL) the first day of the month to add it unless it's already included as Monday.
Note that date + interval results in timestamp, which is the optimum here. Detailed explanation:
Generating time series between two dates in PostgreSQL
Automation
You can provide the start of the date series in a CTE:
WITH t(d) AS (SELECT date '2013-02-01') -- enter 1st of month once
SELECT generate_series(date_trunc('week', d + interval '6 days')
, date_trunc('week', d + interval '1 month - 1 day')
, interval '1 week')::date AS day
FROM t
UNION SELECT d FROM t
ORDER BY 1;
Or wrap it into a simple SQL function for convenience with repeated calls:
CREATE OR REPLACE FUNCTION f_week_starts_this_month(date)
RETURNS SETOF date AS
$func$
SELECT generate_series(date_trunc('week', $1 + interval '6 days')
, date_trunc('week', $1 + interval '1 month - 1 day')
, interval '1 week')::date AS day
UNION
SELECT $1
ORDER BY 1
$func$ LANGUAGE sql IMMUTABLE;
Call:
SELECT * FROM f_week_starts_this_month('2013-02-01');
You would pass the date for the first day of the month, but it works for any date. You the first day and all Mondays for the following month.
select
greatest(date_trunc('week', dates.d), date_trunc('month',dates.d)) as start
from generate_series('2013-02-01'::date, '2013-02-28', '1 day') as dates(d)
group by 1
order by 1