SQL Select all items from all clients with its last price - sql

I have a table with all purchases made. with these columns:
clientnumber,
articlenumber,
datepurchased,
price,
qty
Sample data: (I have got more that 1000 clients and more than 50 products)
client1 - article1 - price 100 - qty 2 - date xx-xx-xxxx
client1 - article1 - price 111 - qty 5 - date xx-xx-xxxx
client1 - article2 - price 1 - qty 5 - date xx-xx-xxxx
client2 - article1 - price 114 - qty 5 - date xx-xx-xxxx
client2 - article1 - price 500 - qty 6 - date xx-xx-xxxx
etc..
i want get a list back that gives me all articles from each client purchased with its last price for each article and each client like this
Client 1, Artikel 1, 50 USD (this price should be the newest datepurchased)
client 1, articel 5, 30 usd
clients 2, articel 1, 30 usd
client 2, articel 2, 20 usd
...

You want to rank your records per client and item and show only the best ranked row (here: the latest purchase). Use ROW_NUMBER to do that.
select clientnumber, articlenumber, price
from
(
select
clientnumber, articlenumber, price,
row_number() over (partition by clientnumber, articlenumber
order by datepurchased desc) as rn
from purchases
) ranked
where rn = 1;

SELECT clientnumber,
articlenumber,
datepurchased,
(SELECT (yourtable.price / yourtable.qty) as unitPrice FROM yourtable yt WHERE yt.articlenumber = yourtable.articlenumber ORDER BY datepurchased DESC LIMIT 1) as latestPrice ,
qty
FROM yourtable
ORDER BY datepurchased DESC

SELECT TOP 1 articlenumber,
clientnumber,
datepurchased,
(yourtable.price / yourtable.qty) as pricePerArticle ,
qty
FROM yourtable
ORDER BY datepurchased DESC

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

Get value based on date

I am trying to get the price point of a product based on future pricing. I can't really do it by Max (Expiration Date) since the price points are different. And I also can't do a Max(Price) since the high price might be the one expiring or it could be the new one. My data would look like this:
Supplier
Product
Price
Effective Date
Expiration Date
Supplier 1
A
800
04-01-2121
12-31-2023
Supplier 1
A
1000
01-01-2121
03-31-2023
Supplier 1
B
500
04-01-2121
12-31-2023
Supplier 1
B
400
01-01-2121
03-31-2023
Supplier 2
D
200
01-01-2121
12-31-2023
Supplier 2
C
600
01-01-2121
12-31-2023
The result I am trying to get is below:
Supplier
Product
Price
Effective Date
Expiration Date
Supplier 1
A
800
04-01-2121
12-31-2023
Supplier 1
B
500
04-01-2121
12-31-2023
Supplier 2
D
200
01-01-2121
12-31-2023
Supplier 2
C
600
01-01-2121
12-31-2023
Any ideas?
To have the product information when expirationdate is the greatest we can product wise group the rows in descending order of expirationdate and find the row with lowest serial number for each product or we can simply use subquery to select the information where expirationdate=max(expirationdate) within the product group.
First approach will be more efficient. But if your dbms doesn't support row_number() then you can use second approach.
Schema:
create table mydata(Supplier varchar(30),Product varchar(30), Price int,EffectiveDate date, ExpirationDate date);
insert into mydata values('Supplier 1', 'A', 800 ,'04-01-2121', '12-31-2023');
insert into mydata values('Supplier 1', 'A', 1000 ,'01-01-2121', '03-31-2023');
insert into mydata values('Supplier 1', 'B', 500 ,'04-01-2121', '12-31-2023');
insert into mydata values('Supplier 1', 'B', 400 ,'01-01-2121', '03-31-2023');
insert into mydata values('Supplier 2', 'D', 200 ,'01-01-2121', '12-31-2023');
insert into mydata values('Supplier 2', 'C', 600 ,'01-01-2121', '12-31-2023');
Query#1
WITH cte
AS (SELECT supplier,
product,
price,
effectivedate,
expirationdate,
ROW_NUMBER ()
OVER (PARTITION BY product
ORDER BY expirationdate DESC)
rn
FROM mydata)
SELECT supplier,
product,
price,
effectivedate,
expirationdate
FROM cte
WHERE rn = 1
Output:
supplier
product
price
effectivedate
expirationdate
Supplier 1
A
800
2121-04-01
2023-12-31
Supplier 1
B
500
2121-04-01
2023-12-31
Supplier 2
C
600
2121-01-01
2023-12-31
Supplier 2
D
200
2121-01-01
2023-12-31
Query#2 for older version of DBMS:
SELECT supplier,
product,
price,
effectivedate,
expirationdate
FROM mydata m
where effectivedate =
(select max(effectivedate) from mydata md where m.product=md.product)
Output:
supplier
product
price
effectivedate
expirationdate
Supplier 2
D
200
2121-01-01
2023-12-31
Supplier 2
C
600
2121-01-01
2023-12-31
Supplier 1
B
500
2121-04-01
2023-12-31
Supplier 1
A
800
2121-04-01
2023-12-31
db<>fiddle here
It looks you can accomplish what you need with a simple row_number by numbering the rows decending ordered against your ExpirationDate and filtering for the first row in each group.
select Supplier, Product, Price, EffectiveDate, ExpirationDate from (
select Supplier, Product, Price, EffectiveDate, ExpirationDate, row_number() over (partition by Product order by ExpirationDate desc)Seq
from table
)t
where Seq=1

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

Compare between values from the same table in postgresql

I have the following table:
id partid orderdate qty price
1 10 01/01/2017 10 3
2 10 02/01/2017 5 9
3 11 01/01/2017 0.5 0.001
4 145 02/01/2017 5 18
5 10 12/12/2016 8 7
6 10 05/07/2010 81 7.5
Basically I want to compare the most recent purchasing of parts to the other purchasing of the same part in a period of 24 months. For that matter compare id=2 to id = 1,5.
I want to check if the price of the latest orderdate (per part) is larger than the average price of that part in the last 24 months.
So first I need to calculate the avg price:
partid avgprice
10 (3+9+7)/3=6.33 (7.5 is out of range)
11 0.001
145 18
I also need to know the latest orderdate of each part:
id partid
2 10
3 11
4 145
and then I need to check if id=2, id=3, id=6 (latest purchases) are bigger than the average. If they are I need to return their partid.
So I should have something like this:
id partid avgprice lastprice
2 10 6.33 9
3 11 0.001 0.001
4 145 18 18
Finally I need to return partid=10 since 9>6.33
Now to my questions...
I'm not sure how I can find the latest order in PostgreSQL.
I tried:
select id, distinct partid,orderdate
from table
where orderdate> current_date - interval '24 months'
order by orderdate desc
This gives :
ERROR: syntax error at or near "distinct".
I'm a bit of a lost here. I know what I want to do but I cant translate it to SQL. Any one can help?
Get the avarage per part and the last order per price and join these:
select
lastorder.id,
lastorder.partid,
lastorder.orderdate,
lastorder.price as lastprice,
avgorder.price as avgprice
from
(
select
partid,
avg(price) as price
from mytable
where orderdate >= current_date - interval '24 months'
group by partid
) avgorder
join
(
select distinct on (partid)
id,
partid,
orderdate,
price
from mytable
order by partid, orderdate desc
) lastorder on lastorder.partid = avgorder.partid
and lastorder.price > avgorder.price;
This can be solved without distinct (which is heavy on the DB anyways):
with avg_price as (
select partid, avg(price) as price
from table
where orderdate> current_date - interval '24 months'
group by partid
)
select f.id, f.partid, av.price, f.price
from (
select id, partid, orderdate, price, rank() over (partition by partid order by orderdate desc)
from table
) as f
join avg_price av on f.partid = av.partid
where f.rank = 1
and av.price < f.price

SQL - Same Table join to calculate profit from last entry

I have a table of transactions for various products. I want to calculate the profit made on each
Product Date Profit Incremental Profit
--------------------- --------------------------- -----------
Apple 2016-05-21 100
Banana 2016-05-21 60
Apple 2016-06-15 30
Apple 2016-08-20 10
Banana 2016-08-20 5
Can I create a SQL query that can group based on product and give me incremental profit on every date for each product. For example on 21-05-2015 since it is first date so incremental profit will be 0. But on 15-06-2016 it will be -70 (30-100).
The expected output is:
Product Date Profit Incremental Profit
--------------------- --------------------------- -----------
Apple 2016-05-21 100 0
Banana 2016-05-21 60 0
Apple 2016-06-15 30 -70
Apple 2016-08-20 10 -20
Banana 2016-08-20 5 -55
maybe u can use this.
select
a.product
,a.date
,a.profit
,isnull(a.profit - (select top 1 x.profit from profit x where x.product = a.product and x.date < a.date),0) as profit
from PROFIT a
order by product, date
Try this
DECLARE #Tbl TABLE (Product NVARCHAR(50), Date_ DATETIME, Profit INT)
INSERT INTO #Tbl
VALUES
('Apple' , '2016-05-21', 100),
('Banana', '2016-05-21', 60 ),
('Apple', '2016-06-15', 30 ),
('Apple', '2016-08-20', 10 ),
('Banana', '2016-08-20', 5 )
;WITH CTE
AS
(
SELECT
*,
ROW_NUMBER() OVER (PARTITION BY Product ORDER BY Date_) RowId
FROM #Tbl
)
SELECT
CurrentRow.Product ,
CurrentRow.Date_ ,
CurrentRow.Profit ,
CurrentRow.Profit - ISNULL(PrevRow.Profit, CurrentRow.Profit) 'Incremental Profit'
FROM
CTE CurrentRow LEFT JOIN
(SELECT CTE.Product ,CTE.Profit, CTE.RowId + 1 RowId FROM CTE) PrevRow ON CurrentRow.Product = PrevRow.product AND
CurrentRow.RowId = PrevRow.RowId
ORDER BY CurrentRow.Date_
Result:
Product Date_ Profit Incremental Profit
Apple 2016-05-21 100 0
Banana 2016-05-21 60 0
Apple 2016-06-15 30 -70
Apple 2016-08-20 10 -20
Banana 2016-08-20 5 -55
Edit:
UPDATE #Tbl
SET [Incremental Profit] = A.[Incremental Profit]
FROM
(
SELECT
CurrentRow.Product ,
CurrentRow.Date_ ,
CurrentRow.Profit ,
CurrentRow.Profit - ISNULL(PrevRow.Profit, CurrentRow.Profit) 'Incremental Profit'
FROM
(SELECT *, ROW_NUMBER() OVER (PARTITION BY Product ORDER BY Date_) RowId FROM #Tbl) CurrentRow LEFT JOIN
(SELECT *, ROW_NUMBER() OVER (PARTITION BY Product ORDER BY Date_) + 1 RowId FROM #Tbl) PrevRow ON CurrentRow.Product = PrevRow.Product AND
CurrentRow.RowId = PrevRow.RowId
) A
WHERE
[#Tbl].Product = A.Product AND
[#Tbl].Date_ = A.Date_