how to get day difference in postgres - postgresql

I want to find out how many days are left until "End_date" is reached in postgres. What will be equivalent for following in postgres?
Days_Left = Column in table - Today's date
GREATEST(INT4(CEIL(("NUMERIC"(DATE_PART('EPOCH'::"VARCHAR", (T1.End_date - "TIMESTAMP"(DATE('now'::"VARCHAR"))))) / '86400'::"NUMERIC"))), 0) AS DAYS_LEFT
--Thanks I tried your suggestion but did not get expected result.
Expected Result -- if use GREATEST(INT4(CEIL(("NUMERIC"(DATE_PART('EPOCH'::"VARCHAR", (CA.END_DATE - "TIMESTAMP"(DATE('now'::"VARCHAR"))))) / '86400'::"NUMERIC"))), 0)
End_date Days_left
2014-11-01 03:59:00 47
2016-01-01 04:59:59 473
2017-01-01 06:59:59 839
2014-12-31 22:59:00 107
Result - date(end_date) - date(current_date)
End_date Days_Left
2014-11-01 03:59:00 46
2016-01-01 04:59:59 472
2017-01-01 06:59:59 838
2014-12-31 22:59:00 106
Result - if use (end_date - current_date)
End_date Days_Left
2014-11-01 03:59:00 46 days 03:59
2016-01-01 04:59:59 472 days 04:59:59
2017-01-01 06:59:59 838 days 06:59:59
2014-12-31 22:59:00 106 days 22:59
Thanks
Sandy

If column_in_table is defined as a DATE you can use this:
select column_in_table - current_date as days_left
from the_table
Edit
As end_date is a timestamp the above expression will return an interval not an integer.
If you don't care about the hours and minutes left, casting the timestamp to a date should work:
select end_date::date - current_date as days_left
from the_table

Related

How to split and aggregate days into different month

db fiddle
run select *, return_date - pickup_date as total from order_history order by id; return the following result:
id pickup_date return_date date_ranges total
1 2020-03-01 2020-03-12 [2020-03-01,2020-04-01) 11
2 2020-03-01 2020-03-22 [2020-03-01,2020-04-01) 21
3 2020-03-11 2020-03-22 [2020-03-01,2020-04-01) 11
4 2020-02-11 2020-03-22 [2020-02-01,2020-03-01) 40
5 2020-01-01 2020-01-22 [2020-01-01,2020-02-01) 21
6 2020-01-01 2020-04-22 [2020-01-01,2020-02-01) 112
for example:
--id=6. total = 112. 112 = 22+ 31 + 29 + 30
--therefore toal should split: jan2020: 30, feb2020:29, march2020: 31, 2020apr:22.
first split then aggregate. aggregate based over range min(pickup_date), max(return_date) then tochar cast to 'YYYY-MM'; In this case the aggregate should group by 2020-01, 2020-02, 2020-03,2020-04.
but if pickup_date in the same month with return_date then compuate return_date - pickup_date then aggregate/sum the result, group by to_char(pickup_date,'YYYY-MM')
step-by-step demo: db<>fiddle
Not quite perfect, but a sketch:
SELECT
id,
ARRAY_AGG( -- 4
LEAST(return_date, gs + interval '1 month - 1 day') -- 2
- GREATEST(pickup_date, gs) -- 3
+ interval '1 day'
)
FROM order_history,
generate_series( -- 1
date_trunc('month', pickup_date),
date_trunc('month', return_date),
interval '1 month'
) gs
GROUP BY id
Generate a set of months that are included in the given date range
a) Calculate the last day of the month (first of a month + 1 month is first of the next month; minus 1 day is last of the current month). This is the max day for returning in this month. b) if it happened earlier, then take the earler day (LEAST())
Same for pickup day. Afterwards calculate the difference of the days kept in one month.
Aggregate the values for one month.
Open questions / Potential enhancements:
You said:
jan2020: 30, feb2020:29, march2020: 31, 2020apr:22.
Why is JAN given with 30 days? On the other hand you count APR 22 days (1st - 22nd). Following the logic, JAN should be 31, shouldn't it?
If you don't want to count the very first day, then you can change (3.) to
GREATEST(pickup_date + interval '1 day', gs)
There's a problem with day saving time in March (30 days, 23 hours instead of 31 days). This can be faced by some rounding, for example.

How to group rows without using GROUP BY clause

Let's say I have simple table:
Date Price
-----------------------
2012-01-05 23
2015-04-08 145
2016-03-09 12
2015-09-09 87
2000-01-15 23
2016-01-15 89
2016-07-12 23
2012-04-08 65
I want to group this rows by year but without using GROUP BY clause. It would be good if I could add another column that would contain year or character that would indicate group, like this:
Date Price Group
-------------------------------
2012-01-05 23 1
2015-04-08 145 2
2016-03-09 12 3
2015-09-09 87 2
2000-01-15 23 4
2016-01-15 89 3
2016-07-12 23 3
2012-04-08 65 1
I tried use over() clause but to be honest I don't know which function use with over().
Combination of extract year from date and dense_rank will do the trick
select *,
dense_rank () OVER(order by extract(year from Date))
from YOURTABLE
Try to do the CASE if you only want to add another column
SELECT DATE,
PRICE,
CASE DATE_PART('YEAR', DATE) WHEN 2015 THEN 1
WHEN 2016 THEN 2 ... END
FROM MYTABLE
But if you want to get the aggregate of something then you do OVER() or GROUP BY

PostgreSQL-10.5 union to have single record (combine null values)

I am using PostgreSQL-10.5. I have two tables to generate a report from. I need to do some calculation using ilkokuma (first reading) and sonokuma (last reading) column.
I could do calculations up to a point. I will be adding some more. I could not see how I can join same table in single SQL and tried to use two use union. Below is my current SQL which I try to combine both calculations.
SELECT
faturadata.*,
(SELECT DATE_PART('days', DATE_TRUNC('month', ilkokuma) + '1 MONTH'::INTERVAL - '1 DAY'::INTERVAL)) as ilkokuma_gun_sayisi,
sabityanma.deger as ilkokuma_deger,
(SELECT DATE_PART('days', DATE_TRUNC('month', ilkokuma) + '1 MONTH'::INTERVAL - '1 DAY'::INTERVAL)) * sabityanma.deger as ilkokuma_carpim,
null as sonokuma_gun_sayisi,
null as sonokuma_deger,
null as sonokuma_carpim
FROM
faturadata
left join sabityanma on sabityanma.yil = date_part('year', ilkokuma) and sabityanma.ay = date_part('month', ilkokuma)
union
SELECT
faturadata.*,
null as ilkokuma_gun_sayisi,
null as ilkokuma_deger,
null as ilkokuma_carpim,
(SELECT DATE_PART('days', DATE_TRUNC('month', sonokuma) + '1 MONTH'::INTERVAL - '1 DAY'::INTERVAL)) as sonokuma_gun_sayisi,
sabityanma.deger as sonokuma_deger,
(SELECT DATE_PART('days', DATE_TRUNC('month', sonokuma) + '1 MONTH'::INTERVAL - '1 DAY'::INTERVAL)) * sabityanma.deger as sonokuma_carpim
FROM
faturadata
left join sabityanma on sabityanma.yil = date_part('year', sonokuma) and sabityanma.ay = date_part('month', sonokuma)
And result is
id tesisat trf kwh tahakkuk ilkokuma sonokuma ilkokuma_gun_sayisi ilkokuma_deger ilkokuma_carpim sonokuma_gun_sayisi sonokuma_deger sonokuma_carpim
2023772 4024546 400 1765 2016-08-31 2016-07-31 2016-08-31 31 9 279
2023772 4024546 400 1765 2016-08-31 2016-07-31 2016-08-31 31 11 341
2023773 4024545 400 3876 2016-08-31 2016-07-31 2016-08-31 31 9 279
2023773 4024545 400 3876 2016-08-31 2016-07-31 2016-08-31 31 11 341
2023774 4024543 400 2126 2016-08-31 2016-07-31 2016-08-31 31 9 279
2023774 4024543 400 2126 2016-08-31 2016-07-31 2016-08-31 31 11 341
As it can be seen that SQL is duplicating records. What I need to do is to have single record for both calculations.
I could not figure how I can do that.
Please shorten your example next time a little bit :)
My I removed some columns in my test table
id tahakkuk ilkokuma sonokuma ilko_gun_say ilko_deger sono_gun_say sono_gun_deger
2023772 2016-08-31 2016-07-31 2016-08-31 31 9
2023772 2016-08-31 2016-07-31 2016-08-31 31 11
2023773 2016-08-31 2016-07-31 2016-08-31 31 9
2023773 2016-08-31 2016-07-31 2016-08-31 31 11
You can do a simple group to aggregate your rows:
SELECT
id, tahakkuk, ilkokuma, sonokuma,
MAX(ilko_gun_say) as ilko_gun_say,
MAX(ilko_deger) as ilko_deger,
MAX(sono_gun_say) as sono_gun_say,
MAX(sono_gun_deger) as sono_gun_deger
FROM
<YOUR QUERY HERE>
GROUP BY
id, tahakkuk, ilkokuma, sonokuma
demo:db<>fiddle
I didn't thought about a general optimization of your query because I get confused by your language. But there is a lot of code repetition. I guess there's a lot to optimize - maybe with CTEs (https://www.postgresql.org/docs/current/static/queries-with.html)?

PostgreSQL - filter function for dates

I am trying to use the built-in filter function in PostgreSQL to filter for a date range in order to sum only entries falling within this time-frame.
I cannot understand why the filter isn't being applied.
I am trying to filter for all product transactions that have a created_at date of the previous month (so in this case that were created in June 2017).
SELECT pt.created_at::date, pt.customer_id,
sum(pt.amount/100::double precision) filter (where (date_part('month', pt.created_at) =date_part('month', NOW() - interval '1 month') and
date_part('year', pt.created_at) = date_part('year', NOW()) ))
from
product_transactions pt
LEFT JOIN customers c
ON c.id= pt.customer_id
GROUP BY pt.created_at::date,pt.customer_id
Please find my expected results (sum of the amount for each day in the previous month - for each customer_id if an entry for that day exists) and the actual results I get from the query - below (using date_trunc).
Expected results:
created_at| customer_id | amount
2017-06-30 1 220.5
2017-06-28 15 34.8
2017-06-28 12 157
2017-06-28 48 105.6
2017-06-27 332 425.8
2017-06-25 1 58.0
2017-06-25 23 22.5
2017-06-21 14 88.9
2017-06-17 2 34.8
2017-06-12 87 250
2017-06-05 48 135.2
2017-06-05 12 95.7
2017-06-01 44 120
Results:
created_at| customer_id | amount
2017-06-30 1 220.5
2017-06-28 15 34.8
2017-06-28 12 157
2017-06-28 48 105.6
2017-06-27 332 425.8
2017-06-25 1 58.0
2017-06-25 23 22.5
2017-06-21 14 88.9
2017-06-17 2 34.8
2017-06-12 87 250
2017-06-05 48 135.2
2017-06-05 12 95.7
2017-06-01 44 120
2017-05-30 XX YYY
2017-05-25 XX YYY
2017-05-15 XX YYY
2017-04-30 XX YYY
2017-03-02 XX YYY
2016-11-02 XX YYY
The actual results give me the sum for all dates in the database, so no date time-frame is being applied in the query for a reason I cannot understand. I'm seeing dates that are both not for June 2017 and also from previous years.
Use date_trunc(..) function:
SELECT pt.created_at::date, pt.customer_id, c.name,
sum(pt.amount/100::double precision) filter (where date_trunc('month', pt.created_at) = date_trunc('month', NOW() - interval '1 month'))
from
product_transactions pt
LEFT JOIN customers c
ON c.id= pt.customer_id
GROUP BY pt.created_at::date

How to get last 3 Months of "Monday to Sunday" dates In Redshift?

How can I get last 3 Months of "Monday to Sunday" dates in Redshift?
S.no Start_dt End_dt week
1 18-Jul-16 24-Jul-16 Week1
2 25-Jul-16 31-Jul-16 Week2
3 1-Aug-16 7-Aug-16 Week3
4 8-Aug-16 14-Aug-16 Week4
5 15-Aug-16 21-Aug-16 Week5
6 22-Aug-16 28-Aug-16 Week6
7 29-Aug-16 4-Sep-16 Week7
8 5-Sep-16 11-Sep-16 Week8
9 12-Sep-16 18-Sep-16 Week9
10 19-Sep-16 25-Sep-16 Week10
11 26-Sep-16 2-Oct-16 Week11
12 3-Oct-16 9-Oct-16 Week12
13 10-Oct-16 16-Oct-16 Week13
I've tried this:
select
trunc(date_trunc('week',sysdate)) st_dt,
trunc(date_trunc('week', sysdate)+6) ed_dt,
'week'||row_number() over (order by null) as week
but it only returns the current week's Monday and Sunday.
You can use generate_series() to generate a range of dates:
SELECT
trunc(day) as start_date,
trunc(day + 6) as end_date
FROM
(select date_trunc('week', sysdate) + (generate_series(1, 12) * interval '1 week') as day)
ORDER BY 1 ASC
This results in:
week start week end
2016-10-24 2016-10-30
2016-10-31 2016-11-06
2016-11-07 2016-11-13
2016-11-14 2016-11-20
2016-11-21 2016-11-27
2016-11-28 2016-12-04
2016-12-05 2016-12-11
2016-12-12 2016-12-18
2016-12-19 2016-12-25
2016-12-26 2017-01-01
2017-01-02 2017-01-08
2017-01-09 2017-01-15
Please note that generate_series() in Amazon Redshift cannot be joined with existing tables. It can only be used as a "Leader-only" query.