name total date
a 100 1/2/2015
b 30 1/2/2015
c 40 1/2/2015
d 45 1/2/2015
a 20 2/2/2015
b 13 2/2/2015
a 30 3/2/2015
b 23 3/2/2015
c 20 3/2/2015
and the table goes on with different dates,
I want to find difference(a-b) for each date and occurence .. i.e
diff total date
a-b 70 1/2/2015
a-b 7 2/2/2015....
how do I do it in postgresql
Use nth_value() window function for that:
WITH t(name,total,date) AS ( VALUES
('a',100,'2016-01-01'::DATE),
('b',30,'2016-01-01'::DATE),
('c',40,'2016-01-01'::DATE),
('d',45,'2016-01-01'::DATE),
('a',20,'2016-01-02'::DATE),
('b',13,'2016-01-02'::DATE)
)
SELECT
DISTINCT ON (date)
'a-b' AS diff,
(nth_value(total,1) OVER (PARTITION BY date) -
nth_value(total,2) OVER (PARTITION BY date)) total_diff,
date
FROM t
WHERE name IN ('a','b');
Result:
diff | total_diff | date
------+------------+------------
a-b | 70 | 2016-01-01
a-b | 7 | 2016-01-02
(2 rows)
Related
I would like to create a table that has a list of dates from a table that doesn't have dates.
So table A would be (without any dates):
product_name
1
2
3
4
5
6
7
8
9
10
table B would be:
dates
01/01/2022
02/01/2022
03/01/2022
04/01/2022
05/01/2022
...
etc to 31/12/2022
My final outcome table would be:
product_name dates
1 01/01/2022
1 02/01/2022
1 03/01/2022
1 04/01/2022
1 05/01/2022
1 05/01/2022
etc all the way to 31/12/2022
but also further down
product_name dates
2 01/01/2022
2 02/01/2022
etc..
Is there a way to do this simply? I have tried
select date_trunc('day', dd) date
from pg_catalog.generate_series
( '2022-01-01' :: timestamp
,'2022-12-31' :: timestamp
, '1 day' :: interval) dd
;
But I couldn't join as there are no dates in table A.
Suppose I have data formatted in the following way (FYI, total row count is over 30K):
customer_id order_date order_rank
A 2017-02-19 1
A 2017-02-24 2
A 2017-03-31 3
A 2017-07-03 4
A 2017-08-10 5
B 2016-04-24 1
B 2016-04-30 2
C 2016-07-18 1
C 2016-09-01 2
C 2016-09-13 3
I need a 4th column, let's call it days_since_last_order which, in the case where order_rank = 1 then 0 else calculate the number of days since the previous order (with rank n-1).
So, the above would return:
customer_id order_date order_rank days_since_last_order
A 2017-02-19 1 0
A 2017-02-24 2 5
A 2017-03-31 3 35
A 2017-07-03 4 94
A 2017-08-10 5 38
B 2016-04-24 1 0
B 2016-04-30 2 6
C 2016-07-18 1 79
C 2016-09-01 2 45
C 2016-09-13 3 12
Is there an easier way to calculate the above with a window function (or similar) rather than join the entire dataset against itself (eg. on A.order_rank = B.order_rank - 1) and doing the calc?
Thanks!
use the lag window function
SELECT
customer_id
, order_date
, order_rank
, COALESCE(
DATE(order_date)
- DATE(LAG(order_date) OVER (PARTITION BY customer_id ORDER BY order_date))
, 0)
FROM <table_name>
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
I have a query that count the total number of users registered in our service per day, per hour. My problem is that i want to appear the hour in range style. You can see below:
Query:
SELECT
case
when extract(dow from us.created::timestamp) = 0 then 'Sunday'
when extract(dow from us.created::timestamp) = 1 then 'Monday'
when extract(dow from us.created::timestamp) = 2 then 'Tuesday'
when extract(dow from us.created::timestamp) = 3 then 'Wednesday'
when extract(dow from us.created::timestamp) = 4 then 'Thursday'
when extract(dow from us.created::timestamp) = 5 then 'Friday'
when extract(dow from us.created::timestamp) = 6 then 'Saturday'
end as wday,
extract(hour from us.created::timestamp) as whour,
count(us.id)
FROM users us
GROUP BY wday,whour order by wday, whour
Query Result:
wday whour count
Friday 0 364
Friday 1 156
Friday 2 79
Friday 3 39
Friday 4 55
Friday 5 32 ....
I want to count and appear the results in this format:
wday whour count
Friday 0-1 364
Friday 1-2 156
Friday 2-3 79
Friday 3-4 39
Friday 4-5 55
Friday 5-6 32 ....
How can i do this?
try smth like :
WITH a as (
SELECT
to_char(us.created::timestamp,'Day') as wday,
extract(hour from us.created::timestamp) as whour,
count(us.id)
FROM users us
GROUP BY wday,whour
)
select
wday
, coalesce(whour,'-',lead(whour) over (partition by wday order by whour)) whour
, count
from a
order by wday, whour
here's the logic sample:
t=# with v(i,e) as (
values
('Friday',2),('Friday',3),('Saturday',4),('Saturday',5),('Saturday',6),('Friday',4),('Friday',5),('Friday',0),('Friday',1)
)
select i,e,concat(e,'-',lead(e) over (partition by i order by e))
from v;
i | e | concat
----------+---+--------
Friday | 0 | 0-1
Friday | 1 | 1-2
Friday | 2 | 2-3
Friday | 3 | 3-4
Friday | 4 | 4-5
Friday | 5 | 5-
Saturday | 4 | 4-5
Saturday | 5 | 5-6
Saturday | 6 | 6-
(9 rows)
I have a table which captures appointments, some are single day appointments and some are multi day appointments, so the data looks like
AppointmentId StartDate EndDate
9 2017-04-12 2017-04-12
10 2017-05-01 2017-05-03
11 2017-06-01 2017-06-01
I want to split the multi day appointment as single days, so the result I am trying to achieve is like
AppointmentId StartDate EndDate
9 2017-04-12 2017-04-12
10 2017-05-01 2017-05-01
10 2017-05-02 2017-05-02
10 2017-05-03 2017-05-03
11 2017-06-01 2017-06-01
So I have split the appointment id 10 into multiple rows. I checked a few other questions like
here but those are to split just based on a single start date and end date and not based on table data
You can use a Calendar or dates table for this sort of thing.
For only 152kb in memory, you can have 30 years of dates in a table with this:
/* dates table */
declare #fromdate date = '20000101';
declare #years int = 30;
/* 30 years, 19 used data pages ~152kb in memory, ~264kb on disk */
;with n as (select n from (values(0),(1),(2),(3),(4),(5),(6),(7),(8),(9)) t(n))
select top (datediff(day, #fromdate,dateadd(year,#years,#fromdate)))
[Date]=convert(date,dateadd(day,row_number() over(order by (select 1))-1,#fromdate))
into dbo.Dates
from n as deka cross join n as hecto cross join n as kilo
cross join n as tenK cross join n as hundredK
order by [Date];
create unique clustered index ix_dbo_Dates_date
on dbo.Dates([Date]);
Without taking the actual step of creating a table, you can use it inside a common table expression with just this:
declare #fromdate date = '20161229';
declare #thrudate date = '20170103';
;with n as (select n from (values(0),(1),(2),(3),(4),(5),(6),(7),(8),(9)) t(n))
, dates as (
select top (datediff(day, #fromdate, #thrudate)+1)
[Date]=convert(date,dateadd(day,row_number() over(order by (select 1))-1,#fromdate))
from n as deka cross join n as hecto cross join n as kilo
cross join n as tenK cross join n as hundredK
order by [Date]
)
select [Date]
from dates;
Use either like so:
select
t.AppointmentId
, StartDate = d.date
, EndDate = d.date
from dates d
inner join appointments t
on d.date >= t.StartDate
and d.date <= t.EndDate
rextester demo: http://rextester.com/TNWQ64342
returns:
+---------------+------------+------------+
| AppointmentId | StartDate | EndDate |
+---------------+------------+------------+
| 9 | 2017-04-12 | 2017-04-12 |
| 10 | 2017-05-01 | 2017-05-01 |
| 10 | 2017-05-02 | 2017-05-02 |
| 10 | 2017-05-03 | 2017-05-03 |
| 11 | 2017-06-01 | 2017-06-01 |
+---------------+------------+------------+
Number and Calendar table reference:
Generate a set or sequence without loops - 1 - Aaron Bertrand
Generate a set or sequence without loops - 2 - Aaron Bertrand
Generate a set or sequence without loops - 3 - Aaron Bertrand
The "Numbers" or "Tally" Table: What it is and how it replaces a loop - Jeff Moden
Creating a Date Table/Dimension in sql Server 2008 - David Stein
Calendar Tables - Why You Need One - David Stein
Creating a date dimension or calendar table in sql Server - Aaron Bertrand
tsql Function to Determine Holidays in sql Server - Aaron Bertrand
F_table_date - Michael Valentine Jones
Clearly a Calendar/Tally table would be the way to go as SqlZim illustrated (+1), however you can use an ad-hoc tally table with a CROSS APPLY.
Example
Select A.AppointmentId
,StartDate = B.D
,EndDate = B.D
From YourTable A
Cross Apply (
Select Top (DateDiff(DD,A.StartDate,A.EndDate)+1) D=DateAdd(DD,-1+Row_Number() Over (Order By Number),A.StartDate)
From master..spt_values
) B
Returns
AppointmentId StartDate EndDate
9 2017-04-12 2017-04-12
10 2017-05-01 2017-05-01
10 2017-05-02 2017-05-02
10 2017-05-03 2017-05-03
11 2017-06-01 2017-06-01