Finding the next occurrence of particular day of the week - postgresql

How can I take a datetime column start_at convert it to a day of week and find out the next future occurrence relative to the current date?
Here I'm trying to add the DOW to the current week but it doesn't appear to be correct.
SELECT date_trunc('week', current_date) + CAST(extract(dow from start_at) || ' days' AS interval)
Full example:
SELECT id event_id,
GENERATE_SERIES(date_trunc('week', current_date) + CAST(extract(dow from start_at) + 1 || ' days' AS interval) + start_at::time, current_date + interval '3 weeks', '1 week'::INTERVAL) AS start_at
FROM events
Events
+-----+---------------------------+---------------------+
| id | start_at | recurring_schedule |
+-----+---------------------------+---------------------+
| 358 | 2015-01-23 20:00:00 +0000 | Weekly |
| 359 | 2016-01-22 19:30:00 +1100 | Monthly |
| 360 | 2016-02-01 19:00:00 +1100 | Weekly |
| 361 | 2016-02-01 20:00:00 +0000 | Weekly |
| 362 | 2014-02-13 20:00:00 +0000 | Bi-Weekly |
+-----+---------------------------+---------------------+
Output
+----------+---------------------------+
| event_id | start_at |
+----------+---------------------------+
| 35 | 2018-04-11 19:30:00 +0000 |
| 94 | 2018-04-12 20:00:00 +0100 |
| 269 | 2018-04-13 18:30:00 +0100 |
| 45 | 2018-04-13 20:00:00 +0100 |
| 242 | 2018-04-13 19:30:00 +1100 |
| 35 | 2018-04-18 19:30:00 +0000 |
| 94 | 2018-04-19 20:00:00 +0100 |
| 269 | 2018-04-20 18:30:00 +0100 |
| 45 | 2018-04-20 20:00:00 +0100 |
| 242 | 2018-04-20 19:30:00 +1100 |
+----------+---------------------------+

Give this a try:
SELECT id event_id,
GENERATE_SERIES(date_trunc('week', current_date)::date
+ (extract(isodow from start_at)::int - 1) + start_at::time, current_date
+ interval '3 weeks', '1 week'::INTERVAL) AS start_at
FROM events

Related

Unable to Calculate 7 Day Moving Average due to inconsistent dates

I just noticed that my code below is not actually a 7 day moving avg, and instead it is a 7 row moving avg. The dates in my table spans several months and I am trying to iron out since I have inconsistent data flow so I can't expect the last 7 rows of the window function to actually represent a 7 day avg. Thanks.
select date, sales,
avg(sales) over(order by date rows between 6 preceding and current row)
from sales_info
order by date
You can get a bit closer to a true 7 day moving average by using RANGE instead of ROWS for your range specification.
Read more about window function frames here.
I believe this should work for you:
select date, sales,
avg(sales) over(order by date range between '6 days' preceding and current row)
from sales_info
order by date;
Here's a demonstration with made up data:
SELECT i,
t,
avg(i) OVER (ORDER BY t RANGE BETWEEN '6 days' preceding and current row) FROM (
SELECT i, t
FROM generate_series('2021-01-01'::timestamp, '2021-02-01'::timestamp, '1 day') WITH ORDINALITY as g(t, i)
) sub;
i | t | avg
----+---------------------+------------------------
1 | 2021-01-01 00:00:00 | 1.00000000000000000000
2 | 2021-01-02 00:00:00 | 1.5000000000000000
3 | 2021-01-03 00:00:00 | 2.0000000000000000
4 | 2021-01-04 00:00:00 | 2.5000000000000000
5 | 2021-01-05 00:00:00 | 3.0000000000000000
6 | 2021-01-06 00:00:00 | 3.5000000000000000
7 | 2021-01-07 00:00:00 | 4.0000000000000000
8 | 2021-01-08 00:00:00 | 5.0000000000000000
9 | 2021-01-09 00:00:00 | 6.0000000000000000
10 | 2021-01-10 00:00:00 | 7.0000000000000000
11 | 2021-01-11 00:00:00 | 8.0000000000000000
12 | 2021-01-12 00:00:00 | 9.0000000000000000
13 | 2021-01-13 00:00:00 | 10.0000000000000000
14 | 2021-01-14 00:00:00 | 11.0000000000000000
15 | 2021-01-15 00:00:00 | 12.0000000000000000
16 | 2021-01-16 00:00:00 | 13.0000000000000000
17 | 2021-01-17 00:00:00 | 14.0000000000000000
18 | 2021-01-18 00:00:00 | 15.0000000000000000
19 | 2021-01-19 00:00:00 | 16.0000000000000000
20 | 2021-01-20 00:00:00 | 17.0000000000000000
21 | 2021-01-21 00:00:00 | 18.0000000000000000
22 | 2021-01-22 00:00:00 | 19.0000000000000000
23 | 2021-01-23 00:00:00 | 20.0000000000000000
24 | 2021-01-24 00:00:00 | 21.0000000000000000
25 | 2021-01-25 00:00:00 | 22.0000000000000000
26 | 2021-01-26 00:00:00 | 23.0000000000000000
27 | 2021-01-27 00:00:00 | 24.0000000000000000
28 | 2021-01-28 00:00:00 | 25.0000000000000000
29 | 2021-01-29 00:00:00 | 26.0000000000000000
30 | 2021-01-30 00:00:00 | 27.0000000000000000
31 | 2021-01-31 00:00:00 | 28.0000000000000000
32 | 2021-02-01 00:00:00 | 29.0000000000000000

Query to verify number 24 hours before the calling

I have a table [POSTGRE] like this...
- Agent who answered the call,
- call number
- datetime end of call.
Agent | Caller | DateTime
------------------------------------
101 | 555-1234 | 12-01-16 00:00
101 | 555-1234 | 12-01-16 01:00
102 | 555-1234 | 12-01-16 02:00
102 | 555-1234 | 13-01-16 06:00
1º - I need to verify each number (each line).. and look 24 hours before call, to check if is Recalling, eg.: day 13th was not recalling because the last call from this number was not in 24 hours.
2º - Need to get the agent from recalling.
need to display like this...
Agent | Caller | DateTime | Recalling | Recalling-From
----------------------------------------------------------------------
101 | 555-1234 | 12-01-16 00:00 | NO |
101 | 555-1234 | 12-01-16 01:00 | YES | 101
102 | 555-1234 | 12-01-16 02:00 | YES | 101
102 | 555-1234 | 13-01-16 06:00 | NO |
my query is ...
SELECT
to_char(datahora_entrada_fila, 'DD/MM/YYYY') as dia,
calleridnum As numero,
MAX(datahora_inicio) AS data_ini
FROM callcenter.chamada_fila_in cfin
WHERE datahora_inicio BETWEEN '2016-12-01 00:00:00' AND '2016-12-01 23:59:59'
AND status_chamada = 'Finalizada'
GROUP BY dia,numero
)
SELECT
to_char(datahora_entrada_fila, 'DD/MM/YYYY') as dia,
(SELECT count(calleridnum) FROM callcenter.chamada_fila_in f INNER JOIN maiordata md ON f.calleridnum = md.numero WHERE calleridnum = cfin.calleridnum AND datahora_inicio BETWEEN md.data_ini - interval '24 hours' AND md.data_ini ) AS QtdRechamada,
calleridnum As numero
FROM callcenter.chamada_fila_in cfin
WHERE datahora_inicio BETWEEN '2016-12-01 00:00:00' AND '2016-12-01 23:59:59'
AND status_chamada = 'Finalizada'
GROUP BY dia,numero
ORDER BY
dia,numero DESC
need a better function or method..
this query is very heavy in my database, need To be optimized.

Calculating the forecasts by month for the last 3 months in postgres

I have a table called forecasts where we store the forecasts for all the products for the next 6 months. For example when we are in November we create the forecast for December, January, February, March, April and May. The forecasts table looks something like the one below
+----------------+---------------+--------------+----------+
| product_number | forecasted_on | forecast_for | quantity |
+----------------+---------------+--------------+----------+
| Prod 1 | 2016-11-01 | 2016-12-01 | 100 |
| Prod 1 | 2016-11-01 | 2017-01-01 | 200 |
| Prod 1 | 2016-11-01 | 2017-02-01 | 300 |
| Prod 1 | 2016-11-01 | 2017-03-01 | 400 |
| Prod 1 | 2016-11-01 | 2017-04-01 | 500 |
| Prod 1 | 2016-11-01 | 2017-05-01 | 600 |
+----------------+---------------+--------------+----------+
Where the table contains a list of product numbers and the date on which the forecast was created i.e. forecasted_on and a month for which the forecast was created for along with the forecasted quantity.
Each month data gets added for the next 6 months. So when the forecasted_on is 1-December-2016 forecasts will be created for January till June.
I am trying to create a report that shows how the total forecasts have varied for the last 3 months. Something like this
+------------+----------------+---------------+----------------+
| | 0 months prior | 1 month prior | 2 months prior |
+------------+----------------+---------------+----------------+
| 2016-12-01 | 200 | 150 | 250 |
| 2017-01-01 | 300 | 250 | 150 |
| 2017-02-01 | 100 | 150 | 100 |
+------------+----------------+---------------+----------------+
Currently I am using a lot of repetitive code in rails to generate this table. I wanted to see if there was an easier way to do it directly using a SQL query.
Any help would be greatly appreciated.
Use PIVOT query:
select forecast_for,
sum( case when forecasted_on + interval '1' month = forecast_for
then quantity end ) q_0,
sum( case when forecasted_on + interval '2' month = forecast_for
then quantity end ) q_1,
sum( case when forecasted_on + interval '3' month = forecast_for
then quantity end ) q_2,
sum( case when forecasted_on + interval '4' month = forecast_for
then quantity end ) q_3,
sum( case when forecasted_on + interval '5' month = forecast_for
then quantity end ) q_4,
sum( case when forecasted_on + interval '6' month = forecast_for
then quantity end ) q_5
from Table1
group by forecast_for
order by 1
;
Demo: http://sqlfiddle.com/#!15/30e5e/1
| forecast_for | q_0 | q_1 | q_2 | q_3 | q_4 | q_5 |
|----------------------------|--------|--------|--------|--------|--------|--------|
| December, 01 2016 00:00:00 | 100 | (null) | (null) | (null) | (null) | (null) |
| January, 01 2017 00:00:00 | (null) | 200 | (null) | (null) | (null) | (null) |
| February, 01 2017 00:00:00 | (null) | (null) | 300 | (null) | (null) | (null) |
| March, 01 2017 00:00:00 | (null) | (null) | (null) | 400 | (null) | (null) |
| April, 01 2017 00:00:00 | (null) | (null) | (null) | (null) | 500 | (null) |
| May, 01 2017 00:00:00 | (null) | (null) | (null) | (null) | (null) | 600 |
Assuming that (product_number, forcast_on, forcasted_for) is unique (so no aggregation is required), then this should do the job:
WITH forecast_dates AS (
SELECT DISTINCT product_number, forcast_for
FROM forecasts
)
SELECT
fd.forcast_for AS "forecast for",
m1.quantity AS "one month prior",
m2.quantity AS "two months prior",
m3.quantity AS "three months prior"
FROM forecast_dates fd
LEFT JOIN forecasts m1 ON fd.forcast_for = m1.forcast_for AND fd.forcast_for = m1.forcasted_on + INTERVAL '1 month'
LEFT JOIN forecasts m2 ON fd.forcast_for = m2.forcast_for AND fd.forcast_for = m2.forcasted_on + INTERVAL '2 month'
LEFT JOIN forecasts m3 ON fd.forcast_for = m3.forcast_for AND fd.forcast_for = m3.forcasted_on + INTERVAL '3 month'
WHERE fd.product_number = 'Prod 1'
ORDER BY fd.forcast_for;

Symfony2 Query to find last working date from Holiday Calender

I had a calender entity in my project which manages the open and close time of business day of the whole year.
Below is the record of a specific month
id | today_date | year | month_of_year | day_of_month | is_business_day
-------+---------------------+------+---------------+-------------+---------------+
10103 | 2016-02-01 00:00:00 | 2016 | 2 | 1 | t
10104 | 2016-02-02 00:00:00 | 2016 | 2 | 2 | t
10105 | 2016-02-03 00:00:00 | 2016 | 2 | 3 | t
10106 | 2016-02-04 00:00:00 | 2016 | 2 | 4 | t
10107 | 2016-02-05 00:00:00 | 2016 | 2 | 5 | t
10108 | 2016-02-06 00:00:00 | 2016 | 2 | 6 | f
10109 | 2016-02-07 00:00:00 | 2016 | 2 | 7 | f
10110 | 2016-02-08 00:00:00 | 2016 | 2 | 8 | t
10111 | 2016-02-09 00:00:00 | 2016 | 2 | 9 | t
10112 | 2016-02-10 00:00:00 | 2016 | 2 | 10 | t
10113 | 2016-02-11 00:00:00 | 2016 | 2 | 11 | t
10114 | 2016-02-12 00:00:00 | 2016 | 2 | 12 | t
10115 | 2016-02-13 00:00:00 | 2016 | 2 | 13 | f
10116 | 2016-02-14 00:00:00 | 2016 | 2 | 14 | f
10117 | 2016-02-15 00:00:00 | 2016 | 2 | 15 | t
10118 | 2016-02-16 00:00:00 | 2016 | 2 | 16 | t
10119 | 2016-02-17 00:00:00 | 2016 | 2 | 17 | t
10120 | 2016-02-18 00:00:00 | 2016 | 2 | 18 | t
I want the get the today_date of last 7 working date. Supporse today_date is 2016-02-18 and date of last 7 working dates as 2016-02-09.
You can use row_number() for this like this:
SELECT * FROM
(SELECT t.*,row_number() OVER(order by today_date desc) as rnk
FROM Calender t
WHERE today_date <= current_date
AND is_business_day = 't')
WHERE rnk = 7
This will give you the row of the 7th business day from todays date
I see that you tagged your question with Doctrine, ORM and Datetime. Were you after a QueryBuilder solution? Maybe this is closer to what you want:
$qb->select('c.today_date')
->from(Calendar::class, 'c')
->where("c.today_date <= :today")
->andWhere("c.is_business_day = 't'")
->setMaxResults(7)
->orderBy("c.today_date", "DESC")
->setParameter('today', new \DateTime('now'), \Doctrine\DBAL\Types\Type::DATETIME));

postgresql copy of rows with index tag

Is there any way to have sql query to return split rows. I do not even know how to ask.
Have 'index_tag' below.
select event.name, tb_ev.start_time, tb_ev.end_time
from block as tb_ev,event
where location_id in ( 28522 )
and tb_ev.event_id=event.id;
name | start_time | end_time
---------------+------------+----------
Register #1 | 06:00:00 | 09:00:00
Register #2 | 11:00:00 | 12:00:00
Register #3 | 11:00:00 | 14:00:00
from 3 rows to 7 rows
name | start_time | end_time | index_tag
---------------+------------+--------------------
Register #1 | 06:00:00 | 09:00:00 | 06:00:00
Register #1 | 06:00:00 | 09:00:00 | 07:00:00
Register #1 | 06:00:00 | 09:00:00 | 08:00:00
Register #2 | 11:00:00 | 12:00:00 | 11:00:00
Register #3 | 11:00:00 | 14:00:00 | 11:00:00
Register #3 | 11:00:00 | 14:00:00 | 12:00:00
Register #3 | 11:00:00 | 14:00:00 | 13:00:00
Try this:
SELECT *, start_time +
generate_series(0,extract(hour from (end_time - start_time))::int -1)
* interval '1 hour' as index_tag
FROM block
It is a bit ugly, but it works (generate_series does not work with time. Only int or timestamp).
Here is the SQLFiddle
Details about generate_series