Grouping PostgreSQL Running Total Results - sql

I am trying to get a running total for 2 stores. The query that I have gets the running total for both stores together. How can I change the query to get the running total for each store separately? Below is my query. The database can be found here.
select store_id,amount, sum(amount) over (order by rental_date)
from payment
join rental on payment.rental_id=rental.rental_id
join store on payment.customer_id=store.store_id
This is the result that I get:
store id amont sum
1 5.99 5.99
1 0.99 6.98
1 9.99 16.97
1 4.99 21.96
2 2.99 24.95
1 4.99 29.94
1 0.99 30.93
1 3.99 34.92

Use partition by store_id in
sum(amount) over (partition by store_id order by store_id, rental_date)

if you want them in different columns you do it like this
select
store_id,
amount,
sum(case when store_id = 1 then amount else 0 end) over (order by rental_date) as store1_sum,
sum(case when store_id = 2 then amount else 0 end) over (order by rental_date) as store2_sum,
from payment
join rental on payment.rental_id=rental.rental_id
join store on payment.customer_id=store.store_id
for different rows you can partition as #BarbarosĂ–zhan

Related

Calculating average time between customer orders and average order value in Postgres

In PostgreSQL I have an orders table that represents orders made by customers of a store:
SELECT * FROM orders
order_id
customer_id
value
created_at
1
1
188.01
2020-11-24
2
2
25.74
2022-10-13
3
1
159.64
2022-09-23
4
1
201.41
2022-04-01
5
3
357.80
2022-09-05
6
2
386.72
2022-02-16
7
1
200.00
2022-01-16
8
1
19.99
2020-02-20
For a specified time range (e.g. 2022-01-01 to 2022-12-31), I need to find the following:
Average 1st order value
Average 2nd order value
Average 3rd order value
Average 4th order value
E.g. the 1st purchases for each customer are:
for customer_id 1, order_id 8 is their first purchase
customer 2, order 6
customer 3, order 5
So, the 1st-purchase average order value is (19.99 + 386.72 + 357.80) / 3 = $254.84
This needs to be found for the 2nd, 3rd and 4th purchases also.
I also need to find the average time between purchases:
order 1 to order 2
order 2 to order 3
order 3 to order 4
The final result would ideally look something like this:
order_number
AOV
av_days_since_last_order
1
254.84
0
2
300.00
28
3
322.22
21
4
350.00
20
Note that average days since last order for order 1 would always be 0 as it's the 1st purchase.
Thanks.
select order_number
,round(avg(value),2) as AOV
,coalesce(round(avg(days_between_orders),0),0) as av_days_since_last_order
from
(
select *
,row_number() over(partition by customer_id order by created_at) as order_number
,created_at - lag(created_at) over(partition by customer_id order by created_at) as days_between_orders
from t
) t
where created_at between '2022-01-01' and '2022-12-31'
group by order_number
order by order_number
order_number
aov
av_days_since_last_order
1
372.26
0
2
25.74
239
3
200.00
418
4
201.41
75
5
159.64
175
Fiddle
Im suppose it should be something like this
WITH prep_data AS (
SELECT order_id,
cuntomer_id,
ROW_NUMBER() OVER(PARTITION BY order_id, cuntomer_id ORDER BY created_at) AS pushcase_num,
created_at,
value
FROM pushcases
WHERE created_at BETWEEN :date_from AND :date_to
), prep_data2 AS (
SELECT pd1.order_id,
pd1.cuntomer_id,
pd1.pushcase_num
pd2.created_at - pd1.created_at AS date_diff,
pd1.value
FROM prep_data pd1
LEFT JOIN prep_data pd2 ON (pd1.order_id = pd2.order_id AND pd1.cuntomer_id = pd2.cuntomer_id AND pd1.pushcase_num = pd2.pushcase_num+1)
)
SELECT order_id,
cuntomer_id,
pushcase_num,
avg(value) AS avg_val,
avg(date_diff) AS avg_date_diff
FROM prep_data2
GROUP BY pushcase_num

SQL Server price table weighted to buying price

I have 3 tables:
Buys
ID
Item
qty
price
1
1001
10
1.00
2
1001
10
2.00
3
1001
10
3.00
4
1002
10
2.00
5
1002
10
1.00
6
1003
10
1.00
7
1004
10
1.00
8
1004
10
2.00
Fallback
Item
price
1001
3.00
1002
3.00
1003
4.00
Stock
Item
stock
1001
15
1002
5
1003
25
1004
15
I have to calculate actually price each item. For that, I have to check the table "buys" each row from the biggest ID to smallest ID and take all prices as long as the stock is sufficient. If not enough buys in the table, I have to use the fallback prices for part of stock, I don't have price in first table.
So for item no. 1001, stock is 15. Price for 10 pcs found in ID 3 (3.00 USD); price for rest 5 pieces in row ID 2 (2.00 USD). So correct actually stockprice is 2.66 USD.
For item no. 1002, stock is 5. Price for latest buy is 1.00 USD in row ID 5 with quantity more than 5. So correct actually stockprice is 1.00 USD.
For item no. 1003, stock is 25. Only one entry in row ID 6 with 10 pcs for 1.00 USD each. so price for missing 15pcs have to take from fallback table 4.00 USD. So correct actually stockprice is 2.80 USD.
Result should be like this:
Item
stock
value
1001
15
2.66
1002
5
1.00
1003
25
2.80
But I have no idea how that works. Thank you very much for help.
Using conditional aggregation when comparring stock to buys runnig totals, finally apply the fallback
select t.item, (s + t.qf * f.price) s, stock, (s + t.qf * f.price) / stock price
from (
select s.Item, s.Stock,
sum(coalesce(case when b.qe <= Stock then b.qty else Stock - b.qs end * b.price, 0)) s,
-- qty for fallback
min(case when Stock > coalesce(b.qe,0) then Stock - coalesce(b.qe,0) else 0 end) qf
from Stock s
left join (
select Item, qty, price, ID,
sum(qty) over(partition by Item order by ID desc) - qty qs, -- starting runnig total
sum(qty) over(partition by Item order by ID desc) qe -- ending runnig total
from Buys
) b on s.Item = b.Item and s.Stock > b.qs
group by s.Item, s.Stock
) t
join Fallback f on f.Item = t.Item;
order by t.Item;
Provided a fallback can be missing for an item a minor tweak is requierd.
select t.item, (s + t.qf * coalesce(f.price, 0)) s, stock, (s + t.qf * coalesce(f.price, 0)) / stock price
from (
select s.Item, s.Stock,
sum(coalesce(case when b.qe <= Stock then b.qty else Stock - b.qs end * b.price, 0)) s,
-- qty for fallback
min(case when Stock > coalesce(b.qe,0) then Stock - coalesce(b.qe,0) else 0 end) qf
from Stock s
left join (
select Item, qty, price, ID,
sum(qty) over(partition by Item order by ID desc) - qty qs, -- starting runnig total
sum(qty) over(partition by Item order by ID desc) qe -- ending runnig total
from Buys
) b on s.Item = b.Item and s.Stock > b.qs
group by s.Item, s.Stock
) t
left join Fallback f on f.Item = t.Item
where t.qf = 0 or f.item is not null
order by t.Item;
The query will not return a row if a fallback is required but is missing. Otherwise the row is returned.
db<>fiddle
You need to create a running sum of the quantity in Buys and calculate the price based off that.
This is somewhat complicated by the fact that you may have too many, or not enough, rows in Buys to fulfil the stock.
SELECT
s.Item,
s.stock,
(
ISNULL(b.FoundStockPrice, 0)
+ CASE WHEN s.stock > ISNULL(b.FoundStock, 0)
THEN s.stock - ISNULL(b.FoundStock, 0)
ELSE 0 END * f.price
) / s.stock
FROM Stock s
JOIN Fallback f ON f.Item = s.Item
OUTER APPLY (
SELECT
FoundStock = SUM(b.qty),
FoundStockPrice = SUM(
CASE WHEN b.FullStock > b.RunningSum THEN b.qty
ELSE b.FullStock - (b.RunningSum - b.qty) END
* b.price)
FROM (
SELECT *,
RunningSum = SUM(b.qty) OVER (PARTITION BY b.Item
ORDER BY b.ID DESC ROWS UNBOUNDED PRECEDING),
FullStock = s.stock
FROM Buys b
WHERE b.Item = s.Item
) b
WHERE b.RunningSum - b.qty < s.stock
) b;
Steps are as follows:
For every Stock take all relevant Buys rows.
Calculate a running sum of qty, and then filter to only rows where the running sum includes the final stock (in other words it must up to the previous running sum).
Sum these Buys rows multiplied by their price, taking into account that we need to net off anything over the necessary stock. Take also a total sum of the quantity.
The final price is: the previous calculated total, plus any remaining unfound stock multiplied by the fallback.price, all divided by the total stock.
db<>fiddle

Running total with if clause or do while

Consider the following table with 3 columns.
Use this to create a SQL query to list the top products by revenue that make up 25% of the total revenue in 2020.
(i.e. If total revenue is 1000 then list of top products that account for <= 250)
Table ProductRevenue:
Date_DD ... date(YYYY-MM-DD)
Product_Name ... varchar(250)
Revenue ... decimal(10,2)
Sample data:
Date_DD Product_Name Revenue
-------------------------------------
2020-11-30 a 100
2020-10-02 b 100
2020-07-07 c 100
2020-04-04 d 100
2020-05-05 f 50
2020-06-06 g 120
2020-05-30 h 90
2020-11-13 k 120
2020-01-30 l 120
I used that code but don't know how to use where clause . Anyone can help?
SELECT
product_name, revenue,
SUM(revenue) OVER (ORDER BY revenue DESC, product_name) AS running _total
FROM
TABLE_PRODUCT_REVENUE
new code
select product_name, revenue, running_total from
(SELECT product_name, revenue, SUM(revenue) OVER ( ORDER BY revenue DESC, product_name) AS running_total
FROM TABLE_PRODUCT_REVENUE ) o
where running_total<(select max(running_total) from
(SELECT product_name, revenue, SUM(revenue) OVER ( ORDER BY revenue DESC, product_name) AS running_total
FROM TABLE_PRODUCT_REVENUE ) o )*0.25
group by product_name, revenue, running_total
order by running_total
I just need to know where can i add where clause where YEAR([Date_DD])=2000 anyone can help?
The question was not very descriptive; however, below might help you narrow down the issue.
Below will show up you the running total for the Product.
SELECT
product_name, revenue,
SUM(revenue) OVER (partition by product_name ORDER BY revenue DESC, product_name) AS running _total
FROM
TABLE-PRODUCT_REVENUE
below would give the result if the product total is more significant than x amount
select
*,case when running _total >=1000 then 'top selling product' else null end
(
SELECT
product_name, revenue,
SUM(revenue) OVER (partition by product_name ORDER BY revenue DESC, product_name) AS running _total
FROM
TABLE-PRODUCT_REVENUE
)t
where running_total >= xxx amount

How to do a Min and Max of date but following the changes in price points

I'm not really sure how to word this question better so I'll provide the data that I have and the result that I'm after.
This is the data that I have
sku sales qty date
A 100 1 1-Jan-19
A 200 2 2-Jan-19
A 100 1 3-Jan-19
A 240 2 4-Jan-19
A 360 3 5-Jan-19
A 360 4 6-Jan-19
A 200 2 7-Jan-19
A 90 1 8-Jan-19
B 100 1 9-Jan-19
B 200 2 10-Jan-19
And this is the result that I'm after
sku price sum(qty) sum(sales) min(date) max(date)
A 100 4 400 1-Jan-19 3-Jan-19
A 120 5 600 4-Jan-19 5-Jan-19
A 90 4 360 6-Jan-19 6-Jan-19
A 100 2 200 7-Jan-19 7-Jan-19
A 90 1 90 8-Jan-19 8-Jan-19
B 100 3 300 9-Jan-19 10-Jan-19
As you can see, I'm trying to get the min and max date of each price point, where price = sales/qty. At this point, I can get the min and max date of the same price but I can separate it when there's another price in between. I think I have to use some sort of min(date) over (partition by sales/qty order by date) but I can't figure it out yet.
I'm using Redshift SQL
This is a gaps-and-islands query. You can do this by generating a sequence and subtracting that from the date. Then aggregate:
select sku, price, sum(qty), sum(sales),
min(date), max(date)
from (select t.*,
row_number() over (partition by sku, price order by date) as seqnum
from t
) t
group by sku, price, (date - seqnum * interval '1 day')
order by sku, price, min(date);
You can do with Sub Query and LAG
FIDDLE DEMO
SELECT SKU, Price, SUM(Qty) SumQty, SUM(Sales) SumSales, MIN(date) MinDate, MAX(date) MaxDate
FROM (
SELECT SKU,Price,SUM(is_change) OVER(order by SKU, date) is_change,Sales, Qty,date
FROM (SELECT SKU, Sales/Qty AS Price, Sales, Qty,date,
CASE WHEN Sales/Qty = lag(Sales/Qty) over (order by SKU, date)
and SKU = lag(SKU) OVER (order by SKU, date) then 0 ELSE 1 END AS is_change
FROM Tbl
)InnerSelect
) X GROUP BY sku, price,is_change
ORDER BY SKU,MIN(date)
Output

How can I sum cost items, grouped by invoice?

I have two SQL Server tables below:
Invoice
InvoiceId Amount [Date]
1 10 2015-05-28 21:47:50.000
2 20 2015-05-28 21:47:50.000
3 25 2015-05-28 23:25:50.000
InvoiceItem
Id InvoiceId Cost
1 1 8
2 1 3
3 1 7
4 2 15
5 2 17
6 3 20
7 3 22
Now I want to JOIN these two tables ON InvoiceId and retrieve the following:
COUNT of DISTINCT InvoiceId from Invoice table AS [Count]
SUM of Amount from Invoice table AS Amount
SUM of Cost from InvoiceItem table AS Cost
HOUR part of [Date]
and GROUP them BY HOUR part of [Date].
Desired Output wil be:
[Count] Amount Cost HourOfDay
2 30 50 22
1 25 42 23
How can I do this?
one approach is to use a derived table:
SELECT CAST([Date] AS DATE) AS [Date],
DATEPART(HOUR,i.[Date]) AS HourOfDay,
COUNT(i.InvoiceId) AS NumberOfInvoices,
SUM(i.Amount) AS Amount,
SUM(it.Cost) AS Cost
FROM invoice i
INNER JOIN
(SELECT InvoiceId, SUM(Cost) AS Cost
FROM invoiceitem
GROUP BY InvoiceId) it ON i.InvoiceId = it.InvoiceId
GROUP BY [Date],DATEPART(HOUR,i.[Date])
or a CTE (Common Table Expression)
WITH InvoiceCosts (InvoiceId, Cost)
AS
(
SELECT InvoiceId, SUM(Cost) AS Cost
FROM invoiceitem
GROUP BY InvoiceId
)
SELECT CAST([Date] AS DATE) AS [Date],
DATEPART(HOUR,i.[Date]) AS HourOfDay,
COUNT(i.InvoiceId) AS NumberOfInvoices,
SUM(i.Amount) AS Amount,
SUM(ic.Cost) AS Cost
FROM invoice i
INNER JOIN
InvoiceCosts ic ON i.InvoiceId = ic.InvoiceId
GROUP BY [Date],DATEPART(HOUR,i.[Date])
SELECT COUNT (DISTINCT inv.InvoiceId) [Count],
SUM (Amount) Amount,
SUM (Cost) Cost,
datepart(HOUR, inv.[Date]) HourOfDay
FROM Invoice inv
INNER JOIN InvoiceItem itm
ON inv.InvoiceId = itm.InvoiceId
GROUP BY datepart(HOUR, inv.[Date]);